diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..6285e5a --- /dev/null +++ b/AUTHORS @@ -0,0 +1,114 @@ +-------------------------------- +|Iter Vehemens ad Necem credits| +-------------------------------- + + +--------- +|IvanDev| +--------- + +Master Programmer +(implemented most of the bugs) +------------------------------ + +Timo Kiviluoto + + +Apprentice Programmer, PR Guy, Porter +(made some of the bugs, but they're +all really features, or OS's fault) +------------------------------------- + +Heikki Sairanen + + +Head Graphics Designer +(drew all the heads) +---------------------- + +Tuukka Virtaperko + + +------------------------------ +|Other people who have helped| +------------------------------ + +Additional coders +----------------- + +In order of importance + +Ilari Kaartinen +Mark Schreiber +Miles Bader +Perttu Luukko +Niko Kosonen + +Additional graphics +------------------- + +In order of importance + +Vesa Peltonen +Corey Martin + +Additional level design +----------------------- + +Corey Martin + +Authors of the RNG we use +------------------------- + +Takuji Nishimura +Makoto Matsumoto + +Author of the font we use +------------------------- + +Shawn Hargreaves's Allegro, a game programming library + +Idea providers and bug hunters +------------------------------- + +In alphabetical order + +Atte Aholainen +Chris Allcock +Brian Angeletti +Laurent Birtz +Christian Harms +Ilari Kaartinen +Wojciech Kaczmarek +Henri Kiviluoto +Thomas Klausner +Niko Kosonen +Perttu Luukko +Corey Martin +Janne Miettinen +Kari Pahula +Vesa Peltonen +Will Riley +Renne Sairanen +Norvell Spearman +Konstantin Stupnik + + +---------- +|Variants| +---------- + + +Instigator of LIVAN +------------------------------ + +Lampshade + + +Continuer of LIVAN +(and unworthy servant of the +cause) +------------------------------ + +Ryan van Herel + diff --git a/About CLIVAN.txt b/About CLIVAN.txt new file mode 100644 index 0000000..8e9fb44 --- /dev/null +++ b/About CLIVAN.txt @@ -0,0 +1,17 @@ +Continuation (in the vein) of Lampshade's Iter Vehemens Ad Necem (CLIVAN) + +http://sourceforge.net/projects/clivan/ + +CLIVAN by Warheck also of www.attnam.com + +It's like LIVAN but with yet more stuff. + +Based on LIVAN by Lampshade of www.attnam.com + +IVAN by a lot of people of http://ivan.sourceforge.net/ + +Contact Lampshade: lampshayde at gmail dot com + +Contact Ryan at rvanherel at users dot sourceforge dot net + +OKAY BAI \ No newline at end of file diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, 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 Library 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 + + Appendix: 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) 19yy + + 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., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy 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 Library General +Public License instead of this License. diff --git a/CVS/Entries b/CVS/Entries new file mode 100644 index 0000000..f437df5 --- /dev/null +++ b/CVS/Entries @@ -0,0 +1,30 @@ +/.customs.emacs/1.4/Fri Apr 1 22:14:18 2005// +/AUTHORS/1.30/Fri Sep 22 10:08:09 2006// +/COPYING/1.5/Mon Dec 9 18:04:15 2002// +/ChangeLog/1.562/Sun Sep 3 18:02:55 2006// +/FeLib.dsp/1.27/Fri Sep 17 21:21:34 2004/-kb/ +/IGOR.dsp/1.1/Mon Feb 10 17:52:26 2003// +/IGOR.dsw/1.1/Mon Feb 10 17:52:27 2003// +/INSTALL/1.4/Sun May 11 16:52:47 2003// +/IVAN.dsw/1.8/Sun Apr 21 18:33:41 2002/-kb/ +/LICENSING/1.4/Thu Aug 5 17:36:41 2004// +/MIHAIL.dsp/1.1/Fri Dec 10 20:11:20 2004// +/MIHAIL.dsw/1.1/Fri Dec 10 20:11:20 2004// +/Main.dsp/1.78/Sun Jul 24 15:05:14 2005/-kb/ +/Makefile.am/1.13/Tue Dec 14 01:12:56 2004// +/NEWS/1.20/Thu Mar 24 10:52:28 2005// +/README/1.35/Fri Dec 10 19:30:54 2004// +/acinclude.m4/1.1/Fri Jan 11 20:09:06 2002// +/aclocal.m4/1.8/Fri Jun 11 15:29:40 2004// +/config.guess/1.1/Thu Mar 7 18:46:09 2002// +/config.sub/1.1/Thu Mar 7 18:46:09 2002// +/configure.in/1.25/Thu Aug 5 17:36:41 2004// +/depcomp/1.1/Thu Mar 7 18:46:09 2002// +/igordj.mak/1.6/Thu Aug 5 17:36:41 2004// +/install-sh/1.1/Fri Jan 11 20:13:16 2002// +/ivandj.mak/1.48/Fri Dec 10 20:11:20 2004// +/ivanmgw.mak/1.34/Fri Dec 10 20:11:20 2004// +/mihaildj.mak/1.1/Fri Dec 10 20:11:20 2004// +/missing/1.4/Wed Dec 18 17:14:34 2002// +/mkinstalldirs/1.2/Mon Jan 14 17:52:02 2002// +D diff --git a/CVS/Entries.Log b/CVS/Entries.Log new file mode 100644 index 0000000..4ad8846 --- /dev/null +++ b/CVS/Entries.Log @@ -0,0 +1,19 @@ +A D/Doc//// +A D/FEEL//// +A D/FELL//// +A D/FeDX//// +A D/FeFile//// +A D/FeIO//// +A D/FeLib//// +A D/FeMath//// +A D/FeWin//// +A D/Graphics//// +A D/In-House//// +A D/InHouse//// +A D/Include//// +A D/LibTest//// +A D/Main//// +A D/Save//// +A D/Script//// +A D/igor//// +A D/mihail//// diff --git a/CVS/Repository b/CVS/Repository new file mode 100644 index 0000000..043d359 --- /dev/null +++ b/CVS/Repository @@ -0,0 +1 @@ +ivan diff --git a/CVS/Root b/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/Continuation of LIVAN.ncb b/Continuation of LIVAN.ncb new file mode 100644 index 0000000..b3709f0 Binary files /dev/null and b/Continuation of LIVAN.ncb differ diff --git a/Continuation of LIVAN.sln b/Continuation of LIVAN.sln new file mode 100644 index 0000000..398e1fc --- /dev/null +++ b/Continuation of LIVAN.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Continuation of LIVAN", "Continuation of LIVAN.vcproj", "{98B6D044-A894-4D8F-A6FB-9A3E8AA65DB8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {98B6D044-A894-4D8F-A6FB-9A3E8AA65DB8}.Debug|Win32.ActiveCfg = Debug|Win32 + {98B6D044-A894-4D8F-A6FB-9A3E8AA65DB8}.Debug|Win32.Build.0 = Debug|Win32 + {98B6D044-A894-4D8F-A6FB-9A3E8AA65DB8}.Release|Win32.ActiveCfg = Release|Win32 + {98B6D044-A894-4D8F-A6FB-9A3E8AA65DB8}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Continuation of LIVAN.suo b/Continuation of LIVAN.suo new file mode 100644 index 0000000..fdf99d9 Binary files /dev/null and b/Continuation of LIVAN.suo differ diff --git a/Continuation of LIVAN.vcproj b/Continuation of LIVAN.vcproj new file mode 100644 index 0000000..d884fee --- /dev/null +++ b/Continuation of LIVAN.vcproj @@ -0,0 +1,820 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Continuation of LIVAN.vcproj.OWNER-3F12AECFA.Owner.user b/Continuation of LIVAN.vcproj.OWNER-3F12AECFA.Owner.user new file mode 100644 index 0000000..ca99d5b --- /dev/null +++ b/Continuation of LIVAN.vcproj.OWNER-3F12AECFA.Owner.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/Continuation of LIVAN.vcproj.ryan-laptop.ryan.user b/Continuation of LIVAN.vcproj.ryan-laptop.ryan.user new file mode 100644 index 0000000..c365615 --- /dev/null +++ b/Continuation of LIVAN.vcproj.ryan-laptop.ryan.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/Continuation of LIVAN.vcproj.user b/Continuation of LIVAN.vcproj.user new file mode 100644 index 0000000..0ce2ced --- /dev/null +++ b/Continuation of LIVAN.vcproj.user @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Doc/CVS/Entries b/Doc/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/Doc/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/Doc/CVS/Entries.Log b/Doc/CVS/Entries.Log new file mode 100644 index 0000000..3c1049b --- /dev/null +++ b/Doc/CVS/Entries.Log @@ -0,0 +1,8 @@ +A D/Data//// +A D/Document//// +A D/Manual//// +A D/Obsolete//// +A D/Source//// +A D/Työt//// +A D/Visual//// +A D/Work//// diff --git a/Doc/CVS/Repository b/Doc/CVS/Repository new file mode 100644 index 0000000..4f2690a --- /dev/null +++ b/Doc/CVS/Repository @@ -0,0 +1 @@ +ivan/Doc diff --git a/Doc/CVS/Root b/Doc/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/Doc/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/Doc/Data/Attributes.txt b/Doc/Data/Attributes.txt new file mode 100644 index 0000000..bcbe99c --- /dev/null +++ b/Doc/Data/Attributes.txt @@ -0,0 +1,31 @@ +Iter Vehemens ad Necem attributes: + +Arm strength - arms +-melee and weapon damage +-melee and weapon to hit value, slightly +Leg strength - legs +-kicking damage +-kick to hit value, sligthly +-carrying capacity +Endurance +-hp +-healing rate +Dexterity - arms +-melee and weapon to hit value +Agility - legs +-kick to hit value +-dodge value +Perception +-to hit value +-dodge value +Charisma +-shop prices +-pet handling (chance to charm a monster etc.) +Intelligence +-ability to cast wizard spells +Wisdom +-ability to cast priest spells +-praying +Mana +-spell efficiency +-mana points diff --git a/Doc/Data/BodyColor.txt b/Doc/Data/BodyColor.txt new file mode 100644 index 0000000..0c539b9 --- /dev/null +++ b/Doc/Data/BodyColor.txt @@ -0,0 +1,35 @@ +Heads +1. skin +2. main color of cap, helmet etc. +3. all hair (including fur, beard etc.) +4. special color + +Torso +1. skin +2. main color +3. belt +4. special color + +Arms +1. skin +2. main color +3. reserved +4. special color + +Legs +1. skin +2. main color +3. boots +4. special color + +Shield +1. main color +2. reserved +3. reserved +4. special color + +Weapon +1. edge +2. handle +3. reserved +4. special color diff --git a/Doc/Data/CVS/Entries b/Doc/Data/CVS/Entries new file mode 100644 index 0000000..87e26fb --- /dev/null +++ b/Doc/Data/CVS/Entries @@ -0,0 +1,13 @@ +/Attributes.txt/1.3/Thu Apr 3 15:23:35 2003// +/BodyColor.txt/1.2/Thu Apr 3 15:23:36 2003// +/Dialog.txt/1.21/Tue May 10 20:32:40 2005// +/Founders.jpg/1.1/Fri Oct 8 12:33:13 2004/-kb/ +/Graphics.txt/1.2/Mon Feb 10 17:52:43 2003// +/IvanProblem.txt/1.1/Fri Apr 25 23:28:56 2003// +/Levels.txt/1.1/Wed Aug 16 10:15:55 2006// +/Lines.txt/1.14/Sun Jan 30 12:07:39 2005// +/Units.txt/1.2/Sat Nov 23 17:01:31 2002// +/Verbalization.txt/1.1/Tue Dec 3 18:28:19 2002// +/WSkills.txt/1.12/Thu Apr 3 15:23:38 2003// +/cellar/1.1/Sun Jul 24 20:13:32 2005/-kb/ +D diff --git a/Doc/Data/CVS/Repository b/Doc/Data/CVS/Repository new file mode 100644 index 0000000..69ee50f --- /dev/null +++ b/Doc/Data/CVS/Repository @@ -0,0 +1 @@ +ivan/Doc/Data diff --git a/Doc/Data/CVS/Root b/Doc/Data/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/Doc/Data/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/Doc/Data/Dialog.txt b/Doc/Data/Dialog.txt new file mode 100644 index 0000000..6235a15 --- /dev/null +++ b/Doc/Data/Dialog.txt @@ -0,0 +1,3439 @@ +---------------------------------------------------------------------------- + + IvanDev, the Only True Socialist Game Development Party, presents + + *********************************************************************** + * * + * Collected conversations of Ivan Gorovits and the Leader of Citizens * + * * + *********************************************************************** + + Starring + + Head Graphics Designer Tuukka Virtaperko as Ivan + + and + + Master Programmer Timo Kiviluoto as leader + +---------------------------------------------------------------------------- + +Leader on March 3 2002 +---------------------- + +Big leader guy ask Ivan make graphics. Ivan respect no big leader guy. Ivan +make no graphics. + +Big leader guy again ask Ivan make graphics. Ivan still make no graphics. +Big leader guy again ask Ivan make graphics. Ivan still make no graphics. +Big leader guy again ask Ivan make graphics. Ivan still make no graphics. +Big leader guy again ask Ivan make graphics. Ivan still make no graphics. +Big leader guy again ask Ivan make graphics. Ivan still make no graphics. +Big leader guy again ask Ivan make graphics. Ivan still make no graphics. +Big leader guy again ask Ivan make graphics. Ivan still make no graphics. +Big leader guy again ask Ivan make graphics. Ivan still make no graphics. +Big leader guy again ask Ivan make graphics. Ivan still make no graphics. +Big leader guy again ask Ivan make graphics. Ivan still make no graphics. + +Ivan never make graphics. No carnivorous plant. No Indiana. No graphics, no +game. + +Ivan stupid head. Ivan no understand no game make big leader guy mad. + +Big leader guy reach knife and test bodypart code with Ivan. +Big leader guy staple Ivan. +Big leader guy vaporize Ivan. +Big leader guy throw pepsi at Ivan. +Big leader guy ping Ivan to death. +Big leader guy throw old joystics at Ivan. +Big leader guy mind control Ivan and make Ivan join NAMBLA. + +Then big leader guy make new game with ASCII-graphics. Ivan get no fame. + +Ivan regret Ivan made no graphics. + +Ivan on March 3 2002 +-------------------- + +CVS not work when Ivan try commit. +Ivan had not notice CVS saying "up yours" to Ivan, because Ivan been tired. +Ivan kick CVS. +CVS be softerware. + +Ivan now realize that ability to breath not be taken for granted. +Ivan bow before mighty leader mister. +Ivan humble. +Ivan apologize arrogance. +Ivan attempt to be better worker in future. +Ivan attempt really really hard. +Ivan pull himself together. +Ivan submit. + +Ivan light cigarette. + +Leader on March 3 2002 +---------------------- + +Ivan make good graphics. +Big leader guy not mad anymore. +Big leader guy just and merciful. +But big leader guy still somewhat angry. +Ivan draw dragon for big leader guy. Scary. Char.pcx, 48x48 pixels. +Ivan be ready within 48 hours. +Then big leader guy content. + +Ivan on March 4 2002 +-------------------- + +The dragon spread its wings. + +Ivan report succes. +Ivan ready and willing do additional work. + +Leader on March 4 2002 +---------------------- + +Ivan make fine graphics before deadline. +Big leader guy happy. +Ivan good worker. + +Next Ivan make graphics for steppe: + +-a stunted tree +-a bison for filthy capitalist steppes +-a proud, mounted Russian Cossack for our steppes + +Again Ivan get 48 hours at most to fulfill his duty to the Party. + +Ivan on March 5 2002 +-------------------- + +Ivan scratch head. +Ivan look at job description. +Ivan sweat vigorously. +Do Ivan make horse separate in char.pcx and Cossack separate in human.pcx? + +Ivan light cigarette of weed. +Ivan puff puff. +Ivan fly. + +Ivan on March 5 2002 +-------------------- + +Ivan recover from confusion. +Ivan work what he can. +Bison say moo. +Ivan shoot bison and put bison horns on Lada hood with duct tape. +Ivan impale slow drivers with horns. +Ivan car now in Party colors. + +Ivan made stunt tree. +Ivan made tree stunt tree +Ivan made so many because stunt tree do funny tricks. + +Ivan also made picture of brick wall that Ivan mauled CVS against. +Ivan now forgive CVS because it be GNU. +But Ivan still not like CVS. + +Ivan suggest some system. +System would indicate in graphic if person is mounted on horse or not. +Same system also show if person mounted on boar, elephant, polar bear etc. +Or if person mounted on ass. +Ivan find mounting on ass funny. +A pagan verse sing of Jesus man mounted on ass. +Jesus be funny man. +If humor can extends so much to think that be funny. +Ivan not sure if and how system work. + +Leader on March 5 2002 +---------------------- + +Big leader guy agree with Ivan. +Ivan make Cossack in human.pcx. +Then Ivan make mounted symbol in symbol.pcx. Like many items' symbol. +Big leader guy then put symbol above Cossack. +Cossack laugh and ride to battle. +But if Cossack too drunk, Cossack fall off saddle. +Ivan make horse without Cossack in char.pcx, too. + +Ivan on March 6 2002 +-------------------- + +Ivan made stuff. +Ivan also converted mistress to human.pcx-format. +Ivan not sure if Cossack look correct. +Ivan wait further opinion from boss man. + +Ivan is eager to do more. +Ivan drink vodka and go sleep. +Ivan dream of level 8 mistress. + +Leader on March 6 2002 +---------------------- + +Big leader guy think Cossack good. +Even though big leader guy not met many Cossacks. +(Big leader guy prefer guerillas to cavalry.) + +Today Ivan get very important work. +Big leader guy notice that human.pcx short of head space. +Ivan can't be head graphics designer if no room for heads. +Human.pcx also not support severed hands and legs. +Ivan surely like break hands and legs from capitalists. +Thus Ivan read instructions in file and convert human.pcx to new format. +Ivan get 72 hours to finish task. + +Ivan on March 6 2002 +-------------------- + +Ivan think. +What if Ivan draw entire legs like usual, but certain area of leg graphics +will not be drawed to screen. +Say a square from point 0,8 to 8,16 when left up corner be 0,0. +Or prehaps this shaped thing: + +A +| \ +| \ +| \ +| \p +| | +| | +| | +| | +|________| + +Only that angle A be 45 degree instead of coarse ascii presentation. +Point p be in location 8,8. + +Ivan know this be less guru method. +And maybe too much inflexible for big boss. +But it make work easier for Ivan. +It make non symmetric leg graphics easier and compacter. +Ivan wait for comment. +But Ivan feel keen to gib evil bourgeois persons. +Ivan like gib. +Ivan jump up and down because exitement. +Ivan break chair. +Ivan take chair gib. Sharp. +Ivan go impale bourgeois. +Ivan see no gibbys because Ivan made no graphic. +Ivan go back to chair and wait assistance from big boss. +Ivan caress pointy stick in hand and wait for it to see lot of action. + +Leader on March 7 2002 +---------------------- + +Big leader guy think square probably bad idea. +Shape could maybe work. +Point p at 8,8 not good with pants now in human.pcx. +Perhaps point A could be at 0,5 and point p at 7,12. +Then most graphics now present seem to work. +Party may well try this method. +Ivan not stupid head at all. +Of course, then Ivan can draw hands as they are now. +Ivan must only convert everything to material colors. + +Ivan on March 7 2002 +-------------------- + +Ivan full of pride for being of use to Party! +Ivan happy! +Ivan thirst to work and serve boss man even better! + +Ivan not sure how kamikaze dwarf like change. +Dwarf have small legs. +But clock say 0:05 and Ivan say time to call it a day for Ivan. +Ivan see dwarf FEtomorrow, and start work, happy and enthusiastic. +Ivan pull furry hat with red star on eyes. +Ivan lay down in Ivan's work hut on big steppe and sleep happy dreams. + +Ivan wake up. +Ivan uncertain. +Ivan make severed body things as graphics in items.pcx? +Oh, wait. +Some be there all ready. +Ivan see if they good, but not until FEtomorrow. + +Ivan snore. + +Ivan on March 9 2002 +-------------------- + +Ivan report success. +Ivan used more material colors than requested. +Ivan bad. +But Ivan think if idea of material color not fully implemented then +material color big trouble no use. +Ivan wait for spank. + +Leader on March 9 2002 +---------------------- + +More material colors mean more code difficulty. +But perhaps it be worth it. +Big leader guy not spank Ivan. Yet. +Big leader guy make tests with graphics first. + +Next Ivan make all remaining wilderness lterrain graphics: + +base tile for snowy terrain +big rock +base tile for desert +base tile for swamp +boghole +dead tree + +Btw. if humanoid sink in boghole, then perhaps we draw only his head. +The same could even apply to swimming people. + +Ivan make pics before 48 hours pass. + +Ivan on March 11 2002 +--------------------- + +Ivan report work complete. +Ivan go into boghole. +Ivan shoot all bogies in boghole. +Boghole be now Ivanhole. +Ivan stalk for meat in Ivanhole. +Ivan wet and cold and muddy. +Ivan feel comfortable. +Ivan grap capitalist leg and drag into Ivanhole. +Ivan eliminate capitalist. +Ivan like idea of drawing only head of sunken humanoid. +Capitalist head cannot be drawn now, because Ivan decapitate capitalist. +Ivan find $800 in capitalist pocket. +Ivan take fashionable capitalist design jacket and sell it in free market. +Ivan have now $950. +But Ivan no capitalist. +Ivan buy barrel extension for AK from State Weapon Factory. +Ivan buy special issue 75-round enlarged AK magazines from State Weapon +Factory. +Ivan buy automatic AK clip reloading machine from State Weapon Factory. +Ivan now ready to convert into Communism with improved AK. + +Leader on March 12 2002 +----------------------- + +Ivan true friend of Party. +Big leader guy reward Ivan with work. +Ivan used more material colors than asked in human.pcx. +Party agree with most, but some must be made more consistent. +Big leader guy write complete material color list: + +Heads +1. skin +2. main color of cap, helmet etc. +3. all hair (including fur, beard etc.) +4. special color + +Changes Ivan must make: + +All beards and hair must be m-color 3. +Horns in helmet picture must be m-color 4. + +Torsos +1. skin +2. main color +3. belt +4. special color + +Changes: + +Third torso in first row: collar must be m-color 4. + +Fifth torso in second row: belt must be m-color 3. +M-color 3 part in same picture should be m-color 1, 2 or 4. +(big leader guy not exactly understand what it is) + +Arms +1. skin +2. main color +3. reserved +4. special color + +Party symbol on Ivan's hands: must be m-color 4. +Few pixels on shoulders of orc hands in second row: must be m-color 4. +But generally Ivan should avoid using only 1-3 pixels to m-color like in +these pics. +Most users have no cyber eyes like Ivan. +They never notice color effects so small. + +Ivan mustn't use reserved m-color 3 yet. + +Legs +1. skin +2. main color +3. reserved +4. special color + +Some pants include belt, as do some torso pics. +If such graphics are attached, two belts are drawn. +In Party's opinion, belts belong to torsos. +Ivan strips them from those legs that have them. + +Ivan mustn't use reserved m-color 3 yet. +(it would be consistent to use it for hair also here, but maybe not yet) + +Shield +1. main color +2. reserved +3. reserved +4. special color + +All ok. + +Weapon +1. edge +2. handle +3. reserved +4. special color + +All ok. + +Ivan make these changes and be ready on Thursday evening. +Then human.pcx perfect. +Big leader guy happy, and laugh like uncle Lenin and uncle Ho Chi Minh. + +Leader on March 14 2002 +----------------------- + +Big leader guy hadn't noticed Ivan made eyes with m-color 3. +Ivan convert eyes to m-color 4. No big task. +M-color 4 should perhaps even be allocated for eyes only. +If Ivan can live without special color for heads. + +Big leader guy notice this because Ivan's new bodypart pics now integrated. +Still need tweaking though. +For example Ivan removed one girl face which caused bug in game. +Many headless women run around Attnam now. +Big leader guy fix as always. + +Leader on March 15 2002 +----------------------- + +Ivan be little late. +Big leader guy not-so-happy anymore. +Still big leader guy forgive, since Ivan worked hard last week. + +Did Ivan even get orders of Party? +Mail system may just be mad. +Copy of all work in two letters below. + +Ivan on March 15 2002 +--------------------- + +Headless women? +Ivan confuse. +Ivan not receive work order. +Ivan start work vigorously now. + +Ivan on March 15 2002 +--------------------- + +Ivan work with joy for gracious Party. +Ivan deliver results of work. + +Leader on March 16 2002 +----------------------- + +Big leader guy now understand. +Fault not Ivan's, but PHPOINT's. +One day not allow access abroad, other day not send mail. +PHPOY evil capitalist company run by wicked bourgeoisie who wish to bring +down Party. +But proletariate stronger than thousand PHPOYs. +Our will be as unshakable as the iron of our hammers and sickles and might +as great as Lenin's forehead. +Ivan be true friend of strong Party when work as hard as today. +Indeed, big leader guy blamed Ivan for no reason. +Of course, leader not apologize, since he still always right. + +Next Ivan must find his machete and slice up a few humanoids. +This way, their bodyparts can also be severed graphically. +Bodyparts need NOT be compatible, albeit it would be even better so. +It be most important that they look good together. +List of humanoids in char.pcx that ought to be dismembered: + +Enner beast, skeleton, dark knight, goblin, Oree, golem, imp, angel, +gibberling, kobold + +Ivan be ready within 48 hours. + +Ivan on March 19 2002 +--------------------- + +Ivan been pulling too many ADSL cables. +Ivan wrist now dysfunctional and wobbly wobbly. +Ivan not able hold mouse. +Ivan apologize many many times. +Ivan try again after sunrise. + +Ivan on March 19 2002 +--------------------- + +Ivan finish most of work. +Ivan not submit angel. +Angel require complete and utter redraw. +Ivan have no time for redraw. +Ivan must focus on other urgent matter. + +Leader on March 20 2002 +----------------------- + +Visible angel legs not imperative. +Angel can well have bottom part of current hood as legs. +If one leg broken, it just won't be shown, because hood still there. +And wings can be part of torso. +Hands and hair will just be drawn over them. +Angel picture very good already. +It would be pity if it needed to be redrawn for no reason. + +However big leader guy reserve human.pcx for some time now. +Next Ivan feed mammoth with bourgeoisie and make it grow as large as 48x48 +pixels. +Party also need new corpse pictures: small, medium, large and mega-large. +Small corpse is used for dark frog corpses etc, medium for dogs and such, +large for human-sized creatures (corpse now in item.pcx ok as large corpse) +and mega-large for dragon and mammoth. +Mega-large should be of size 48x48. + +Ivan be ready on Friday evening. +Ivan has plenty of time to finish all urgent matters first. + +Leader on March 23 2002 +----------------------- + +Party need corpses. +Ivan design corpse graphics. +Otherwise Party use graphics designer corpse. + +Ivan on March 26 2002 +--------------------- + +Ivan has been disobedient. +Ivan bow before mighty leader man and seek reconciliation. +Ivan submit some corpses. +Only two, because Ivan out of bourgeois flesh. +Ivan know. Ivan know. It not enough. +Ivan go reap bourgeois with mighty knife. +Ivan promise he return till next sunset. + +Leader on March 27 2002 +----------------------- + +Ivan read scroll of change material. +Ivan pick "head" in target menu. +Ivan wish for "wood". +Ivan forget to make graphics. +Is this how it happened? + +Just in case: +Ivan should make tiny corpse, very large 32x32 corpse and mega-large 48x48 +corpse. +Ivan also draw bigger mammoth in char.pcx. +Ivan can choose whether to make it 32x32 or 48x48, no big deal actually. +Ivan note that all three corpses must be made in either case. + +But if Ivan make no mega-large mammoth soon, +Party send Ivan on mission to fight mega-large mammoth in Siberia. + +Ivan on March 29 2002 +--------------------- + +Ivan dumb and disobedient. +But Ivan submit now mammoth and dead mammoth. +New corpses, by the way, might be able to rotate 90 degrees. +It create variance. +But Ivan work more fe-tomorrow. +Ivan apologize mighty big boss with nerve staple amplifier switch. + +Leader on March 29 2002 +----------------------- + +Big leader thank for art but accept only one apology: more vigorous work. +Party plan that weapons may break in game. +Ivan make broken picture for each weapon of IVAN. +Ivan use 48 hours at most. +Not much, but Party schedule tight and Ivan used too much time with corpses. + +Ivan on March 29 2002 +--------------------- + +Ivan stick weapons in capitalist torso. +Capitalist torso full of acid because capitalist evil. +Weapons say snap. +Ivan put fist in capitalist torso. +Fist not say snap. +Torso say plop. +Ivan leathery glove be now corroded. +But capitalist have hole on torso. + +Leader on March 29 2002 +----------------------- + +Ivan faster than bourgeois fleeing from victorious Red Army! +Though one thing Ivan forgot. +Leader asked to make broken picture of all weapons. +Banana say bang. +Ivan draw broken banana too. + +Next Ivan draw bear trap and land mine in item.pcx. +Capitalist step on mine. +From then on, capitalist use wooden leg to kick workers. + +Ivan on March 29 2002 +--------------------- + +Ivan make two bear traps: set and unset. +Ivan install bear trap. +Ivan go smoke cigarette. +Ivan hear snap. +Ivan take prey from bear trap. +Bear trap caught one frosty. +Ivan go drink what he got from bear trap. + +Leader on March 29 2002 +----------------------- + +Gut. With traps Party can protect its territory. +Alas, so can capitalists. +But Leader clever and send some lazy proles to trigger mines first. +Party brigades can advance again. +But Party soldiers must be well protected in close combat with bourgeoisie. +Ivan draw wearable equipment for characters: +shield, cloak, gauntlet, boot, belt, amulet and ring. +Note that only one gauntlet and boot need to be made, not a pair. +If hand is severed, character loses only one gauntlet with it. +Party wait for glorious success. + +Ivan on April 5 2002 +-------------------- + +Ivan complete graphic. +If Party not happy, Party promptly e-mail. +Ivan suspicious if some graphics will look good... + +Leader on April 6 2002 +---------------------- + +Party integrate equipment today. +Party then inform whether they ok or not. +However, leader think Ivan forgot boot. +Party proganda not work well if members walk barefooted. +Beside boot, Ivan draw two symbols in symbol.pcx. +They be of same size as many items and mounted symbol. +First, tame symbol similiar to Nethack 3.4.0's pretty heart symbol. +It be shown over tame creatures. +Without it, it be e.g. hard to distinguish hostile mistress from tame, as +both like to lash you. +Second, Ivan draw battle symbol. +It be shown in worldmap over towns and terrains where armies fight. +Party use it once we add first government dynamics. +If possible, Ivan make these pics today, in order to compensate idleness of +last week. +If not, tomorrow evening will be absolute deadline. +Ivan not betray us for his own sake. + +Ivan on April 6 2002 +-------------------- + +Ivan curse. +Ivan spit blood. +Ivan light akimbo cigarette. +Ivan do work. +Ivan think whole Attnam full of people with heart symbol be gay. +So Ivan make graphic for alternative system: Small triangle in left upper +corner. If red, hostile, if blue, peaceful. +And every time player enter a place, a text will appear: "This village be +hostile" or "This village be peaceful", depending on are there more hostile +or more peaceful creature in the place. +If village peaceful, then every peaceful creature be non-marked and every +hostile be marked with red. +If village hostile, then hostile creatures non-marked and peaceful blue-marked. +Leader like? + +Leader on April 6 2002 +---------------------- + +Idea might work, but heart symbol used nevertheless, +because it not meant for 'peaceful' creatures. +In IVAN, characters have four possible relations towards player: +hostile (attacks), peaceful (don't care), +friendly (attacks enemies of player) and tame (obeys player fully). +Heart used for tame creatures, like player's dog. +If blue triangle used for peaceful people and red for enemies, +perhaps Ivan should make another triangle for friendly characters. + +Btw. cloak in item.pcx and mistress head in human.pcx both have error: +one pixel outside the 16x16 tile boundary. +These pixels should be moved inside tile. +Leader can imagine this be an utmost difficult and dangerous task. + +After next release Party begin expanding world. +First there be need for some Istour's character graphics: +thief, grave digger and black mage. +Ivan expand human.pcx downwards as needed. +All characters must look as evil and gay as possible. + +Ivan be ready within 48 hours. + +Ivan on April 6 2002 +-------------------- + +Ivan fight courageously. +After hard work problem is fixed. +Ivan sweaty like pig. + +Leader on May 4 2002 +-------------------- + +Ivan make no gay folk for evil town. +Ivan irresponsible, lazy and careless. +Ivan smoke cigarette and drink vodka in his home cottage on boundless +steppe. +Ivan hear thumping of many feet. +Ivan become suspicious. +Ivan look out of window. +Ivan notice cottage besieged by countless enraged IVAN fans wielding sharp +objects. +Ivan also catch sight of big leader swinging big axe at cottage door. +It be then too late for Ivan to make graphics. + +Ivan on May 5 2002 +------------------ + +Ivan be currently at Helsinki. Ivan examine way to bring down capitalist +town. Ivan examine in Helsinki until tuesday. Ivan make graphics by +wednesday evening. Ivan very sincere. +Ivan apologize for Ivan's pursuit of personal interest. Ivan be sad and +bitter now that gay folk won Finland in ice hockey, and Ivan gain +additional artistic angst because of that. Ivan convert angst to graphics. +Ivan promise. + +Leader on May 14 2002 +--------------------- + +Ivan work! +It be miracle! +It surely be due to Lenin's great guidance! +We must together go thank and worship him! +But mausoleum door locked. +Ivan draw key. +Tonight. + +Ivan on May 15 2002 +------------------- + +Ivan hope it be ok +Ivan find out that new computer does not like deluxe paint +It not work good +Damn +Must use old computer +Ivan feel downgraded and slow +Damn + +Leader on May 15 2002 +--------------------- + +Do Ivan use some newer version of bad capitalist OS? +They not like old programs. +Old programs work well. +Anything that work well object to capitalist plans. + +Ivan now done all items needed for next release. Gut. +But yet Ivan must draw better silhouette to char.pcx attached. +As Ivan see, upper part of figure must be in 64x64 rectangle at 0,64 +and bottom part in 64x64 rect at 64,64. +Use same m-colors for bodyparts as in example. +As leader said, Ivan can also use some sort of gradient in figure. +Ivan be ready on fefriday evening. +But if Ivan going somewhere on weekend, +IVAN MAKE GRAPHIC BEFORE LEAVING. + +Leader on June 3 2002 +--------------------- + +Now that Ivan has celebrated freedom's dawn enough, +Ivan should convert genie and angel to humanoids. +Leader deliver latest humanoid and char.pcx. +Ivan be ready fetomorrow. + +Leader on June 15 2002 +---------------------- + +If Ivan really is too kind-hearted to cut angel and genie to pieces, +big leader can do it someday himself (poorly). +It wasn't a particulary creative assignment after all. +However, Party now needs a chest where we can lock insubordinate graphics +designers in. +Of course, big leader or Hex could also draw it as well as possible +(extremely poorly). +But that would be far too great a punishment for anyone inside. +So Ivan draw it. + +Please? + +Ivan on June 16 2002 +-------------------- + +Ivan is drunk of vodka currently. But that not matter. Ivan download gimp +rightaway and do the gfx. Wait. + +Ivan on June 16 2002 +-------------------- + +Ivan awaken. +There be no memory from the past. +All memory vanished. +Ivan remember nothing. +Ivan not feel his body. +Ivan try to regain his sense of universe. +- +Ivan open eyes. +Dust and smoke in the air. +Hear screams and gunfire. +Ivan see house of Party. +Party house in flames. +Dead comrades on the ground. +Their fingers be gripping the handles of their Kalashnikovs, of which some +are still warm and smoking. +Ivan remember flash, and realize something. +Ivan look at corpse. It have red ribbon on shoulder with hammer and sicle. +A small, black pool of blood come out of corpse torse. +Ivan remember Lenin. Ivan look at corpse. +Ivan frown. +Ivan slowly turn head. +Sand fall off Ivan's furry hat. +Neck feel sore. +Kalashnikov resting on Ivan side. +Ivan move hand toward Kalashnikov. +Pieces of debris fall off Ivan sleeve. +Leather glove grip wooden handle of Kalashnikov. +It feel comfortable and reliable as ever. +- +Ivan still stunned. +Ivan rest Kalashnikov on his chest. +Ivan close eyes and think. +Ivan be in cloud. +All sounds be softer and different. +Ivan feel the force of intuition. +Ivan slowly reload Kalashnikov. +Something warm and familiar move deep inside Ivan brain, but Ivan cannot +quite grasp it. +Ivan stand up slowly. +Ivan feel stiff. +Ivan take a few steps. +Shells on the ground. +Shells say clickety-click under Ivan's combat boots. +Now all return to Ivan's mind. +- +Ivan run. +Ivan run towards office of Master. +Ivan kick broken tables and debris out of his way. +Gunshots appear louder. +Ivan turn corner. +Capitalist swine soldiers oppressing Master and his last allies in shelter +of Master. +Ivan pull trigger. +One long and sudden burst combine with one hand grenade clear capitalist +swine soldiers. +Hallway now full of dead capitalists. +Ivan walk to office. +Ivan report to duty. +Ivan apologize deeply for critical absence. +Ivan submit some graphic. + +Leader on August 6 2002 +----------------------- + +Holiday has grown old. +Ivan rested enough. +Ivan draw stethoscope. +Leader use stethoscope on bourgeois. +Leader hear coins clinking inside. +Leader order Ivan shoot bourgeois. +Leader use stethoscope on bourgeois. +Leader hear nothing. +Good. + +Ivan be ready within two days. + +Ivan on August 9 2002 +--------------------- + +Ivan obey. +When dealing with bourgeois, only must clink AK mechanism. +No other may clink. +Ivan listen to sad music of CCCPians singing how poor they be. +Ivan gather bitter attitude. +Ivan stand up, take AK safety switch off. +Vacation be over now. +Ivan heed the call of Plains, work and bourgeois-eliminationing. + +Leader on August 9 2002 +----------------------- + +Good. Bourgeois in multiple pieces be best beourgeois. +Ivan may take capitalist scalp to his vast collection. +When Ivan removed scalp, Ivan put skull to item.pcx. +Leader send it to capitalist bosses as warning, along with personal insults. +As bourgeois be very disgusting even as dead, +Ivan is allowed to use two days for deed. + +Ivan on August 10 2002 +---------------------- + +Ivan find subject bourgeois. +Ivan load AK with rotating shuriken blade ammunition. +Ivan click full auto. +Ivan click trigger. +Ivan make very minced meat. +Skull fly out of vast blade limbo. +But suddenly something hairy fly on the ground too. +Ivan see. +Ah, cleanly cut bourgeois scalp left undamaged. +Ivan add scalp as a bonus to work file. + +Leader on August 10 2002 +------------------------ + +Excellent. +Next Ivan draw thief. +Leader not mean bourgeois. +Leader need thief who steals from bourgeoisie. +With money thief buy stuff, put it in needle and make himself fly. +Latest humanoid.pcx be attached. +Ivan be ready on night after Monday at the latest. + +Ivan on August 15 2002 +---------------------- + +Ivan agree! +Ivan agree! +Leader not mean bourgeois! +Leader wise socialist. +Leader not mean bourgeois. No, no. + +Ivan apologize humbly for being lazy. +Ivan submit thief. +Thief cut capitalist purse and run. +Then capitalist be poor and ready for collective farm. + +Leader on August 16 2002 +------------------------ + +Hmm. Ivan interpret leader sentence funny way. +But now Ivan must work harder. +Only then may leader accept apologize. +But Ivan need tools. +Ivan draw hammer and sickle to item.pcx. +Ivan be ready to wield them ere Sunday dawn. + +Leader on August 18 2002 +------------------------ + +Leader ask Ivan draw hammer and sickle. +Ivan late. +Leader suspicious. +Ivan prove loyalty by being ready ASAP. + +Ivan on August 18 2002 +---------------------- + +Ivan was away for construction work in collective farm far at Viitasaari! +Ivan been unable to read e-mail. +Ivan read his dealyed work assigment only today and work as fast could. +Ivan submit two sections of holy emblem of the only True Party. + +Leader on August 19 2002 +------------------------ + +Leader understand. +Now Ivan has much work to do with new instruments of labour. +Ivan begin making new armor for Red Army. +But apart tools be not as eternal as the Party symbol they together make. +Ivan be so eager and so strong that hammer break and sickle twist. +Chain mail and shield Ivan was making be also broken and ruined. +Scrap-iron be bad propaganda for Party. +All four items must be hidden in item.pcx before Wednesday ends. + +Ivan on August 19 2002 +---------------------- + +Biological scum gather on Ivan HD at frequent speed. +Luckily Ivan use Eudora, so bourgeois plot at eradicate Ivan not success. +Ivan take sicle and cut numerous sample of swarming biological goo mass. +Ivan scan all specimen with F-Prot 3.12a. +Ivan find following: + +W32/Klez.H@mm +W32/Hybris.worm.B +W32/Badtrans.B@mm +W32/Hybris.worm.D +And one mysterious which F-Prot 3.12a not identify, which be file class1.scr. + +Leader likely have some (prehaps all?) of them already, but Ivan send +anyway as he about to empty Eudora trash bin soon. + +But oh no! What grief and agony! +Glorious sicle be corroded from bourgeois biological scum! +Ivan rush to forge and attempt to regenerate the holy emblem of CCCP might +with sacred hammer named after Mighty Man Lenin. +Red fire of forge be lit and mighty booming of hammer sound like thunder. +But bourgeois be so sinister! Scum engulf sacred hammer and consume it! +Hammer turn to poo. +Ivan bitter. +Ivan discard raped symbols of CCCP might to Item.pcx. + +Leader on August 19 2002 +------------------------ + +Class1.scr maybe a new Klez variant, but Leader not yet able to verify. +And Hybris.worm.D new. +Leader very pleased with Ivan. + +Ivan remember thief trained last week? +He better able to practise robbery profession if armed. +Any who harm enemies of Party be friend of Party. +Thus Ivan send thief dagger and short sword. +Then thief stab bourgeois heart. +Heart made of solid concrete and weapon say snap. +Thief wait for Revolution in capitalist salt mine. +But it be worth a try. +Ivan put broken weapons to item.pcx, too. +And be ready in night before Friday. + +Ivan on August 20 2002 +---------------------- + +Ivan obey. +Ivan now reveal his utter contempt toward GIMP. +GIMP be not a fruit of hard work. +GIMP be funny chunk of putrid excrement. +Ivan yearn for DP. +Ivan plan to start do graphics on old computer because of DP compatibility. + +Leader on August 21 2002 +------------------------ + +GIMP socialist work. +This good basis but it not guarantee quality. +Good kolkhoz project need strong leader, +unquestioned obedience of workers +and alert execution officials to succeed. +World better place if people remembered this. + +Next Ivan draw flail. +This be experimental weapon proposed by Party scientists. +Flail must have three separate heads. +Ivan use m-color 1 for handle and m-colors 2-4 for heads. +In game, some flails can be missing a head or two. +If so, colorize system replace head's m-color with transparent color. +More heads mean more damage. +There may also be magical heads that have special effects, +for instance fire damage or poisoning. +Party try this and see if it's fun. + +Party also need picture of horn. +This because in IVAN characters may now panic and flee. +Party smiths forge horn of bravery and horn of fear that use this effect. +Leader give them to Ivan and send him to lead guerilla squad +against wicked bourgeois battalion. +Bourgeoisie strong and Ivan's friends turn to retreat. +But Ivan stay calm and blow horn of bravery. +Allies feel Lenin be with them and fight furiously again. +Then Ivan blow horn of fear. +Capitalists realise futility of resisting Ivan's AK and run away. +Victory be Party's as always. + +Ivan on August 26 2002 +---------------------- + +Indeed must Ivan serve. +Ivan travel in frozen land of CCCP plains. +Bolt of AK mechanism covered in frost. +Ivan furry hat white of snow. +Blizzard go not through Ivan long coat. +It be of CCCP quality. +Manufactured in factory by brave workers who sew in name of Mighty Man Lenin +and knit in name of Wisest Man Marx. +True communist eager to sacrifice own well-being to rid bourgeois of world and +make way to glorious worker. +But what shineth in horizon? +It be bourgeois limo with tractor on front to clear road long lost under snow. +Ivan approach. +Ivan put finger through finger hole in glove to permit AK trigger usage. +Ivan crouch in snowy plain. +Mother CCCP offer cover for Ivan once again. +Ivan ammo detonate with latency because of super coldness. +Bolt of AK mechanism temporarily melt of ice. +Bourgeois down. +Ivan throw bourgeois body out of car and give tractor driver address of nearest +kolkhoz. +Arrogant bourgeois limo not be of use in happy CCCP society. +Ivan light bourgeois limo gas tank on fire. +Gasoline nearly solid because of super cold, but it light nicely. +Ivan have warm. +Ivan clean AK and melt big block of ice that was in Ivan canteen and drink. +Ivan fry sausages and remember old Lenin times. +Ivan stay overnight on mighty CCCP plain near burned car. +Ice ravens frolick in bourgeois corpse. +Morning only snow bump remain of worthless bourgeois limo. +Ivan continue journey. + +Leader on August 27 2002 +------------------------ + +Mission success. +Leader congratulate Ivan. +But Ivan forget one thing. +Bourgeois corpse be hazardous waste. +CCCP ravens go ill in no time and their plumage fall. +Besides, bourgeois stink. +Corpse must soon buried soon, and deep. +Ivan draw grave digger in humanoid.pcx. +Dirty work must be accomplished within 48 hours. +And next time Ivan be sure to burn capitalist pig along with limo. + +Leader on August 29 2002 +------------------------ + +Bourgeois stench now insufferable. +Grave digger needed immediately. +If it difficult, Ivan draw clothes peg for leader first. +When sticked to nose, it good temporary protection against foul rotting +greenback smell. + +Btw, IVAN once again more or less playable. +Would Ivan like to see how it now look? +If CVS not like Ivan, leader could send code or even compiled executable. + +Leader on August 30 2002 +------------------------ + +Grave digger not really grave enough. +But time short and he will do for now. +Next Ivan draw following two world map terrains: native village, which in +game will be placed in deep jungle, and entrance to underwater tunnel. +Oh, and note: LATEST WTERRAIN.PCX ATTACHED. +Ivan make pictures before this month ends. +Or grave digger dig pit for Ivan use. + +Btw, if Ivan find out that some graphics leader want already exist or can be +assembled from old pieces, Ivan should tell this *before*, not two days +*after* deadline. + +Ivan on September 3 2002 +------------------------ + +Ivan read: "entrance to underwater tunnel". +Ivan not certain what he supposed to do. +Ivan scratch head vigorously. +Alas, answer not become apparent. +Ivan draw rocky hole. + +Leader on September 3 2002 +-------------------------- + +Hole ok. +It connect village island to Attnam area. +Now Ivan draw imperialist to humanoid.pcx. +Village folk serve white man. +Otherwise he kick them. +Ivan ready on feThursday. + +Leader on September 6 2002 +-------------------------- + +Free imperialist dangerous. +Ivan catch and put him to humanoid.pcx immediately. +And be not too kind to him. + +Ivan on September 8 2002 +------------------------ + +Ivan make imperialist, but he forget to send file. +Ivan head very hollow. +Ivan drink gun oil. + +Leader on September 8 2002 +-------------------------- + +Oh. +Leader finally understand why Ivan so forgetful. + +Leader had some fun whipping kulaks with flail Ivan made. +Flail had flaw and broke. +Ivan tidy up floor and put broken flail to item.pcx. +Leader also expressed wish that fail could have additional heads. +Therefore Ivan make also picture of single separate head. +Ivan finish work within 48 hours. + +Ivan on September 11 2002 +------------------------- + +Ivan obey but hate GIMP deeply and force insert it to deepest rectal cavities +of bourgeois. +However now Ivan have no graphic facilities. +Ivan wear glove and take GIMP out and wash with acid. +Ivan have cuntlike feeling. + +Leader on September 12 2002 +--------------------------- + +Good socialist no complain. +But if Ivan morale really down, +leader give unusual and exciting task to keep interest up. +This great chance for originality and creative imagination. +Ivan draw levitating ostrich. +Latest char.pcx attached. +Saturday would be fine deadline, but if Ivan busy, Sunday also do. + +Leader on September 17 2002 +--------------------------- + +Ivan task yet unaccomplished. +However, leader merciful and not demand Ivan exertion during test week. +Drawing levitating ostrich no good for sense of reality. +But on feFriday Ivan sketch pic or regret dearly. +And if Ivan going somewhere, image ought to be completed ere departure. + +Ivan on September 21 2002 +------------------------- + +Oyster now levitate like bird of Mighty Man Lenin flying to the dawn. + +Leader on September 21 2002 +--------------------------- + +Ivan finally made picture. Good. +Leader already bought several cans of gasoline to burn Ivan atelier down. +Now leader can save them and use next time Ivan disobedient. + +Although graphic little odd. +Leader always thought ostrich have long neck and legs and they probably +wouldn't use wings if could levitate. +But leader of course no artist. + +Ivan planned lots of weaponry for Party lately. +However, mere prototypes not too useful. +Party must now establish working mass production. +Ivan draw proud smith for new weapon factory. +Cheka men come check Ivan progress on Tuesday. +Ivan see that he ready by then. + +Ivan on September 21 2002 +------------------------- + +Ivan attempt spell leader previous work order. +Ivan find something queer. +Ivan take dictionary in hand. +Ivan see. +Ostrich and oyster different things. +What embarrasment. +Ivan draw ostrich later after he have eated glorious CCCP oat porridge. +Ivan say smith already can be assembled from previous parts. +Ivan see. +Hmm. Existing hammer too big for smith unless smith forge glorious CCCP +I-beam or giant weapon. +Ivan draw little hammer beneath the glorious AK. +Also smith need new torso. +Then Ivan say: +Hands 5, row 1. +Torso 13, row 2, color tan. +Legs 18, row 1, color dark brown. +Head 9, row 1, skin color greyish tan because of fiery forge, hair rather +dark brown. Or any colour. Master smith have white hair. +Alternate head would be head 11, row 2. + +Ivan request leader send font file so Ivan can do cyrillic alphabet. + +Leader on September 21 2002 +--------------------------- + +Leader appreciate smith construction recipe and attach font file. +Color must be same, i.e. palette entry 200. +Characters must fit inside 8x8 rectangles like old ones. +Ivan can make new separate file in which old alphabets are replaced, +or he can put new letters in lines 7 and 8. +They seem to be unused even though they have space for 32 characters. +Leader already eager to see remade ostrich and font. + +Leader on September 28 2002 +--------------------------- + +Leader has question. +Usually brilliant soviet mastermind perceives all answers in no time. +But leader confess this one bad. +Endless thinking bring no solution. +When Ivan going to make new ostrich and cyrillic font? + +Leader on October 6 2002 +------------------------ + +Ivan back from promised land. +Ivan return to work. +Leader repeat: Ivan should draw ostrich again. +It not need look like levitating. +Instead, Ivan draw little arrow facing upwards to symbol.pcx. +It symbolize that creature flying or levitating. +In addition people climbed in a tree could use this arrow. +Arrow can also be flipped so that edge direction be reversed; +this way it may show that person has dived in deep water. +Latest symbol.pcx attached. + +Leader also certain Ivan now learned lot about Russian speech. +Therefore cyrillic font not beyond Ivan ability. +But it no obligatory task. + +Ivan on October 10 2002 +----------------------- + +Ivan eat grain. +Ivan powaah. + +Leader on October 10 2002 +------------------------- + +Ivan heed the words of Only True Party, as new assignment follows. + +First Ivan required to draw sign. +This mean terrain which can contain readable text. +For instance state military areas may have signs +warning that trespassers will immediately be sent to Siberia. + +Then leader want strong-box. +This small metallic container that very hard to open. +KGB use it to store important intelligence documents. + +Latest olterrain.pcx and item.pcx attached. + +Ivan on October 14 2002 +----------------------- + +Ivan be mighty worker with hammer in his hand and sicle in his other hand. +Ivan obey with pleasure. + +Leader on October 15 2002 +------------------------- + +Party grateful for pictures. + +Leader notice morning star in rubbish on right side of item.pcx. +Fourth last row to be exact. +Leader fall instantly in love. +Ivan convert weapon to material colors with haste. +Leader then scratch back with it. +In case leader not satisfied he detach ball and use it for bowling. +Perhaps Ivan best make morning star that quite broken after leader use, too. +Ivan deliver new items on feWednesday. + +Leader on October 24 2002 +------------------------- + +Ivan ask leader send reminder. +This understandable. +That how Party work: +Leader duty-bound to command and Ivan duty-bound to obey. +Leader enjoin Ivan make morning star. +Both broken and intact. +Hurry. + +Ivan on October 25 2002 +----------------------- + +Ivan curse gimp but admit own arrogance. +Ivan idea. +Ivan say: Ivan go get Virtual Machine. +That be PC emulator. +Ivan install DRDOS in Virtual Machine and use Deluxe Paint like in good old +days when Brezhnev and other asses no even born. + +Leader on November 19 2002 +-------------------------- + +Ivan rested again enough. +Jungle village camouflaged too well. +Party can't put end to evil imperialist rule in town if it hidden. +Ivan find village and make it bigger and visible to Party troops. +Ivan ready on Thursday at the latest. + +Ivan on November 21 2002 +------------------------ + +Ivan Ready, Rocking, and On Time. +As always. +Glory to Lenin! + +Leader on November 22 2002 +-------------------------- + +Party smithies short of anvils. +No anvils mean no scythes and no sickles. +No kolkhoz equipment mean no harvest. +No harvest mean no vodka ingredients. +Ivan make sure this serious threat eliminated before week ends. + +Ivan on November 24 2002 +------------------------ + +Ivan carve mold of anvil. +Ivan rub ore between big Ivan hands. +Ore melt. Ivan pour melten ore to mold. +Ivan glare mold fiercely. +Mold and ingredient freeze of terror. +Ivan remove mold. +Ivan present: hardended glorious CCCP anvil! + +Leader on November 24 2002 +-------------------------- + +Ivan saved country again. +Vodka once more produced. +Leader like it cold. +Leader make brilliant plan as always. +Leader put spirits in chest Ivan made. +Leader carry chest outside to cold tundra night. +Leader lock chest tightly to prevent thivery. +Very soon leader may enter confused state. +But plan not work. +Chest far too heavy for carrying. +Ivan draw small and medium chest which easier to move. + +Leader also noticed problem in IVAN. +Bear traps can be used to sever bodyparts from kamikaze dwarves. +This not problem. +This fun: fundamentalists have no place in good socialist society. +Problem is, method too powerful. +Bear traps should break eventually. +Ivan draw broken picture for them. + +Ivan deliver graphics on Tuesday at the latest. + +Ivan on November 26 2002 +------------------------ + +Ivan pridely announce improved and flexibleved vodka kontainer series! +Ivan also introduce salvation for beer in the form of broke trap. +Ivan sicle glitter like bourgeois golden tooth on floor after good punch! + +Leader on November 27 2002 +-------------------------- + +Praised be Lenin who guide Ivan in his work of saving our weekends! +Leader organize parade to honor Great Founder. +Problem come up. +Marching CCCP soldiers need impressive helmets. +Ivan drawn only a bicycle cap. +Ivan add new head protection fine enough for Lenin's army. +Something like the one guard head wears in humanoid.pcx. +Old picture may be used as weaker helmet, so Ivan not remove it. + +Leader also plan invite foreign VIP guests to watch parade. +All world must understand socialist might and advancement. +After Lenin celebration leader take visitors to admire State Zoo. +Since Party can't really afford to feed many real animals, +Ivan draw a chameleon, like in Nethack. +Leader clone enough copies of it to fill one zoo section. +Leader then make them polymorph to exotic and near-extinct animals. +Full of pride leader present them to guests. +When visitors seen one section of zoo, leader distract them for some time. +Meanwhile someone move chameleons to another part of area. +There they polymorph once more and leader again show them to foreigners. +Guests full of awe and return home to tell tales of CCCP glory. + +Ivan ready on feFriday at the latest. + +Ivan on November 30 2002 +------------------------ + +Clanking be the forge! +Fiery be the hammer! +Mauling meet the anvil! +Ivan work in CCCP gold glory! + +Leader on December 2 2002 +------------------------- + +Leader grateful for work well done. +However there be grave news. +Yesterday Red Army practice Lenin commemoration. +Final rehearsal end up in disaster. +Suddenly one chameleon become 32x32 mammoth, +kick down zoo wall and stamp batallion. +Fortunately leader there. +Leader gaze at mammoth in same deterring and will-melting way +leader always look at Ivan when Ivan late. +32x32 mammoth polymorph into a frightened ant. +Leader stamp ant. +Party not discouraged by minor setback. +Celebration be held anyway. +Leader order new soldiers quickly from Middle Asia to replace the flattened. +But street leading to Lenin mausoleum now mess. +Many glorious CCCP helmets, both new and old, lie everywhere badly mangled. +Ivan clean them to item.pcx apace. +When there, Ivan also fix ring picture. +It not look as valuable as it could. +Ivan ready tomorrow evening. + +Leader on December 4 2002 +------------------------- + +Party intelligence have job for Ivan. +Leader currently suspicious of certain citizens' loyalty. +But thought police already busy reading all letters and listening phone +calls of our free folk. +So Ivan draw floating eye to char.pcx. +It cheap substitute for mikes and such. +Floating eye swim in air through city and check all windows. +In one house it discover capitalist sympathizer worshipping big picture of +Adam Smith. +Traitor notice giant pupil in window and freeze in horror. +Next day he work in labour camp in Ural. +Ivan ready on feSaturday. + +Ivan on December 7 2002 +----------------------- + +Ivan harvest muck from drive. +Ivan find new form of capitalist sinisterity. +Ivan send muck to processing. + +Also Ivan remove eyeball from prisoner #05512, condemned to Ivan use +because of suspicion to Party's omnipotence. +Ivan punch eyeball. +Eyeball swell. +Ivan put eyeball to vacuum. +Eyeball enlage. +Ivan add some water with pipette, wrap in styrox and submit. + +Leader on December 9 2002 +------------------------- + +Ah! Young worm called Lentin.F among other aliases. +Leader always like acquaint himself with new breeders. +But name much better if it hadn't had the t-letter. +Then it represented in concrete way the power of our fast-spreading +Ideology. + +Next leader ask Ivan draw battle-axe. +It like axe but has pair of curved edges on opposite sides of shaft. +This not only useful in battle but also in forestry. +Axe with two edges can hit a tree and immediately be pulled out of it and +swinged at adjacent tree without changing direction. +In other words it can chop two trees at once. +When vast CCCP forests cut down with double efficiency, +Party can print twice as many Pravdas. +Indeed, leader plan to pose on the front cover of next publication. +Leader shall be surrounded by diligent and happy forest workers and the +first lumbering site using this ingenious double-edge technology shall loom +on the idyllic background of the photograph. +Since leader must show example for citizens, he wield brand new battle-axe +in one hand and in other he hold great steel scythe sparkling in sunlight +like Lenin's eyes when he content. +Needless to say, Ivan draw the scythe, too. +And broken pictures of both items. +Ivan finish art within two days. + +Ivan on December 10 2002 +------------------------ + +Scythe be of imperialist. +Scythe be easy way of harvest. Wuss way. +Sicle only. Scythe cheat. +Ivan snap cheater trust imperialist bourgeois pagan scythe in two piece. + +Battle-ax of creat coolity tough. +Ivan admire. +Ivan see: it be equipment of maul! + +Leader on December 10 2002 +-------------------------- + +Leader like new axe, too. +Leader now filled with desire to broaden his weapon collection further. +First leader ask for bastard sword. +This crossbreed of long sword and two-handed sword. +It can be wielded with both arms or strong people can use it in only one +hand. +Leader like think it manufactured by bastards in west so name very fit. +Then Ivan make club. +This been little out of fashion for some time but leader still like it. +After all it very cheap and handy interrogation tool. +It hurts lot but seldom injures permanently. +Ivan add intact and broken versions of both on feFriday at the latest. + +Ivan on December 15 2002 +------------------------ + +Ivan fuck + +Leader on December 17 2002 +-------------------------- + +Leader has problem. +Ghost of Brezhnev started to haunt Party headquarters lately. +He think he still in power and demand everyone honour him when he passes. +If someone say leader time better than his, he jump out of wall and say BOO. +Ivan take vacuum cleaner and suck him out of house. +Ivan then put ghost into char.pcx for safe-keeping. +Ivan ready on feWednesday. + +Ivan on December 20 2002 +------------------------ + +Ivan take soul and bring agony. +Ivan go to fucking sleep. + +Leader on December 21 2002 +-------------------------- + +Now leader want eddy in space-time continuum. +This be creature, or rather thing, from great book of Douglas Adams. +They teleport people around and create odd sofas. +This perfect since Ivan already drawn usable couch. +The Guide say not how eddy look like. +Comrade Hex believe they be collection of small vortices. +However, Ivan may be as creative as Ivan like. +Ivan just make sure char.pcx contain eddy on feSunday. +Deadline soon so Ivan not pressurized when Santa Claus come. +Santa interesting guy. +Live in place as cold as Siberia, dress in red and not use money. +Also send little elves to spy on everyone and see if they good whole year. +Santa very much like leader. + +Leader on December 26 2002 +-------------------------- + +Now that Santa gone and Ivan thanked granny for new home-made kevlar underwear +etc. enough, Ivan should return to work and make eddy in space-time continuum. + +Ivan on January 5 2003 +---------------------- + +Ivan work unforgetful and unclouded by thought of else like always. +Ivan think: space-time continuum not such place that happiness likely to occur. +Ivan draw distressed and agonized Eddy. +Leader much like Santa. +Orwell spin in grave for thought of both you. +Ivan put transmission belt around Orwell torso and use for sicle sharpener +power source. + +Leader on January 8 2003 +------------------------ + +Eddy scream and make luxurious sofa materialize next to leader. +Leader sit and happily uncork vodka bottle. +But drinking not possible. +Leader mansion so cold during CCCP winter that all liquid freeze. +Leader annoyed. +Luckily proud soviet nuclear submarine guarding mansion in nearby lake. +Leader order engineers remove its reactor and install it in leader basement. +Soon whole house and surrounding taiga as warm as steppes in south. +Next day leader notice two-headed moose devouring giant mushrooms in forest. +Leader taste, too. +Three little Lenins dance on leader nose. +Wow, great stuff. +As reward for eddy, Ivan may also gather some. +Ivan put one to char.pcx so chemists may analyze and clone it. +Ivan also catch moose there so it won't eat all mushrooms. +Ivan ready on feThursday. + +Leader on January 13 2003 +------------------------- + +Ivan should deliver two-headed moose soon. If finding one difficult, Ivan catch +two one-headed animals, take head of other and attach it to other with glue. + +Ivan on January 14 2003 +----------------------- + +So mighty CCCP that Ivan again at work! Praise be of Lenin! Party overjoy +of achievment! +Ivan not sure where to put extra moose head so alternatives made. +Ivan also make monoheaded moose. +Mushrooms of kaleidoscopic wisdom +Kaleidoscopic wisdom worthless but funny. + +Ivan on February 12 2003 +------------------------ + +Ivan nearly as reliable and workful as the System! +Ivan not now how animation seem. +Animation probably look bad. But Ivan will do more work when Ivan see +vortex in action. +By the way where can Ivan download Igor, the little friend? +Ivan hope Leader fastly check no chars are missing in char2.pcx, as there +is a chance of so due to accident. +Accidents CCCP tradition. + +Leader on February 12 2003 +-------------------------- + +Leader think eddy twirl as finely as tornado striking a capitalist port! +Leader include IVAN with spinning eddy. +To see it, Ivan activate WMode from 'X' and summon eddy from '&' cheat. +Up-to-date graphics are all included, as well as IGOR. +Ivan should enter "graphics" when it asks for art directory. +Alas, IGOR poorly documented so Ivan need figure out himself how it work. +Leader like to order, not to explain. +IVAN and IGOR both executables for The Wicked Bourgeois OS. +Ivan just ask if Ivan want some less lame port. +Btw, Graphics/graphics.txt contain collected info about Party graphic files. +It be created some time ago for certain people asking about them. +Leader probably told Ivan everything in it already, but it may still be +useful since Ivan head so often get spontaneously formatted. + +Leader not given much work for Ivan on last weeks. +This because Ivan had already done all important tasks. +Ivan may have guessed that when drawing mushrooms and multi-headed elks. +Now Party noticed IVAN players powerful enough encounter only mammoths. +This rather odd since they extinct. +Party need more deadly enemies. +Therefore Ivan add dark wizard to humanoid.pcx. +Ivan think of swedish teacher when drawning and result will certainly look +evil enough. +And of course, pointy hat be a must. +Ivan ready on feFriday. + +Leader on February 15 2003 +-------------------------- + +Ivan not make graphics. Ivan understood System wrong. To lie be only leader +duty. + +Ivan on February 16 2003 +------------------------ + +Swedish teacher not have wizard quality. Swedish teacher evil indeed but +only brute instead of clever. +Wizard not be nincompoop, or else he likely injure only self with spell +casting attempts. +Ivan think of Rockefellers. That evil and intelligent. + +Ivan see eddy be whirling, thwirling, like a rotating blade ready to +mutilate idiot driving wide car! +Ivan very impressed about animated enemies. +Leader certainly very big-brained. He able to bring such utopy to life. +Elks funny. Ivan like to receive low-priority poop graphics assingments if +they funny and absolutely nothing else to do. +Ivan Linux not yet working. Ivan install Mandrake, but it broken and fat. +Ivan plan Redhat download now that Ivan possess ADSL. +Ivan thank for addition of mechanical companion Igor. + +Ivan plan reset of nervous system next weekend for test week then over. +Ivan not likely to function during and shortly before and after reset. + +Leader on February 16 2003 +-------------------------- + +Leader thank for compliment. +No amount of leader-worship ever bad for social system stability. +Next Ivan draw more meaningless creatures to Party collection. +First be hedgehog in estrus. +It needed as interrogation device. +Ivan not need to know more. +Second be skunk. +Kamikaze dwarf nation may use this as banned chemical weapon. +Ivan put both to char.pcx within 48 hours. + +Leader on February 27 2003 +-------------------------- + +Leader voice nerd curses mumble mumble! Let Ivan head break into prime factors! +Let Ivan limbs be severed and xor themselves! Let Ivan torso fly to dev/null! +Let Ivan groin be iconified! + +Ivan on February 27 2003 +------------------------ + +Ivan make glorious CCCP animals of Soviet order: hedgehog and skunk! +Hedgehog is almost as fast in running as Ivan in working! + +Leader on February 27 2003 +-------------------------- + +Leader smell work completed successfully. +Skunk stink so strong it will be represented by cloud of smoke in game. + +Next Ivan has more important task. +Party run out of money. +In CCCP this not problem. +There be nothing to spend it on, anyway. +But Party have some minor depts to neighbouring countries. +State tried to print quadrillion roubles to pay them back. +It enough only for monthly interest, so different approach needed. +Leader make plan. +First Party send free and happy workers to build roads in these nations. +New traffic infrastructure keep them satisfied for some time. +Then free and happy CCCP defenders come to test roads with tanks. +Money lenders soon so free and happy they forget we owe anything. +But, Ivan must first draw road prototypes to wterra.pcx. +Road may enter or exit a tile from any corner or edge. +It may do this multiple times in same tile, but the intersection point is +always at the center. +Paths should be quite curved so they look vivid. +In theory two pictures would suffice: road from center to one edge and +to one corner. +Rotating and drawing these many times on one square should work, +but leader think the result would be too simple and repeating. +Therefore leader suggest eight tiles, of which each would contain road from +center to one edge or corner, all twisting slightly differently. +This not too much to ask. +Leader just checked graphic files of Civ3 and they had 256 road pictures. +Anyway, Ivan will be ready on feSunday. + +Ivan on March 14 2003 +--------------------- + +Ivan pave road like caterpillar. The worm type. +But now school pressure over for time creature, and Ivan ready for tasks. + +Leader on March 17 2003 +----------------------- + +CCCP road no CCCP road without tank. +Ivan draw one. +Char.pcx, size 32x32. +As always, there be slight shortage of materials and tools though. +Ivan only have steam engine and museum cannon to equip tank with. +Still leader trust Ivan conjure up great icon of Party glory. + +Leader promised award for Ivan diligence. +Of course, it Party custom that workers make their own rewards. +Ivan put Gorovits family gas mask in item.pcx. +Leader heard it passed down from father to eldest son for generations. +It still block Ivan smell after two weeks on the steppes without shower. +Not much else though. + +Ivan ready on feWednesday. + +Ivan on March 22 2003 +--------------------- + +Ivan have very fat sicle. + +Leader on March 23 2003 +----------------------- + +Joy be of Ivan, as he reunited with the family relic! +Leader attach latest version. +Ivan probably not seen many of its other new elements. +Ten new Ivan-drawn monsters have been added since last send. +Lanterns now surrounded by pretty alpha halo and slimes use wave animations. +Skunks, magical mushrooms, explosions and genies utilize new smoke fully. +Best of all, player's representation on game screen depend on what he wear. +Head graphics designer should check all look as it should. + +Then to next assignment. +Leader somewhat ashamed. +For ages leader known Ivan talented and celebrated bunny artist. +Yet leader never requested even a tiny rabbit. +This must be corrected. +Ivan draw three versions to char.pcx: +bunny, large bunny and Vladimir, the gigantic carnivorous mutant bunny. +The latter should be 32x32. +Ivan draw these whenever he may rest enough from other duties. +Ivan try to be ready during the new week, however. + +Ivan on March 24 2003 +--------------------- + +First Ivan annouce utter impressment to new IVAN. Leaders has done wonders +of might and glory! New graphic effects resemble utter CCCPness, and new +monsters of high viciosity! +Ivan problem report as follows: +Skunk perhaps too much of danger. Ivan have fought two skunks to find this +burden nearly non tolerable. Bad ass monsters make game fun indeed, but +skunk is furry not bad ass. +Ivan also desire mine plant prompt, as in "Are you sure want to plant +mine?". This because Ivan accidentally have set explosive traps while +reaching for pick axe. +For long time has an issue been buggering Ivan: hammer make more damage +than axe! This be of no logic, say Ivan, since hammer heavy and blunt but +axe heavy and sharp. +Upon the seeal of smoke sprites, Ivan slight disappointment. Animation look +bad. Ivan need make more and better smoke sprite animation frames. Also +multiple smoke sprites seem repetitive and it not nice. +Lamp aura make very impress to ivan! It be practical effect too. One issue +bother Ivan: aura go dark and light over and over again. However at the +darkest stage the aura be darker than rest of enviroment. This look werrid, +since lamp source of light any way. +Also Ivan recognise that when wearing all leather armor, game player seem +like utter perv. This please Ivan. +Mommo animations very impressive. Ivan like wobbly wobbly enemies. They +remind of porridge at siberia camp. Work men always have kill their +porridge before eatal. +Ivan still of puzzle, since high difficulty to kill floating eye imminent. +Any advice? +Also upon his journey Ivan see that mutilated but still alive mushrooms +panic. This possibly unintentional, but Ivan find funny. What do panicing +mushroom do? Perhaps it wobble or start to spin. +Ivan also think too much food at Genetrix Vesana level. Excess food make it +worthy of experience camping, such as ghouls in old version of nethack, +except that ghoul possess no food of it self. +It seem that although clothing be incorporated, Ivan still non able to see +wielded weapon on player. +Ivan got strangest idea while writing this. Ivan think game lacks pipes and +magical herbs. Perhaps there could be green cave of gnome junkies smokeing +opium, staring at fountain/puddle and chanting. +Also Ivan interested to see rooms of different shape than rectangle, and +some pre-scripted rooms with simple ornamental desing or even special function. +Ivan say, highly exellent and encourageing work! +Now school busies have gone out of Ivan way for time creature, thus Ivan +have more free time. School busies related of lazy math studies. Ivan +present math number row so far: 8, 9, 9, 4. The last number have cause work +load for repeat test. Luckily work load now over and succesfully enough, +repeat test turn out 8. Also big Finnish homework about angsty books of +Finnish writer Timo K. Mukka was late, but now has passed. +Ivan anxious to get his hand to the bunnies. Also carrot be required. + +Ivan on March 24 2003 +--------------------- + +Other Ivan crash due to mine explosion. Game crash immediately after +sentence: "Equipment broken! Press any key to continue." +Also funny thing, game say: "You hear a faint thumb. Below you you see an +active mine" +Fingers can be used for communicate, but rare with audio. Better word thus: +"thump" or "click" Ivan suggest. + +Leader on March 25 2003 +----------------------- + +Leader must admit Ivan been of irreplaceable help. +With autosave, leader could trap the ferociously evil mine bug. +Wand of teleportation in Ivan inventory broke and its effect crashed. +Leader fix error and test again. +Ivan die horribly because of mine and score 1512 points. +Crash not made game too much shorter. + +Leader make some other changes to ameliorate Ivan gaming: + +Skunk now easier and kiwi amounts reduced. Ivan check if enough. + +Mine prompt now present. + +Axe damage now greater. +Leader had thought hammer more as dwarven war hammer used to reshape orcs, +whereas axe intended to be mere tool used to chop wood and nagging wives. +However, they were not too useful so more damage mean advanced game balance. + +Leader added random flips to make smoke animations look less repetitive. +Improvement not too great however, so Ivan could indeed draw more frames. +Comrade Hex suggested though that Party used animated smoke border +tiles to make big areas of gas continuous, just like in world map terrains. +If Ivan want to experiment with idea, he draw some 48x48 smoke tiles +somewhat like wterra.pcx graphics. + +Problems with lamp aura and panicking mushrooms now fixed. + +Floating eyes annoy leader, too, so their hp now halved. +Nevertheless they still not very easy to kill. +Leader usually carry daggers and short swords and throw them at eyes. +Leader may also drop lantern and lead them to dark where he can't see them +and therefore not faint. +Gods may also help, and Dulcis be best. +It fun to see stupid monsters faint when gazing at pet eye. + +Inhandspics not yet reintegrated, since Party short of them. +Neither battle-axe, scythe nor sickle present. +In fact lack of armor pics exist, too, since there no head wearing gas mask. +Ivan should make these at some point, but leader not want to give every task +at once unless Ivan desires so. + +Exercising math understandable reason for work delay. +As great teacher said, calculating and playing trumpet be best man can do +with pants on. +Leader has final math test tomorrow and can't agree more. + +Ivan on March 26 2003 +--------------------- + +Ivan repport strange journeys. +Seem like there no way to put lantern off? Lantern not exist in tool menu. +Also if player only weapon be pick axe, then pick axe not found when +pressing use or apply key. You first must dis arm pick axe to dig with it. +Also seem like iron spear almost as good weapon as iron mace. That not very +realistic, say ivan. Spears perhaps need a one accuracy or damage level +down. Also spear be bit fragile, where as daggers, which considered now +very fragile, are in fact rather stury due the small size. +Ivan prefer that wielded weapon and worn item also be shown in inventory. +Perhaps the text colour could be different, red for maybe. +Last Ivan say that it be nice to see slight vary in the accuracy of weapons +of the same class. It would make some item feel unique. Weapon design ways +are different and some design be better than other. +Ivan be of tired metal. +Ivan go wash dirty black hair of floating eye gibs and then go to bed and slip. +Retina very slippery. +Ivan put eye ball retina in ice box and next morning he plan to use it for +a sponge bath. + +Leader on March 28 2003 +----------------------- + +Leader proud to announce IVAN 0.410 now conquering the gaming world. +Alas, there were not enough time to integrate InHandsPics. +But there be many other tweaks. +For instance, dipping to corpses is now possible. +One can use sword covered with mushroom to make enemies very confused. +Also thrown potions break more often, and allies no longer dodge bottles +containing healing liquid or vodka. +This was very odd, as Ivan understand. +Problem with pick-axe Ivan reported now corrected. +Mace also better. It sucked. +And dagger now harder. +But why should wielded and worn items be shown in inventory? +Equipment menu easy enough to use for leader. +Ivan supply some arguments for wish before leader change anything. +Weapon property variation very good idea. +It will be implemented soon. +Party allow putting off lantern when it uses fuel and dims if short of it. +Then it practical to extinguish light to save fuel in light places. +Before this it would only be tactical trick and would hurt game balance +since floating eyes would no longer be of any risk. + +Ivan on March 31 2003 +--------------------- + +Ivan make pitiful little bunny suitable for seat cushion purpose. +Ivan make glorious large bunny suitable for roaming, hole digging and +mighty CCCP carrot eating. +Ivan make also big arse Vladimir bunny bounce with mighty power muscle. +It may be that Vladimir (Vile Lurking Agressive Devourer with Infernal +Muscles Intended to Rampage) look bit unclear. +Leader opinion? +Clarity increase with moderate ease by contrast increase. +By the way Ivan note that tank graphic likely to look very unclear unless +second material colour be chosen a light colour. + +In the winter of CCCP... +...bunnies bounce. +Ivan attempt reload glory Soviet AK, but only thing that say clickety-click +be Ivan teeth. +Night fall fast and food be of out. +Ivan have but some coffee beans made in sweet plains of Ukraine. +Ivan chew coffee beans and take wise Marxist book of chest pocket. +It so dark that Ivan read in light of cigarette lighter. +Lighter go out in shame; petrol ended. +Ivan of despair. +At sudden! Flame of revolution light, snow melt from big diameter! +Covers of mighty Marxist book turn into mouth, say Ivan: Thou excellent +servant! May CCCP heed your call!" +Fleet of heavenly manna emerge from skies of glory CCCP taiga! +Flame of Lenin Love light large pine, Ivan warm hand and thank Lord Lenin +of Highest Mercy! +AK melt of Soviet Ice. Ivan run to the plain and shoot hundred thousand bunny! +Then Ivan go back and feast with little vodka in pocket of great Lord +Lenin's Might! +Ivan put tent and sleep in light of Lenin Fire. + +At this point reader wonder, why Ivan still eat bunny flesh in spite of +manna falling from taiga sky? +Manna, of course, be of card board. Budget cuts. + +Leader on April 2 2003 +---------------------- + +Leader glad to hear of Ivan miraculous rescue. +Leader think Vladimir fine for now. +Party start breeding bunnies immediately. +Leader love them. +They make great Red Army fur hats. +But rabbits must grow big before being flayed. +Ivan put carrot in item.pcx to feed them. + +Leader also desire inhandspics of all weapons smashed to pieces. +Of course, it Party propaganda council tradition to erase everything broken +from official CCCP photographs. +But player can be capitalist arse, too, so there no need to hide lousy gear. +Also head with gas mask required. +Leader like walking incognito on streets wearing mask and scaring children. +Ivan draw pics in humanoid.pcx during this week. + +Btw, leader report obsolete fep link on 'About me' page of Ivan homesite. +Fep represented no honest socialism. +There were suspicious rituals like voting and selling fep-cds. +Therefore revolution renamed it as IvanDev. +Voting still possible, though. +But there always only one choice which leader decide alone. +Ivan redirect link to IVAN homepage. + +Leader on April 18 2003 +----------------------- + +Leader apologize for not whipping Ivan to tough work lately. +High school homepage say Ivan had test week and leader short of tasks. +But this now corrected. + +First leader attach latest IVAN. +Wielded items now shown correctly on game screen. +They also animated if original thing be. +Ivan try equipping lanterns, glittering items, spoiled items, outlined +artifacts, thunder hammers, flaming swords and holy bananas. +Leader especially like flaming sword. +It really cool. +Er. Warm. + +Ivan behold new outlook and report problems. +There probably also numerous bugs. +Last week leader debug ages one vile code ruiner which turn out to be in +Microsoft's std::string class. +Well, what else can be expected from bourgeoisie! +Leader make new better string class and continue journey. + +Leader notice there no broken shield in humanoid.pcx. +Ivan fix this by breaking one with his strong fingers. + +Leader also plan to add invisible stalker monster. +They clouds of intelligent air mass which move hidden from all eyes. +They make excellent KGB officials, indeed! +However, with infravision they can be seen. +Ivan check his cyber eye batteries recharged and add revealed stalker to +char.pcx. + +Ivan ready on feMonday. + +Leader on April 26 2003 +----------------------- + +Leader delighted to not see stalker. +But problem arise in no time. +Leader never clean his room, so it filled by vast clouds of dust. +Stalker step inside and quickly look like grey mummy. +Leader annoyed. +Visibility good reason for invisible stalker retirement. +Leader decide to take vacuum cleaner and finally hoover his sanctum. +Leader forget stalker so transparent because it made of air. +Cleaner swallow new spy into its dirty inside. +Leader very annoyed. +Stalker useless and good old CCCP quality vacuum cleaner jammed. +Ivan fix latter and put it to item.pcx with haste. +And this time it not matter if result suck. +That its purpose. + +Ivan report some smoke frames look no good, as do some wielded graphics. +Ivan fix these simultaneously. + +Lack of war hammer picture also hazardous to economy. +Forge hammers break easily in battle, leaving smiths nothing to work with. +Ivan eliminate dearth of beating equipment ASAP. +Or on feMonday at the latest. +Release soon so there lots of last minute work. + +Ivan on May 4 2003 +------------------ + +What utter goodness! Ivan hero of Work again! +Ivan be master CCCP carpenter! Mighty be his saw and hammer, blessed under +pleased eye of Lenin the Lord! +Whoops. Ivan insert bourgeoisie to wood chipper! What mess! +Lucky that glorious CCCP sewer rats ooze from holes in workshop floor and +devour and clean bourgeois mess! Glory be Communism! + +Leader on May 6 2003 +-------------------- + +Leader command Ivan test bonefiles. +To do so, Ivan must die. +It leader habit to delegate all tough tasks to citizens. +Although kicking the bucket not so bad in IVAN due to wands of resurrection. +Oh, if just one of them existed in real life! +Uncle Lenin could finally return and lead country to glorious future. +In fact leader already asked CCCP scientists research these but to no avail. +They keep zapping stick made of bone at corpses and nothing happens. + +When Ivan done with task, he draw harp of charming. +It used to tame creatures. +Another item which leader like to have. +Just a couple of tunes and all adjacent political enemies would become +friendly and obedient. +Indeed, they'd follow leader without question! +And get stuck in corners a lot. + +Ivan ready on feThursday. + +Ivan on June 23 2003 +-------------------- + +In name of mighty CCCP:ian ideal! I salute leaders who are the Vicarius +Lenin and invincible! + +Ivan have been to isolation in work camp at Viitasaari, Siberia. That +cause that Ivan unable to submit fruit of work to mighty leader men Above. +That why Ivan decide to add extra working to make up for lost time. + +Font file with the character system of the holy land have now generated. +Also scandick letters work. Ivan generate some Russian sentence that use +cyrillic Holy font later. Ivan also have attach nocturnal memo of deep +pondering. + +At this point Ivan wield lawn-mover. Ivan start lawn-mover. Ivan slice and +dice gimp the horrible swamp animal with utter vigour. Ivan make sure no +visible trace of intern organs left. +At this point Ivan wield flame thorwer. Ivan start flame thorwer. Ivan +burn and flame bloody mess of gimp the horrible swamp animal with utter +vigour. Ivan make sure no trace of DNA left. +At this point Ivan wield nuclear disintregration drill. Ivan start drill. +Ivan drill and mutilate dirty ashes of gimp the horrible swamp animal with +utmost vigour. Ivan make sure no carbon compound left. + +Leader on June 28 2003 +---------------------- + +Leader spend summer holiday in Soviet Korea. +Leader receive score of sumo wrestlers as gift from uncle Kim. +These wondrous creatures great souvenir, indeed! +Leader want Ivan put one on display in humanoid.pcx and show western +journalists that citizens in CCCP of today eat extremely well. + +Ivan on July 5 2003 +------------------- + +Here be - Summo wretsler! +Ivan seek forgivance for utter late being. + +Leader on July 5 2003 +--------------------- + +In fact leader not furious angry berserker this time. +Wrestler less late than Ivan graphics normally and not even too critical. +But next task much more important. +Multi-tile creature engine progress slowly but surely. +When it completed, current bosses too tiny to be taken seriously. +Ivan enlarge genetrix vesana and Elpuri to 32x32 size. +Pics should be ready on feTuesday. + +Ivan on July 8 2003 +------------------- + +Where frostfrog frolicks in land of frost and spinning ether +There walks Ivan ponderful in the mist +Ice say crickety crick under weight of combat boot +Never to fail, emblems of father Lenin shine in furry hat +And AK gleaming in the space of airborn snow spirales + +Where bourgeois roam with fat car +In the land of farmers, welders and factory-men, starved +There Ivan tear through wall of mist and clouds +Angels of CCCP rocketing below sun +And bourgeois dwelling in sins trembles + +Father Lenin in the Sun +Make mighty mountains into lowland valleys +And look, the weight of expensive clothing is crushing the bourgeois +The weight of worker blood +And children of Lenin, look, your cup runneth over of Lenin grace + +Leader on July 11 2003 +---------------------- + +Leader imitated Ivan even more. +But here be test version finally: +Vesana and Elpuri now 2x2 squares large. +Border tiles have been added to earth. +Enner beast now animated. +Lyre of charm integrated. +Strange side dungeon added to UT by foreigner named Corey. +Many new bugs included. + +In fact leader planned to send this yesterday as birthday present to Ivan. +However he did not expect to drive car accident and reduce two vehicles to +scrap iron IRL yesterday. +Stupid trafic rules. +Leader already gathered a committee to plan new ones. +Something like "§1, Thou shalt keep thyself at least 500 meters away from +thy Leader, so thou won't disturb Him" would do fine. + +Ivan next assignment hattifattener. +This creature from Tove Jansson's books store electricity of thunder. +Leader plan to use it as backup power supply for his computer. +Ivan catch one and put in char.pcx during weekend. +Ivan also plant cactus or two in olterra.pcx by then. +Leader's mistresses enjoy them. + +Ivan on July 15 2003 +-------------------- + +Ivan furry hat spin in heat of combat +Sweat mix with blood of minor hand wound +Like hurricane Ivan rampage through bourgeois estate +Paid wimpy mercenary guards fleeing one by one +For the power that lie within dollars is infant compare to power that lie +within devotion +And knowledge + +The land is of palm trees, sun and rivers flowing through jungle! +Let the caterpillar come and utilize the fertile soil! +A few years forwards, and here corn and rice flourish in endless field +And a small kolkhoz hut with tractors in near the beach, where happy +workers make food and sleep with good conscience! + +Leader on August 2 2003 +----------------------- + +Hattifatteners crackle with electricity. +Hedgehogs' spines so sharp they better not be attacked unarmed. +AI now very cunning if monster intelligence greater or equal ten. +Bunnies seek food and produce babies madly. +Vladimir follow Ivan loyally and crush walls as he walk. +Glory of new features so bewildering Ivan not notice angry grizzly bear +charging at him. +Bear manage to shred Red Army uniform before Ivan cut its neck with Gorovits +family sickle. +Ivan must find tailor to fix his outlook ere feTuesday. +Ivan then order him to wait in humanoid.pcx for further assignments. +Official release on Monday so Ivan also report any bugs ASAP. + +PS. Vladimir change color often as noted in Ivan diaries. +However, he not yet get green stripes under aurora borealis. +Leader implement this before IVAN v3.0. + +Leader on August 14 2003 +------------------------ + +Leader forgot to send next assignment. +Of course there can't be nothing wrong in this. +After all leader embodiment of universal wisdom and justice. +Nevertheless Ivan get small gift as compensation for the horrific torture of +a few days' idleness. +Leader rewrote Febot in C++ for fun and present result. +In test he at least 5000 times faster than the old fossil. +Now it finally possible to import really large files. +Leader attach pagan old testament dictionary as vt.dic. +This file prove the presumption that one can't make any distinction between +Febot speech and the original book. +Next leader feed him the Communist Manifesto. +Party now has cheap source of infinite propaganda, in file cm.dic. +Leader also take all emails Ivan has sent to him in English and vice versa. +(They fun memories so leader always store them in text file.) +This produce very comical files called ivan.dic and leader.dic. +Ivan test the version works correctly and then draw necromancer. +He ought to be really mad and frightening creature of the night, +who summon skeletons and zombies to do his bidding. +Leader help inspire Ivan by importing book of Vesa as vesa.dic. +Should Ivan drink vodka, smoke cigarette of weed and read this, his +imagination certainly able to summon the monstrous personification of +Darkness needed. +Ivan ready on feFriday. + +Ivan on August 18 2003 +---------------------- + +Head col2 row9 mask white with slighlty yellow, eyes bright red +Arms col2 row7 sleeve dark gray, armour gray/steel blue, glove yellowish brown +Torso col2 row23 cloth dark gray, belt gray +Feet col2 row11 cloth dark gray + +Ivan take artistic freedom and design bizarrh mask on necromancer. Mask be +actually crude illustration of the head of an wanker who come to horrify +Ivan in recent feverish nightmere. +Ivan find that necromancer look heartless and obscure. He carry items of +dark magic and pervade grave of long fallen warrior with supernatural force. + +Ivan on August 19 2003 +---------------------- + +Ivan furiously wield pipette and make inky outlines of droids in mind + +Leader on August 21 2003 +------------------------ + +Necromancer laugh diabolically as he finally free. +He begin collecting allies to spread havoc and terror amidst innocents. +First he descend into hideous cave of malevolent spider army. +But necromancer disappointed to see all spiders Ivan drawn small and puny. +They scare no one. +Ivan crossbreed tiny spider and Siberian tiger to create giant spider. +Then it mate with mammoth and baby become mighty 32x32 spider queen. +Both should be grown up and ready for rampage in char.pcx ere Monday dawn. +Ivan also have them spin sample webs in effect.pcx. +Necromancer then decide whether they worthy for his needs. + +Ivan on August 25 2003 +---------------------- + +Here be spyder. +Giant spyder not bigger than little spyder but more dangerousy-looking. + +Ivan on August 26 2003 +---------------------- + +Ivan have sleep deprivation the size and weight of Ural. Ivan chew +Ukrainyan coffee bean and take dose from guerilla fighter stimulant pack. +Ivan roam in everlasting windy woods of Novosibirsk, where roam of AK clear +him way as snow melt to freeze again. Red star tank wrecked to middle of +field and smoking, but Ivan tighten fist embracing AK and proceed to drive +imperialists in peril. Life for Lenin! + +Leader on August 30 2003 +------------------------ + +Leader gave Ivan some nights to sleep. +If Ivan still near Novosibirsk he check its newest factory. +It make shiny Ladas for loyal soviet citizens. +Ivan park one in char.pcx so leader can check quality. +32x32, deadline 2.9. + +Ivan on September 7 2003 +------------------------ + +Ivan in state of UURGH due to hangover. +UURGH good for doing crude work. +Ivan present: Lada! + +Leader on September 9 2003 +-------------------------- + +Leader must confess minor thing. +Ivan recall leader trip to Korea last summer. +One night leader drank vodka, played card game with Kim and lost. +Vladivostok and next year's State budget, for instance. +Former not too bad as CCCP so mighty no one notice one city missing. +Latter however a problem. +Leader forced to allow some foreign visitors the privilege of walking on +holy CCCP soil. +First tourists be fat American couple, both equipped with expensive cameras +like in Nethack, and their 10 year old child. +They arrive on feFriday. +Ivan escort them to humanoid.pcx without delay and protect their wallets +with his life. +Party release them from the burden of evil capitalist money ASAP. + +Ivan on September 13 2003 +------------------------- + +Ivan carve icon of disgust +A pseudo-god far away from Lenin + +where idiots dwell in land of green paper pieces +and oppression +there this icon exist exalted above thrones +and healthy men and women fall with puddle of drool before it + +Far, oh Far is Lenin's light +in eternal night of Sodom of the Dollar +where torches flame and fattest man is king + +Ivan instruct leader in assembly of putrifious sculpture +Bourgeois wife: +Leg colour - pantyhose gray, shoe black +Jacket, skirt and shirt colour - choose some and random. Skirt and +jacket preferabily darker and shirt lighter +Skin colour - Vary between death grey and redneck red +Head - use already existing versions + +Bourgeois man: +Shirt - Hawaii shirt colours (pink, light green, light purple for example) +Skin - carefully adjust skin tone to "wannabe-tanned" +Short - jean blue or grey +Shoe - light brown, or maybe tasteless like red or light gren + +Bourgeois brat: +Cap and clothes - All annoying good, randomize + +Ivan on September 18 2003 +------------------------- + +Shame on them who profit on Master's tragic death! + +Skin: Pale yellow, light +Hair and beard: Light, reddish +Clothe: Dark gray (black) + +Leader on January 1 2004 +------------------------ + +Leader been quiet for quite some time now. +This be due to a minor disagreement within government. +In Autumn leader managed to fill hole in CCCP budget by tourism income, +but he could not hide the aforementioned loss of Vladivostok in poker game. +Wicked capitalist sympathisers use this as excuse to revolt. +They claim leader not fit for omnipotence. +What rubbish! +However, rebels highly influental and leader forced to temporarily leave the +capital and his beloved glittering palaces, luxuriant gardens and vodka +swimming pools behind. +For the last months leader hide in a small cave in northern Ural, +vowing for revenge all day and night. +And soon its time will come! +Leader has already formed alliances with several local Finno-Ugric native +tribes and gathered an army of many hundred men armed with state-of-the-art +sticks and stones, ready to take back Kreml at any moment. +However, some slight additional power could still prove handy. +This is where Ivan comes in the picture. +Leader taken over a small nearby oil deposit and want to use the liquid as +flame thrower fuel. +Ivan build a prototype for leader troops and put it in item.pcx. +To make victory absolutely certain, Ivan also add sling as a backup ranged +weapon. +Ivan ready in four days. + +In order to motivate Ivan, leader also present latest alpha for testing. +It protentially buggy but still fun. +The greatest chance in the dungeon is the new fluid model which rather +detailed. +Necromancers should also be quite interesting. +New Attnam has quite a lot of new features which however have little +practical value. +(Btw, could Ivan tell which bodyparts are meant to belong to the tourist +wife? Leader chose some but they seem a little ugly and thus most likely +incorrect.) +Ivan report any problems ASAP. + +Leader on January 1 2004 +------------------------ + +Leader and comrade Hex tested the game through the night. +Several bugs which made the game almost unplayable were found. +Leader attach new executable. +Ivan override the one in the last archive with it. + +Ivan on August 9 2004 +--------------------- + +Ivan mind bend into facsinate shape. Quivering brain? Suddenly brain too +big to fit to Ivan skull of 8,2mm thick human bone. Thunder strucking as +brain part gush through Ivan left and right temple! +Ivan left brain half involve logic, thus gush be rather small. Ivan +right brain half involve emotions, thus gush be even more small. But +behold and hearken! Spirit of party engulf Ivan brain and it gloriously +jump from other temple and assume massive 32x32 form with CCCP pride! +Ivan now left with no brain but put mayonnaise in brain cavity and no +change apparent. As luck has it AK usage be in spine not brain. +Mind worm now existent. Ivan recommend check that graphic file miss no +older graphic (due to long work pause - CURSE EDUCATION CCCP MEN OUGHT +FOR WORK). + +Also Ivan require newest version to avoind producting mistakes in Ivan's +work and for playing. + +Ivan on August 30 2004 +---------------------- + +Ivan free from stench of GIMP the annoying smiling putrifier animal. +Ivan taste work with mitrhil Deluxe Paint 2 +3 (in both hands)! Gimp the +small wuss idiot animal become T-34 roadkill. + +Ivan on September 14 2004 +------------------------- + +Ivan perform damage with jolliness: + +Point increases chance of critical hit by +50% +Cut and blunt damage simultaneous +Long weakens against small enemies and improves against large, short +works inversely + +POINT CUT BLUNT | ACCURACY DURABILITY BLOCK | special + +Spear P8 C0 B2 | A8 D1 Blo6 | long, 2hand, throw +Quarterstaff P3 C0 B2 | A6 D1 Blo6 | long, 2hand +Bastard sword P4 C6 B4 | A4 D7 Blo5 | +Short sword P6 C4 B2 | A6 D8 Blo7 | short, 1hand +Longsword P6 C5 B2 | A7 D6 Blo8 | 1hand +2-handed sword P1 C7 B7 | A4 D6 Blo6 | long, 2hand +Axe P0 C6 B5 | A2 D4 Blo4 | +Battle-axe P0 C7 B6 | A4 D6 Blo5 | +Halberd P7 C8 B8 | A4 D4 Blo5 | long, 2hand +Poleaxe P0 C8 B8 | A4 D4 Blo4 | long, 2hand +Mace P0 C0 B6 | A4 D4 Blo5 | +Dagger P6 C2 B0 | A8 D2 Blo1 | short, 1hand + +Ivan on September 21 2004 +------------------------- + +Somehow Ivan find such inspiration of given task that boundaries of +adequate behaviour now require to bend little. Leader observe that huge +analysis written mostly only because siren be nude: + +***COMMENCE ANALYSIS + +Because Siren be not overwhelmed with clotheing, Ivan design set of skin +colours and more info that be full of use. +New set also may be appleyd to human characters that all ready exist. + +In name of clarity, Ivan define siren parts now: +LEG 16,2 +TORSO 19,2 +HAND 16,2 +HEAD 24,2 25,2 26,2 26,1 + +Informaton about siren: + +Head may have hair pin. Let this be yellow, red, purple, pink, black +for ease rgb colour for pink be provide: 255,173,173 + +Hand have some jewel or band. Let this be yellow, purple, gold, black or +dark brown + +Foot have sandal. Let this be: +If siren be negro or darkish: black, dark green (0,70,0), dark red (70,0,0) +If siren be european: orangy (255,75,0) – peach (255,140,0) – porno blue +(215,150,215) – pale peach (255,180,70) – yellowy (255,233,65) +If siren be asian: take same as european but remove pale peach and +yellowy and insert pale pink (255,180,140) + +Torso have special colour for both nipple and pubic hair. Ivan +assumption that pubic hair colour correlate with hair colour. But not +only that; correlation with skin colour also necessary. Hair colours +probably already exist within leader system, so Ivan no look up for rgb +value in here. However suggestions: + +Negro and asian: allow only black, blackish and dark blown color for +both hair and nipple + +Asian: too red nipple not allowed + +Europoean: Lighter red hair color must only occur with skin that be +noticably pale and then nipple must too be noticeably reddish. Nipple +colour not allowed dark brown or black, and very light nipple colours +allowed only when skin also very light. + +In general, nipple colour is suppose to meet following requirement set: +be a little darker than skin, of different tone than skin, or both +to be not too dark (that distract visually) +if tone (not darkness) of colour change, then it change to slightly more +red or brown + +Also, regarding pubic hair colour: +If hair colour blonde, then pubic hair good to be slightly darker than +scalp hair + +Oh, asian skin colour probably not yet define in IVAN. Ivan indtoruse +asian skin colour. This skin color exist within follow range: +R 254 +G 247 +B 183 - 208 +Blue be random in given area. If this not possible, then suggest make +darker asian and lighter asian in which amount of blue be first at the +lowest and then at highest, respectively. +In asian skin set only black, brown and dark brown hair be allowed. + +Ivan also introduce peach skin, which be part of european skin family. +This skin colour associate with dark orange hair in particcular, but +allow also other type of hair. +r 255 +g 178 +b 96 + +Hmm. Come to think of it, sirens be magical beings! That mean skin +colour may also be bluish or purple or grey or anything. Suggestions: +Purple 222,250,255, hair red, magenta, blue, dark violet +Green 180, 255, 150, hair blue, dark green, dark violet +Magenta 255, 180, 255, hair blue, magenta, dark violet +Cyan 200,255,255 hair blue, red, dark violet +(NOTICE: Ivan not very sure what be difference between magenta, violet +and purple. Do not ask.) +Same nipple and pubic hair laws are apply to magical skins. + +Perhaps magical colours be reserved to sirens and new skin colours +aplyed only to ordinary humans. Ivan not sure at the moment. Some +testing needed. + +Ivan find that analysis be perhaps of unessential comlexity, but it was +unable to be avoided after Ivan discover enthusiasm of opportinity of +nipple definition + +***END ANALYSIS + +Ivan overjoy that a total of months of glorious cccp porn not go for +waste! Lenin smile in mausoleum for omnipresence of cccp ideal! Even +when dweller of lost cccp dwells in depths of dirt, father Lenin emerge +from red lights of porn district! + +Ivan also improvise new character inspired both by nudity and by current +decadence state of Motherland: bum! +Bum: +LEG: 17,2 +TOSRO: 19,1 +HAND: 13,2 +HEAD: 11,2 +Bum is to have light grey scalp- and pubic hair and wander naked in +Attnam complaining about social security issues. If possible, other +citizens have AI that attempt to stay at least one square away from bum! +When Attnam later be improved, bum be able to beg and then, if get any +money, he buy vodka from Attnam inn. When inn established, he perhaps +also sell all items he able to find to be able to invest more to passion +for vodka. + +Ivan have furniture add to olterrain. These incluse chair, table, desk, +luxury bed, bed. Also include fireplace (open and shut), stove (open and +shut) and oven (open and shut and 2 x partially open). Ivan not sure if +now be any application for open and shut graphic but they there for +future reference. Fire and heat sources of course luminant. Also mat. +colours 3 and 4 intent to glow like fire. Note also that window has been +altered to look naturaller. + +Ivan suggest that in future one can select the directon from which he +enter any city. This make easier access to many shop and building in the +border. Ivan still drawing new infrastructure for attnam. + +Ivan September 21 2004 +---------------------- + +Ivan skyrocket from low plain to cccp clouds which highly float above. +There spirits gather around Ivan furry hat and whisper of interior +decoration tips! +Ivan is yet to test beard growth. + +Ivan further improve Attnamian life: Drawer (open/closed), bench, +workbench, well +Thing that appear like puddle be natural fountain, for why some one +erect something that look like decorative bourgois fancy fountain in the +deep dungeon? +Ivan suggest that in darker dungeon levels there be fancy magical +fountains in whish mat1 is black and mat2 is purple. Drinking from these +cause similar effect than mushroom gas exposure. Ivan also hope for acid +fountain in Enner Beast level! +Ivan propose following: Table, Workbench and Desk funiture be drawn ON +any 16x16 character that is on same square. That way shop keeper or +smith can sit behind him desk and thus look professional. +As for furniture: +Well - does not block movement (one must to stand above well if want +drop something to well, say, a rotting corpse. or drink from well) +Bookshelf, drawer, stove, oven, fireplace - will block movement +Bench, chair, table, desk, workbench - will not block +Ivan suggest also that when finding altar, no god symbol be draw on it +before player been on it to recognize associated god. + +Ivan also suggest that if accidentally injure ally or neutral, following +rules apply: +If ally really dumb, say Kenny, he never attack master +If ally smarter, then possible to ask for forgiveness. But if player +injure same ally again within, say, 50 turns, no forgiveness be given. +If neutral been injured, he may forgive, or ask for money to forgive. +If injure again within, say, 500 turns, no forgiveness. + +Ivan have beta tester discovere. He give following feedback: + +However he not have save files any more for crash. + +Ivan on September 23 2004 +------------------------- + +Ivan finish suggestion of Attnam. Suggestion crude to say a least. Ivan +should have millimetre paper or small square paper use. Too late. But +lot of detail and new thing: + +New: +Carpenter - construct items of wood +Apprentices - make -1 items for 1/3 or 1/2 of cost +Bakery - produce and sell bread. maintain stock of 50 bread +Healer - heals stuff +Bar - interesting people and beer in bar. lot of poor and tough and +adventurous people. +Brewery - right next to the bar. contain hidden altar of Nefas where +bartender sacrifice gold and beer! Five paid mercenary soldier guard +brewery and shut up about altar. +Tomb of the unknown soldier - Guarded by two guard with fake mithril +armour and wood sword. Guard line: "This is the Tomb of the Unknown +Soldier! His name was Lamusius! Oops." +Apprentice priest - no fuction yet +Royal prison for trophy prisoners - Exotic prisoner collection of +different race and species! +Obedience station - To keep all prisoner happy +Punisher - Ivan plan smiley face mask graphic for punisher +Garden - Nothing special but lot of plants +Training dummy - Can be mauled to keep up weapon skill +Entrance points - When entering, prompt: "From which direction do you +want to enter (N,E,S,W)? South recommended." + +Beds not included for every resident because in IVAN people no sleep +unless they get bump in head. +In later version Attnam be several square big in world map, and there be +suburb and homes and farm in these other square? +Oh, Ivan has forget windows on everywhere but garden. Leader add them as +he wish. +If leader wish for a city wall, then one should include. However outside +city wall must be enough space to use wand of fireballs and have 100 +gazillion super pets as allies. +It may be that city square size have to be increase a bit. +Lot of new graphic require. Ivan do some time. + +Map NOT as attachment because of hugeness. See +http://www.tuukkavirtaperko.net/attnam.png + +Leader on September 23 2004 +--------------------------- + +Ivan represent fine ideas. +Leader share some of his plans, too, and comment Ivan's. +Attnam grow so large that it be bother for Petrus to decide everything. +After all, he must have enough time to use wives and dolphins, too. +He read 1984 and name four cardinals to aid in rule: +Cardinal of Truth, who responsible for propaganda. +Cardinal of Plenty, who responsible for tax collecting and treasury. +Cardinal of Peace, new title of Sir Galladon, who responsible for guards and +leading army to preemptive peacekeeping operations. +Cardinal of Love, who head of Inquisition. +Entrance to Cathedral can indeed be more more impressive, with tiled way +leading inside. +Red mat can as well be wider. +However Leader suggest Cathedral remain in frog shape. +This tradition after all. +Obedience station be constructed underground. +Cardinal of Love, two Inquisitors and a few law students +(this be their summer job) interrogate prisoners there. +Happy face mask brilliant idea for them all. +Trophy prisoners also kept safe on this floor. +This because in Ivan plan it too easy for clever enemy to drop kamikaze +dwarves from hot-air balloon so they explode wall and release prisoners. +Jail residents moved to ground level only on special occasions, for instance +to entertain foreign VIP visitors. +Guards' quarters also underground. +Above ground level there another, smaller floor where Cardinal of Love place +four telepaths so intelligent they detect mind of every thought criminal in +town with their amulets of ESP. +They avoid brewery, of course, since Petrus like to occasionally enter it +incognito and examine Nefas's mistress or two throughoutly. +"One must know one's enemy as well as himself" he explain if player +recognize him. +Since in future characters can avoid being telepathically detected by +wearing tin foil as helmet, giant floating eye placed on top floor as added +security measure. +It rotate all the time, its gaze penetrating all hidden corners of Attnam, +making enemies of Church despair. +Training dummies can be on back yard whose access be easier. +There also very hard wall against which prisoners condemned to capital +punishment be led. +There they shot by execution squad armed with wands of fireballs. +North of it be graveyard. +This be church after all. +There well kept graves of numerous world famous, powerful, just and +righteous Attnamese heroes who Cardinal of Truth invent. +There be also gravedigger named Ummah. +He genius mathematician who dig graves and play saxophone as hobby. +Tomb of unknown soldier there as well. + +Town south of Cathedral can be as Ivan planned. +Leader question usefulness of carpenter, though. +Of course it fun when he ask "How much wood would a woodchuck chuck if a +woodchuck could chuck wood?" but wooden equipment not very powerful in IVAN. +Perhaps Ivan could invent a couple of interesting items he would himself be +ready to buy from carpenter. +Cathedral will not use windows because this forbidden in holy book of +Valpurus. Windows is Evil. +Other buildings may have them though. +Mammoth-proof city wall good idea to protect Church property. +Citizens, for example. + +Leader also satisfied with graphics Ivan send. +It'd be great if some day Party could convert all graphics to 32x32 pictures +and increase resolution. +Leader like to see more detailed siren. +And IVAN would truly be great playground for Ivan artistic ability. +Alas such task would mean much graphics work. +Party could of course make small program which do rough doubling and then +smooth result, but Ivan still would have to check all graphics. +If Ivan has much free time at some point in the future and want to try doing +at least some part of conversion, he announce this to Leader. + +Leader distribute new furniture to citizens ASAP. +Ivan idea of NPCs potentially forgiving hostile actions good, but it most +likely won't make to this version. +And bug comrade Vilho find fixed in current beta binary. + +Ivan on October 14 2004 +----------------------- + +Ivan too vital: what unpleasantity! +Ivan stay up for 30 hours. Ivan like old man who have smoke billion +cigarette. Ivan not vital. Ivan feel urge to cccp! Ivan cccp and võila!! +Panda bear emerge fuzzily from scratch +Ivan now very på*§n and go to sleep + +Leader on October 16 2004 +------------------------- + +Good! +Next Ivan draw some happy soviet peasant children, eg. boy and girl head & a +couple of randomizable cloth styles. +They good for making Attnam's peasant houses more lively. +Leader also let them all play with panda, pose with them and take a few +pictures of group. +They prove leader devoted friend of nature and youth. +Appealing propaganda vital in these dire days. +Ivan recall wicked capitalist sympathisers seize Kreml from leader year ago. +But fortunately bold leader loyalists fare well in civil war now. +Party already regained control of almost half of Kirghiz Soviet Socialist +Republic! +When Red Army make its destined return to Moscow Ivan rewarded amply for his +services. +Leader plan to erect giant statue of himself on Red Square, and Ivan can +have his name carved on its left pinky toe! +Ivan also get honor of high position in Citizen Purification Committee. +This institution will be founded to reeducate those who cooperated with +false government. +Ivan should prepare for this long and tedious task by starting to recruit +punishers immediately. +Ivan put one in humanoid.pcx along with children. +And he ought not forget happy mask. + +Leader on December 8 2004 +------------------------- + +Leader hope Ivan had pleasant and vodka-plenty Independence Day. +It one day when all Finnish should recollect the munificence of Uncle Lenin, +who graciously gave them their sovereignty! + +But Leader disappointed Ivan not submit any score to past IVAN competition. +Leader order happy mask punisher beat Ivan to pulp. +Happy mask punisher disobey since Ivan create him. +Leader annoyed. +Everyone disregard Leader these days. +Just because Leader was overthrown and has unluckily failed in every attempt +to regain power not mean people allowed to have different opinions than He! +Some of latter become ridiculously outrageous lately. +Last week Tajik newspaper call Leader "one stupid and predictable gay +terrorist-supporting mass murderer". +Leader lose temper completely after seeing article. +Leader forced to stop strip poker match with Osama bin Laden and Mullah Omar +and swear vengeance on spreaders of such blatant lies. +Leader order his guerillas to march into Stalinabad and remove it from CCCP +map with flamethrowers Ivan made. +Alas, this trap of Enemy. +When city burned, troops notice they surrounded by army of Emmanuel +Goldstein, the malevolent mastermind of anti-Leader revolution. +Leader loyalists engage in fierce flame war to defend socialist honor. +But enemy numbers overwhelming and eventually gas reserves used up. +Leader give order to use vodka as flamethrower fuel instead. +This however minor miscalculation. +Soon soldiers become sober, start to think and switch sides. +Soviet cause suffer defeat and Uncle Lenin cry tears of blood deep in His +mausoleum. + +Fortunately Leader still partying wildly with bin Laden and Omar and high +enough not to become depressed by setback. +After a rather exciting round of Twister Leader ask if they could invest a +bit to Leader plan of retaliation against Goldstein. +Osama scratch his masculine beard and say perhaps he indeed can fund Party +with $100 million. +It gift between friends so he want nothing in return. +But 60 megaton Tsar Bomba or two would naturally make money appear sooner in +Leader Swiss account. +Unfortunately all these in enemy hands now. +Leader recall Ivan unemployed so he have time to arrange one. +Ivan just infiltrate the appropriate nuclear facility and smuggle bomb to +item.pcx. +To identify the toy, Leader provide crypted description at + +http://a25a.mtalo.ton.tut.fi/wiki/index.php/Item_ideas#Misc + +under the codename 'thaumic bomb'. +As additional recreational task Ivan design gas grenade in same file. +This can be loaded with eg. sleeping, mustard or tear gas. +Ivan has one week to fulfill duties. +Then Leader has next strategic meeting with his muslim allies and their +harems and Leader want to suprise all with some laughing gas grenades to +provide proper mood for negotiations. + +Leader on December 8 2004 +------------------------- + +Ivan should not fail to keep in mind Great Deadline on Friday. +This mean he should, if any way possible, be available on IRC in today and +tomorrow evening, and preferably in moderately sane condition, too. +Small but critical graphics checks and opinions about important game, +homepage and marketing issues will most likely be needed. +There also additional, longer term assignment Leader forget in last letter. +Ivan express eagerness to add monster descriptions which seen if player +press some help button, move cursor above monster and push another key. +Or, alternatively, use some help command and write name of the monster. +This open menu, where verbal description of monster's nature or quote from +famous book relating to the creature, or something similiar, be shown, and +below it some information which Party decide later. +These could include normal or estimated stats, approximate danger or +whatever, depending on a number of factors (whether the player has used a +stethoscope on the monster, has killed many or few of them, or is more or +less intelligent, could affect the data shown). +This however too big a job to do in one session alone. +Therefore Leader ask Ivan write eg. about ten descriptions in some file and +deliver it to Leader. +Entries should preferably range from 50 to 500 words. +Ivan note he not need to describe every creature around, just each category. +Id est he not pay attention to palm leaf golem, kamikaze dwarf of Nefas nor +orc officer. +Instead he consider general golems, kamikaze dwarves and orcs. +Some good examples or quote-style entries (siren, mind worm) exist at + +http://a25a.mtalo.ton.tut.fi/wiki/index.php/Monster_ideas + +Ivan not restricted to literature, movie, game, etc. quotes, though. +He can conjure up frightening prosaic portraits of the creature's hideous +nature. +He can write poems that affect the unconscious depths of reader's mind, +inspiring his subjective understanding of the complexities of the monster's +inner spiritual self. +He can design small tales which feature the respective being, thus giving +additional insight into its lifestyle, habits, basic tactics, and so forth. +In short, Ivan can write almost anything he like as long as it concerns the +creature somehow and sounds good. + +Because Leader given Ivan so much work, he provide short list which Ivan +ought to tattoo on his muscular arm so it in his mind all the time: + +-------------------------------------------------------------------------- + +1. thaumic bomb in item.pcx, which looks like a nuclear bomb; this should be +very big but have some space available for a magical halo caused by thaumic +leakage (low priority) +2. dwarven gas grenade in item.pcx (medium priority) +3. verbal descriptions of about 10 monsters, for in-game reference (high +priority) +4. 16x16 -> 32x32 conversion of tiles in each file (low priority in short +term, high in long term) +5. advising Party in the myriad of critical questions which may arise before +the Deadline at 10.12., 23:59 EET (ultra-high priority) + +-------------------------------------------------------------------------- + +Ivan on December 8 2004 +----------------------- + +Ivan make bombs out of Grand Ire towards the capitalist pig bathing and +sweating in his pool of coins! Why has enemy wanted to disgrace the pure +red Stalinabad to obey their mindless, limiting and unjust dogma of +liberal society? Father Lenin is crestfallen, only because of the greed +of Goldstein and his fellow criminals! Let justice be! Ivan percent a +new thaumic bomb tool for the Party to make a "Just Adjustment". Let our +motto be: "Enemy may succeed in entering a city of ours, but they won't +succeed in leaving the city without three heads!" + +Ivan notice smell of chlorine. Oh no! Ivan must have left the light bulb +boiling! Then Ivan will QWERTYUIOP and MNBVCXZ. It seems like there is +faint leakage in thaumic bomb! Ivan rub a clock and leakage shuts. What +good luck! + +A note on gas grenades: Ivan make deflated and inflated graphic versions +of the device, so that user can pick desired gas type from the filling +station himself. + +Ivan now get keen on monster descriptions. + +Leader on December 9 2004 +------------------------- + +Great Day beginning. +Leader ask following: + +Is broken gauntlet (item.pcx, 80:400) good-looking enough for IVAN? +It not Ivan-drawn, since Leader needed it immediately the day it was +integrated in era long forgotten. +Leader ripped intact one quickly to shreds and he not really proud of it. + +Ivan once complained about the outlook of certain game areas. +Are these now better? +Especially GC levels 6, 9, 10, 11 and 12 should be checked. + +Screenshots in game homepage horribly old. +Leader ask Ivan look at + +http://a25a.mtalo.ton.tut.fi/wiki/index.php/Screen_shots + +and choose, say ten, best pictures. +Leader and comrade Hex will also choose their favourites. +Out of these Party get very appealing propaganda poster collection. + +All these questions require answering ASAP. + +Ivan on 10 December 2004 +------------------------ + +Ivan comprehend. Favourites of pictures are: +screen 1 - main menu good to show because it create familiar feeling to game +screen 7 - show the glory of combat! and illustrate fine equipment handling +screen 8 - show what happen when shit hits the fan +holyscreen 2 - show some fine city +holyscreen 33 - show some totally trashed dungeon +holyscreen 17 - show some fine magic gas and regular dungeon fighting +holyscreen 23 - seeing items invigorate viewer +holyscreen 30 - some new landscape +holyscreen 28 - being able to buy pets is a rare priviledge in the +roguelike genre! + +ivan also pick joker extra bonus 11th picture!: +holyscreen 39 - show lost limb! + +Gauntlet look completely fine in ivan eye, is seeming like a few metal +plate and screw or leather strip be loose, which is perfect. + +Ivan will now dig into dungeon levels and return with reports. + +Ivan on 10 December 2004 +------------------------ + +Wait, Ivan incorrect. Gauntlet look rather clumsy. Ivan remind that +there have been a better-looking versio of gauntlet somewhere? Hmm.. +mystify. Ivan check hard drive and draw new if needed + +Ivan on 13 December 2004 +------------------------ + +Ivan have been drink 900 bottles of CCCP vodka and so usage of IvanWiki +at the moment be terrifyingly inconvenient. Thus Ivan text submit to leader: + +Team Formations: +Teams members can be arranged to formations. A dialog window opens, and +in the window there is a matrix in which you can pick team members from +a list and place them on it. + +Example: (j and x are some monsters, @ is player) + +----------------------- +| | | | | | | | | | | | +----------------------- +| | | | | | | | | | | | +----------------------- +| | | |x|j|x| | | | | | +----------------------- +| | |x|j|@|j|x| | | | | +----------------------- +| | | |x|j|x| | | | | | +----------------------- +| | | | | | | | | | | | +----------------------- +| | | | | | | | | | | | +----------------------- + +Team members will attempt to create the formation whenever the enviroment +permits it. This means they will also attempt to copy players movements. +Formation can be set to modes Strict (members try to stick in formation +no matter what) or Loose (members will help each other in combat etc.). +Perhaps the function would also require a certain INT from the members, +otherwise it could not be performed. It might also require a certain INT +from the player, and maybe higher INT would increase the maximum allowed +size of the matrix? +On the side of the dialog window there could be following buttons: +Rotate formation 90 degrees to left +Rotate formation 90 degrees to right +Mirror formation (left/right) +Flip formation (up/down) +The function would be of importance, because having and controlling a team +is essential in the harder levels of the game. +Perhaps there could also be an Allow team rotation -switch, which, if set, +would allow the team to try to adjust to the environment by rotating, +mirroring or flipping itself. + +Ivan on 16 December 2004 +------------------------ + +Ivan play and feed back: + +Iventories should seem like opened trees, so that you could see the +contents of chests like any other objects + +There should be a selectable default container, so that it would be +faster to use containers. + +There should be a button for Wear, so that Wearing items would be faster. + +Leader on 16 December 2004 +-------------------------- + +Leader like tree-like inventory screen idea very much. +Leader not think of it before. +Default container idea also good, though it has been presented before and +could perhaps be developed further. +Perhaps player have inventory structure somewhat like this: + +player +-helmet +-amulet +-etc. +-right pocket +--item +--item +-left pocket +--item +-sack +--item +--item +--chest +---item +---strongbox +----item +----etc. +---etc. +--etc. + +Choosing default container work like this. +When game start, player get sack which default container. +Player push certain key. +Screen opened. +Player choose from: + +-right pocket +-left pocket +-sack +--chest +---strong box + +If container locked, it ignored. +When player press comma, item go to default container. +If default container full, player prompted with screen above to choose +another container. +Sack quite big, enough for most normal items but not for 10 ass corpses, for +example. +Pockets small, but they take almost no time to access, so a wand of teleport +could be stored there. +These like quick item slots in certain games. +They only usable if player wear certain armours. +There also command for emptying one container and putting its items to +another. +This usable when eg. changing sack. +There waterproof, fireproof etc. different kinds of sacks. +They can of course be nested in tree-structure for extra protection, but +retrieving an item which is inside chest inside sack inside strong-box +inside sack very slow, so basic sack usually quite important to upgrade. +Also, there magical containers like bag of holding, which stores its +contents in another dimension, so it can hold an enormous amount of things +and still have constant weight. +Of course, there could be some drawback, for instance if there thousand +items stored in BoH, retrieving one would be very slow since the player +wouldn't be able to find right one quickly. +The time would depend on dexterity, perception and intelligence (if very +intelligent, he remember where he put each item almost instantly). +There could also be magical chest where time does not pass or does so very +slowly. +This perfect for storing food, but useless for lyre of charm, magic whistle, +etc. + +By command for wearing, what does Ivan exactly mean? +There no sense in opening menu where each equipment slot shown, since this +almost identical to equipment screen. +Perhaps key, which open inventory, and when player select for instance a +cloak, the player chooses the only slot where cloak fits, ie. the cloak slot +and empties it first, if necessary? +If player choose gauntlet and there pair, both gaunlet slots emptied and +gauntlets put in place. +What would Ivan like to happen if he choose only one gauntlet? +If only one gauntlet slot empty, it of course go there. +But if two are available? +Right arm? +The arm which the game thinks more important, according to some formula? +Or would there be question where player is prompted to choose either 'r'ight +or 'l'eft gaunlet slot? + +Leader not commented team formations yet. +First of all, Ivan idea imply displacement system has to be modified. +If team moving in formation along a hallway like this + +##### +.x@y. +##### + +and player presses right arrow key, he should automatically wait for team +member y to move eastward and then follow it. +Now he displaces it or, worse yet, fails to displace it because it so +stubborn. +Ivan agree? +In any case, IVAN AI code not very good and Ivan probably have to wait for +formations for some time. + +Leader on 19 January 2005 +------------------------- + +Leader summon Ivan to front line in central Turkmenistan where he lead proud +and determined soviet forces pushing west towards Ashgabat. +In scorching desert heat Ivan hurry towards Leader command nexus. +Above him traitor Tupolevs swarm like fireflies, lighting night of Karakum +with a myriad of deadly missiles, turning battlefield into nightmarish rain +of falling stars. +Surrounded by deafening artillery fire Ivan arrive to destination. +Ivan find Leader and his generals bathing in an oasis hidden amidst a +graveyard of smoking tanks, drinking vodka and being creative by telling war +stories of their past heroic victories. +One officer currently barbequing a KIA Akhal-Teke horse in flame of burning +petroleum pipeline. +Leader grab towel and asks Ivan to have a bite with him. +Mouth full of roasted horse meat he explain urgent intelligence mission +waiting Ivan deep in enemy territory. +Leader trust Ivan only one who can succeed in job. +All other Leader subjects likely to defect to rebel side if allowed to go +that far from watchful Leader eyes. +Ivan ought to take 32x32 Lada and drive to Kiev ASAP. +There he seek agent Fidel of Cuban intelligence service in Boryspil airport. +Of course, Fidel do all he can to hide identity and not arouse suspicion of +Goldstein's spies. +Fidel choose role of American patriot, wear big cowboy hat, wave big flag of +stars and stripes and loudly sing US national anthem. +When Ivan locate him, he ask, "I have always wondered, what was the maiden +name of the wife of the fourteenth president of your mighty nation?" +If he answer, "I have no idea", Ivan know he Fidel. +Every real American patriot would know answer. +Then Fidel ask Ivan light his cigar. +Ivan swap it with roll of tobacco Leader give him, and light it instead. +After Fidel and Ivan part, Ivan put cigar to item.pcx and send it to Leader. +No one suspect Leader just received important microfilm from comrade Castro! +Leader wait for rapid success. + +Leader and Ivan on March 3 2005 +------------------------------- + +Leader: Leader need the magnificent ancient grenade relic soon to defeat enemies. +Ivan: ah ok +Ivan: getting to it +Leader: When does ivan have big final tests btw? How many has he left? +Ivan: work done +Ivan: sent to timo.kiviluoto@mail.suomi.net +Ivan: ivan complete english in autumn and result a puny E, but Party man not fond of schools, only work +Ivan: spring ivan complete finnish +Ivan: high school go tho 3,5 or 4 years +Ivan: next autumn the Real and short mathetamics +Leader: Leader see. And he glad to hear, as CCCP citizen ought not to know too much too soon, it often give but funny political thoughts. +Ivan: indeed, liberal "intellectuals" twist concept of intelligence. itelligence involves shovel, hammer, sicle or firearm. +Ivan: and paint brush for social realism +Ivan: or to colour vast cccp flats with fabulous gray +Leader: Besides, Leader himself has studied very slowly lately as well, only doing about half year's work in one and half. This due to too liberal academic society which not punish from being absent. This good proof that freedom only encourages laziness! CCCP men ought to be forced to work vigorously to be happy! +Ivan: exact! liberality corrupt even most able soul! +Ivan: ivan should in near future deticate a few day worth of work for 32x32 conversion +Ivan: work begun but progress slow +Ivan: ivan just wait for suitable winds +Ivan: but Party men blow winds them selves +Ivan: ivan yet to grasp this well +Ivan: but now ivan momentarily retire to shoot "counter"-terrorists with ak47 +Leader: When Ivan return to work, here be next task: Leader noticed several elite guards of Cathedral quite athletic and virile and may arouse lust in weak women. He ask Ivan next draw chestity belt for Petrus's wives, to prevent accidents. +Ivan: ah what pleasurability task! +Ivan: ivan excess in uncomfortability design for honour of sexual frustration +Leader: Excellent! +Ivan: work done +Leader: Good good. Now Petrus can sleep peacefully (eg. with guards' wives) knowing his beautiful possessions are safe from sinful temptations. +Leader: Next Ivan build some interrogation devices for obedience officers to olterra.pcx. +Leader: Ivan choose at least three machines which are easy to construct with his technical skills and efficient in the task. +Leader: Suggestions include, but are not limited to, eg. red-hot coals, brazen bull, Judas Chair, rack, iron maiden and spanish boot. +Ivan: hmm +Ivan: investagating on topic + +Ivan on 3 March 2005 +-------------------- + +Communism may have wipe out christian folly, but christian armament suit +even man of Party! Thrown in middle of bourgeois hall, fatty with cigar +exclaim: "oh joy! some one believe in static and sturdy value of God and +Church! It look precious too, maybe give me money!" Then ignite and +bourgois sent into clouds of sunset golden eastern sky, in graceless +gibs and splurts of blood. End of bourgeois; belief in things that false +make him new bed in red velvet horizon where he lay for rest of +eternity, rotten soul no more. + +Ivan on 3 March 2005 +-------------------- + +Chastity belt covered in crude welding spots from inside to ensure pain +when whory thought make wife even touch groin and lust when belt finally +remove. Ivan also think idea of some sort of mouth gag to both prevent +oral sex with worthless trash humans but also reduce unnecessary +woman-talk of gossip, shallow topics and disgusting nagging. + +Leader on 18 March 2005 +----------------------- + +Two strings of footsteps stretch along a path leading up to the top of our +world. +Enormous white mountains pierce the serene azure sky above them, like +ancient titans placed to guard the land from corrupted civilization. +Here the glorious nature which gave birth to Marx and Lenin shows its true +might. +High above the footsteps dive in a small cave hidden in a Himalayan scene +radiating beauty of Earth of yore, untouched by capitalist filth. +Like a serpent, the tunnel twists and winds through endless masses of rock. +Finally, the trails of feet reemerge in a heavenly valley, deeply happy to +be isolated from the outside world, now so completely engulfed in a horrific +war of men. +The footsteps make their way downwards following two hooded figures like +shadows. +Slowly snow is beginning to disappear. +Through a village full of marvellous far estern architecture, past +glittering waterfalls and through gardens resembling paradise journey the +two figures. +They enter a tall and beautiful hexagonal manor surrounded by six finely +carved statues of Buddha. +In a large wooden hall with no furniture present they sit in front of an old +monk emitting an aura of pure wisdom, spiced with razor-sharp intellect. +The hoods are removed, revealing the respected and self-confident face of +the Leader of Citizens and the huge, muscular body of his feared bodyguard +Ivan Gorovits. +"Welcome to my humble abode, brethren", the valley elder states. +"Blessed be Lenin who guided us here and shielded us from rockslides, +avalanches and enemy smart missiles! We've come to negotiate about the +alliance, O elder of Shangri-La, mayor of Shambala, leader of Tralla La, the +wisest man in Xanadu!" +"Ah, so our anguish is not unfamiliar to you. +For two millenia we lived in utmost joy, embracing the Wisdom of Buddha, +until one fateful year recently. +A wicked capitalist called Scrooge found our valley, entered and taught us +how to use bottlecaps as money. +We now presume he had deviced a malevolent plan to buy our valley with a +billion bottlecaps and turn it into a theme park for western tourists! +We were almost drowned in scrap iron until we drove the man out. +We cursed capitalism forever and send explorers to search for an elixir to +cure us from this terrific cancer. +They brought back das Capital, the wisest book on Earth since Tripitaka! +Soon everyone in the valley joined the Nepalese Maoist party and began to +wage civil war against the evil bourgeois-sympathizing feudal monarch of the +country in its ranks." +"Leader know all too well. +CCCP, our beloved homeland, was defiled by malicious capitalist named +Goldstein just eighteen months ago. +Its happy collective farms were abolished, our people driven to poverty, +unemployment and despair. +Dark influence of Enemy spreading like plague across our planet! +We must strike back, we must srike together, we must srike hard!" +"Indeed. We ought to work together. +However, a chain is as strong as its weakest link. +I must be assured you are a worthy ally. +First I must test your wisdom. +Tell me, how many Zen buddhists does it take to change a light bulb?" +Leader smile. +"Answer be naturally the cypress tree in the garden!" +Elder nods and claps his hands. +A novice monk enters the hall with a white cow. +"Tell me, does this cow have Buddha-nature or not?", elder asks. +Leader kick cow, who say "MU". +"Correct. Last but not least I must ask you to describe the sure sign of a +CCCP kolkhoz being founded in the middle of deep Siberian taiga." +"Tree falling in the forest." +"Praise Buddha! Russian communists are indeed as wise as I'm told", elder +cries out in enthusiastic joy. +"Great, then signing pact is but formality now?" Leader queries. +"Not too fast my sons. +Enlightenment is the most important trait of a man, but it alone won't win +wars. +Strength of body is needed also. +As per tradition, you ought to solve a difficult and dangerous quest before +the alliance becomes reality. +A frightful monster has been pestering our village recently. +It appears from the mountains, freezes everything it touches, extinguishes +all fires by sitting on them, and according to legends is bound to the name +Groke. +I will quote its description from an ancient text: + +'On tienoo äkkiä niin hiljainen ja musta ja mörkö niin kuin vuori tuijottaa, +ja jäinen maa on täynnä kammotusta, kun kuustakin pois värit putoaa. Ja +Nyyti sanoi: helppoa ei tule olemaan!' + +Slay the beast, skin and stuff it and bring its body to character.pcx!" +Leader sighs and whispers something briefly to Ivan. +Ivan opens his backpack, takes his AK-47, rocket launcher, flame thrower, +grenade collection, gas mask, armour, etc. etc. out of it. +Swiftly he equips everything and strides to the door. +Before disappearing in the refreshing Himalayan outdoor air he turns and +states his grave farewell: +"Ivan ready to hunt." + +Ivan on 12 April 2005 +--------------------- + +Groke have been put into misery. Colour grayish phtalo blue - suggest +but not conclude to: (81,77,140) +Colour 2 teeth colour - white with hint of yellow suggest (255,253,229) +Teeth may not have sufficient contrast to resemble a grinning row of +teeth, in case this the case then Ivan fix. +Colour 3 let be light grey or white for the time being. + +---------------------------------------------------------------------------- + +End of document. diff --git a/Doc/Data/Founders.jpg b/Doc/Data/Founders.jpg new file mode 100644 index 0000000..f6033e4 Binary files /dev/null and b/Doc/Data/Founders.jpg differ diff --git a/Doc/Data/Graphics.txt b/Doc/Data/Graphics.txt new file mode 100644 index 0000000..8e1aaeb --- /dev/null +++ b/Doc/Data/Graphics.txt @@ -0,0 +1,333 @@ +Iter Vehemens ad Necem graphics documentation +--------------------------------------------- + +The material color system +------------------------- + +Most IVAN graphics use special palette indices to mark areas whose +color may vary depending eg. on material, on skin color or on special +magical effects. These are called material colors or m-colors. Up to +four m-colors may be used in one picture and each may exist in 16 +different brightness levels. The palette indices attached to each +m-color are: + +m-color 1: 192-207 +m-color 2: 208-223 +m-color 3: 224-239 +m-color 4: 240-255 + +When objects and beings are created, IVAN first determines which +actual colors are to appear in place of m-colors. It breaks these +colors into RGB components and analyzes the tile pixel by pixel. +If it encounters an m-color, the components of the color appearing +at that place in game are determined as follows: + +[component in game] += [requested component] +* ([palette index] - [lowest index of that particular m-color]) +/ 8 + +rounded down, or 255, whichever is smaller. + +Example: a mithril spear is generated. The game knows that the edge +is always drawn using m-color 1 and the handle/shaft using m-color +2. (see file descriptions below for more info about when a particular +m-color should be used) Mithril's and wood's RGB values are found +in material.dat and equal 224:224:224 and 140:96:48. While scanning +the spear tile, suppose index 202 is encountered. It belongs to +m-color 1 gradient. The color appearing in the game (under normal +light conditions) will be + +Red = min(224 * (202-192) / 8 = 280, 255) = 255 +Green = min(224 * (202-192) / 8 = 280, 255) = 255 +Blue = min(224 * (202-192) / 8 = 280, 255) = 255 + +i.e. pure white. Next suppose index 214 is encountered. The +resulting color is + +Red = min(140 * (214 - 208) / 8 = 105, 255) = 105 +Green = min(96 * (214 - 208) / 8 = 72, 255) = 72 +Blue = min(48 * (214 - 208) / 8 = 36, 255) = 36 + +which is slightly darker than the requested color. + +Note that you don't have to understand this formula to make +good-looking graphics. Use the same m-colors as the other tiles +in the particular file and you'll do fine. + +Indices 0-190 are called regular colors or r-colors and they appear +in each picture in the same way. Graphics designers may change the +RGB values of r-colors as needed, but they should be careful not +to ruin existing pictures by doing so. + +Index 191 is used for the transparent color and its RGB value must +be 255:0:255. Areas filled with this index will not be drawn in the +game. + +Graphics files +-------------- + +(*) uses m-colors + +char.pcx (*) non-humanoid character graphics +cursor.pcx the cursor +font.pcx (*) the default font +fow.pcx the fog of war tile +glterra.pcx (*) ground terrain graphics +humanoid.pcx (*) humanoid bodypart graphics +icon.bmp the banana and pick-axe icon +item.pcx (*) item graphics +menu.pcx the main menu background +olterra.pcx (*) over terrain graphics +symbol.pcx symbols and explosions +wterra.pcx world map terrain graphics + +char.pcx +-------- + +All graphics of non-humanoid characters (those that don't have +severable bodyparts) are contained herein. They must be 16x16, +although these exists two 32x32 pictures which are reserved for +future. The actual colors are defined in char.dat but may be +overridden in the code. The m-colors which may be used are: + +m-color char.dat value +1 SkinColor +2 TorsoMainColor +4 TorsoSpecialColor + +M-color 1 should always be the main color of the picture. Others +are used only in certain pictures like carnivorous plants, which +have varying flower colors. Indices 195-200, 211-216 and 243-248 +tend to be the best choices; beyond that the colors are often too +dark or bright in the game. + +The character silhouette used in the game panel is also located +here. The format should be pretty obvious, except that it is a +mirror image, i.e. that the right leg and the right arm are on the +left and vice versa, because they are that way in the silhouette's +view (it is thought to look at the user's direction). + +Btw. that guy on the right side of the pcx file is our head +graphics designer being tortured for missing yet another +deadline. + +cursor.pcx +---------- + +The cursor shown over the player or in the lookmode etc. Size +must be 16x16. Index 255 is used for the transparent color. +It must be 255:0:255. + +font.pcx +-------- + +This file contains the character set used for the text in the game. +It should include ascii-characters 32-255. Each or the 14 lines +should have 16 characters. All characters should be m-color 1, +located in distinct 16x16 pixel blocks but using only an 8x8 area. +In the game the pixels are material colorized and little shades are +added to them, so that they actually use a 9x9 area in the picture. + +fow.pcx +------- + +The fog of war tile, which is masked blitted over tiles not seen +but vaguely memorized. Size must be 16x16. Index 0 is used for the +transparent color. It must be 0:0:0. The reason for the odd color +is optimization; if a completely dark square is added to the +player's fow area, this picture is blitted in the memory instead +of creating a black tile and masked blitting fow.pcx onto it. + +glterra.pcx +----------- + +Ground terrains, for instance grass and floors are located here. +They must be 16x16. M-color 1 depends on the material. Other +colors are defined in glterra.dat as follows: + +m-color glterra.dat value +2 MaterialColorB +3 MaterialColorC +4 MaterialColorD + +M-color 1 should always be the main color of the picture. +The tiles should be quite bright so that eg. a stone golem on a +stone floor can be seen easily. Indices 198-201, 214-217, 230-233 +and 246-249 seem to be the best. For obvious reasons, the +transparent color may not be used. + +humanoid.pcx +------------ + +All humanoid bodypart pictures are contained herein. They must be +16x16. The first two vertical lines are allocated for leg/groin +pics, the next two for torsos, the next for arms and the next for +heads. The four columns on the right are for "in hands pictures" +which aren't currently used, so ignore them. Humanoid pictures +are build up from these bodyparts, and if the character loses for +example a limb, its graphic is removed from his/her tile and the +severed limb item will use it instead. + +The leg/groin pictures actually contain three different bodyparts: +right leg, left leg and groin. Only the six last lines in the +16x16 tiles are used, and the pixels allocated for each bodypart +are shown in the following map: + +rrrrrgggggglllll +rrrrrrggggllllll r = right leg +rrrrrrrgglllllll g = groin +rrrrrrrrllllllll l = left leg +rrrrrrrrllllllll +rrrrrrrrllllllll + +Note that the right leg is on the left and vice versa, because +it is that way in the character's view (he/she always looks at +the user's direction). + +The arm picture contains both left and right arms. The left half +of the picture is allocated for the right arm and vice versa, +for the reason above. + +The actual colors are defined in char.dat but may be overridden +in the code. The m-colors which may be used are: + +Leg/groin pictures: + +m-color char.dat value +1 SkinColor +2 LegMainColor (clothes) +3 BootColor +4 LegSpecialColor + +Torso pictures: + +m-color char.dat value +1 SkinColor +2 TorsoMainColor (clothes) +3 BeltColor +4 TorsoSpecialColor + +Arm pictures: + +m-color char.dat value +1 SkinColor +2 ArmMainColor (clothes) +3 - (reserved for future) +4 ArmSpecialColor + +Head pictures: + +m-color char.dat value +1 SkinColor +2 CapColor +3 HairColor +4 EyeColor + +Defining the ClothColor value in char.dat sets LegMainColor, +TorsoMainColor, ArmMainColor and CapColor all at once. + +Preferable material color indices are the same as those in char.pcx. + +icon.bmp +-------- + +The window icon. Must be 32x32. Index 255 is used for the +transparent color. It must be 255:255:255. + +item.pcx +-------- + +All item graphics are stored here, with the exception of severed +bodyparts, which use the tiles in humanoid.pcx. They must be 16x16, +although one 32x32 pictures exists which is reserved for future. +The colors depend on the material as follows: + +Weapons: + +m-color depends on +1 edge material +2 handle/shaft material + +Containers (cans, bottles, etc.): + +m-color depends on +1 container material +2 contained material + +Other items: + +m-color depends on +1 material + +Other m-colors must be overridden in the source code for each +item. M-color indices 197-203, 213-219 and 229-235 seem to +look best in the game. + +menu.pcx +-------- + +The main menu background. Must currently be 800x600. + +olterra.pcx +----------- + +Over terrains, for instance walls and decorations are located +here. They must be 16x16. M-color 1 depends on the material. +Other colors are defined in olterra.dat as follows: + +m-color olterra.dat value +2 MaterialColorB +3 MaterialColorC +4 MaterialColorD + +Fountain is an exception, since its m-color 2 depends on +contained material (water, for example). Indices 197-199, +213-215, 229-231 and 245-247 seem to be the best for outlook. + +symbol.pcx +---------- + +Collection of miscellaneous symbols used in the game. Sizes +must be 16x16. Index 255 is used for the transparent color. +It must be 255:0:255. + +Explosions are also currently stored here. They should be square- +shaped and their sides should be an odd number times 16 in length. + +wterra.pcx +---------- + +All world map graphics are contained here. Index 255 is used for +the transparent color, and it must be 255:0:255. + +Ground terrains dominate the upper part of the file and they are +stored in 48x48 squares so that the middle 16x16 square is the +actual tile and the others are so called border tiles which may +be drawn over lower terrain to make forest borders etc. look +much better. + +Here's an exact explanation of how this happens. Each pair of +(vertically, horizantally or diagonally) adjacent squares on +the world map is scanned, and their terrains are compared based +on a priority value which been attached to all terrains and is +determined solely by outlook factors. If the values are the equal, +i.e. the terrains are the same, no borders will be drawn, +otherwise a suitable border tile of the terrain with the higher +priority is drawn over the other. Note that of all terrains, +water has the least priority, so it doesn't need border tiles. +Instead, it has 16 wave animation frames which make it look more +vivid in the game. + +Care must be taken that each side of a tile fits correctly +to the opposide side and to the attached border tile. Note +also that multiple border tiles may be drawn over a tile and +even over each other and they still must look good. + +Beneath the ground terrain tiles are the over terrains. They +are much easier to draw, since one 16x16 tile is enough. Also +a nice smiley is included, just to make all people making +graphics happy. + +End of document +--------------- diff --git a/Doc/Data/IvanProblem.txt b/Doc/Data/IvanProblem.txt new file mode 100644 index 0000000..e8268e5 --- /dev/null +++ b/Doc/Data/IvanProblem.txt @@ -0,0 +1,15 @@ +Ur-Khan the orc marshal has appeared way too early in GC level 6. +The situation map: + +#### #### +#### #### +#### #### # = moraine earth + G U @ = you +####@#### G = your pet adamant golem +#### #### U = orc marshal +#### #### + +You have a scroll of taming and all wands. +Reading the scroll takes two turns. +You cannot survive for more than one turn if Ur-Khan concentrates his attacks on you. +How can you capture him? diff --git a/Doc/Data/Levels.txt b/Doc/Data/Levels.txt new file mode 100644 index 0000000..0286a75 --- /dev/null +++ b/Doc/Data/Levels.txt @@ -0,0 +1,22 @@ + Mons Inter Diff E- E+ Items MinPr +UT1 5 200 5 10 1 15:30 0 +Ut2 15 175 20 5 2 20:40 5 +Ut3 10 150 35 0 3 15:30 10 +UT4 25 60 100 0 40 15:30 70 + +GC1 10 140 50 0 5 25:50 20 +GC2 12 130 60 0 10 25:50 30 +GC3 14 120 70 0 15 25:50 40 +GC4 16 110 80 0 20 25:50 50 +GC5 18 100 90 0 25 35:70 60 +GC6 30 90 100 0 30 25:50 70 +GC7 22 80 110 0 35 25:50 80 +GC8 24 70 120 0 40 25:50 90 +GC9 26 60 130 0 45 0 100 +GC10 30 50 150 0 50 25:50 120 +GC11 34 40 170 0 55 35:70 140 +GC12 50 30 190 0 60 45:90 160 + +AC3 10 100 10 25 5 0 - +AC4 15 75 20 15 15 0 - +AC5 20 50 30 5 25 0 - \ No newline at end of file diff --git a/Doc/Data/Lines.txt b/Doc/Data/Lines.txt new file mode 100644 index 0000000..f67b479 --- /dev/null +++ b/Doc/Data/Lines.txt @@ -0,0 +1,40 @@ + public source datafiles graphics +name version date release lang ports files lines bytes files lines bytes files bytes +RL prod. began 08.04.1999 +RL last build 04.05.1999 N C DOS 28 1262 26188 3 7385 22157 0 0 +RL2 last build 27.07.1999 N C DOS ? ? ? ? ? ? 0 0 +RL3 last build 28.07.1999 N C DOS 6 1424 24899 2 3703 11111 0 0 +RLPMM 0.100 alpha 10.12.1999 N C DOS 25 3261 78122 0 0 0 0 0 +RLPMM 0.190 alpha 24.12.1999 N C DOS 29 4070 96149 0 0 0 1 2950 +RLPMM 0.191 alpha 26.12.1999 N C DOS 29 4175 101119 0 0 0 3 27437 +RLPMM 0.192 alpha 26.01.2000 N C DOS 29 4397 107856 0 0 0 3 27850 +RLPMM 0.193 alpha 20.02.2000 N C DOS 30 5139 122083 0 0 0 3 29701 +RL5 0.200 alpha 11.02.2001 N C++/Asm DOS 33 7083 176441 0 0 0 6 117998 +RL5 0.210 alpha 25.03.2001 N C++/Asm DOS 33 8850 234228 0 0 0 8 141861 +RL5 0.220 alpha 23.04.2001 N C++/Asm DOS 37 10541 270063 0 0 0 8 141996 +IVAN 0.230 alpha 01.05.2001 N C++/Asm DOS 43 12110 316597 0 0 0 9 155737 +IVAN 0.231 alpha 07.05.2001 Y C++/Asm DOS ? ? ? ? ? ? ? ? +IVAN 0.240 alpha 27.07.2001 Y C++/Asm Win32 60 16210 440478 0 0 0 9 155755 +IVAN 0.300 alpha 20.11.2001 N C++/Asm Win32 97 26624 724371 1 782 20469 9 149021 +IVAN 0.301 alpha 10.12.2001 Y C++/Asm Win32 101 28206 766894 1 797 20825 9 149109 +IVAN 0.310 04.02.2002 Y C++/Asm Win32/DOS/Linux 104 33502 881187 1 916 21887 9 196192 +IVAN 0.311 25.02.2002 Y C++/Asm Win32/DOS/Linux 108 33786 888564 1 912 21881 9 196380 +IVAN 0.400 10.12.2002 Y C++/Asm Win32/DOS/Linux 123 52615 1430270 7 9013 205706 11 270042 +IVAN 0.401 05.02.2003 Y C++/Asm Win32/DOS/Linux 138 51457 1398352 7 9440 215166 12 355227 +IVAN 0.410 27.03.2003 Y C++ Win32/DOS/Linux 137 50032 1395504 7 10276 246754 13 285473 +IVAN 0.420 11.05.2003 Y C++ Win32/DOS/Linux 137 53490 1533799 7 10801 264237 13 333229 +IVAN 0.430 05.08.2003 Y C++ Win32/DOS/Linux 137 55705 1600010 7 11612 283226 13 344036 +IVAN 0.500 10.12.2004 Y C++ Win32/DOS/Linux 155 74367 2022380 7 15718 422035 13 397339 + +Competition: + +ADOM prod. began 12.07.1994 +ADOM 0.20 07.05.1995 N C DOS/Linux 62 23918 516497 ? ? ? 0 0 +ADOM 0.99 30.04.1997 Y C DOS/Linux/Amiga 143 103484 2593377 ? ? ? 0 0 + +ZAngb. 2.72 23.12.2002 Y C incredibly many 200 279614 6056321 34 32425 793279 45 1541919 + +Hack 1.00 17.12.1984 Y C several Unixes 73 14652 339084 2 431 19791 0 0 +Nethack 3.40 20.03.2002 Y C roughly all 385 242120 6573492 37 19191 705513 3 356014 + +Slashem 0.06E4F8 30.03.2002 Y C roughly all 418 283664 7659049 68 28007 1083943 28 2911824 diff --git a/Doc/Data/Units.txt b/Doc/Data/Units.txt new file mode 100644 index 0000000..10a815d --- /dev/null +++ b/Doc/Data/Units.txt @@ -0,0 +1,3 @@ +[Volume] = 1 ml = 1 cm^3 +[Weight] = 1 g +[Size] = 1 cm diff --git a/Doc/Data/Verbalization.txt b/Doc/Data/Verbalization.txt new file mode 100644 index 0000000..7246ce1 --- /dev/null +++ b/Doc/Data/Verbalization.txt @@ -0,0 +1,21 @@ +THV + +0 unbelievably inaccurate +1 inaccurate +2 decently accurate +3 accurate +4 highly accurate +5 extremely accurate +6 can't miss + + +SV: + + +0-2 fragile +3-4 rather sturdy +5-7 sturdy +8-10 durable +11-15 very durable +16-20 extremely durable +20- unbreakable diff --git a/Doc/Data/WSkills.txt b/Doc/Data/WSkills.txt new file mode 100644 index 0000000..f5b13bc --- /dev/null +++ b/Doc/Data/WSkills.txt @@ -0,0 +1,59 @@ +The 15 main weapon skill categories are: + +Uncategorized +*Unarmed Combat +*Biting +*Kicking +Daggers +Small Swords +Large Swords +Clubs +Hammers +Maces +Flails +Axes +Halberds +Spears +Whips + +* Also available for nonhumanoids + +Category experience chart: + +Level Successful hits Unuse penalty Idle Turns for Bonus + needed level minus % + +0 0 10/50000 - 0/0 +1 20 15/25000 50000 10/-5 +2 50 25/20000 40000 20/-10 +3 100 50/15000 30000 30/-15 +4 200 75/5000 20000 40/-20 +5 500 100/3000 15000 50/-25 +6 1000 200/2500 12500 60/-30 +7 2000 600/2000 10000 70/-35 +8 5000 1000/1500 7500 80/-40 +9 10000 2500/1250 5000 90/-45 +10 20000 3000/1000 10000 100/-50 + +Hit limit is 50000. Skill level confers a bonus of +10% per level to damage +and THV and -5% to AP cost. + +Single weapon experience chart: + +Level Successful hits Unuse penalty Idle Turns for Bonus + needed level minus % + +0 0 5/10000 - 0/0 +1 5 5/10000 10000 15/-7 +2 10 5/4000 8000 20/-9 +3 20 15/3000 6000 25/-11 +4 50 25/2000 4000 30/-13 +5 100 50/1500 3000 35/-15 +6 200 150/1000 2000 40/-17 +7 500 250/750 1500 45/-19 +8 1000 500/500 1000 50/-21 +9 2000 1000/250 750 55/-23 +10 5000 1500/200 6000 60/-25 + +Hit limit is 50000. Skill level confers a bonus of +15% to damage and THV +and -7% to AP cost in the first level and +5%/-2% per level thereafter. diff --git a/Doc/Data/cellar b/Doc/Data/cellar new file mode 100644 index 0000000..a8bf747 --- /dev/null +++ b/Doc/Data/cellar @@ -0,0 +1,50 @@ +************************************************************ +************************************************************ +************************************************************ +*****************************###########################**** +*****************************#²²²#²²Å²²²²#²²²²Å²²²²Å²²²#**** +*****************************#¿²²²²Å²²²²#²²²²ÅÅÏÅÅŲ²²#**** +*****************************#²²¿²²²ÅÅÏÅÅ#ÅÅÏÅŲ²²²Ï²²²#**** +*****************************#²²²#²²²²²²²²²²²²²²²²²#####**** +*****************************#######²²#####²²²####²Ï²²²#**** +***********************************#>²#***#²²²#**#²Å²²²#**** +***********************************####***#²²²#**#²$²²²#**** +*****************************************##±©±##*#²#####**** +*****************************************#²ß²G²#*#²Ï²²²#**** +*****************##v#####v#####v#####v###¯²²²²²#*#²Å²²²#**** +*****************#G²²²²²G²²²²²G²²²²²G²²²²²²²²²²#*#²Å²²²#**** +*****************#²²²ß²²²²²ß²²²²²ß²²²²²ß²²²²²²²#*#######**** +*****************#ÅÏÅ###ÅÏÅ###ÅÏÅ###ÅÏÅ###²²²²²#************ +*****************#²²²#*#²2²#*#²²3#*#²²²#*¯²²²²²#************ +*****************#²²1#*#²²²#*#²²²#*#4²²#*#²²²²²#************ +*****************#####*#####*#####*#####*##²²²##*#######**** +******************************************#²²²#**#WWWWW#**** +*****************#####*#####*#####*#####*##²²²##*#²²²²w#**** +*****************#²²8#*#²²7#*#²²²#*#5²²#*#²²²²²#*#²wwww#**** +*****************#²²²#*#²²²#*#6²²#*#²²²#*¯²²²²²#*#²#####**** +*****************#ÅÏÅ###ÅÏÅ###ÅÏÅ###ÅÏÅ###²²²²²#*#©#******** +*****************#²²²ß²²²²²ß²²²²²ß²²²²²ß²²²²²²G###²##******* +*****************#G²²²²²G²²²²²G²²²²²G²²²²²²²²²²²²²²²#******* +*****************##^#####^#####^#####^###¯²²²²G###²²#******* +*****************************************#²²²²²#*#²²#******* +*****************************************##²²²##*#²²#******* +******************************************#²²²#**#²²#******* +***************************************####²²²##*#²<#******* +***************************************#~²²²²²~#*####******* +***************************************¯²î²º²e²®************ +***************************************#~²²²²²~#************ +***************************************#G÷²f²öG#************ +***************************************#~²²²²²~#************ +***************************************¯²ô²á²­²®************ +***************************************#~²²²²²~#************ +***************************************###²#####************ +*****************************************#©#**************** +***************************************###²###************** +***************************************#²²²Ä²#************** +***************************************#²š²²#************** +***************************************#ò²²=²®************** +***************************************#²²²¿Â#************** +***************************************#######************** +************************************************************ +************************************************************ +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ diff --git a/Doc/Document/CVS/Entries b/Doc/Document/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/Doc/Document/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/Doc/Document/CVS/Repository b/Doc/Document/CVS/Repository new file mode 100644 index 0000000..373f184 --- /dev/null +++ b/Doc/Document/CVS/Repository @@ -0,0 +1 @@ +ivan/Doc/Document diff --git a/Doc/Document/CVS/Root b/Doc/Document/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/Doc/Document/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/Doc/Manual/CVS/Entries b/Doc/Manual/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/Doc/Manual/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/Doc/Manual/CVS/Repository b/Doc/Manual/CVS/Repository new file mode 100644 index 0000000..bd9b56b --- /dev/null +++ b/Doc/Manual/CVS/Repository @@ -0,0 +1 @@ +ivan/Doc/Manual diff --git a/Doc/Manual/CVS/Root b/Doc/Manual/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/Doc/Manual/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/Doc/Obsolete/Algs.txt b/Doc/Obsolete/Algs.txt new file mode 100644 index 0000000..2abcb1c --- /dev/null +++ b/Doc/Obsolete/Algs.txt @@ -0,0 +1,10 @@ +Map algorithms: + +* Lux = Emitation / Distance ^ 2 + +Character algorithms: + +* HP Maximum = Endurance * 2 +* Speed = 100 + (Agility - 10) / 2 +* Attribute increase demand > 1.18 ^ Attribute * 193 +* Attribute decrease demand < 1.18 ^ (100 - Attribute) * -193 \ No newline at end of file diff --git a/Doc/Obsolete/Alignment.txt b/Doc/Obsolete/Alignment.txt new file mode 100644 index 0000000..72325fe --- /dev/null +++ b/Doc/Obsolete/Alignment.txt @@ -0,0 +1,66 @@ +Alignment is a combination of two factors: morals (lawful, neutral and chaotic) +and ethics (good, neutral and evil). The player, NPC:s and gods each have their +own alignment. + +Relations between alignemnt combinations: + + LG NG CG LN NN CN LE NE CE +LG +++ +NG ++ +++ +CG + ++ +++ +LN + = -- ++ +NN = = = = = +CN -- = + -- = ++ +LE - -- --- + = -- +++ +NE -- -- -- = = = ++ +++ +CE --- -- - -- = + + ++ +++ + +Alignments of gods are as follows: + +L G Valpurus, the King of Gods +L G Seges, goddess of health and nutrition +N G Atavus, god of charity and munificence +N G Sophos, god of knowledge and magic +C G Dulcis, goddess of love and art +C G Silva, goddess of nature +L N Legifer, god of law and order +L N Loricatus, god of fire, machines and weaponry +C N Nefas, goddess of forbidden pleasures +C N Cleptia, goddess of assassins and thieves +L E Mellis, god of money, trade and politics +L E Cruentus, god of war and blood +N E Infuscor, god of wrong knowledge and vile magic +C E Scabies, goddess of mutations, disease and famine +C E Mortifer, the Destroyer of Worlds + +Working sets of worshipped gods: + +Benevolent king (LG) set +Friend gods: Valpurus, Seges, Atavus, Sophos, Legifer, Loricatus +Neutral gods: Dulcis, Silva +Enemy gods: all else + +Good druid/ranger (NG) set +Friend gods: Valpurus, Seges, Atavus, Sophos, Dulcis, Silva +Neutral gods: Legifer, Loricatus +Enemy gods: all else + +Robin Hood (CG/CN) set +Friend gods: Atavus, Sophos, Dulcis, Silva, Cleptia, Nefas +Neutral gods: none +Enemy gods: all else + +Merchant (LE/LN) set +Friend gods: Legifer, Loricatus, Mellis, Cruentus, Infuscor +Neutral gods: none +Enemy gods: all else + +Evil warrior (NE) set +Friend gods: Mortifer, Scabies, Infuscor, Cruentus, Mellis +Neutral gods: Nefas, Cleptia +Enemy gods: all else + +Evil bandit (CE) set +Friend gods: Mortifer, Scabies, Infuscor, Cleptia, Nefas +Neutral gods: Mellis, Cruentus +Enemy gods: all else \ No newline at end of file diff --git a/Doc/Obsolete/Alter.txt b/Doc/Obsolete/Alter.txt new file mode 100644 index 0000000..163f23a --- /dev/null +++ b/Doc/Obsolete/Alter.txt @@ -0,0 +1,812 @@ +The longest if sentence ever written in IVAN +-------------------------------------------- + +void square::AlterLuminance(vector Dir, USI AL) +{ + #define WMACRO(X, Y) if(!game::CCurrentArea()->CSquare(vector(X, Y))->COverTerrain()->CIsWalkable()) return; + + vector Player = game::CPlayer()->CPos(); + + if(!OverTerrain->CIsWalkable()) + { + if(Player.X < Pos.X) + { + if(Player.Y < Pos.Y) + { + if(Dir.X < Pos.X) + { + /* Dxxxx + xPxxx + xxTxx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y); + + /* xxxxx + xPxxx + xDTxx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xPxxx + xxTxx + xDxxx + xxxxx */ + + if(Dir.Y > Pos.Y) + WMACRO(Pos.X - 1, Pos.Y) + } + + if(Dir.X == Pos.X) + { + /* xxxxx + xPDxx + xxTxx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y); + + /* xxxxx + xPxxx + xxDxx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xPxxx + xxTxx + xxDxx + xxxxx */ + + if(Dir.Y > Pos.Y) + WMACRO(Pos.X - 1, Pos.Y) + } + + if(Dir.X > Pos.X) + { + /* xxxxx + xPxDx + xxTxx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y) + WMACRO(Pos.X, Pos.Y - 1) + + /* xxxxx + xPxxx + xxTDx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y) + WMACRO(Pos.X, Pos.Y - 1) + + /* xxxxx + xPxxx + xxTxx + xxxDx + xxxxx */ + + if(Dir.Y > Pos.Y) + return; + } + } + + if(Player.Y == Pos.Y) + { + if(Dir.X < Pos.X) + { + /* xxxxx + xDxxx + xPTxx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y); + + /* xxxxx + xxxxx + DPTxx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxxxx + xPTxx + xDxxx + xxxxx */ + + if(Dir.Y > Pos.Y); + } + + if(Dir.X == Pos.X) + { + /* xxxxx + xxDxx + xPTxx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y); + + /* xxxxx + xxxxx + xPDxx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxxxx + xPTxx + xxDxx + xxxxx */ + + if(Dir.Y > Pos.Y); + } + + if(Dir.X > Pos.X) + { + /* xxxxx + xxxDx + xPTxx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y) + WMACRO(Pos.X, Pos.Y - 1) + + /* xxxxx + xxxxx + xPTDx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y) + return; + + /* xxxxx + xxxxx + xPTxx + xxxDx + xxxxx */ + + if(Dir.Y > Pos.Y) + WMACRO(Pos.X, Pos.Y + 1) + } + } + + if(Player.Y > Pos.Y) + { + if(Dir.X < Pos.X) + { + /* xxxxx + xDxxx + xxTxx + xPxxx + xxxxx */ + + if(Dir.Y < Pos.Y) + WMACRO(Pos.X - 1, Pos.Y) + + /* xxxxx + xxxxx + xDTxx + xPxxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxxxx + xxTxx + xPxxx + xDxxx */ + + if(Dir.Y > Pos.Y); + } + + if(Dir.X == Pos.X) + { + /* xxxxx + xxDxx + xxTxx + xPxxx + xxxxx */ + + if(Dir.Y < Pos.Y) + WMACRO(Pos.X - 1, Pos.Y) + + /* xxxxx + xxxxx + xxDxx + xPxxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxxxx + xxTxx + xPDxx + xxxxx */ + + if(Dir.Y > Pos.Y); + } + + if(Dir.X > Pos.X) + { + /* xxxxx + xxxDx + xxTxx + xPxxx + xxxxx */ + + if(Dir.Y < Pos.Y) + return; + + /* xxxxx + xxxxx + xxTDx + xPxxx + xxxxx */ + + if(Dir.Y == Pos.Y) + WMACRO(Pos.X, Pos.Y + 1) + + /* xxxxx + xxxxx + xxTxx + xPxDx + xxxxx */ + + if(Dir.Y > Pos.Y) + WMACRO(Pos.X, Pos.Y + 1) + } + } + } + + if(Player.X == Pos.X) + { + if(Player.Y < Pos.Y) + { + if(Dir.X < Pos.X) + { + /* xxxxx + xDPxx + xxTxx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y); + + /* xxxxx + xxPxx + xDTxx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxPxx + xxTxx + xDxxx + xxxxx */ + + if(Dir.Y > Pos.Y) + WMACRO(Pos.X - 1, Pos.Y) + } + + if(Dir.X == Pos.X) + { + /* xxDxx + xxPxx + xxTxx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y); + + /* xxxxx + xxPxx + xxDxx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxPxx + xxTxx + xxDxx + xxxxx */ + + if(Dir.Y > Pos.Y) + return; + } + + if(Dir.X > Pos.X) + { + /* xxxxx + xxPDx + xxTxx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y); + + /* xxxxx + xxPxx + xxTDx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxPxx + xxTxx + xxxDx + xxxxx */ + + if(Dir.Y > Pos.Y) + WMACRO(Pos.X + 1, Pos.Y) + } + } + + if(Player.Y == Pos.Y) + { + if(Dir.X < Pos.X) + { + /* xxxxx + xDxxx + xxPxx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y); + + /* xxxxx + xxxxx + xDPxx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxxxx + xxPxx + xDxxx + xxxxx */ + + if(Dir.Y > Pos.Y); + } + + if(Dir.X == Pos.X) + { + /* xxxxx + xxDxx + xxPxx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y); + + /* xxxxx + xxxxx + xxQxx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxxxx + xxPxx + xxDxx + xxxxx */ + + if(Dir.Y > Pos.Y); + } + + if(Dir.X > Pos.X) + { + /* xxxxx + xxxDx + xxPxx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y); + + /* xxxxx + xxxxx + xxPDx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxxxx + xxPxx + xxxDx + xxxxx */ + + if(Dir.Y > Pos.Y); + } + } + + if(Player.Y > Pos.Y) + { + if(Dir.X < Pos.X) + { + /* xxxxx + xDxxx + xxTxx + xxPxx + xxxxx */ + + if(Dir.Y < Pos.Y) + WMACRO(Pos.X - 1, Pos.Y) + + /* xxxxx + xxxxx + xDTxx + xxPxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxxxx + xxTxx + xDPxx + xxxxx */ + + if(Dir.Y > Pos.Y); + } + + if(Dir.X == Pos.X) + { + /* xxxxx + xxDxx + xxTxx + xxPxx + xxxxx */ + + if(Dir.Y < Pos.Y) + return; + + /* xxxxx + xxxxx + xxDxx + xxPxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxxxx + xxTxx + xxPxx + xxDxx */ + + if(Dir.Y > Pos.Y); + } + + if(Dir.X > Pos.X) + { + /* xxxxx + xxxDx + xxTxx + xxPxx + xxxxx */ + + if(Dir.Y < Pos.Y) + WMACRO(Pos.X + 1, Pos.Y) + + /* xxxxx + xxxxx + xxTDx + xxPxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxxxx + xxTxx + xxPDx + xxxxx */ + + if(Dir.Y > Pos.Y); + } + } + } + + if(Player.X > Pos.X) + { + if(Player.Y < Pos.Y) + { + if(Dir.X < Pos.X) + { + /* xxxxx + xDxPx + xxTxx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y) + WMACRO(Pos.X, Pos.Y - 1) + + /* xxxxx + xxxPx + xDTxx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y) + WMACRO(Pos.X, Pos.Y - 1) + + /* xxxxx + xxxPx + xxTxx + xDxxx + xxxxx */ + + if(Dir.Y > Pos.Y) + return; + } + + if(Dir.X == Pos.X) + { + /* xxxxx + xxDPx + xxTxx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y); + + /* xxxxx + xxxPx + xxDxx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxxPx + xxTxx + xxDxx + xxxxx */ + + if(Dir.Y > Pos.Y) + WMACRO(Pos.X + 1, Pos.Y) + } + + if(Dir.X > Pos.X) + { + /* xxxxD + xxxPx + xxTxx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y); + + /* xxxxx + xxxPx + xxTDx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxxPx + xxTxx + xxxDx + xxxxx */ + + if(Dir.Y > Pos.Y) + WMACRO(Pos.X + 1, Pos.Y) + } + } + + if(Player.Y == Pos.Y) + { + if(Dir.X < Pos.X) + { + /* xxxxx + xDxxx + xxTPx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y) + WMACRO(Pos.X, Pos.Y - 1) + + /* xxxxx + xxxxx + xDTPx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y) + return; + + /* xxxxx + xxxxx + xxTPx + xDxxx + xxxxx */ + + if(Dir.Y > Pos.Y) + WMACRO(Pos.X, Pos.Y + 1) + } + + if(Dir.X == Pos.X) + { + /* xxxxx + xxDxx + xxTPx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y); + + /* xxxxx + xxxxx + xxDPx + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxxxx + xxTPx + xxDxx + xxxxx */ + + if(Dir.Y > Pos.Y); + } + + if(Dir.X > Pos.X) + { + /* xxxxx + xxxDx + xxTPx + xxxxx + xxxxx */ + + if(Dir.Y < Pos.Y); + + /* xxxxx + xxxxx + xxTPD + xxxxx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxxxx + xxTPx + xxxDx + xxxxx */ + + if(Dir.Y > Pos.Y); + } + } + + if(Player.Y > Pos.Y) + { + if(Dir.X < Pos.X) + { + /* xxxxx + xDxxx + xxTxx + xxxPx + xxxxx */ + + if(Dir.Y < Pos.Y) + return; + + /* xxxxx + xxxxx + xDTxx + xxxPx + xxxxx */ + + if(Dir.Y == Pos.Y) + WMACRO(Pos.X, Pos.Y + 1) + + /* xxxxx + xxxxx + xxTxx + xDxPx + xxxxx */ + + if(Dir.Y > Pos.Y) + WMACRO(Pos.X, Pos.Y + 1) + } + + if(Dir.X == Pos.X) + { + /* xxxxx + xxDxx + xxTxx + xxxPx + xxxxx */ + + if(Dir.Y < Pos.Y) + WMACRO(Pos.X + 1, Pos.Y) + + /* xxxxx + xxxxx + xxDxx + xxxPx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxxxx + xxTxx + xxDPx + xxxxx */ + + if(Dir.Y > Pos.Y); + } + + if(Dir.X > Pos.X) + { + /* xxxxx + xxxDx + xxTxx + xxxPx + xxxxx */ + + if(Dir.Y < Pos.Y) + WMACRO(Pos.X + 1, Pos.Y) + + /* xxxxx + xxxxx + xxTDx + xxxPx + xxxxx */ + + if(Dir.Y == Pos.Y); + + /* xxxxx + xxxxx + xxTxx + xxxPx + xxxxD */ + + if(Dir.Y > Pos.Y); + } + } + } + } + + if(AL > Luminance) + Luminance = AL; +} \ No newline at end of file diff --git a/Doc/Obsolete/Artifact Ideas II.txt b/Doc/Obsolete/Artifact Ideas II.txt new file mode 100644 index 0000000..f9fb6f3 --- /dev/null +++ b/Doc/Obsolete/Artifact Ideas II.txt @@ -0,0 +1,17 @@ +small swords + +dagger of venom - permanent poison +incredibly sharp dagger named Mucro - bypass armor +banshee sickle - screams, makes enemies panic + +large swords + +sword of giant slaying - double damage against monsters of > 250 size (bastard sword) + +axes + +mammoth battle axe - enormous damage, may bounce enemies, very heavy, very difficult to hit + +polearms + +scythe of undeath - raises all killed humanoids as temporary zombies diff --git a/Doc/Obsolete/Artifact Ideas.txt b/Doc/Obsolete/Artifact Ideas.txt new file mode 100644 index 0000000..480afd1 --- /dev/null +++ b/Doc/Obsolete/Artifact Ideas.txt @@ -0,0 +1,24 @@ +small swords + +//short sword named Saal'thul - invisibility + +large swords + +//flaming sword - fire damage, glow, may burn enemy items +//holy broadsword named Valpurus's Justifier - haste, infravision, esp, polymorph control, teleport control + +blunt weapons + +//mace named Turox - explosion +//ancient mace named Neerc Se-ulb - great damage, hp drain +//thunderbird hammer - lightning + +polearms + +//runed spear named Vermis - teleports enemy +//unholy halberd named Mjolak - energy damage, evil thing to use + +whips + +//whip of thievery - steals weapon +//chameleon whip - polymorphs enemy, depends on relation to Scabies diff --git a/Doc/Obsolete/Asiakirja.txt b/Doc/Obsolete/Asiakirja.txt new file mode 100644 index 0000000..780dbed --- /dev/null +++ b/Doc/Obsolete/Asiakirja.txt @@ -0,0 +1,99 @@ +Esineet + +Tavallisten aseiden värit + +Musta +Harmaa +Teräs +Valkoinen +Kulta/keltainen +Ruskea +Jäätynyt +Hehkuva +Taika +btw piirsin hehkuvaan miekkaan lieskat koska +en nähnyt punaiselle värille muuta käyttöä kuin +tuli. Pronssikin on ennemmin ruskeaa kuin +punaista. + + +Sitten Valpuri's Justifier +Luulen että pyhä ase pystyy vastustamaan +taikoja jotka muuttaisivat sitä. Jos se muuttuisi +vaikka lateksiksi niin se olisi vähän...eri ase. +Itseasiassa uskon että sen pitäisi muuttua +tavalliseksi broadswordiksi jos sitä ryhdytään +muuttelemaan. Sitäpaitsi en ole innostunut +ajatuksesta että joku Justifierinlihaa oleva dark frog +zappaa jollain Wand of Make Excrementillä miekkaani +ja tappaa minut. +Eli tein kuvat vain hehkuvasta, taiotusta ja +jäätyneestä. + +Kiviä ja pulloja tein monta koska niitä varmaankin +tullaan tarvitsemaan. Ne potionit jotka ovat kokonaan +punaisia, siis niin että pullo on punainen eikä neste, +ovat kiehuvia. Siniset jäätyneitä. Vihreät taiottuja. + +Tuolla alempana on sitten lisää yksivärisiä potioneita +mutta ne ovatkin tyhjien eri versioita. + +Banaaneita on noin monta koska tein niista alun perin +monia versioita joita en raaskinut hävittää. Eli tässä +banaanit, alkaen ylhäältä vasemmalta. + +Musta +Harmaa +Teräs / Jäätynyt +Valkoinen +Puu +Kulta / grillattu +Raaka / taiottu +Ylikypsä +Kuivunut banaani +Normaali +Möhjööntynyt + +Elpurin päitäkin tein vain kolme, eli normaalin, kylmän +ja kuuman. En usko että itse Pahuutta voisi muuttaa +vaikkapa sementiksi muun kuin ylipapin taioilla. + +Taidan joutua muuttamaan lattiatilejä niin että eri +väiriset aseet tai tavarat eivät hukkuisi niihin. + +Muuten olisiko hyvä idea jos kentät luotaisiin +randomilla mutta niihin sijoitettaisiin myös jotain +ennalta suunniteltuja rakenteita randomisti? +(kauppoja, alttarihuoneita, varastoja, hautahuoneita) +Miten olisi myös jos tiloista tehtäisiin vähän +yhtenäisempiä? + +Tässä on lista löytämistäni bugeista versiossa 0.192 +A. Luultavasti tiedätte ne kuitenkin mutta silti... + +Jossain pelissä wieldini vaihtui aina kun vaihdoin +kerrosta. Kolmannessa se oli Elpuri's head + +Menetän hitpointteja taistelussa vaikka saan +ilmoituksen että vihollinen löi ohi. + +En pysty käyttämään esineitä jotka ovat +inventaarioni toisella sivulla. + +LOPPUHUOMAUTUS + +Mopo.pcx on tuman +Mikko.pcx on mikon tekemä + +Nämä ovat siis nasujen kuvatiedostoja. He eivät +oikein ole osanneet laittaa niitä kunnolla enkä minä +viitsi tehdä sitä ennenkuin saan varmuuden siitä että +mielestänne ne ovat "käyttökelpoisia". + +Mopo.pcx on aika ruma. Mutta oikeassa yläkulmassa +oleva homo-Olog näyttää edustavalta. Pidän myös +about keskikohdassa olevasta vihreästä möhköstä. +Muut ovat rumia kuin uloste. + +Mikko.pcx kuvan pinkki tonttu on siistin näköinen. +Kirves on... en tiedä. diff --git a/Doc/Obsolete/Attnam.txt b/Doc/Obsolete/Attnam.txt new file mode 100644 index 0000000..8a42c35 --- /dev/null +++ b/Doc/Obsolete/Attnam.txt @@ -0,0 +1,61 @@ +Attnam, the Holy City of Valpuri +-------------------------------- + +Geographical information + +Attnam is a relatively small city located in a thick evergreen forest called Pertturia, +where it acts as a capitol of the Holy Empire of Perttuania, that in turn controls all +of the Isle of Perttuera and is the world's 324th biggest sovereign state. The city is +physically quite isolated from the rest of the world, but the citizens don't mind it +all since almost all of them have high speed net connections. The oceanic coast is quite +near the border of the forest, however, and pilgrims wandering to the Cathedral and +traders delivering goods, luxury items and dolphin food for the clergy class are not +entirely uncommon in Attnam. + +Nature + +Nature is close in everyday life of Attnam, because civilization ends completely no +further than at the city gate (if it has ever even begun) and wilderness takes over. +The city guards are obligated to keep the area inside the city wall safe, but outside +wolves, lynxes and wild bears may roam freely. Settlement of goblins, ogres and mutant +school food mushrooms are also common. Even Enner Beasts have been heard wailing in +the darkness that covers the forest, but they, of course, are killed with magic +missiles if they ever wander closer than 500 yards to the city, in order to prevent +structural damage caused in the city. + +Sources of livelihood + +The citizens live mostly by hunting bears and such from the forest and whittling +sacred-looking items from wood that foreigners that seek truth and Valpuri's blessing +from Attnam buy eagerly as holy relics. The public expenditures of the state, e.g. +salaries of Überpriestial elite guards, cost of the enormous amounts of sacramental +Coke needed in the Cathedral and the price of replenishing Perttu's slave staff and +harem monthly, are funded almost entirely by a state-owned valpurium mine located in +the tundra section of Perttuera. + +Government + +Attnam and the Empire of Perttuania are officially ruled by Valpuri the Great Frog and +the King of Gods, but since He is quite often absent when decision are made, His Most +Pious Überpriest acts as a messenger who delivers His orders to mortal men from godly +planes. The Überpriest retains his position for his entire lifetime. When he dies, the +next leader is chosen immediately by the Valpuristic Conclave of High Priests, and is +almost always the one who killed the last Überpriest (although there seem to be some +other, not so popularily known requirements for this crowning). + +History + +The former headquarters of the Empire were located up in the tundra area of Perttuera +but were eaten by a swarm of angry polar bears in 913 Anno Valpurus. Attnam was founded +in the next year, and, as can be well guessed by looking the map, its place was decided +randomly by choosing the coordinates by dice-throwing. + +The first building to rise in Attnam was the Cathedral, which yet today looms over the +city as a symbol of Valpuri's might. The Überpriest of that day used much of his mighty +magical abilities to aid the building process and to protect it from the attacks of +wildeebeasts. So much that after the Cathedral and the city wall were finally up, he +was easily beaten in duel by Perttu, the nowaday Überpriest. Perttu moved to the +Cathedral immediately and started his reign by naming the country and all important +landmarks (Attnam was not considered as such) inside its borders after himself. And +during the following decades under his rule, Attnam has gradually become what it is +today. \ No newline at end of file diff --git a/Doc/Obsolete/BadEsim.txt b/Doc/Obsolete/BadEsim.txt new file mode 100644 index 0000000..7150738 --- /dev/null +++ b/Doc/Obsolete/BadEsim.txt @@ -0,0 +1,9 @@ +void golem::MoveRandomly(void) +{ + USI ToTry = rand() % 9; + if(ToTry == 8 && rand() % 5) + Engrave("Golem Needs Master"); + else + if(!game::CCurrentArea()->CSquare(CPos() + game::CMoveVector()[ToTry])->CCharacter()); + TryMove(CPos() + game::CMoveVector()[ToTry]); +} \ No newline at end of file diff --git a/Doc/Obsolete/Balance.txt b/Doc/Obsolete/Balance.txt new file mode 100644 index 0000000..6767122 --- /dev/null +++ b/Doc/Obsolete/Balance.txt @@ -0,0 +1,22 @@ +* high priority +// done +/ partly done or needs testing +(r) rejected +(s) suspended + +(r) Lion appearing on first level. +//Bone helmet is too weak with 1 ap. +//Banana peels can be abused. +//Bats should be made more dangerous or easier +//Snakes are too fast. (normal snakes can all be easily outrun by a human) +//Halberd requires too much strength (weapons experts opinion). +//Phoenix feather limbs may be too powerful. (I'm not sure, I just discovered they even are possible to gain) +//Guy with beginning stats and no equipment in gloomy cave level 2 -> Ur-Khan the orc marshal appears. (I was in WMode, but still...) +//There's a LOT of school food in the game. +//People shouldn't get permanently confused from fountains! +//Shields may be a little too weak. +//Lanterns are too heavy. +//Repeadatly reading and cancelling reading a scroll gives lots of wisdom +//Selling silver golem limbs is too good business. +//Horn of bravery is useless. +//Wand of cloning is too frequent. diff --git a/Doc/Obsolete/Bodyplan2.txt b/Doc/Obsolete/Bodyplan2.txt new file mode 100644 index 0000000..41b0d34 --- /dev/null +++ b/Doc/Obsolete/Bodyplan2.txt @@ -0,0 +1,91 @@ +//Potkiminen onnistuu vain kahdella jalalla. +//Jos molemmat kädet on poissa, voi vain purra. +//Liikkuminen on 3 kertaa hitaampaa jos on vain yksi jalka. +//Liikkuminen on 10 kertaa hitaampaa jos ei ole jalkoja. +//Dodgaus on vaikeampaa jos on vähemmän jalkoja. +Oven avaaminen/sulkeminen on 3 kertaa hitaampaa ilman käsiä. +Diggaaminen vaatii käden. +Heittäminen vaatii käden. +Zappaaminen vaatii käden. +Dippaaminen vaatii käden. +Uhraaminen vaatii käden. +//Wieldaaminen vaatii käden. + +//Wieldattuja tavaroita on kaksi: oikea ja vasen. +Osumistarkkuus kärsii jos kaksi asetta on wieldattuna; se puolitetaan. +Tätä voi myöhemmin parantaa two weapon style skillillä. +Kilvilläkin on category "weapon" skill ja single weapon skill. +Single weapon skillit on käsissä. + +(suspended) Asetaidot parantaa to hit valueta, dodge valueta ja damagea. +(suspended) Bonukset riippuu kategoriasta. + +//Materiaalit on joko eläviä tai elottomia. +//Itemin SV = StrengthModifier * MaterialSV / 1000. +//Bodypartin SV on itemin SV, jos se on eloton. +//Jos se on elävä, käytetään kaavaa StrengthModifier * Endurance / 1000. +/Päällä olevan itemin SV sekä bodypartin SV vähennetään vahingosta. +//Critical hit tekee kuitenkin vähintään 1 dam. +(suspended) Mahdollisuus rikkoutua iskusta on (Dam / SV - 1) * 10%. +(suspended) Tämä pätee myös bodyparteille. HP:n on kuitenkin silloin oltava < HP/3. +(suspended) Aseeseen kohdistuu vahinko, joka on yhtä suuri kuin jos sen lyöjä löisi sitä vihollisen ruumiinosalla tai armorilla. +(suspended) Artifakti voi särkyä vain jos toinen artifakti osuu siihen. +Tietyt iskut, kuten huuto, penetroi armorin. Se voi rikkoa samalla armorin. +//Bodypartin HP lasketaan kaavalla Volume * Endurance / 1000 tai elottomilla Volume * MaterialSV / 1000. +//Kun HP <= 0, bodypart irtoaa. +//Tässä on poikkeuksena pää, joka ei voi irrota ensimmäisellä iskulla, ts. kun se on max hp:ssa, jos se on > 1. Silloin hp asetaan yhteen. +Jos pää saa damagea, olento menee tajuttomaksi. +//Jos irtoava bodypart on vitaali, ihmisellä pää, torso tai groin, olio kuolee. +Vain elolliset bodypartit healaa. +Pääkallo lasketaan armoriksi. +//Normaalitilassa mahdollisuus osua bodypartiin jakautuu volumejen suhteessa. +//To hit value lisää ja dodgevalue vähentää mahdollisuutta osua kriittiseen ruumiinosaan (suora verrannollisuus). + +Ways to cure lost limbs: + +Temples. +Gods. +Potion. +Smithy. +Angels. +Petrus? + +StrengthModifiereita: + +Aseet ja raajat 50 +Chain mail 100 +Groin 100 +Pää 150 +Plate mail 200 +Torso 250 + +HP:t: + +human 60000 * 10 / 10000 = 60 +head 4000 * 10 / 10000 = 4 +arm/groin 5600 * 10 / 10000 = 5 +leg 7466 * 10 / 10000 = 7 +torso 24268 * 10 / 10000 = 24 + +high level human 80000 * 30 / 10000 = 240 +head 4000 * 30 / 10000 = 12 +arm/groin 7600 * 30 / 10000 = 22 +leg 10133 * 30 / 10000 = 30 +torso 32934 * 30 / 10000 = 98 + +valpurium golem 100000 * 400 / 10000 = 4000 + +mammoth 5000000 * 10 / 10000 = 5000 + +darknight 110000 * 30 / 10000 = 330 + +ArmorSV:t: + +iron chain mail 100 * 100 / 1000 = 10 +mithril chain mail 100 * 200 / 1000 = 20 +diamond chain mail 100 * 300 / 1000 = 30 +iron plate mail 200 * 100 / 1000 = 20 +mithril plate mail 200 * 200 / 1000 = 40 +diamond plate mail 200 * 300 / 1000 = 60 +valpurium plate mail 200 * 400 / 1000 = 80 +Shirt of the Golden Eagle 100 \ No newline at end of file diff --git a/Doc/Obsolete/Bugs.txt b/Doc/Obsolete/Bugs.txt new file mode 100644 index 0000000..073490f --- /dev/null +++ b/Doc/Obsolete/Bugs.txt @@ -0,0 +1,213 @@ +* high priority +// fixed +/ partly fixed or needs still testing +(r) rejected +(s) suspended +(?) not understandable + +(r) Messagesystem may show an empty [Y/N] boolquestion, if many messages are displayed and the question is preserved for the next page. This may be inconvenient. +//Item's GetSquareUnder() doesn't work. +//Picking sale items after polymorphing and killing the shopkeeper crashes. +//Shopkeeper + levelchange? +(r) WMode bug. (what was this?) +//Screenshots don't work in the DOS port. (do they in Linux?) +//DarkLevel monsters. +//Bats get damage from splinters. +//Potion name bugs. +//Do slimes slip down on banana peals? Or bats? +//Hex's buggy messages. +//Pressing esc in stringquestion crashes game!!! +//Background problem with config screen. +//Dos-prompt bug. +//Walls & moving light. +//The volume bug. +//*Boot of speed in hand. +//Can a player controlled carnivorous plant move? +//If an Attnamian severs a bodypart which hits the player, the city shouldn't become mad! +//Multiple message boxes. +//Petrus's wives' corpses use indefinite article. +//ESP + Wmode look bugs. +//Wishing for ring of teleport bugs. +//Teleportation in Attnam bugs. +//Corpse + loricatus. +//Visual bugs with close in Linux with the X-button. +//Unicorns can eat silver stones. +//Exp is gained even if bodypart is non-living, even though it affects nothing. +//One can kick mines. +//new meleeweapon(LONGSWORD, MAKE_MATERIAL(IRON)) may create a long iron sword with a mithril handle. +//Doors close when they break. +//Can one block when fainted? +//Wand of resurrection doesn't work +//DonateSlotTo bugs. +//RestoreBodyParts() may crash. +//AddScoreEntry bugs. +//When bear traps become visible, Memorized isn't updated. +//"You are hit by the explosion!" "You are not hurt." "Your torso bleeds." +//StuckTo bugs; it isn't cleared at least when the bodypart is severed, and saving crashes afterwards. +//Lamps can be seen through walls in lookmode. +//Oree dies. Oree looks slower. +//Digging doors doesn't work. +//Eating kobolds is evil. +//(2x) you wake up. +//Your weak kick has no effect. +//Memorized doesn't update correctly. +//Splinter message has incorrect order. +//Kamikaze dwarves don't use backpacks correctly. +//Highscore of no entries doesn't work properly. +//Carnivorous plants inside walls! +//Scroll of charging ("no items to charge") +//Alpha + rotate doesn't work. +//Beartraps + groinless creatures. +//Zapping is possible without arms. +//Hit effects of flying items. +//Head of Elpuri, Shirt of the Golden Eagle and the Left Nut of Petrus aren't recognized by the storyline if they are equipped. +//Consummo message. +//Picking up a bear trap. +//Turox etc. + poison doesn't work. +//Bear trap + displace. +(?) Attnam + Elpuri. +//Headless player zombie + Chat. +//Panic + vital bodypart. +//Poison + poison resistance message. +(?) "(7x) the werewolf resists." +//Bloated monster eat AI. +//Omle urine doesn't really work. +//*Infravision range. +//Carnivorous plants spill blood. +//Oree's blood golems' HPs' are incorrect. +//(In Linux) push X-button, then cancel +//Skeletons' health bugs. Some have over 20000 hps. +//Going down from underwater tunnel level 3 in WMode crashes. +//"Stone stones" +//Bodyparts hit by sound or energy are never severed nor destroyed, so they can reach negative hps. +//Consuming chests probably crashes the game. +//Go seems to be slower than manual walking. +//Bear trap + level change crashes. +//Polymorphing while wearing a ring of infravision crashes. +//Message history may crash if many many very long messages are successively printed. +//Unicorns kicking items in shop. +//Seges and others may give genies and angels a groin and legs. +//You can change a sword to water with a scroll of change material. (maybe the whole sword could just vanish. Maybe add a bit fluid on the square the player is standing on) + (doesn't exists and hasn't; changing only works in WMode) +//Teleportation state works in the wilderness. +//Golden plate mails are worth less than iron ones. +//The imperialist makes no reaction if the player destroys his golden room completely with pick-axe etc. +//When walking towards locked door there's a bad message: "The closed, locked stone door with triangular lock is locked" +//Skulls are insanely heavy (8 kg). +//Carnivorous plants can be kicked around. +//Merka's money amount bugs? +//The dead are counted when calculating population. +//"The kamikaze dwarf manages to free heself from the bear trap." +//Pet plants are able to follow the player to the world map. +//It is impossible to polymorph into a kamikaze dwarf, the game just says "Be more precise" since there are many configurations with same name. (low priority) +//"0 scrolls of wishing dropped." +//Destroying shops, libraries, smithies and temples (except the cathedral) with a pick-axe yields no penalties. +//The Hideous RAM Boulder Monster Spawn Error. +//"The severed right leg of Petrus's wife number 6 hits the open, locked iron door with a triangular lock and the open, locked iron door with a triangular lock opens." +//Polymorphed into a mistress warlady and wielding the Justifier, I attacked Attnam. After killing a dozen citizens inside + and outside the Cathedral, I noticed odd lagging, activated the see whole map cheat and discovered Petrus had gone mad + and was killing everyone. I suppose some item kicked by guard hit him and he thought Attnam's folk had started a rebellion? + A funny side note: a kamikaze dwarf had just blown up his wives, which would perhaps be a humane reason for his actions :) +//Lanterns put inside itemcontainers still emit light. +//Digging book shelves probably doesn't work correctly. +//Can angry Atavus or Cruentus destroy a quest artifact inside a container? +//Items left on shop floor do not spoil even if the shopkeeper is dead. +//"A wolf looks much healthier" (weird bug, can't really trace how this happened, but it did) +//Wand of locking doesn't update memorized description correctly. +//The three letter player name minimum can be avoided by adding extra spaces which will eventually be discarded. +//You can fill a potion with ice. +//The bad pray effect of Sophos can sometimes leave the player in a state where he has more than max HPs. +//Hitting imperialists palm waving slaves causes no real action on the behalf of the imperialist. +//Amulet of Life saving generates high score entry. +//Offering chests in prohibited. (They are too important according to the game) +//Spiders can lock doors. +//If multiple chests are stacked, only the top one can be opened with key. +//Odd monsters seem to be able to apply. +//Dipping to corpses doesn't work. +//Guards in the library. +//(example) Even though the golem is not seen the message "The attack of the glass golem interrupts you." is displayed +//(might be a feature... But probably isn't even thought Timo may say so) Merka the shopkeeper walking around in the dungeon. +//Dark wizards are too hard. +//TOO MANY KAMIKAZE DWARFS. +//"XXXX, killed by a werewolf polymorphed into a werewolf" +//Go-command shouldn't walk to squares with smoke. +//Skeletons are probably seen by infravision. +//It seems that due to certain bitshift rounding errors, resting causes more exp minus than pressing the NOP key. +//Skeletons can be poisoned. +//Does using a two-handed weapon yield exp to only one arm? +//I think one can get a score of hundreds of thousands by cloning vast amounts of wands of resurrection and repeatedly killing and resurrecting Elpuri and collecting the dropped heads, each worth 2000p. +//'G'oing on the the ostrich landing site and interrupting a banana grower may sometimes make New Attnam hostile. +//Highscores don't always fit to the screen. +//It is possible to chat with fainted creatures. +//AI monsters don't mind about smokes. +//Panicking monsters can eat. +//Pet ghosts don't gain any strength exp. +//Ghosts can be poisoned. +//"You hit something! Something manages to block the attack with its steel sword +1!" +//It is not possible to zap a wielded item. +//"You faint. You hit the floating eye! It is not hurt." +//"You feel a slight tingling in your hands." when armless. +//Mirage dogs. +//Magical air and bone are shown by infravision. +//Sophos doesn't accept parchment golem corpses. +//Using a wand of door creation in shops is allowed. +//Do psi waves damage armor? +//Pickaxes can't dig squares on the edges but explosions can destroy them. +//It is possible to protect items from explosions by putting them in hand, digging, or eating them (very low priority). +//Strength bonus is shown in the battle info screen even for Bill's wills. +//The player often becomes burdened, stressed or overloaded when he eats big corpses. +//The bone file code doesn't handle shelves correctly. (but there are never shelves in bone levels) +//The bone file code doesn't handle shops correctly. (but there are never shops in bone levels) +//Chests in inventory cannot be unlocked. +//Mistresses like shields and jewels more than their whips. +//Headless zombies can eat. +//Broken thunder hammer emits light. +//Squares near should be redrawn when digged. (bad edge tiles) +//Creating doors doesn't update the lookmode description. +//Teleported, activated mines can be seen by player. +//Angels shouldn't eat +//Squares near should be redrawn when digged. (bad edge tiles) +//Creating doors doesn't update the lookmode description. +//Dying + being resurrected doesn't eliminate poison. +//Teleport control can be used to detect walls. +//RED ommel urine ALERT! +//Displacing plants is again allowed. +//(hs) "You look down. You see an active mine." in the dark. +//(hs+) NetBSD and trailing slash. +//Using change material scroll to change large items (ie plate mail) to carrot can be abused to get too much perception. +//(hs) Choosing a name including one of characters '?', '*', '|', '<' or '>' and saving will crash (at least in Windows). +// (hs) Choosing a name including one of characters '/', '\' as any but the first characters and saving will crash (at least in Windows). +// (hs) Choosing a name including the character ':' will result in very odd consequences, like crashes, disappearing saves and savefiles which cannot be deleted (at least in Windows). +//(hs--) "You push your fingers down to your throat and vomit." msg incorrect when a dolphin. +//(hs) Headless zombies can vomit. +// (hs) Mushrooms can be kicked around. +//Carnivorous plants can move by displacing pets. +//EditAttribute() probably doesn't update all caches correctly. +//Polymorph default bugs if the string is very long. +//Leprosy can cause player to become overloaded in world map and there is no way to drop items. +//Lyre of charm works on monsters that don't hear. (like headless zombie) +//(hs-) If player tries to apply when he has no arms, the message says that there are no items to apply. +//Leprosy can cause player to become overloaded in world map and there is no way to drop items. +// (hs) Can you see the cause of death if you don't get to highscore list? +//You can sell mirrored items. +//Vladimir can destroys terrains in Attnam even if the citizens are not hostile. +// (hs) Smith not repairing equiped items. +//Graphics bug in vesana corpse. +//(hs) Shopkeepers/librarians can buy items even if unconsciouss. +//If an NPC is poisoned and vomits at the player, the game thinks it's an attack and makes teams hostile to each other. +//One has no way to use stairs covered by sticky slime. +//Typo in GPL notice +//(hs) Tailor doesn't fix straight from equipment +//Spider corpses are not poisonous. +//Charming a tourist, leaving Attnam and entering a dungeon crashes. A usable autosave results, though, and the game can be continued with the tourist. +//Invisible golems block light. +//You can create a human flesh golem from your head, without even dying. +//Then you can pray to a god and and get a new head made of eg. marble :-0 +//You get stuck to slime even when flying. +//You can probably give commands to unconscious monsters. +//"The magic mushroom grunts happily." +//Mammoth's corpse's weight is negative. +//Damaged gear shouldn't rise dexterity but lower it. (think about it. At least this is the most probable outcome) +//Are the angels supposed to leave the items in a bonefile? (stupid me. They just disappear) +//"You sense danger message" seen after genie comes up and wish is made. +//Messages after teleport. diff --git a/Doc/Obsolete/CVS/Entries b/Doc/Obsolete/CVS/Entries new file mode 100644 index 0000000..e91b371 --- /dev/null +++ b/Doc/Obsolete/CVS/Entries @@ -0,0 +1,116 @@ +/Algs.txt/1.1/Thu Jul 11 23:49:35 2002// +/Alignment.txt/1.1/Tue Sep 10 10:41:34 2002// +/Alter.txt/1.2/Tue Dec 14 18:27:08 2004// +/Artifact Ideas II.txt/1.1/Tue Mar 1 18:00:29 2005// +/Artifact Ideas.txt/1.1/Mon May 12 17:12:56 2003// +/Asiakirja.txt/1.1/Tue Dec 14 18:27:08 2004// +/Attnam.txt/1.1/Mon Oct 14 21:07:05 2002// +/BadEsim.txt/1.1/Mon Feb 10 17:52:43 2003// +/Balance.txt/1.3/Fri Feb 11 12:44:29 2005// +/Bodyplan2.txt/1.1/Thu Jul 11 23:49:35 2002// +/Bugs.txt/1.3/Fri Feb 11 12:44:29 2005// +/ChangeLog.040/1.1/Thu Mar 27 16:44:49 2003// +/ChangeLog.0401/1.1/Thu Mar 27 16:44:50 2003// +/ChangeLog.0410/1.1/Sat Mar 29 22:49:49 2003// +/ChangeLog.0420/1.1/Sun May 11 19:24:42 2003// +/ChangeLog.0430/1.1/Wed Aug 13 23:09:41 2003// +/ChangeLog.050/1.1/Tue Dec 14 18:27:08 2004// +/Changes.txt/1.1/Tue Jul 31 22:31:32 2001// +/Creation.rtf/1.1/Tue Dec 14 18:27:08 2004// +/Danger.txt/1.1/Thu Jul 11 23:49:35 2002// +/Demon.txt/1.1/Sat Oct 26 17:33:59 2002// +/ExampleChanges.txt/1.1/Tue Jul 31 22:31:32 2001// +/Exp.txt/1.1/Thu Jul 11 23:49:35 2002// +/FEEL.txt/1.1/Thu Jul 11 23:49:35 2002// +/FELL.txt/1.1/Thu Jul 11 23:49:35 2002// +/FeDX.txt/1.1/Thu Jul 11 23:49:35 2002// +/FeVesa.txt/1.1/Tue Feb 4 21:04:10 2003// +/FeWin.txt/1.1/Thu Jul 11 23:49:35 2002// +/Format.txt/1.1/Thu Jul 11 23:49:35 2002// +/Future.txt/1.1/Thu Jul 11 23:49:35 2002// +/GenePoints.txt/1.1/Thu Jul 11 23:49:35 2002// +/Genetest.txt/1.1/Thu Jul 11 23:49:35 2002// +/Goals 0-41.txt/1.1/Thu Apr 3 15:23:40 2003// +/Goals.txt/1.1/Thu Jul 11 23:49:35 2002// +/Gods.txt/1.1/Tue Feb 4 21:04:10 2003// +/Graphics.txt/1.3/Fri Feb 11 12:44:29 2005// +/Great Jobs.txt/1.1/Thu Jul 11 23:49:35 2002// +/Hexhit.txt/1.1/Sun Jul 29 14:09:04 2001// +/Hierarchy.gif/1.1/Thu Jul 11 23:49:35 2002/-kb/ +/Include.txt/1.1/Thu Jul 11 23:49:35 2002// +/Item ideas II.txt/1.1/Tue Dec 14 18:27:08 2004// +/Item ideas.txt/1.1/Mon May 12 17:12:56 2003// +/Items.txt/1.1/Thu Jul 11 23:49:35 2002// +/Items2.txt/1.1/Thu Jul 11 23:49:35 2002// +/Jumal-auta.txt/1.1/Thu Jul 11 23:49:35 2002// +/Kivipaperisakset.txt/1.1/Sun Aug 18 11:47:52 2002// +/LibTest.txt/1.1/Thu Jul 11 23:49:35 2002// +/LinesOld.txt/1.1/Mon Feb 10 17:52:44 2003// +/Main.txt/1.1/Thu Jul 11 23:49:35 2002// +/Material Ideas II.txt/1.1/Fri Mar 4 13:08:06 2005// +/Material Ideas.txt/1.2/Fri Feb 11 12:44:29 2005// +/Materials.txt/1.1/Thu Jul 11 23:49:35 2002// +/Message.txt/1.1/Tue Sep 10 10:41:34 2002// +/Monster Ideas.txt/1.1/Tue Dec 14 18:27:08 2004// +/Monsters.txt/1.1/Thu Jul 11 23:49:35 2002// +/Monsters2.txt/1.1/Thu Jul 11 23:49:35 2002// +/Monsters3.txt/1.1/Thu Jul 11 23:49:35 2002// +/Monsters4.txt/1.1/Thu Apr 3 15:23:40 2003// +/Monsut.txt/1.1/Thu Jul 11 23:49:35 2002// +/NP.txt/1.1/Thu Jul 11 23:49:35 2002// +/NameSystem.txt/1.1/Mon Oct 14 21:07:06 2002// +/Names.txt/1.1/Tue Jul 31 22:31:32 2001// +/Need.txt/1.1/Tue Sep 10 10:41:34 2002// +/NeededItems.txt/1.1/Thu Jul 11 23:49:35 2002// +/NewGoals.txt/1.1/Thu Jul 11 23:49:35 2002// +/NewGoals1.5.txt/1.1/Thu Jul 11 23:49:35 2002// +/NewGoals1.6.txt/1.1/Thu Jul 11 23:49:35 2002// +/NewGoals2.txt/1.1/Thu Jul 11 23:49:35 2002// +/Next Release 0.40.txt/1.1/Sat Jan 18 16:58:51 2003// +/Next Release 0.401.txt/1.1/Thu Apr 3 15:23:40 2003// +/Optimization.txt/1.2/Fri Feb 11 12:44:29 2005// +/Panthenon.txt/1.1/Thu Jul 11 23:49:35 2002// +/Pitää.txt/1.1/Tue Dec 14 18:27:08 2004// +/Planplan.txt/1.1/Thu Jul 11 23:49:35 2002// +/Possib.txt/1.1/Thu Jul 11 23:49:35 2002// +/Races.txt/1.1/Thu Jul 11 23:49:35 2002// +/Random.jpg/1.1/Tue Dec 14 18:27:08 2004/-kb/ +/Readme.txt/1.1/Thu Jul 11 23:49:35 2002// +/Remember.txt/1.1/Thu Jul 11 23:49:35 2002// +/SecretList.txt/1.1/Tue Feb 4 21:04:10 2003// +/Small Ideas.txt/1.4/Fri Mar 4 13:08:06 2005// +/Something.txt/1.1/Thu Jul 11 23:49:35 2002// +/Spiral.txt/1.1/Sun Aug 18 11:47:52 2002// +/Struktuuri.txt/1.1/Thu Jul 11 23:49:35 2002// +/Tasks.txt/1.1/Thu Jul 11 23:49:35 2002// +/Tavoitteet.txt/1.1/Tue Jul 31 22:31:32 2001// +/Teams.txt/1.1/Thu Jul 11 23:49:35 2002// +/Tuukka.txt/1.1/Thu Jul 11 23:49:35 2002// +/Type.txt/1.1/Sun Jul 29 14:21:07 2001// +/Valot.txt/1.1/Sun Jul 29 14:21:07 2001// +/Value Plan.txt/1.1/Thu Apr 3 16:49:45 2003// +/Virhe.txt/1.1/Tue Feb 4 21:04:10 2003// +/Weapons.txt/1.1/Thu Jul 11 23:49:35 2002// +/WhatToWishFor.txt/1.1/Tue Dec 14 18:27:08 2004// +/attnam.jpg/1.1/Thu Sep 23 21:13:16 2004/-kb/ +/balance3.txt/1.1/Tue Feb 4 21:04:11 2003// +/battles.txt/1.1/Tue Feb 4 21:04:11 2003// +/char.txt/1.1/Thu Jul 11 23:49:35 2002// +/files.txt/1.1/Thu Jul 11 23:49:35 2002// +/form.txt/1.1/Tue Feb 4 21:04:11 2003// +/general.txt/1.1/Thu Jul 11 23:49:35 2002// +/holydoc2.jpg/1.2/Thu Sep 23 21:13:16 2004/-kb/ +/item.txt/1.1/Thu Jul 11 23:49:35 2002// +/item4.txt/1.1/Tue Feb 4 21:04:11 2003// +/itemohje.txt/1.1/Thu Jul 11 23:49:35 2002// +/jobs.txt/1.1/Tue Dec 14 18:27:08 2004// +/jumalehdotukset.txt/1.1/Sun Aug 18 11:47:52 2002// +/kääntö.txt/1.1/Thu Apr 3 15:23:41 2003// +/levelf.txt/1.1/Thu Jul 11 23:49:35 2002// +/light.txt/1.1/Thu Jul 11 23:49:35 2002// +/priest.txt/1.1/Mon Apr 14 21:19:37 2003// +/project.txt/1.1/Thu Jul 11 23:49:35 2002// +/tehty.txt/1.1/Tue Feb 4 21:04:11 2003// +/towns.txt/1.1/Tue Feb 4 21:04:11 2003// +/wish balance.txt/1.1/Tue Feb 4 21:04:11 2003// +D diff --git a/Doc/Obsolete/CVS/Repository b/Doc/Obsolete/CVS/Repository new file mode 100644 index 0000000..5a6b110 --- /dev/null +++ b/Doc/Obsolete/CVS/Repository @@ -0,0 +1 @@ +ivan/Doc/Obsolete diff --git a/Doc/Obsolete/CVS/Root b/Doc/Obsolete/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/Doc/Obsolete/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/Doc/Obsolete/ChangeLog.040 b/Doc/Obsolete/ChangeLog.040 new file mode 100644 index 0000000..4a41928 --- /dev/null +++ b/Doc/Obsolete/ChangeLog.040 @@ -0,0 +1,2878 @@ +------------------------------------------------------------------------------------ + +>>> NOTICE!!! <<< + +This file contains SPOILERS, which might ruin your IVAN experience totally. The file +is also provided AS IS and is probably completely unreadable. + +------------------------------------------------------------------------------------ + +December 10 2002 Heikki Sairanen + + * Kobolds tweaked + * Added Spoiler etc warning to ChangeLog + +December 10 2002 Heikki Sairanen + + * Menu.pcx looks a bit clearer + +December 10 2002 Timo Kiviluoto + + * Outlook tweaks + +December 10 2002 Timo Kiviluoto + + * Added a terrible gum solution to fix the unicorns kicking + +December 10 2002 Heikki Sairanen + + * Sophos' bad pray effect changed (now works!) + * Dipping in frozen fountains no longer works + * Snakes are a bit slower + * Fixed last problems (I feel like an optimist) in makefile generation. + * NEWS updated + +December 10 2002 Timo Kiviluoto + + * Overall difficulty lowered by 33% + * Fixed one typo + +December 10 2002 Timo Kiviluoto + + * Whips' durability descriptions are no longer shown in the inventory screen + * Price balancing + * Increased the force kicks give to items when sending them to flight + * Kicking chests now prints less "THUMP"s + * Tweaked the lock breaking algorithm of chests + * Corrected two bugs in the wand of locking code + * The game no longer crashes when Atavus or Cruentus destroys a chest + * Added some information about genetrix vesana's origin to Kaethos's replies + +December 10 2002 Timo Kiviluoto + + * Wishing for a (special) boot or gauntlet now yields a pair + * Loricatus can now change the material of two identical gauntlets/boots + if they are placed to both hands simultaneously before praying + * Item-affecting scrolls now handle gauntlets/boots as pairs, so for + example a scroll of enchant armor will give the +1 bonus to both items + * Zapping chargeless wands and bananas no longer trains perception, since + this could be abused + * Valpurus's Justifier and the Shirt of the Golden Eagle can no longer be + enchanted further under any circumstances (why wouldn't Petrus have done it + already?) + * There is now a chance of failure if one tries to enchant items beyound +5 + +December 10 2002 Timo Kiviluoto + + * Praying to Loricatus while wielding a corpse no longer crashes + * Added unique replies for patrol guards + * Patrol guards now use helmets of piercing perception and rings of infravision + * Moving is now 25% faster when panicked + * All guard and dark knight configurations look now different + * The graphics files of IVAN are now much cleaner + +December 9 2002 Timo Kiviluoto + + * Bats' bite strength decreased a bit + * Snakes can no longer open doors + * ToHitValues of thrown items increased + * Food in cans (and cans made of food) no longer spoil + * Worthless food now spoils even in shops + * All food now spoils in abandonned shops + * Carnivorous plants now drop kiwis + * Increased the possibility of food items + * The camera now updates a little earlier on right and bottom borders + * README updated + +December 9 2002 Timo Kiviluoto + + * Added license info to the main menu + * Corrected a bug in the ass corpse emitation + * BoolQuestion now doesn't interpret enter as "yes" + * Wisdom now increases your ability to deal with gods + +December 9 2002 Heikki Sairanen + + * README updated again + +December 9 2002 Heikki Sairanen + + * README updated + +December 9 2002 Heikki Sairanen + + * Corrected fatal mistake in Makefile.am (thank Valpurus that I caught this before we release) + * Adjusted color of flaming sword. + * Added BoolQuestion confirmation for appling wands. + * License changed to GPL. + +December 9 2002 Timo Kiviluoto + + * Fixed the Foul Iron Penetrating Light of Death Bug + * Added "Please send bug report..." message to aborts + * Wands that have run out of charges are now worth nothing + * Corrected probably all gcc warnings + * Corrected a bug in innermost SpillFluid() function that caused fluid to + appear on the ground with a slight delay + * Panicked monsters are now much better in escaping along long tunnels + * Dipping wielded items is now possible + * Corrected a typo in potions' break message + * Zombie flesh is now initially spoiled + * Pick-axes are now more sturdy + * Digging consumes henceforth more NP + * Floating eye picture added to char.pcx by Kahvi + * I had to decrease the ambient light of New Attnam, since otherwise lanterns + would not have been visible there, which certainly would have looked like a bug + +December 9 2002 Timo Kiviluoto + + * Cloaks and rings of invisibility can no more be wished, since they were so + much more powerful than any other wishable items + * Charisma now affects shop prices (both buying and selling) + * Dealing with shopkeepers now trains charisma + * Drinking ommel urine is now eeevil + * Go command is now disabled if confuse state is active + * Reading when confused is no longer very buggy and abusable + * Stethoscope info is now somewhat more useful + * Since killing the enner beast is so difficult, I added a horn of fear to its + inventory as a reward for the trouble + * Petrus and priests can now cure poison + * Petrus's and angels' heal counters now work correctly even if the player is not + always on the same level as they + * "Single weapon skills" have been renamed as "accustomizations" + * Bear traps now occasionally break + * Corrected a fatal bug that caused a crash if a monster killed by a bear trap was + resurrected + * Polymorphing when caught in a bear trap no longer crashes when the polymorph ends + * Corrected a memory leak in the book shelf code + * Digging and exploding a book shelf now works a little more correctly + +December 9 2002 Timo Kiviluoto + + * Removed the Mega-Monstrous Equipment of Cursed Hands Bug Named IVAN's Doom -5 + * Destroyed the Infamous Infralight Eating Bug Fiend Covered With Blood -3 + * Corrected a memory leak in the wall breaking/stone creation code + * Petrus no longer starts slaughtering everyone in Attnam when a kamikaze dwarf + explodes near him + * _Lots_ of smaller bugs fixed + * BoolQuestion now interprets enter as "yes", not as the default answer + * Corpses of unique monsters now use a definite article + * AskKeyPress is now called upon bear trap and mine activation + * Alignment is once again shown in the player name over the message panel + * High score entries now show if the player was killed by a polymorphed monster + * Reading now trains intelligence + * Legless people no longer step on splinters + +December 8 2002 Heikki Sairanen + + * Not walking (eg carnivorous plants) can't follow player to world map + * shopkeepers can no longer follow from level to level + * carnivorous plants, genies and bill's wills no longer spill blood + * Bear trap + level change crashes corrected + +December 7 2002 Timo Kiviluoto + + * Confused people no longer try to move against walls; there is no message and + no time penalty, so this was a very unclear reason not to accept a key press + * Increased nutrition given by Seges + * Increased nutrition given by life saving + * Seges can now heal the player or cure his poison state if needed + * Nefas can now confuse enemies and give the player a pet mistress + * Scabies can now polymorph the player + * Mithril can no longer be wished; equipment made of it made the game boring, + since updating was virtually impossible + * Decreased phoenix feather's SV to make the choice between steel and it more interesting + * Corrected the possibilities of broken helmets + * Decreased the overall difficulty values in the game by 25% + * Doubled all monster generation intervals in the game + * Decreased (greater) dark frogs' danger modifier a bit + * AI will not try to chase enemies seen by ESP anymore (this made angels rather useless) + * Throw system remade + * Terrain damage system remade + * Newton no longer rolls in his grave + * Since it was odd that the player could kick down locked stone doors so easily, the doors + in the underwater tunnel are now made of wood + * Gain all items cheat now drops the items to the ground under the player, so he doesn't + have to drop a thousand items to be able to move + * Added a fast gum solution to remove the material description of "stone stones" + +December 06 2002 Heikki Sairanen + + * "THUMB" -> "THUMP". Somebody might think it's a funny and somebody might think it's a typo. + * Zombies Endurance raised and dextrity and agility lowered to indicate even less brain function + +December 06 2002 Heikki Sairanen + + * Slightly adjusted helmets in favor of "normal" helmets. (it was far too easy to find special helmets and the normal ones were not really needed by player at any point of the game) + * Monsters in panic don't follow player from levels + * Vesana now leaves a 3 pineapples on the ground when it is killed + +December 06 2002 Heikki Sairanen + + * I mixed to gods in last entry, so now that is fixed... + * Cleptia now gives invisibility state if haste is already activated. If even invisibility is active, then it adds time to one of the activated states. + +December 05 2002 Heikki Sairanen + + * Infuscor's effect changed + * Corrected bug with Beartraps and CheckDeath + +December 5 2002 Timo Kiviluoto + + * Corrected a major bug in the score system + * Attacking civilians doesn't raise relations to evil gods anymore, but + killing does + * Full helmets are now used by several monsters formely wearing a normal helmet + +December 4 2002 Timo Kiviluoto + + * Added parasitized state which increases food consumption, can be activated + by eating spoiled flesh and cured by healing liquid, antidote and sometimes + by vomiting + * Decreased poison's effect more + * Numerous devirtualizations + * Equipment screen now warns about low strength more often + * Added horn of bravery and horn of fear items + * Added vodka material + * Vodka and Valdemar are now explosive + * The mistress pain message Hex added (but did not mention) is now occasionally + printed if she is hit by the enner beast's scream + * Corrected a fatal bug in the chameleon polymorph code + * Broken helmet pictures added to item.pcx by Kahvi added modified by me + * Ring picture in item.pcx remade by Kahvi + * Added full helmet + * Helmets can now break + * Added troll blood material which acts like weakened healing liquid + * Cruentus's pray effect is now better + * Shields' BCs have been decreased a bit + * Removed the smaller temples of Attnam + * Valpurus's pray effect is now a little less superior + * Show battle info is henceforth a WMode command + +December 03 2002 Heikki Sairanen + + * weapon and shield info verbalized + * Perception increase message changed + * Mellis's GoodPrayEffect adjusted to give more often gods + +December 03 2002 Heikki Sairanen + + * chests can now by unlocked from same square with key + * anvil added to smithy + * digging produces stones + +December 01 2002 Heikki Sairanen + + * "Chest's lock shatters to pieces" -> "The Chest's..." + +December 3 2002 Timo Kiviluoto + + * There's some light in Attnam again + * Puppies now have 50% more HP + * Tried to optimize script instantiations without noteworthy success + * Corrected a bug that prevented NPCs from searching unseen enemies + * NPCs normally standing idle now usually return to their home positions after + the dangers in the level are eliminated + * The smith now belongs to the same team as the other Attnamians + * Decreased snake's DangerModifier + * Wand of polymorph's effect time was way too low (probably a test I'd forgotten) + * Added chameleon which polymorphs all the time and in the rare normal form changes + color all the time + * Added chameleon flesh which activates the polymorphing state + * Mutant ass flesh now also triggers the polymorphing state + * Kamikaze dwarves and chameleons now use a special generation system; they both + start to appear rarely (frequency 1000) after the player reaches 80 HP + * Added IsExtraCoward character database boolean which allows easy adding of + civilian AI which panics immediately when an enemy is seen + * Petrus's wives, housewives, female slaves/servants and chameleons in their normal + form are now extra cowards + * Added a special message to Petrus's wives' and housewives' SpecialEnemySightedReaction() + * Decreased penalties for eating spoiled bananaflesh and other high NPModifier materials + * ESC now works in equipment selection screen correctly + * Genetrix vesana, mutant asses and their materials now radiate red light + * Corrected a problem that slowed down the game during most actions + * Go is no longer 10% slower than manual walking + * Added quick keys 'w' and 'W' for wielding items in the right and left arm + * Decreased poison's effect a bit + * Poison and sound can't sever bodyparts anymore + * Tripled healing liquid's effect + * Items now break somewhat less often + * Items in strong-boxes now take very little damage and almost nothing can break + an item inside a strong-box inside another container + * Decreased the effect of haste and slow to balance boss battles + +December 3 2002 Timo Kiviluoto + + * Corrected a fatal bug in the monster generation code + * Eating spoiled food now yields less messages + * Since palm branches are fundamental parts of female slaves/servants, their + inhandspics are now drawn using a gum solution function, even though the general + system doesn't currently exist + +December 1 2002 Timo Kiviluoto + + * Corrected a design flaw in the state system that occasionally caused odd permanent + states to be activated + * The counter of the confuse effect of spoiled items was usually rounded to zero so + I increased the effect a bit + * Esc now works in scroll bar questions + * Area around Attnam is now revealed when the game begins and gloomy cave's + environment is uncovered when the Elpuri quest is given + * Chameleon added to char.pcx by Kahvi + * Better helmet added to item.pcx by Kahvi and slightly made smaller by me + * Snake's agility decreased + +December 01 2002 Heikki Sairanen + + * quit key changed from q to Q + +November 30 2002 Heikki Sairanen + + * Mommos don't leave corpses but just slime. + * Fixed effect for Loricatus. + +November 30 2002 Timo Kiviluoto + + * Decreased exp gain for being burdened/stressed + * Decreased endurance exp rate + * Loricatus now gives a steel hand instead of a mithril arm and Cruentus an iron one + instead of a steel one + * Fainting from kobold flesh works again + * The game no longer crashes if character wearing a ring of infravision is polymorphed + * If needed, the game now asks whether the player wants to wield a weapon which he is + too weak to use + * Inventory entries of weapons and shields are now more informative + * Gravel, moraine, grass and school food can no longer be wished + * Integrated Kahvi's new chest pictures + * Corrected a bug that sometimes allowed small spoiled corpses to trigger a permanent + confused state + +November 30 2002 Timo Kiviluoto + + * Some bones and skulls added to the enner beast level + * Skulls severed from skeletons no longer weight eight kilograms + * A dying skeleton now always drops a skull if it has a head + * Removed the ambient light of Attnam + * Added 100 bananas to Hulbo's shop + * Reduced the possibility of people stepping on banana peels and the average damage + caused by the fall + * Elpuri's lair and AI are now a little more interesting + * There is now a one-way magic portal in Elpuri's lair which leads to level 7 and + is the only way to go for the seekers of the Shirt + * It was impossible to correct the bugs in the EnabledMember tracking structure of teams, + so I remade the hole system differently + * Score calculation code rewritten + * High score list no longer behaves oddly if there are no entries + * Healing liquid, priests and wands of resurrection may no longer give angels or genies + prohibited bodyparts + * Gods can again attach old bodyparts (Hex had forgotten the const tag after humanoid's + CanCreateBodyPart()) + * If a bodypart's HPs are at max when endurance is increased, its HPs are set to new max + * Killed the Hideous RAM Boulder Monster Spawn Error once and for all! + * Destroying a temple no longer *increases* relation to the divine owner + * Selling into and buying from chest on store floor works again + * The books and scrolls of the library are now inside the shelves + * The AI no longer eats spoiled food + * Items with different spoil levels are no longer piled together + +November 30 2002 Heikki Sairanen + + * Bugs squashed: + -When walking towards locked door there's a bad message: "The closed, locked stone door with triangular lock is locked" + -Carnivorous plants can be kicked around. + -"The kamikaze dwarf manages to free heself from the bear trap." + -slight problem with key msgs corrected + * Features added: + - Loricatus now changes the material of items that have a StrengthValue that is larger than the strengthvalue of steel + - if the item that the player is carrying is steel, then there is a 10 % that he will be rewarded a scroll of repair + +November 28 2002 Timo Kiviluoto + + * Stats of summoned bodyparts are now more intelligent + * The dead are no longer counted when calculating population + * Anvil, broken bear trap, large chest and "small" chest added to item.pcx by Kahvi + +November 28 2002 Heikki Sairanen + + * Totally weird and odd bug in charde.cpp. + +November 28 2002 Heikki Sairanen + * Bugs corrected: + - Headless player zombie + Chat. + - Seges and others may give genies and angels a groin and legs. + - Teleportation state works in the wilderness. + - "0 scrolls of wishing dropped" + - Destroying shops, libraries, smithies and temples (except the cathedral) with a pick-axe yields no penalties. + - The imperialist makes no reaction if the player destroys his golden room completely with pick-axe etc. + +November 28 2002 Timo Kiviluoto + + * A lethal bug in the banana grower encourager's and a minor one in the ostriches' + save code corrected + * Language fine tuning + +November 28 2002 Timo Kiviluoto + + * Decreased the amount of wolves Silva creates in towns + * The fly symbol is a bit clearer now + * Banana growers and ostriches now displace any movable creature on the landing + platform; if it isn't possible (the creature is a plant or something), they'll + attack it + * Added yet another silhouette color level; colors of old ones adjusted + * Dark frogs and rarely even greater ones appear in fountains (Hex forgot them) + * Added ice and snow materials + * Added snowy ground & pines and firs covered with snow + * Winter has arrived in Attnam + * Added new background story + * Corrected Decos's female servants' replies + * Richel Decos's and the banana growers' replies adjusted heavily + * Added new characters: banana grower encourager and Kaethos the village elder + * The banana grower encourager now occasionally whips or kicks the passing banana + growers + * Added new quest artifact: encrypted scroll + * Finally added the new starting quest + * Elpuri's cave is now hidden until the Player finishes the starting quest + * All Petrus's replies rewritten + * The archaic English used by Petrus, Haathbar, priests and gods is now more correct + +November 23 2002 Timo Kiviluoto + + * Kahvi's levitation & fly symbol integrated + * Added more color levels to the silhouette + +November 23 2002 Heikki Sairanen + + * poisoning and confusion can now be optained by consuming spoled items + +November 23 2002 Timo Kiviluoto + + * Level entering messages and death messages now show the description of the currect + dungeon, too + * Dropping items in a shop while invisible no longer automatically donates the item + but asks whether it should be done + * Corrected a bug in the auto reveal feature of New Attnam + +November 23 2002 Heikki Sairanen + + * scroll of repairing added + +November 22 2002 Heikki Sairanen + + * snakes, frogs and spiders sometimes appear from fountains + * confuse state + * reading activates random state when confused + +November 23 2002 Timo Kiviluoto + + * Corrected a fatal script error that sometimes caused a crash when entering Oree's lair + * Hunters now carry bear traps + * Invisible New Attnam made almost visible by Kahvi + * Almost visible New Attnam made visible by me + * Sign's Text wasn't saved at all but now is + * The player now learns the layout of New Attnam without any exploration when he begins + the game (he has lived there, after all) + * Removed the random factor of starting endurance, its effect was too great + * Added "none" option to equipment selection menu + * Added possibility to select target from equipment when reading item-affecting scrolls + * The holy banana of Liukas Vipro no longer spoils + * The former value of HasFeet() function comes now from the script + * Characters' flesh materials can now be defined in the script + * Death messages can now be defined in the script (it uses the same parser as replies) + * Reply and death message parser now understands pronouns + * Renamed little dog to puppy in order to reduce the Nethack-clone impression of the game + * Some devirtualizations + * Richel Decos's mansion is now prettier and better guarded + * Levitating ostrich picture modified somewhat by me + * Ostrich landing site picture added to glterrain.pcx by me + * New terrains added: holy tree and ostrich landing site + * Added levitating ostriches + * Added banana transportation AI for banana growers and levitating ostriches + +November 21 2002 Timo Kiviluoto + + * Water *really* makes gunpowder useless + * Selecting a pile in the eat menu doesn't open a scroll bar question + anymore + * Corpse consumption is now more ergonomic + * Item weights are now correctly updated when eating + * The player now knows no gods when he begins the game + +November 21 2002 Timo Kiviluoto + + * The default answer of drop and pickup amount question is now the number + of items, instead of one + * Merka is now the shopkeeper of the dungeon, not one of his guards + * AI sight code works (very) little better now + * The player can no longer fly over oceans if he has walking creatures + in his team + * Broken (runed) whips are no longer generated; since the intact ones + can't break, they were somewhat odd + * The possibilities that items explode or burn (if they can) now depend + on the amount of fire/energy damage + * Monsters no longer attack the enner beast; its death this way wasn't fun + * Reduced the enner beast's damage somewhat + * Enner beast's scream now detonates activated mines + * Unicorn code is now simpler + * Wish parser now asks the player to be more precise if there are more than + one equally good matches; this way typos like "wand of clonning" don't + yield a wand of polymorph etc. just because NameSingular is correct + * It is now possible (and necessary) to specify material when polymorphing + into a golem + * Leg pictures' MColor 3 is now allocated for boots + * Carnivorous plants' color have been adjusted: normal are green, greater blue + and the mother is red + * Genetrix vesana no longer creates plants around dead members of the player + team + * Magical whistle effect no longer attemps to teleport dead members of the + party + +November 20 2002 Timo Kiviluoto + + * Polymorph state causes polymorph less often (the frequency was ridiculous) + * Genetrix vesana level is now easier + * Shops are now bigger + * Nutrition values decreased + * Possibilities of broken bananas decreased greatly + +November 19 2002 Timo Kiviluoto + + * Monster amounts and generation intervals can now be defined in the script + * Decreased the monster generation values in the tunnel + * The middle level of the tunnel is now twice as long as before + * Corrected a fatal bug in the skeleton bodypart severing code + * Gcc warning corrections + * Fixed the Vicious Endless Dinner Time of Mad Slaves Bug + * ApplyExperience() is now called also during actions (I can't understand + why there was an if-sentence disabling it...) + * Rounding error corrections + * More exp balancing + +November 19 2002 Timo Kiviluoto + + * Exp rates quadrupled + * Fixed a bug in the battle info screen: it didn't work correctly if a + two-handed weapon was too heavy to be used + * Animated set of magic portal pictures added to OLTerrain.pcx by me + * Added magic portal olterrain + * Oree's lair can now only be entered through a magic portal in level 9 and + exited via another portal which teleports the player straight to level + one but can only be used if he has the Shirt + * Changed the layout of Oree's lair a little + * The game no longer crashes if the player tries to go down in tunnel end + level while WMode is active + * Added level teleport WMode command ('|') + * Reading in WMode now takes only about one tick + * S&R done: + * WizardModeActivated() -> WizardModeIsActive() + * GetSeeWholeMapCheat() -> SeeWholeMapCheatIsActive() + * GetGoThroughWallsCheat() -> GoThroughWallsCheatIsActive() + * Increased bone's strength value a little + +November 18 2002 Timo Kiviluoto + + * Practically every battle algorithm has been rewritten + * *Lots* of attribute and equipment balancing + * Show battle info screen is now very (perhaps even too) informative + * Automatic dropping of banana peels via config option is now considered + a dexterity action of difficulty one + * The smith no longer fixes broken bananas + * Gave the smith a mithril hammer, because without it he was completely useless + * The "What do you want me to fix?" menu of now shows valpurium swords etc. + which cannot actually be fixed, so that the smith can verbally give the precise + reason why he can't do it + * The smithy now fixes armor, too + * Added IsMetal database bool for materials + * The smithy now fixes only items made of metal + * Added a couple of random mines and bear traps in the artifact vault + * Corrected a bug in the code that prevented pets from eating the player's bodyparts + that prevented *all* other monsters from eating *any* severed bodyparts + * To avoid accidents, an "Are you sure?" question is now asked if the player tries + to eat his own severed limb + * Scroll of create monster is again destroyed after reading + * Magical whistles can no longer be piled + * Added summon monster WMode command ('&') + * Added runed whip to fill the gap between whip and whip of thievery + * Added festring::SearchAndReplace() which searches and replaces + * Character chat replies can now be defined from the script + * Added DefaultName character database attribute + * The following script-placed characters have been named: + * Hulbo the shopkeeper (the one in Attnam) + * Merka the shopkeeper (the one in Elpuri's Cave) + * Haathbar the librarian + * Ikiros the smith + * Richel Decos the imperialist + * It is now possible to add unique monster which are generated at most once + * Added DangerModifier which allows monster designers to force certain creatures + to be generated earlier or later than they normally would + * The following monsters are now unique and more powerful than monsters + normally encountered at the time they are generated: + * Rondol the kobold patriarch + * Guugzamesh the goblin king + * Xinroch the skeleton warlord + * experiment ZQ-29 the spider silk golem (very rare) + * Ur-Khan the orc marshal + * Golgor Dhan the grand master dark knight + * Sherarax the mistress queen + * Valpurium golems are now both rarer and more powerful when they are generated + * IsSolid material database boolean renamed as IsGolemMaterial + * CreateSolidMaterialConfigurations character database boolean renamed as + CreateGolemMaterialConfigurations + * Added an alive material called magical air of which Bill's wills and genies are made; + this solves the problem with their strengths + * Guards are now divided to rookie, veteran, shop and elite configurations + * Added Haedlac Galladon VII the master guard to the Cathedral + * Added Gorovits family hammer and Gorovits family sickle for Ivan + * The imperialist can again fight (he was just overloaded because of that 80 kg golden + plate mail :) + * Added boot of kicking for the imperialist + * Added greater dark frogs, greater light frogs and greater carnivorous plants + * Genetrix vesana now has a 1/3 chance to create a greater carnivorous plant instead + of a normal one + * Genetrix vesana's AI no longer crashes if one of the player team members is at the + very edge of the level + * Greater frog, greater carnivorous plant and genetrix vesana pictures added + to char.pcx by me + * Torso picture and head pictures for goblin prince and king added to humanoid.pcx + by me + * All kobold, goblin and skeleton configurations now look different + * Elpuri's cave consists again of 10 levels; while balancing I realised it wasn't + too long in the last version and now we have thrice as many monsters to meet and + items to collect so I believe shortening was not justified + * Added pick-axe to level 9 (fan request) + * Displacing is no longer faster than normal moving + * Displacing creatures to terrain which they can't walk on is now not allowed + * Polymorphing is now a secret state + * The player will no longer become hungry if saved by WMode life saving + * Mammoths no longer gain insane amounts of endurance experience + * Decreased the healing rate of high endurance monsters + * Corrected a fatal bug in the armor breaking code + * Poison works again + * Added "peaballs" to insults + * The game now asks for keypress if a worn or wielded item is broken + * Messages are again splitted before sending them to history; this way panel + scrolling works more conveniently and great amounts of very long messages + no longer cause a crash when pressing 'M' + * Corrected a bug in the script reading functions that prevented the divide + operator '/' from working correctly + * Corrected a fatal bug in Mjolak's and flaming sword's hit effects + * Added character attributes option to SecretKnowledge + * Removed bone support; it was realistic but not fun and slowed down the game + * Stacking system GUI improved even more + +November 12 2002 Heikki Sairanen + + * Stacking system's GUI improved + +November 7 2002 Heikki Sairanen + + * Linux version improvements (now seems to function like a proper program) + * Genedrix Vesana added to tunnel + * Shopkeeper now offers his remaining money if he doesn't afford the full price + +November 1 2002 Heikki Sairanen + + * Belt of carrying added + +November 1 2002 Heikki Sairanen + + * Pets shouldn't eat the player's bodyparts. + * Look stretch config option. + * If a mine in the inventory is active, it should be displayed somehow. + * Artifact vault. + * Stethoscope info now shows HP and MaxHP + * Daemon flesh is now poisones + * Strong box now looks like a strong box (also edited its graphics) + +October 31 2002 Timo Kiviluoto + + * Item strength values are now somewhat more balanced + * A few gcc warnings corrected + * Many devirtualizations + * Kahvi's sign pic now uses only two m-colors, one for the text and another for the rest + * Optimized contentscript instantiations by removing the older optimizations that slowed + them down + * Most inventory initializations are now script's responsibilities + * Armor breaks again + * Block capacity algorithm remade + * Added script support for random squares inside rooms + +October 29 2002 Heikki Sairanen + + * fixed bug: (In Linux) push X-button, then cancel + * magical whistle now acts as a normal whistle if used less than 1000 ticks after last use. + * Smith added in Attnam + * Spoiler warnings to all script files added + * Snake corpses are poisones + * sign (says "under construction" and IS under construction) + +October 29 2002 Timo Kiviluoto + + * The extremely uninformative battle information of the panel removed + * Added show battle info command ('B') which reveals them instead + * Item pictures are now shown when looking at people + * Worldmap generation is now faster + * Added armor of great health which increases endurance but is heavier, clumsier + and weaker than plate mail + * Invisible monsters seen by infravision are again transparent + * Generation modifiers are no longer calculated for monsters that can't be generated + * Game balancing: + * Body armor now increases head's AV somewhat (let's say it protects the neck) + * Body armor's effect on limb AVs increased (now 3/4) + * Damage needed for breaking items doubled + * New script features: + * Items can now be added to character inventories both in dungeon.dat and char.dat + * Items can be added to the ground much more conveniently + * Chest contents can be defined in the script (even chests inside chests inside chests + and so on are possible) + * One can now define items in the script that have an arbitrary chance to occur in a + given place (inventory, ground, equipment etc.) + +October 27 2002 Timo Kiviluoto + + * Original bodypart system is now a lot better + * Resurrection works again correctly + +October 27 2002 Timo Kiviluoto + + * Items in shops or on the cathedral floor no longer spoil + * Spoil level is now symbolized by Hex's fly effect + * Halved all spoil rates + * Fixed a bug that caused the pick up AI of limb-lacking monsters to crash + * Corrected a bug in the new random item code that created numerous ghost items + * Removed a fatal bug in the block code that resulted in a div by zero + * Killed a minor bug in the bodypart attachment code that prevented attribute + bonuses from updating correctly + * Added flame effect for holy bananas + * Tweaked the shape of the jungle island a little + +October 26 2002 Timo Kiviluoto + + * Weapon prices are now correct + * S&R:ed "omel" to "ommel" + * New materials added: hardened leather, troll hide, nymph hair, ommel hair, + angel hair, phoenix feather, golden eagle feather, spider silk and kevlar + * Added many new material choices for cloaks, belts, gauntlets and boots + * Unarmed, kick and bite THVs decreased + * Corrected a problem with lamps that generated names like "oil iron lamp" + * Corrected a fatal bug in the spoil code + * Intact and broken morning star pictures added to item.pcx by Kahvi + * The old starting room moved to the tunnel + * Size of Elpuri's cave decreased by three levels + * Added scrolls of enchant weapon and armor as a reward to wolf room + * Added random item option to script + * Most items sold in shops and the library are now random + +October 24 2002 Timo Kiviluoto + + * Inelastic armor now decreases dexterity and/or agility + +October 18 2002 Timo Kiviluoto + + * The danger system now takes haste and slow into account + * Monster no longer panic when they die + * Weapon weights are now *much* more realistic + * New weapons added: hammer, sickle, dagger and short sword + * Names of the following weapons modified: + * curved two-handed sword -> two-handed scimitar + * poleaxe -> halberd + * spiked mace -> mace + * Whips no longer break + * Possibility of enchantment scrolls increased + * Some search & replace errors corrected + * Messages printed out during a hit are now concatenated + * The game now aborts if an obligatory room cannot be placed during generation + * Game balance is now completely broken and most damage values insane + +October 16 2002 Heikki Sairanen + + * Added ANTIDOTE effect + * Added antidote liquid + * "Omle" -> "Omel" because *CLASSIFIED* + * added treasure room to Valpuri's cathedral + * slightly increased the possiblity of bottles (to get about as much other potions with the new liquid..) + +October 15 2002 Timo Kiviluoto + + * Sign added to olterrain.pcx by Kahvi + * Ostrich added finally to char.pcx by Kahvi! VICTORY! + * Strong-box added to item.pcx by Kahvi + * Flail head and broken flail added to item.pcx by Kahvi + (he had done them months ago but I'd forgotten to commit them) + * Fix function added for items, though I didn't test it + * Added inline tool function template Recurse() to femath.h, + which isn't and probably won't ever be used, but it's guru + +October 14 2002 Heikki Sairanen + + * Compiles again on linux + * added imperialist and banana grower classes + * added rough outline of New Attnam + * added boulder + +October 6 2002 Timo Kiviluoto + + * Underscores now separate the words of all macros in the game + * The prices of the items added in the last commit are now sane + +October 5 2002 Timo Kiviluoto + + * Optimized the generation of characters somewhat by disallowing + a few unnecessary cache recalculations + * Mana is now hidden + * Finally added a working equipment attribute bonus system + * Added gauntlets of strength and dexterity + * Added boots of strength + * Boots of speed replaced with boots of agility which increase + the attribute instead of hasting the user; this way wands of + haste and Cleptia are useful even if the boots are equipped + * Added helmets of piercing perception, understanding, brilliance + and attractivity + * Added DefaultEnchantment database value for items + * The game now says "Entering level x" instead of "Generating + level x" + +October 5 2002 Timo Kiviluoto + + * Using felist is now perhaps a bit easier + * Picking up multiple items is now more convenient + * Chest usage is now easier + * Chests' StorageVolumes now work correctly + * You can no longer earn money by putting things inside a chest + in your *own* inventory in a shop + * Applying a chest now opens it + * Tweaked the placement of the jungle island a little + * The librarian now starts with some money so he can buy things + from the player even if the latter hasn't yet bought anything + * Fixed a bug in New Attnam's entry pos + +October 05 2002 Heikki Sairanen + + * Support for round corners for rooms added + * Some rewriting of some really awful code in level.cpp, but not enough... + * Started New Attnam (1 % done now or something like that) + +September 30 2002 Heikki Sairanen + + * Gunpower can now get wet + +September 30 2002 Heikki Sairanen + + * Scabies' good effect now doesn't inflict damage upon friends and neutrals + +September 29 2002 Timo Kiviluoto + + * Skull's weapon category changed from MISC to UNCATECORIZED; + the former is not a weapon but an inventory category and caused + a crash when the skull was wielded + * Sparkles and/or flies over transparent pictures are no longer + themselves transparent + * Camera now works even if the area is smaller than the screen + * The underwater tunnel and New Attnam are now placed correctly + * worldmap::WhatTerrainIsMostCommonAroundCurrentTerritorySquareIncludingTheSquareItself() + and worldmap::SmoothAltitude() optimized somewhat + * Corrected the Mysterious Absence of Elpuri on the Busy Screen Bug + +September 26 2002 Heikki Sairanen + + * Added lightning effect to bitmap + * Prevented digging with broken pick-axes + +September 24 2002 Heikki Sairanen + + * Now compiles on Linux again + +September 23 2002 Timo Kiviluoto + + * Monster generation *might* be a bit better again + * Corrected a minor outlook bug in the equipment pictures around + the silhouette + * Corrected a minor outlook bug in the equipment screen + * Added colored light effects for many monsters and items + +September 22 2002 Timo Kiviluoto + + * Added parentheses around several macros in ivandef.h + * Added MakeRGB24(), GetRed24(), GetGreen24() and GetBlue24() inline + functions + * Renamed old color handling routines to MakeRGB16() etc. + * Added rgb24 function to script and renamed rgb to rgb16 + * Emitation and luminance are 24 bit rgb values; lights may now be + colored and they can be mixed dynamically + * Note: since rgb elements are in the range 0-255 instead of 0-511, + old light values are converted to new ones like this: + MakeRGB24(Light / 2, Light / 2, Light / 2) + +September 17 2002 Heikki Sairanen + + * Flies added + * Items inside chests carrid by player at death shown + * 3 configurations added to chests: small, normal, large + * GCC define added to configure.in + * StorageVolume moved to script for chests + * Added MaxGeneratedContainedItems for chests' parameters + * deactivated Scabies temporarily + * Fixed msg when trying to put chest into the same chest + +September 17 2002 Timo Kiviluoto + + * Capitalization functions of festring are now more flexible + * Added festring::IgnoreCaseFind() which acts like std::string::find() except + that it ignores case + * Wish parser is no longer case sensitive + * Added SoCM alias for scroll of change material + * Position script now allows one to select a random squares inside arbitrary + rectangles + * Added owterrain classes and script entries for New Attnam and the underwater + tunnel + * Added femath::CompareBits() which allows easy ordering of POD objects + (but alignment of structure members must be disabled first!) + * The < operators of graphic_id and configid are now more elegant + * Saving and loading graphic_id and configid objects is now more elegant + +September 14 2002 Timo Kiviluoto + + * Banana peal is now banana peel (why wasn't this typo noticed before?) + * Wands' inventory entry is now correct + * Corrected a fatal bug in the lump hit code + * Completely black carnivorous plants are no longer generated + * Interlevel transportation code is now a lot better + * Levels can now have multiple entrances which one may link without restrictions + * Names consisting only of spaces are now prohibited, and spaces at the end of + names are now discarded + +September 11 2002 Heikki Sairanen + + * The Price of cheap copies of Petrus lowered from 500 to 50. (They aren't THAT sacrate!) + * Bone changed from two handed weapon to one handed. I belive so, but might need debate. + * Skull added + +September 10 2002 Timo Kiviluoto + + * Corrected lots of warnings given by gcc compilation + * The game doesn't generate monsters that can't hurt the player nor can + be themselves hurted, like a mammoth against an AV 60 enemy + * The same applies to monsters which deal a ton a damage but can also be + killed really easily, like skeleton warriors + * Armies of monsters of single type are now a lot less common + * Attribute decrease due to negative exp is now four times slower than before + * Mammoth and buffalo stats tweaked a bit + * Attribute system balancing + * Valdemar added by Hex + * Omle urine is now more powerful + * Omle urine messages work now better + * Healing liquid doesn't work on non-living bodyparts anymore + * Healing liquid effect adjusted + * Healing liquid now prints a message when it grows new limbs + * Useful potions are now more common + * Enchantments can now be defined in the script + * Added lots of enchantments for the initial equipment of high level monsters + * Pictures of a native village and an entrance to an underwater tunnel added + to WTerrain.pcx by Kahvi + * Picture of an imperialist added to Humanoid.pcx by Kahvi + * Wall with Petrus poster added to OLTerrain.pcx by me + * Added some Petrus posters around Attnam + * Probably something else, I was nine days without coding and my memory + about what I did is dizzy + +September 07 2002 Heikki Sairanen + + * The frequency of suicide dwarfs increased from 1000 to 10000 + * Kicking locked chests can now shatter the lock + * The contained items of a chest now receive 50 % of the physical damaged inflicted to the chest + * Chest's StorageVolume increased from 1000 to 5000 (even bottles couldn't fit inside before) + +August 30 2002 Heikki Sairanen + + * Whistle now sets the waypoint of some creatures to users pos + * Displacing monsters that are stuck no longer works + * Bug with braking bottles probably fixed + +August 29 2002 Timo Kiviluoto + + * Equipment is now dropped correctly after Sophos teleport a bodypart away + * Corrected a bug that caused dogs catching bones to crash + * Added "magic whistle" alias for magical whistle + * Magical whistle now prints a message when blown + * Removed rings of energy and acid resistance, as they were completely useless + * Doubled all block values (is this too much?) + * Scroll of change material reading can now be cancelled + * Bodypart healing is now faster + * Rest command cannot be used anymore if only non-alive bodyparts are hurt + * Rest command is now terminated if only non-alive bodyparts are hurt + * Female slaves again wield palm branches + * Sparkle effect time doesn't anymore need to equal 0 mod 16, and their + position and timing varies depending on class instance + * SparklePos now prefers the middle parts of the picture, since clipping + the sparkle isn't really aesthetic + * Unseen far away stacks can now be 'l'ooked at if SeeWholeMapCheat is active, + and full information about their items is shown + * Corrected a fatal bug that caused the breaking of wielded potions to crash + * Added weapon and armor enchantments (the +x thing) + * Added scrolls of enchant weapon and enchant armor + * '?' screen is now easier to read in WMode + * Corrected a bug that caused incorrect SS levels to be printed in the inventory + * Horn and flail pictures added to Item.pcx by Kahvi + +August 27 2002 Timo Kiviluoto + + * Sparkling now works with all items made of sparkling material + * Sparkle effect is now less frequent + * All prices are now more or less correct + * Valpurus now gives a valpurium two-handed sword, not a curved one + (old effect was a bit too powerful...) + * Loricatus now changes things to steel, not mithril (same as above) + * UpdatePictures() is now not called when creating temporary monsters + and equipment for danger calculations + * Iterative UpdatePictures() calls are now less common + * Reduced enner beast's damage by 99.99% + * Kicking works a bit better now + * Wand of locking works again + * Quest artifacts no longer spoil + * Adjusted bear trap damage a little + * Offer values now depend on price + * Cleptia now hastes/slows the player instead of increasing/decreasing + his agility + * Name length is again limited + * Removed bodypart animation structures, as they weren't used and were + already outdated + * The game crashes no more when an item spoils when it's currently being + eaten + * Wolves are now easier + * The dungeon shop now appears before the wolf level + * The dungeon shop has now more useful items for sale + * Vomiting in the world map no longer crashes + * Digging doesn't destroy your weapon anymore if both hands are full + * Decreased difficulty in general + * Probably much that I have forgotten (night coder in a hurry has a poor + memory) + +August 22 2002 Timo Kiviluoto + + * Kamikaze dwarves now die in the initial explosion + * Kamikaze dwarves' bodyparts now fly to random directions in the explosion + * The game now prints a "You hear an explosion." message when a square + outside LOS explodes + * Fixed many bugs in StepOnEffects + * Corrected a bug in bananas' volumes + * Experience and nutrion values balanced + * Using the whip of Cleptia is now an evil deed + * Flaming sword's hit message was fixed + * Vermis, Turox and the whip of Cleptia now use a definite article + * Adjusted new artifact weapons' volumes, materials and hit effects + * Hammer, sickle, short sword and dagger added to Item.pcx by Kahvi + * Broken pictures of the following items added to Item.pcx by Kahvi: + hammer, sickle, short sword, dagger, chain mail, shield + * Chain mails and shields may now break + * Corrected an arithmetic error in the ReceiveDamage code + * Inactive mines no longer explode when stepped on + * Inactive mines no longer explode from physical damage, only from fire + and energy + * Mines and bear traps are now automatically dropped upon activation + * Bill's wills and snakes no longer slip on banana peals + * Corrected a bug that caused the "you wake up" message after faint to be + printed twice + * Cured a corpse bug that caused DrinkableSorter to crash + * Oree doesn't "look slower" when he dies and his boots of speed are + dropped to the ground + * Corrected a typo in Oree's death message + * GetNearestFreeSquare calls no longer crash if no free square was found + (usually RandomSquare is used instead) + * Vomit amount is no longer discarded + * Dipping works again + * Severed bodyparts and corpses now bleed for some time + * Weapon poisoning etc. is now only temporary + * Meleeweapons with special hit effects now call meleeweapon::HitEffect(), too + * Broken artifact weapons now don't cause special hit effects + * Whip of Cleptia must be made of material with > 5 flexibility to work + * Kahvi's stethoscope picture integrated + * Elpuri's head is again black + * "Your xxx bleeds (very badly)." message is now replaced with "Your xxx is in + (very bad) condition." if the bodypart is not made of living material + * Golem corpses aren't bloody anymore + * The idol which an NPC wants to become when polymorphing with polycontrol + active now depends on sex (undefined -> mammoth, male -> communist, female -> + mistress queen) + * Polymorphed creatures now drop all equipment they can't use + * The dolphins can now be polymorphed + * AskForKeyPress() now automatically capitalizes its topic + * Starting a new game after drowning doesn't crash anymore + * Ocean's IsWalkable function did not take a const parameter, + which caused seas to become walkable, but this is now fixed + * Corrected the "...it is worth 9085104 gold pieces!" message that appeared + in shops because the price was read from a random RAM address + * The enner beast now screams every turn if panicked + * Fearless creatures panic no more when their bodyparts are severed, and almost + fearless less often + * Block strengths are now shown correctly in the panel + +August 18 2002 Heikki Sairanen + + * Added sparkle animation cycles + +August 18 2002 Heikki Sairanen + + * Wand-system's code shortened by maybe 80 % + * Wand of cloning added + +August 18 2002 Timo Kiviluoto + + * Added item piling + * Removed numerous tests forgotten in the last commit + * One can no longer see weights etc. when looking at far away stacks + * The Obscure Mithril-Handled Wooden Sword Bug corrected + * Fixed some warnings given by gcc compilation + * Corrected a database bug that caused non-living creatures to print + odd messages when running a gcc-compiled binary + * Removed two other simultaneously found bugs concerning non-living + creatures' attributes + * Weapon and armor prices are somewhat more correct now + +August 17 2002 Timo Kiviluoto + + * StringQuestion again shows a "Too short!" message when appropriate + * Names and descriptions of several gods changed + * Stack interface is now much simpler and its usage much easier, + as in the following example: + + for(stackiterator i = Stack->GetBottom(); i.HasItem(); ++i) + { + i->MemberFunctionOfItem(); + item* CurrentItem = *i; + } + + * Corrected an enormous amount of bugs caused by the new item visibility + system + * Added lsquare::GetSideStackOfAdjacentSquare() which allows much + more convenient usage of side stacks + * Description of sidestacks is now shown on the squares above which + they really are (because previously you could "see" lanterns through + walls in lookmode) + * Level-leaving code now works little differently with ESP-monsters + (they must now see the stairs before they can follow the player) + * Panel's level description is now always capitalized + * RaiseStats + ESP works correctly now + * Added "severed" adjective for severed bodyparts + * Item information shown in the inventory screen now depends on category + * festring is now a fully static class, not a namespace, since namespaces + are evil + * festring::IntegerToChar() now calls festring::InstallIntegerMap() + if it hasn't yet been done; manual initialization is thus no more necessary + * Added a couple of festring::SplitString() functions that make line + cutting and chapter managing much easier + * felist now splits lines correctly + * Added ushort Marginal parameter for felist::AddEntry, which makes splitted + lines occasionally better looking + * Corrected an outlook bug in the "You didn't get to the high score list" message + * Poison is now a lot less deadly + * Default outline colors may be a bit less ugly + * Lots of C-style casts converted to reinterpret_casts + * The whip of Cleptia is now derived from whip + * The player now starts with a copper spear and a leather helmet, so that + he would suck a little less + * Stethoscope, skull and scalp pictures added to item.pcx by Kahvi + * Thief picture added to humanoid.pcx by Kahvi + * Critical hit message was not displayed correctly if the player was the hitter + and this was fixed + * Created a more or less consistent system for broken items + * Integrated Kahvi's broken weapon pictures -> all normal weapons and bananas + may now break both when blocking and hitting + * Changing material to air is now prohibited + * Bananas are now derived from meleeweapon so that they can be poisoned + * Food finally spoils + ! Note: Due to spoil system, it is now illegal to send characters and items + to hell in destructors, so be sure delete everything there + * Bone and bread are now organicsubstances so that they can spoil (albeit + slower than other materials) + * Meleeweapons' name now shows if they are dipped into something + * Corrected a few small bugs in the Memorized and MemorizedDecription update + request code + * Resurrection works again + * Prohibited unoriginal bodypart attachment during resurrection since limb + images are not universally compatible + * Duplicating corpses now works + +August 11 2002 Heikki Sairanen + + * Monsters now panic more or less correctly + * Item info option added to Wizard mode's secret knowledge command + +August 10 2002 Heikki Sairanen + + * corrected funny (ok it's not very funny) spelling mistake. The text said before: "...ThOu HaSt SlAuGtHeReD pEtRuS..." + * Added panic. At the moment all monsters panic when their HPs reach 1 which will have to be tweaked. + +August 9 2002 Heikki Sairanen + + * Added parameter IgnoreVisibility to all stack::DrawContents and their calls + * Tweaked the number of beartraps and mines in level (now 0-4 of each) + +August 8 2002 Timo Kiviluoto + + * Carnivorous plants are now longer born inside walls nor over monsters + * StuckTo system works better now, maybe + * Added Duplicate() functions for character and item + * Gauntlets and boots are now generated in pairs + * Monsters pick up and use equipment again + * Added cloak of invisibility + * Invisibility is now taken into account when calculating danger + +August 4 2002 Heikki Sairanen + + * Msgs of special items corrected + * Success of using Whip of Calamus now depends on players relations to Calamus. + * Wishing for "lamp" gives now an oil lamp + * Headless creatures don't screams + * Backpacks work (-> Suicide dwarfs work) + * GetDeathMessage functions changed to const + * Bug with bitmap::DrawFlames that did funny things to the RAND() function corrected + * Corrected a minor typo in chest + +August 4 2002 Heikki Sairanen + + * Bug in poisoned fixed + +August 4 2002 Heikki Sairanen + + * Mines and beartraps work well with the new system. + +August 4 2002 Timo Kiviluoto + + * Eating is no longer an evil deed + +August 4 2002 Timo Kiviluoto + + * Added char* festring::IntegerToChar(long) which is a much faster + variant of the nonstandard itoa + +August 3 2002 Timo Kiviluoto + + * SecretKnowledge's defence part now shows block info + * Player can no longer eat scrolls, silver stones etc. + * Eating is no more fatal + +August 3 2002 Heikki Sairanen + + * CanBeSeen and IsVisible systems merged together to CanBeSeen. + * Added a lot of consts + * Mine and bear trap pretty much recoded. + +August 3 2002 Timo Kiviluoto + + * Blocking is no longer possible when fainted + * Shields now affect RelativeDanger + * Blocking now increases weapon skills + * Added SHIELDS weapon skill category + * Added more parameters for hit effects + * Hit effects of gauntlets and boots now work + * Added system for special unarmed and kick effects + * Some S&R done: + * SingleWeaponSkill -> SWeaponSkill + * CategoryWeaponSkill -> CWeaponSkill + * gweaponskill -> cweaponskill (where the dev/null did that g come from?) + * Single weapon skill is now removed correctly when an arm is severed + +July 31 2002 Heikki Sairanen + + * Added whip of Calamus + * Tweaked items thrown at doors + * Possibly something else (sorry. Bad commit) + +July 30 2002 Timo Kiviluoto + + * Mistress hair color now depends on configuration + * All orc configurations look different + * Orc's article is now correct + +July 30 2002 Timo Kiviluoto + + * Hostile and neutral NPCs no longer forget weapon skills like the player and + his team + * Corrected a lot of warnings given by the gcc compilation + * Gauntlets and boots now increase unarmed and kicking damage, respectively + * The AI now fights invisible enemies correctly if they have attacked first and + have not moved anywhere + * DodgeValue is now doubled if the hitter can't see the dodger + * ToHitValue is likewise multiplied by two if the hitter is invisible to the + dodger + * Added poison material that has an effect of activating the poisoned state + * RestoreBodyParts() doesn't crash anymore, I think + * Priests, gods, Petrus and angels now detect the original bodypart even if + it is wielded + * Corrected many smaller bugs in Hex's bodypart healing codes + * Fixed a minor bug in the item drop code + * Bodyparts are now randomly rotated, flipped and/or mirrored when severed + * Macros transformed to inline functions: + * GET_RED -> GetRed, GET_GREEN -> GetGreen, GET_BLUE -> GetBlue + * MAKE_RGB -> MakeRGB + * MAKE_SHADE_COL -> MakeShadeColor + * level::CollectCreatures() now test whether the monsters and pets can see + the player, not vice versa like before, which was illogical and made + leaving the dungeon nearly impossible if ESP was active + * Corrected the Colossal Giant of Seven League Legs Bug + * CarryingStrength has now a minimum of one so that effectively 2500 grams + can be carried even without legs + * Blitting no longer crashes when width or height is <= zero after clipping + (this happened occasionally if HP didn't fit onto the panel due to WMode + cheating) + * colorizablebitmap::MaskedBlit() is now clipped, too + * Added move AP cost (MAPC) to the info panel + * Being burderned, stressed or overloaded no longer makes you move faster :) + * Added proper replies for orc and cossack + * Fixed a bug that allowed one to paralyze creatures by talking to them + * Emitation is again calculated correctly after load + * Added item pictures to the equipment menu + +July 29 2002 Timo Kiviluoto + + * Added orc, orc slaughterer, orc squad leader, orc officer and orc general + * Added cossack + * Goblins look slightly better over wooden parquet + * Skeletons look slightly better over marble floor + * Corrected a logical error that caused multiple attack style creatures like + mammoths and unicorns to be considered less dangerous than they really were + +July 28 2002 Timo Kiviluoto + + * Equipment screen now shows if a weapon is wielded with both hands + * It is now possible to define starting equipment of a character from the datafile + * Added rings of energy, acid and poison resistance + * Added "reload datafiles" WMode command: it is now possible to add characters, + items, terrains and materials at run time! + * Added AttributeBonus character database value, which is a percental value + added to or substracted from all attributes and very useful with configurations + * If a hit does no damage, the game now prints a special message + * Category and single weapon skills can now be set in the datafiles + * Panel and all info commands now show min and max damage instead of the + incomprehensible "attack strength" + * Corrected the rounding errors of the weapon skill screen caused by floating + point arithmetic + * Corrected a fatal bug in the corpse save code + * 17 new character configurations added: + * rookie, veteran, elite, master and grand master darkknight + * skeleton warrior and warlord + * goblin berserker, butcher, prince and king + * kobold chieftain and lord + * mistress torturing chief, whip champion, warlady and queen + +July 27 2002 Heikki Sairanen + + * Added poleaxe named Mjolak + * Added spear named Vermis + * Both have currently _BAD_ messages. The system is not ready yet. + +July 26 2002 Timo Kiviluoto + + * Added system to estimate the danger of a monster relative to another + * Removed old danger system; it just didn't work + * Monsters generation is at least a little more balanced and less dynamic now + * Corrected a bug in the wizard mode activation code + * Config system is now much simpler + * Corrected a bug in std::map save code + * Added Limit(Value, Minimum, Maximum) inline function to femath.h, which limits Value + to range [Minimum, Maximum] and returns it + +July 25 2002 Heikki Sairanen + + * Zombies' and skeletons' heads are no longer vital + * Zombies are generated missing some bodyparts + +July 25 2002 Heikki Sairanen + + * Doors stay closed if closed when broken + * Visual bugs with close in Linux with the X-button corrected + * Skeletons now drop bones instead of bodyparts + +July 25 2002 Timo Kiviluoto + + * A really bad memory leak corrected: materials were never destroyed. I'm bottomlessly + ashamed! + * The Ferocius Luxification Bug fixed + * level::Luxify() renamed as level::FiatLux() + * Slot donations work again, maybe + * Danger system is really mad + +July 25 2002 Heikki Sairanen + + * Flaming sword added + +July 24 2002 Heikki Sairanen + + * Added special flame effect to bitmaps + * Added flames to bananas ;) + +July 24 2002 Timo Kiviluoto + + * Single weapon skill's LevelDownMessage is now printed correctly when the item is + wielded + * Doubled all dodge values + * Added "character defence values" option to SecretKnowledge + * SecretKnowledge now writes the shown information to a file afterwards + * Added CreateSolidMaterialConfigurations character database boolean that can be + used to easily add golem-like monsters + * SecretKnowledge now shows all possible golems as separate entries + * Attack info of nonliving bodyparts is calculated correctly at last + * Strength no longer affects THV, as it made golems far too powerful + * Halved the AP cost of biting + +July 24 2002 Heikki Sairanen + + * void globalwindowhandler::UpdateTick() changed to return Tick + +July 23 2002 Timo Kiviluoto + + * Wishing for "ring of teleport" now works + * The game now asks whether one wants to remove saves when quitting in WMode + * Real save is now removed and autosave is done when WMode is activated, to prevent + the potential abuse of the latter + +July 23 2002 Timo Kiviluoto + + * The number of BodyParts and AllowedWeaponSkillCategories cached + * Some S&R done: + * Description() -> GetDescription() + * PersonalPronoun() -> GetPersonalPronoun() + * PossessivePronoun() -> GetPossessivePronoun() + * ObjectPronoun() -> GetObjectPronoun() + * There were some brackets missing in the attribute experience code, which caused them + to be increased and decreased twice as fast as they should; but this has been fixed + * Nonhumanoids' StrengthExperience and AgilityExperience are now correctly initialized + with zero during creation + +July 23 2002 Timo Kiviluoto + + * Removed some obsolete and commented code + * Dodge value is now cached + * Optimized weaponskills and regeneration a bit + +July 22 2002 Heikki Sairanen + + * new Fountain effect: randomly activates a mode temporarily or permanently depending on luck. + * teleporting yourself in Attnam no longer makes all the citizens angry. + +July 22 2002 Timo Kiviluoto + + * AnimationController now redraws only the equipment; stats and msg panel are left + untouched + +July 22 2002 Timo Kiviluoto + + * BurdenState and all non-trivial attack information is now cached + * Some recently added bugs in TemporaryStateIsActivated(), EquipmentStateIsActivated(), + IsUsingArms(), IsUsingLegs() and IsUsingHead() fixed + * Corrected a fatal bug in the interlevel travel code born due to Hex's search & replace + * Fixed some odd block messages + * Many new bugs included + +July 22 2002 Timo Kiviluoto + + * Config screen now shows "disabled" as the autosave interval if it's zero and + "1 turn" if it's one (not "1 turns") + * LOS is now updated immediately when perception is naturally increased; + previously you had to move before the sight range was increased + * RaiseStats and LowerStats cheats now work correctly when one is polymorphed into + a nonhumanoid + * Fixed an AP system bug that caused the AI of stationary monsters (most Attnamians + and carnivorous plants) to be run each tick instead of each turn, thus slowing + down the game considerably + * Corrected the Great Attnamian Blitzkrieg Bug added by the last commit + +July 21 2002 Heikki Sairanen + + * Consummos bad pray effect works. + +July 21 2002 Timo Kiviluoto + + * typeid calls replaced with GetClassId()s, thus making the script error messages of a + gcc compiled binary a bit less bizarre + * Weight and Volume cache system remade + * Equipment no longer increases HP + * Corpses' weights now work + * Emitation cache system added + * Emitation updating is now handled correctly when items are wielded or worn + * Fixed some other light system related minor problems + * If reading is terminated because the light level drops, the game now prints a message + explaining what has happened + * Scroll of create monster creates only one monster again (although I am not positively + sure whether that horde of monsters appearing was a bug or feature...) + * Corrected a bug that caused that caused ESP-seen people on never seen squares to + darken after they were 'l'ooked at + * HP and MaxHP of characters cached + * Master and MaxHP of bodyparts cached + * Added parentheses around the MAKE_RGB macro, because conditional expressions of the form + if(Color == MAKE_RGB(R, G, B)) caused a syntax error without it + * Added a powerful colorized font cache system + * Optimized weaponskill::Tick(), Regenerate functions, MainMaterial handling routines, + gear retrieval functions, and so on... + +July 16 2002 Timo Kiviluoto + + * Added rect struct to femath.h which can be easily used to define rectangles in 2D-space + * DO_FILLED_RECTANGLE macros replaced with loops going through a rectangle generated by + femath::CalculateEnvironmentRectangle that uses the mentioned rect structure + * Many key functions (GetBodyPart() etc.) unvirtualized to gain speed + * Equally many small but often called functions moved to headers so that they can be inlined + (though I had to bury my mission of preventing Main's headers from including other Main's + headers) + * Continued Hex's crusade against long lines by adding character::GetStackUnder() and + many xxx::GetNear(L)Square()s (shortcuts of xxx::GetArea/LevelUnder()->Get(L)Square()s) + * Replaced a few statements of the form RAND() % (2^n) with the somewhat faster equivalent + RAND() & (2^n-1) (for instance, RAND() % 2 -> RAND() & 1) + +July 15 2002 Heikki Sairanen + + * added LevelUnder() to item, character and lterrain + * Replaced code in item, character and lterrain and their derives to use LevelUnder(). + * Added GetPos() function to item + * Added stethoscope + * Snake is now "lying" not "standin" + * You can no longer engrave when you cannot read. + +July 15 2002 Timo Kiviluoto + + * Compiles again under gcc 3.04 + +July 15 2002 Timo Kiviluoto + + * SecretKnowledge command now offers information about the attack capabilities of every character + * DO_FOR_SQUARES_AROUND macros have been replaced with loops using various GetNeighbourSquare() + functions + +July 14 2002 Timo Kiviluoto + + * Angels now carry a holy book of their divine master + * Decreased the brigthness of palette entry 151 by 5 in all m-colored graphics files, + for it conflicted with the transparent color and caused problems with some image editors + * Configuration is now saved even if the game is terminated abnormally + * Corrected the remaining problems of the message history + * Animations now work correctly during faint, reading, digging and similiar automated actions + * Continent number and terrain height are again shown while looking on the world map when + SeeWholeMapCheat is active + * Disabled the FOW on the world map, for IMO it doesn't make sense there (far away forests, + towns and continents are not "seen", after all, they are known to be there, and the + area revealed by moving around there is actually learnt from the natives' speeches etc.) + * LOS range in the wilderness doesn't depend on perception anymore, for reasons explained above + * Optimized the world map draw code greatly + * Modified dungeon LOS so that when walking in darkness, only one square in each direction is + considered seen (it can be touched) and its memorized picture is updated. It was rather odd + that you could "see" darkness really far away before + +July 13 2002 Heikki Sairanen + + * Added teleport control, teleport randomly and polymorph randomly states + * Added rings for all these new states. + +July 13 2002 Timo Kiviluoto + + * Oceans and the dolphin pool are now animated + * Decreased the global frame rate somewhat + * Macro system tweaks + * Corrected a bug in the temporary life saving code + * Reversed the order of the message history + * Corrected some bugs in the message history code, but not all of them + * Corrected a minor problem with database Aliases + +July 12 2002 Timo Kiviluoto + + * Corrected many warnings given by gcc 3.04 compilation + * Personal pronouns are now correct while looking at far away people while SeeWholeMapCheat is on + * Dolphins are now rotated and mirrored randomly every turn, which makes them seem more playful + * The second sentence of the kamikaze dwarf hymn message now starts correctly with an uppercase letter + * Macro system is yet again a bit shorter and more elegant + * Removed the unused function definitions of charde.h + * Bodypart pictures of farmers and housewives are again randomized correctly + * The flower color of the carnivorous plant is again randomized correctly + * Adjusted brightness levels of many bodyparts in humanoid.pcx + * Made some tiny corrections to the graphics in humanoid.pcx and char.pcx + (for instance, enner beasts mouth is again black and white, not m-color 1 and white) + * Corrected all buggy bodypart BitmapPoses + * Fixed all IMO ugly bodypart colors + * There was also an attempt to optimize the level generation; as a result it's now somewhat slower + * Stretchblit is now masked (I needed this feature for IGOR 1.20 that now includes a CW-like draw queue system) + * Added colorizablebitmap::Roll(), which is able to simultaneously move the contents of a given rectangle + by an arbitrary amount of pixels horizontally and/or vertically so that the clipped pixels appear on the + opposite side (also needed for IGOR 1.20 as an option) + * Added #defines for the eight direction keys to felibdef.h + * Corrrected a typo in the monster polymorph message + * ESP no longer crashes in the world map + * Air is now transparent and thus are Bill's wills and the unclothed parts of genies, too + * Mommo configs can again be generated + * Added Flexibility material database value and removed the old IsFlexible boolean + (non-living bodyparts now use this as their dexterity or agility and the FormModifiers of + whips and belts are multiplied by it) + * The strengths and agilities of nonhumanoids made of non-living materials are now correct + * Characters with zero strength (eg. Bill's wills) can now move as long as they aren't carrying anything + * Skeletons are no longer considered as bone golems when calculating strengths, dexterity and agility, + for IMO they draw their attributes from the necromantic magic that sustains them, not from the material + (and, it seemed very odd that animated skeletons were thrice as strong as average humans with similiar + skeletons inside them) + * Corrected an extremely fatal bug of utmost deep roots that allowed the player to enter the dolphin + pool of the Cathedral by displacing a pet bat or angel flying over it, even though he couldn't swim + nor float himself + * Added fiber material of which carnivorous plants are now made, instead of leaf (I firmly believe that the + stem is more important than leaves. Someone with a deeper understanding of plant anatomy correct me + if I'm wrong) + * A player controlled carnivorous plant can no longer move + * Banana peal golems are now allowed + * Forced Ivan's plate mail to be made of iron again + * Made a gum solution that allows angels and genies to have leg strengths and be able to carry things around + despite the fact that they don't have legs + * Updated the Docs and moved the older files to the Obsolete directory + * Added some truly ancient files concerning the earliest incarnations of IVAN there, too, + including even holydoc2.jpg, the legendary scanned documentation of RL1! + * Translated the Finnish entries of Small Ideas.txt and Great Ideas.txt. + However, I did not touch Code Improvements.txt, since my English is not good enough to make them as + cryptical and incomprehensible as they are now. There is a notice also in the beginning of the file + explaining this. I didn't alter the entries of NextRelease.txt (former Remember2.txt) either, + since that file is highly temporary and thus a translation would be a waste of time + * Added a bool character::StateIsSecret[] array that makes it possible to add secret states which + are not shown on the panel + * Life saved and lycanthropy states are now secret + +July 11 2002 Heikki Sairanen + + * Poisoned state added + * spiders, school food and snakes to use the state + +July 9 2002 Heikki Sairanen + + * Now compiles under gcc 3.1 + +July 8 2002 Timo Kiviluoto + + * BodyParts() search & replaced with GetBodyParts() + * If SeeWholeMapCheat is active, character look info is now available even if the target can't be seen + * Name functions are now much faster + * New, *yet* faster techniques for string handling are available: + * AddName(String, Case); which is much faster than String += GetName(Case); + * String << AnotherString << Int << CString << Etc; which is much faster than + String += AnotherString + Int + CString + Etc; (you must #include "stdover.h" though) + * Script generated altars again randomize their divine owner correctly + * Attnam's and level 10's altars are again owned correctly + +July 8 2002 Heikki Sairanen + + * snake added (although it doesn't work) + * angel healing friends fixed. + +July 8 2002 Timo Kiviluoto + + * Chest and unicorn look better now + * Corrected The Unicorn of Godly Strength and Speed Spawning Bug + * Decreased the brightness of palette index 148 by seven in all materialcolorized + graphics files (don't ask why) + +July 8 2002 Timo Kiviluoto + + * Added CreateDivineConfigurations boolean datavalue to all databases, which can be used + to add new god-dependent characters, items, materials and terrains very easily, + and to define varying attributes and specialities for them. It is now used for + angels, kamikaze dwarves, holy books and altars and reduces their overlapping code a lot, + and unique holy waters for each god and other similiar wonderful features are + a piece of cake with the new system + * Angel, golem and genie graphics have been converted to humanoids by me. (btw, Kahvi is a moron) + * All lterrains have been datafiled to glterra.dat and olterra.dat; + it is now possible to add new terrains without modifying code and blaah blaah + * All glterrains have been converted as configurations of solidterrain and liquidterrain + * Most olterrains have been converted as configurations of wall, decoration and container + (the latter holds the bookcase and is reserved for future use) + * Integrated Kahvi's older looking walls for dungeon rooms + * Finally integrated Kahvi's new chest picture + * AnimationController no longer crashes when a full screen DOS prompt is activated in Windows + * Cheated lookmode wilderness terrain descriptions don't disappear after the first glance anymore + * Added periods at the end of some of Hex's incomplete messages + * Corrected a memory leak in the fluid load code + * Removed the seemingly ever-lasting problems with item's SquareUnder by removing it. + Instead, GetSquareUnder() now passes the request for position to Slot + * The game now aborts if an unrecognized datavalue is found in the datafiles + (previously it just discarded it) + * All script and datafile aborts now print the line number of the respective error + * All low level inputfile aborts (like ReadNumber syntax errors) now print the file name and + the line number of the error (the latter only if it's appropriate, as it isn't for instance + in the case of an unexpected EOF error, since it always happens in the last line) + * If the player isn't able to use certain equipment (usually due to a lost limb), + its slot isn't shown in the panel anymore even as an empty rectangle + * Mine's explosion power now depends on its ContainedMaterial + * Removed more than 1800 lines of obsolete code left commented all around the project + (but I didn't delete anything I wasn't sure to be irrelevant) + * Angel and genie don't have legs nor a groin anymore (what a terrible fate) + * Animation controller loops are now not run while wieving menus + +July 6 2002 Heikki Sairanen + + * Poison state added. + * Now compiles under GCC 3.1. Again problems with typenames in proto.cpp + +July 5 2002 Timo Kiviluoto + + * Temporary states whose counters are zero are now considered permanent intrinsics + * The character database value PermanentStates renamed as ClassStates + * The character data member PermanentState renamed as EquipmentState, + and the associated function names updated as well + * Added BeginStateHandlers and EndStateHandlers that are called always when states + (either temporary, intrinsic or controlled) are activated/deactivated + * Removed the old handler system used only when switching a temporary state on/off + * Simplified the inner structure of the code in such way that haste and slow can now be + in effect simultaneously, but this is represented in the panel as if neither was active + * Added infravision and ESP states + * See invisible state removed, as infravision now confers the same benefits + * HasInfraVision character database value removed + * Added wand of invisibility + * Added ring of infravision + * Added amulet of ESP + * Draw system rewritten completely to make draw functions far more flexible and + easier to use and to remove some duplicate code + * Added transparency effect for invisible creatures sensed by infravision or ESP + * Added object::MaxAlpha() function to make the adding of transparency effects again a bit easier + * Characters seen by infravision or ESP are no longer affected by varying luminance; + instead they are always seen as if the light level was 256 + * Corrected a recently-added bug which caused AlterLuminance not to update + MemorizedDescription correctly when the first emitter was added + * Added item::IsVisible(), stack::GetVisibleItems() and stack::GetBottomVisibleItem() + functions which allow the convenient use of hidden items + * Script generated land mines and bear traps are now correctly invisible + * Hidden items are now shown if SeeWholeMapCheat is on + * Added iosystem::ScrollBarQuestion which acts as a graphical version of the normal NumberQuestion + * Contrast can now be set with ScrollBarQuestion and the display is still updated constantly + * Removed the increase and decrease contrast ('b' and 'B') commands + * Separated the eat and drink ('e' and 'D') commands + * Redefined much of the keyboard layout + * Decreased reading times required when WMode is activated + * Skeleton's TotalVolume and FleshMaterial are now correct + +July 03 2002 Heikki Sairanen + + * Teleporting from a bear trap works ok + * Mine works (except is seen normally) + +July 03 2002 Heikki Sairanen + + * SDL's key repeat activated + * Pressing a control key during action no longer stops the game until key press. + * Helmet added + +June 19 2002 Heikki Sairanen + + * Loricatus, Scabies, Cruentus fix players BodyParts (each with their own specialities of course) + * Angels also fix players bodyparts. + * Tweaked the healing methods for the other healing gods for slight elegance in code. + * Some small changes in proto.cpp to make it compile with gcc 3.1 (one typename added). + +June 17 2002 Timo Kiviluoto + + * AP system remade: it is now the actions' duty to decrease AP, not Be()'s + * Added read action, thus allowing reading times longer than one turn and + read interupts + * Lookmode's messages and information keys work far better now + * Fixed a minor bug in the lookmode zoom code + * Removed the cursor from the zoom screen (exception: over the player it remains) + * Petrus's PostFix is now shown correctly + * Petrus's wife number 4 is now dumper than the others, since she's blond + * Invisibility and see invisible states added, but they don't work yet + +June 16 2002 Heikki Sairanen + + * Atavus, Seges and Silva now fix players BodyParts + +June 16 2002 Heikki Sairanen + + * Priests in altars now attach old arms and legs to their old places. + +June 16 2002 Timo Kiviluoto + + * Added equipment pictures to panel + * The config system of items and characters works now correctly (I think). + This means that one can add new equipment or monsters without touching code at all, + using one of the existing classes as a base + * Converted almost all weapons to configurations of meleeweapon. + Weapons not converted: pickaxe (special, can dig), whip (special, FormModifier depends on material), + Valpurus's Justifier and Neerc Se-Ulb (artifacts, will get animation functions soon) + * Converted chain mail, amulets, rings and boots to configurations of their base classes + * Added possibility to specify config in script in brackets following a classname + (example: Square, Pos 3,1; { Item = boot(BOOTOFSPEED); Times = 2; } adds two boots of speed to square at 3,1) + * Removed the former way to set it + +June 15 2002 Timo Kiviluoto + + * Added a new state system that allows both temporary and permanent states + * Added GearStates item database value that allows one to easily add equipment that activate permanent states + * Added PermanentStates character database value that makes it easy to add permanent states to certain races + * Added boot of speed that hastes the user (but you need to have two of them equipped!) + * Added a bug that activates permanent states when the user wields the right piece of equipment, like a boot of speed + (by the way, this bug did already exist with amulets of life saving) + * Monsters are now polymorphed only temporarily in the same way as the player, + so you don't have to kill yourself when that goblin zaps its WoP at your ultra-trained level 8 mistress + (when it someday becomes wise enough to do it) + * Added lycanthropy state that occasionally polymorphs the character into a werewolf (wolf form) + * Werewolf flesh causes lycanthropy now + * Remade werewolf; it is now a human-like creature that is permanently lycanthropic + * Added wish functions for character and CanBeWished and Alias values to its database + * Added polymorph control state + * Added ring of polymorph control + * Added life saving state and made amulet of life saving use it + (this allows very pleased gods to grant temporary life saving, for instance) + * Removed the code that allowed fatness (I just couldn't work out how a gold golem can carry itself) + * Nonhumanoids have now a chance to train their martial skills (unarmed combat, kicking or biting) + * Biting doesn't make bodyparts fly anymore + * Fixed a bug of temporarily hasted/slowed dogs crashing on world map + * The wish parser is again more intelligent as it chooses the item with most correctly specified name parts, + so a wish for "that holy banana thingy" gives the holy banana of Liukas Vipro and not just a banana + * Fixed a bug that caused golems' bodypart materials to be randomized individually, + thus creating fabric legged, stone armed and bone headed golems with wooden torsos etc. + * Hunters are once again one armed + * Added some new booleans to character database: CanUseEquipment, CanKick and CanTalk + * Amulet's article is now correct + * MColor 1 of amulets and cloaks is now used correctly + * Corrected a bug that caused a crash when a flying bodypart ought to hit a legless humanoid + +June 15 2002 Heikki Sairanen + + * Added beartrap (which probably will need some more tweaking) + +June 14 2002 Timo Kiviluoto + + * Pool and LOS codes optimized greatly and character AI (which takes most of the processor time now) somewhat. + As a result running around Attnam with debug on is about 40% faster than before + * Added Roundness database value for items (on scale ]0, 100]) + * It is now possible to block attacks with any item, + the possibility depending on size, roundness and To Hit Value and effect depending on StrengthValue + * Shields work correctly: one cannot attack with them and their block chance is doubled + * Doors' LockTypes and BoobyTraps are now saved correctly + * The game doesn't crash anymore when fluid vanishes + * Fixed a bug that caused a delay before the tamed symbol appeared over charmed monsters + * Fixed a minor bug with walls' MemorizedDescriptions + * The key handler *might* be smoother in Linux now + * Gcc 3.1 *might* compile the game with less warnings + * Wand of door creation added + * Added the following bugs: + * Equipment increases HP + * Walls' luminances are not updated when a lantern flies by them + +June 11 2002 Timo Kiviluoto + + * The New Attribute System is now working + * Most likely very badly + * Wishing works again and probably better than before, since the spelling rules have been relaxed. + For instance, if one wishes (in WizardMode) for "the holy ultra-powerful broadsword + named "Valpurus's Justifier" that this Perttu-dude carries", the game will instantly know you mean + "holy broadsword "Valpurus's Justifier"", since the three key parts of the name exist somewhere in the wish string + * Also added an alias system that allows one to define alternative names for items used by the wish system + * Panel is now more informative + * Kicking works again + * If a character is armless, he kicks enemies, if also one- or zero-legged, bites them + * Added an AttackStyle attribute to characterdatabase that controls how monsters will attack (with arms, legs and/or head) + * Unicorns, mammoths and buffalos can now both kick and bite their enemies + * If a chest is locked, the chest::Open doesn't ask whether the player wants to put in or take from it + * Chest's contents are now deleted properly + * BoolQuestion's OtherKeyForTrue works again + * Doors can again be opened + * Chests can now be unlocked and locked (well, the latter was in theory sometimes possible before, but only in theory) + * The names of chests, doors and keys show their LockType verbally + * Petrus's face turns correctly red during the Final Battle + * Message history isn't drawn under highscores anymore + * Added a system to determine whether a bodypart is the character's original one + * Gear items emitate light correctly but updating still bugs + * The game asks for key press when a bodypart is severed + * Explosions sever bodyparts and spill blood more correctly + * Polymorphing items works correctly + * Decreased the characters' global possibility to hit weaker bodyparts + * Corrected a bug that caused lights falling during an explosion to crash + * Corrected a bug with doors that caused dummy boobytraps to spawn all the time + * Probably much else that I have forgotten... + +June 10 2002 Heikki Sairanen + + * Taking and putting things in chests is now handled correctly in special rooms. + +June 05 2002 Heikki Sairanen + + * Added "show all item here" command to look mode. + * Also fixed some issues with polymorphing chests. + +June 04 2002 Heikki Sairanen + + * Added mirroring, flipping and rotateing to items too. + +June 02 2002 Heikki Sairanen + + * Added chest. It seems to work probably but it can not be sold from a shop yet and the polymorph-code has not been tested, because of crashes in the polymorph code even without polymorphing a chest) + * Cans are now more or less disabled. + +May 31 2002 Timo Kiviluoto + + * Menu version description looks now better and is more informative + * Fade optimization + * DynArray finally eliminated + * Fixed a bug that crashed the game if one died of a food effect or an explosion of a digged door + * Attribute class added but not used yet + +May 29 2002 Timo Kiviluoto + + * Datafilezation checking + * IsPolymorphable added to item and char databases + * The Vile Dog Bulimia Bug of Ultimate Damage fixed + +May 29 2002 Timo Kiviluoto + + * Corrected a bug in the corpse creation code + * Color tweaks + * Added an alpha blend transparency effect for glass and liquids, + although it's hard to see when objects are so small :( + But if you lay a bottle over a flickering banana, you'll certainly notice it + +May 28 2002 Timo Kiviluoto + + * Animations work in the lookmode + * Carpets are visible again + +May 27 2002 Timo Kiviluoto + + * Key handler works smoother + * Corrected a bug in the material load code + * All name routines datafiled + * Material configurations datafiled + * Animations work in felists + * Lookmode and highscores are totally ugly and mad :( + * Hex's key code compiles + * MColor brigthnesses standardized with IGOR + * Lots of MColor checking done + * Wall tile graphics changed + * LTerrain divided into two + * Silhouette redrawn by Kahvi + +May 27 2002 Heikki Sairanen + + * Added library room to Attnam + * Version number now shows in the menu + +May 25 2002 Timo Kiviluoto + + * Fountains and default materials work + +May 25 2002 Timo Kiviluoto + + * The game doesn't crash when you descend into the dungeon + * Applying a pick-axe to a monster asks whether you want to hit it + +May 25 2002 Timo Kiviluoto + + * Bug with bodypart volumes corrected + * A minor bug with skin colors removed + * Örmöpium removed + * Wishing works somehow again + * Even dummies can read if SeeWholeMapCheat is on + +May 25 2002 Heikki Sairanen + + * Added a lot of #include s because this is somehow required by Linux's GCC. + * Added tin + * Added whistle and magical whistle + * Changed Teleport() command to TeleportRandomly() in item, stack and character and made a function called Teleport(vector2d) to character. + +May 25 2002 Timo Kiviluoto + + * Database configuration system added + * Pertus's wives (almost) work now + * Added a new innovative super-powerful valuesystem that allows constants to be easily shared between code and script + * Deleted the new innovative super-powerful valuesystem that allowed constants to be easily shared between code and script and caused my gcc to run out of memory + (this is what you get when you plan your code in a Swedish textbook margin) + * Instead added a global script define datafile which understands a format similiar to C/C++ preprocessor language: + project's release compilation time decreased by about 5% + * Names and effects of materials datafiled + * Ultra major structural change problematic enough to become a source for a thousand heroic coding sagas: + All materials converted as configurations of five bases, just for fun! + This allows new materials to be added without changing code at all, + and in addition, project's release compilation time was decreased by about 5% + * "Generating dungeon" messages changed to "Generating " (thanks to Will Riley for the idea) + * Much search & replace done with boolean variable retrieval names (GetIsPlayer() -> IsPlayer() etc.) + * Corrected the Excruciating All-Devastating Color Bug of Empty Bottles + * Prototype and database security tightened + * Database initialization code rewritten: + project's release compilation time decreased by about 10% + * Most of StaticType()s removed so that Hex cannot make more gum solutions with them + +May 22 2002 Timo Kiviluoto + + * Compilation optimizations that certainly didn't optimize anything + +May 21 2002 Timo Kiviluoto + + * Major macro system change: database derivation now works + * Item and material datafilezation checked throughoutly + +May 20 2002 Timo Kiviluoto + + * Compilation optimizations + +May 20 2002 Heikki Sairanen + + * Amulet of life saving added + * game.cpp: Zoom for PositionQuestion added + +May 20 2002 Timo Kiviluoto + + * Macro system remade again for everything but character + +May 19 2002 Timo Kiviluoto + + * Reading and seeing works again + +May 19 2002 Timo Kiviluoto + + * More character macro tweaks + * The game *might* compile now + +May 18 2002 Heikki Sairanen + + * Itemde.h converted largely to Script/item.dat + (The current version does NOT compile on Linux) + +May 13 2002 Timo Kiviluoto + + * Character macro remade + +May 12 2002 Timo Kiviluoto + + * Characters datafiled (mostly) + * The game is still mad + +May 12 2002 Timo Kiviluoto + + * The game executes again + * The game is totally mad again + * Polymorph works again, and better than before + * Character database structures added + * Define headers added + +May 11 2002 Timo Kiviluoto + + * Price added to item database structure + +May 10 2002 Heikki Sairanen + + * Copied the materials to Script/material.dat (however with some problems.) + * Added ring of fire resistance + +May 10 2002 Timo Kiviluoto + + * Very lousy item database structures added + * The Ultimate Script Malfunction Error Bug of Underscore Conflict corrected, not maybe + * Many new bug adds + +May 10 2002 Timo Kiviluoto + + * The Ultimate Script Malfunction Error Bug of Underscore Conflict corrected, maybe + +May 9 2002 Timo Kiviluoto + + * Bug corrections + +May 9 2002 Timo Kiviluoto + + * Material database structures added + +May 9 2002 Heikki Sairanen + + * Wand of resurrection added + +May 9 2002 Heikki Sairanen + + * Animations work now with SDL + * Wand of locking added + +May 7 2002 Timo Kiviluoto + + * Animations work in the DOS port now + +May 7 2002 Timo Kiviluoto + + * Heavily modified the material system + * Added animation engine + * Removed much commented outdated code + * Removed many useless string casts + * Added some new operator overloads fro string + +May 5 2002 Timo Kiviluoto + + * String and save routine optimizations + +May 5 2002 Timo Kiviluoto + + * Most of script code structures and data reading routines remade + * Some template instantiation relocations committed + * Warning corrections + +May 3 2002 Timo Kiviluoto + + * Some possible memory leaks prevented + * Eating corpses works again, I think + * The Great Optimized Inventory Bug corrected + * Other bug corrections + +2002-05-01 Heikki Sairanen + + * Silhouette added to humanoids + +May 1 2002 Timo Kiviluoto + + * Bug corrections + +May 1 2002 Timo Kiviluoto + + * Warning corrections + +May 1 2002 Timo Kiviluoto + + * Monsters can move again + * Some list tweaks + +May 1 2002 Timo Kiviluoto + + * Pentagram moved from menu to generation screen + * Proto and macro systems remade + * Some obviously shared virtual functions of proto-using classes made static + * Pool system tweaked + * Damage and material effect system remade + * Many outlook tweaks + * Message screen added to panel + * Remade all question systems (vector, boolquestion, positionquestion, strinquestion, numberquestion etc.) + * Felist tweaks (parameter adds etc.) + * Consume system remade + * Structural change: type removed + * Resistance system added + * Wizard info option to lookmode + * Weight system works correctly and no one can move anymore + * Gear drop on death and bodypart severing works may work correctly (I didn't test it) + * Corpse and death system remade, although special monsters probably bug + * Added a mass of bitmap blit functions with varying amounts of optional vector2d parameters + * More vector2d parameters added elsewhere too, but I don't remember exact places + * Added femath::WeightedRand and new material generation system + * Added ::ReadType that makes loading temporary variables easier + * Many gum solutions removed + * Numerous bugs added + * Regeneration may work (again, I didn't test it) + * etc. etc. etc. etc. etc. + +April 26 2002 Timo Kiviluoto + + * Damage system revised + * The Great Ultimate Line of Sight Bug of Doomsday corrected + +2002-04-26 Heikki Sairanen + + * charde.cpp/h: Added lion, buffalo and carnivorousplant + * charba.cpp, itemde.cpp: Added & marks to functions calls for xSorter functions. (didn't compile with gcc without them) + +April 26 2002 Timo Kiviluoto + + * Equipment menu works somehow. + +April 25 2002 Timo Kiviluoto + + * Added new action system + * Added Stack sorting + * Remade dig & some other action systems + * Consume system temporarily disabled + * Some bugs corrected and many added + +2002-25-25 Heikki Sairanen + + * charde.cpp: Added CheckKick(), GetLengthOfOpen(), CheckApply(), CheckOffer() to humanoid also added GetArms(), GetLegs() and GetAPMultiplier() + * charba.cpp: Added the above checks + relevant calls. + * charde.h: (added functions) and tweaked some colors + * charba.cpp: added functions + +April 22 2002 Timo Kiviluoto + + * charde.cpp/h: Petrus's "temporary" AddHitMessage finally removed + * felist.cpp: List order bug corrected + +April 22 2002 Timo Kiviluoto + + * felist.cpp/h: Added AddEntryToPos + * message.cpp: Messages are now split correctly + +2002-04-22 Heikki Sairanen + + * AUTHORS: Fixed a small typo + +2002-04-22 Heikki Sairanen + + * charde.h: Tweaked bitmap positions and colors for Oree, goblin, skeleton, imp, gibberling, kobold, enner beast and dark knight + +April 22 2002 Timo Kiviluoto + + * felist.cpp/h & files with felist calls: Added more parameters (pos and width etc.) for felits + * hscore.cpp: HScore draw uses felist now + +April 21 2002 Timo Kiviluoto + + * All libraries united + +April 21 2002 Timo Kiviluoto + + * bitmap.cpp/h, colorbit.cpp, graphics.cpp: Subbitmap support added + * colorbit.cpp/h, graphics.cpp: Text shading added + * felist.cpp/h: Added felist support for pictures, entry categories and arrow key selection + * hscore.cpp & all files with felist calls: List colors are now more consistent + * charba.cpp: Added icons for pray menu + * itemba.cpp/h, itemde.h, stack.cpp/h: Added icons and categories for items and added sorter support (which isn't yet used) + * itemde.cpp, slot.cpp, proto.h: Save works once again + +April 20 2002 Timo Kiviluoto + + * itemde.cpp/h, slot.cpp/h: gearslot added + * feio.cpp, igraph.cpp/h, main.cpp: New menu backround (by Kahvi) added + * charba.cpp/h, charde.cpp/h: Equipment screen addition prepared + +2002-04-20 Heikki Sairanen + + * Main/Include/charde.h: Changed all cordinates in bitmap to vector2ds + * Main/Source/charde.cpp: Some changes that relate to the above change + +April 19 2002 Timo Kiviluoto + + * itemba.cpp/h, itemde.cpp/h, charba.cpp, charde.cpp: Stack parameter removed from Apply funcs + +April 19 2002 Timo Kiviluoto + + * charba.cpp/h, charde.cpp/h, itemde.cpp/h, stack.cpp, slot.cpp/h: Bodyparts are now correctly in characterslots + +April 19 2002 Timo Kiviluoto + + * charba.cpp/h, charde.cpp/h, itemba.cpp/h, itemde.cpp/h: Warning corrections + * game.cpp, stack.cpp/h: The game compiles again + +April 19 2002 Timo Kiviluoto + + * stack.cpp: Inventory works again + +April 19 2002 Timo Kiviluoto + + * Everything changed. + +2002-04-18 Heikki Sairanen + + * Main/Include/itemde.h: Price of Avatar of Valpuri added. Also added boot, gauntlet, belt, ring and amulet + * Main/Source/itemde.cpp: Added some generic functions for the added items + * Main/Source/lsquare.cpp: Added tame symbol + +2002-04-06 Heikki Sairanen + + * Main/Include/itemde.h: Added cloak and shield (actually did this a week ago, but didn't commit) + +April 13 2002 Timo Kiviluoto + + * Symbol.pcx: battle, tame, peaceful and hostile symbols added by Kahvi + +April 6 2002 Timo Kiviluoto + + * Item.pcx: boot added and an error in cloak fixed by Kahvi + * Humanoid.pcx: mistress hair shortened by Kahvi + +2002-04-06 Heikki Sairanen + + * Main/Source/itemde.cpp (pickaxe::Apply): Added new formula for calulation the length of digging + * FELL/Include/hscore.h: Add changed to return bool telling whether the score got to the list or not + * FELL/Source/hscore.cpp: Add changed to return bool telling whether the score got to the list or not. And if the player doesen't get to the list the result is not drawn in function Draw + * Main/Include/charba.h: Added #defines for hungerlevels and hunger states and added GetHungerState function + * Main/Source/charba.cpp: Added GetHungerState function and changed every reference to the old (bad) hunger level systems to use it. + * Main/Include/materde.h: deleted all CanBeDigged functions + * Main/Include/materba.h: + * Main/Source/materba.cpp: CanBeDigged now examines the StrengthValues of the materials. + * Main/Source/game.cpp: DrawPanel now draws Satiated and Bloated hunger states also and all hungerstate are calculated by character's GetHungerState function + + +April 6 2002 Timo Kiviluoto + + * Item.pcx: shield, cloak, gauntlet, belt, amulet and ring added by Kahvi + +2002-04-01 (later) Heikki Sairanen + * Main/Include/itemba.h: #included lterrade (fountain), added function HasBeenDippedInFountain + * Main/Include/itemde.h: Added function HasBeenDippedInFountain and CanBeDipped to fountain + * Main/Include/lterraba.h: Added functions ReceiveDip and HasDipEffect + * Main/Include/lterrade.h: Added functions ReceiveDip and HasDipEffect to fountain + * Main/Include/roomba.h: Added functions Dip and HasDipHandler + * Main/Source/charba.cpp: Altered Dip function to ask wheter the player wants to dip into a square or to an item in his inventory + * Main/Source/itemde.cpp: Added HasBeenDippedInFountain for potion + * Main/Source/lsquare.cpp: Put a GetIsPlayer checker on Apply (for the message "You cannot apply that on this"), added function ReceiveDip + * Main/Source/lterrade.cpp: Added ReceiveDip for fountain + * Main/Source/roomde.cpp: Added Dip for cathedral + + +2002-04-01 Heikki Sairanen + * Main/Include/itemba.h: GetLockType() function added to item (returns 0 for everything else but key) + * Main/Include/itemde.h: Key added + * Main/Include/lterraba.h: Added function ReceiveApply to LTerrain + * Main/Include/lterrade.h: Added define NUMBER_OF_LOCK_TYPES and added lock type for class door + * Main/Include/materde.h: Added copper and bronze + * Main/Source/charde.cpp: *classified* + * Main/Source/itemde.cpp: Added Apply function for key + * Main/Include/lsquare.h: + * Main/Source/lsquare.cpp: Added function ReceiveApply + * Main/Source/lterrade.cpp: Added function ReceiveApply for door + + +2002-03-31 Heikki Sairanen + * Main/Source/itemde.cpp: + * Main/Include/itemde.h: Added wandofhaste and wandofslow (and their needed funcitons: BeamEffect, Zap) + * Main/Source/charba.cpp: Added states SLOW and HASTE (and their functions: EndX, X + * Main/Include/charba.h: Added the defines for SLOW and HASTE (and upped the STATES counter by two) + +2002-03-30 Heikki Sairanen + + * Main/Source/materde.cpp: healingliquid::EatEffect and HitEffect added + * Main/Include/materde.h: healingliquid added + * Main/Include/itemde.h: + * Main/Source/itemde.cpp: added GeneratePotionMaterials() to bottle for generating half bottles with omle urine and half with healingliquid + +March 30 2002 Timo Kiviluoto + + * Item.pcx: broken weapons (include banana), land mine and set & unset bear traps added by Kahvi + +March 29 2002 Timo Kiviluoto + + * Char.pcx: mega-large mammoth added by Kahvi + * Item.pcx: tiny and mega-large corpses added by Kahvi + +2002-03-29 Heikki Sairanen + + * Main/Include/itemde.h: Added mine. + * Main/Source/itemde.cpp: Added functions for mine + * Main/Source/lsquare.cpp: Added a if clause for flying creatures in StepOn function. + +2002-03-25 Heikki Sairanen + + * Main/Include/lterraba.h: Added function CreateBoobyTrap that is really an empty function + + * Main/Include/lterrade.h: Added function CreateBoobyTrap in door, which is actually used and functions CreateBoobyTrap, ActivateBoobyTrap (also to door) + + * Main/Include/script.h (constructor... probably): Added support for AllowBoobyTraps + + * Main/Source/script.cpp: Added support for AllowBoobyTraps + + * Main/Source/itemde.cpp: Reduced randomness in wandoffireballs + + * Main/Source/level.cpp: Boobytraps are now generated one per 5 locked doors + + * Script/dungeon.dat: Added parameter AllowBoobyTraps to both dungeon and Attnam. + +2002-03-23 Heikki Sairanen + + * Main/Include/charde.h: Changed ivan class to communist and assigned it the name "Ivan". Also changed Elpuri, Oree and Petrus to get their _name_ from AssignedName + * Main/Source/charba.cpp: character::Save Changed to save AssignedName + + * Main/Include/charba.h (class character): Added functions Name(), Article() Adjective(), ShowClassName(), CanBeAssignedAName(). For the AssignedNames + + * Main/Source/game.cpp: Added the function key to character's AssignName + + * Main/Source/charba.cpp (AssignName): Function added + + * Main/Source/game.cpp (PositionQuestion): Function added. + +2002-03-22 Heikki Sairanen + + * Main/Source/charba.cpp (Look): Deleted odd GetIsPlayer() test + +2002-03-22 Heikki Sairanen + + * FELL/Include/felist.h (class felist): DrawDescription moved to .cpp to correct an odd Linux bug. + * FELL/Source/felist.cpp: + +March 22 2002 Timo Kiviluoto + + * Humanoid.pcx: Some humanoids dismembered by Kahvi + +March 21 2002 Timo Kiviluoto + + * GetHitValue() of material and GetArmorValue() of material and item replaced by StrengthValue() + * Modified files: + * stack.cpp + * itemba.cpp + * ushort item::StrengthValue() const added + * itemde.cpp + * ushort bodypart::StrengthValue() const added + * charba.cpp + * charde.cpp + * materba.h + * ushort material::GetHitValue() const removed + * ushort material::GetArmorValue() const removed + * ushort material::StrengthValue() const added + * materde.h + * Numerous ushort xxx::GetHitValue() consts removed + * Numerous ushort xxx::GetArmorValue() consts removed + * Numerous ushort xxx::StrengthValue() consts added + * itemba.h + * uchar item::SurfaceMaterial() const added + * itemde.h + * Some ushort xxx::GetArmorValue() consts removed + * Master data member added to bodypart + * Modified files: + * itemde.cpp + * itemde.h + * character* bodypart::GetMaster() const + * void bodypart::SetMaster(character* What) + * StrengthModifier attributes added to items + * Modified files: + * itemba.cpp + * itemba.h + * ushort item::StrengthModifier() const added + * itemde.cpp + * itemde.h + * Numerous ushort xxx::StrengthModifier() const added + * IsAlive() attributes added to materials + * Modified files: + * itemde.cpp + * materba.h + * bool material::IsAlive() consts added + * materde.h + * Numerous bool xxx::IsAlive() consts added + * Secret knowledge command partially added + * Modified files: + * game.cpp + * charba.h + * charba.cpp + * bool character::SecretKnowledge() added + * itemba.h + * bool item::AutoInitializable() const added + * itemde.h + * bool bodypart::AutoInitializable() const added + +March 17 2002 Heikki Sairanen + + * Graphics/Makefile.am: Human.pcx changed to Humanoid.pcx + +March 16 2002 Timo Kiviluoto + + * charde.h: Bodypart color tweaks + +March 16 2002 Timo Kiviluoto + + * New bodypart: groin + * Modified files: + * charde.cpp + * ulong humanoid::GroinVolume() const added + * void humanoid::CreateGroin() added + * groin* humanoid::GetGroin() const added + * void humanoid::SetGroin(groin* What) added + * itemde.h + * New class: groin + * Added SpecialType to graphicid. Arms, legs and groin are no more drawn multiple times. + * Modified files: + * worldmap.cpp + * object.cpp + * object.h + * igraph.cpp + * igraph.h + * Some SpecialType definitions added + * graphicid and handling functions updated + * charde.cpp + * return type of leftarm* humanoid::GetRightArm(), rightarm* humanoid::GetLeftArm(), rightleg* humanoid::GetRightLeg() and leftleg* humanoid::GetLeftLeg() changed + * parameter type of humanoid::SetRightArm(rightarm* What), void humanoid::SetLeftArm(leftarm* What), void humanoid::SetRightLeg(rightleg* What) and void humanoid::SetLeftLeg(leftleg* What) changed + * itemde.h + * arm and leg are now abstract + * New classes: rightarm, leftarm, rightleg, leftleg + * virtual uchar xxx::GetSpecialType() consts overrided in rightarm, leftarm, groin, rightleg, leftleg + * Dwarf legs are now drawn correctly + * Modified files: + * itemba.cpp + * void item::DrawToTileBuffer(vector2d Pos) const added + * itemba.h + * charde.cpp + * void dwarf::DrawToTileBuffer() const added + * Werewolves are now drawn correctly + * Modified files: + * charde.cpp + * void humanoid::UpdateBodyPartPictures(bool CallUpdatePicture) added + * void humanoid::UpdateHeadPicture(bool CallUpdatePicture) added + * void humanoid::UpdateTorsoPicture(bool CallUpdatePicture) added + * void humanoid::UpdateRightArmPicture(bool CallUpdatePicture) added + * void humanoid::UpdateLeftArmPicture(bool CallUpdatePicture) added + * void humanoid::UpdateGroinPicture(bool CallUpdatePicture) added + * void humanoid::UpdateRightLegPicture(bool CallUpdatePicture) added + * void humanoid::UpdateLeftLegPicture(bool CallUpdatePicture) added + * charde.h + * charde.cpp: Bug in void humanoid::SetSize(ushort Size) fixed + * charde.h: Bodypart color tweaks + * lsquare.cpp: Extremely rare fatal bug in lsquare::CalculateEmitation() fixed + +March 15 2002 Timo Kiviluoto + + * charde.h: Bodypart color tweaks + * Humanoid.pcx: Material color tweaks by Kahvi + +March 14 2002 Timo Kiviluoto + + * charde.h: Bodypart color tweaks + +March 14 2002 Timo Kiviluoto + + * bitmap.cpp: Added bool DrawSides parameter for bitmap::DrawPolygon + +March 14 2002 Timo Kiviluoto + + * Material colors added to humanoid bodyparts. All humanoids are now very ugly. + * Modified files: + * Human.pcx: renamed as Humanoid.pcx and converted to material colors by Kahvi + * object.cpp: + * ushort object::GetMaterialColor0() const added + * ushort object::GetMaterialColor1() const added + * ushort object::GetMaterialColor2() const added + * ushort object::GetMaterialColor3() const added + * Added argument for void object::UpdatePicture(bool RemoveOld), which determines whether old pic will be removed. The func now updates GraphicId based on the preceding functions. + * object.h: + * void object::ColorChangeSpeciality(uchar, bool) removed + * lterrain.h: + * void stairsdown::ColorChangeSpeciality(uchar, bool) removed + * ushort stairsdown::GetMaterialColor1() const added + * void stairsup::ColorChangeSpeciality(uchar, bool) removed + * ushort stairsup::GetMaterialColor1() const added + * itemde.cpp: + * void potion::ColorChangeSpeciality(uchar Index, bool EmptyMaterial) removed + * void bodypart::Save(outputfile& SaveFile) const modified so that Colors are saved + * void bodypart::Load(inputfile& SaveFile) const modified so that Colors are loaded + * void torso::Save(outputfile& SaveFile) const removed + * void torso::Load(inputfile& SaveFile) const removed + * itemde.h: + * torso divided into two childs classes: normaltorso and humanoidtorso + * void bodypart::SetColor(ushort Index, ushort What) added + * ushort xxx::GetMaterialColorxxx() const funcs added here and there + * igraph.h: + * humanoid is now raw type + * charde.cpp: + * All references to complexhumanoid removed + * void humanoid::CreateTorso() added + * All void humanoid::Create() functions modified to init bodypart colors + * void humanoid::DrawToTileBuffer() const added + * charde.h: + * All references to complexhumanoid removed + * All SetLegType(xxx) and similiar function calls removed from SetDefaultStatses + * uchar xxx::GetHeadType() const added to humanoid and all children + * uchar xxx::GetTorsoType() const added to humanoid and all children + * uchar humanoid::GetRightArmType() const added + * uchar humanoid::GetLeftArmType() const added + * uchar xxx::GetArmType() const added to humanoid and all children + * uchar humanoid::GetRightLegType() added + * uchar humanoid::GetLeftLegType() added + * uchar xxx::GetLegType() const added to humanoid and all children + * ushort humanoid::SkinColor() const added + * ushort humanoid::CapColor() const added + * ushort humanoid::HairColor() const added + * ushort humanoid::HeadSpecialColor() const added + * ushort humanoid::TorsoMainColor() const added + * ushort humanoid::BeltColor() const added + * ushort humanoid::TorsoSpecialColor() const added + * ushort humanoid::ArmMainColor() const added + * ushort humanoid::ArmSpecialColor() const added + * ushort humanoid::LegMainColor() const added + * ushort humanoid::LegSpecialColor() const added + * charba.cpp: + * Removed argument from void character::CreateTorso(bool Humanoid) and modified it to init torso color + * charba.h: + * AllocBodyParts added as the fourth character constructor parameter + * void character::AllocateBodyPartArray() added + * Ergonomic FeFile modifications: + * save.cpp: + * Fast-to-use long inputfile::ReadNumber() added (affect also config.cpp) + * save.h: + * typedef std::map valuemap added (affects also script.h) + * godba.cpp: Fatal bug in god::Save and god::Load corrected + * IvanEdit removed from CVS + * LTerrain.pcx: desert, swamp, snow base terrains, three stunt trees, two rocks and boghole added by Kahvi + +March 10 2002 Timo Kiviluoto + + * gccblit.s: added alternative gcc asm func names with only one lowline + +Friday, March 08 2002 Heikki Sairanen + (however all changes actually coded by Mark Schreiber) + + * FeDX/Source/graphics.cpp: ToggleFullScreen for USE_SDL added, + * FeWin/Source/whandler.cpp: Function globalwindowhandler::ProcessMessage() funcion for SDL changed to call ToggeFullScreen when alt + enter pressed + +March 07 2002 Timo Kiviluoto + + * itemde.cpp: Two unused parameter warnings corrected + * lterrade.cpp: Two unused parameter warnings corrected + * entity.cpp: Two unused parameter warnings corrected + * ivandj.mak: Added reference to new files + * ivanmgw.mak: Added reference to new files + +March 07 2002 Timo Kiviluoto + + * Unit class added + * This class holds materials. It is derived from entity and is base for object and fluid. + * Modified files: + * object.cpp + * object.h + * fluid.cpp + * fluid.h + * entity.cpp + * see unit.cpp + * entity.h + * see unit.h + * Added files: + * unit.cpp + * unit::unit(bool AddToPool, bool HasBe) added + * unit::~unit() added + * void unit::Save(outputfile& SaveFile) const added + * void unit::Load(inputfile& SaveFile) added + * ushort unit::GetEmitation() const moved here from entity.cpp + * void unit::PreserveMaterial(ushort Index) moved here from entity.cpp + * material* unit::GetMaterial(ushort Index) const moved here from entity.cpp + * ushort unit::GetMaterials() const moved here from entity.cpp + * unit.h + * material* unit::GetMaterial(ushort) const moved here from entity.h + * ushort unit::GetEmitation() const moved here from entity.h + * ushort unit::GetMaterials() const moved here from entity.h + * bitmap* unit::GetPicture() const moved here from entity.h + * void unit::PreserveMaterial(ushort) moved here from entity.h + * Bodyparts added + * These are stored as an array of items in character, although they can't be severed yet :( + * Character is not a unit and so contains materials no more; use bodypart materials from here on. + * Modified files: + * itemde.cpp + * void bodypart::Save(outputfile& SaveFile) const added + * void bodypart::Load(inputfile& SaveFile) added + * void torso::Save(outputfile& SaveFile) const added + * void torso::Load(inputfile& SaveFile) added + * itemde.h + * bodypart abstract class added + * head class changed to bodypart child + * torso class added + * arm class added + * leg class added + * charba.cpp + * void character::CreateBodyParts() added + * material* character::CreateTorsoBone(ulong Volume) const added + * void character::SetSize(ushort Size) added (this sets torso size) + * ushort character::GetSize() const added + * void character::CreateTorso(bool Humanoid) added + * torso* character::GetTorso() const added + * void character::SetTorso(torso* What) added + * void character::SetMaterial(uchar Index, material* NewMaterial) added (this sets bodypart materials) + * charba.h + * see charba.cpp + * character's base class changed to type + entity + * bodypart* GetBodyPart(ushort Index) const added + * void SetBodyPart(ushort Index, bodypart* What) added + * uchar TorsoBonePercentile() const added + * ulong TorsoVolume() const added + * material* CreateTorsoFlesh(ulong) const = 0 added + * ulong TotalVolume() const = 0 added (used by create functions to calculate bodypart volumes) + * uchar BodyParts() const added + * vector2d GetBitmapPos() const = 0 temporarily added + * macro modifications, see charde.h + * charde.cpp + * void humanoid::SetSize(ushort Size) added (this sets bodypart sizes) + * ushort humanoid::GetSize() const added (this calculates humanoid size based on bodypart sizes) + * ulong humanoid::HeadVolume() const added (this and following are used only during construction) + * ulong humanoid::TorsoVolume() const added + * ulong humanoid::ArmVolume() const added + * ulong humanoid::LegVolume() const added + * void humanoid::CreateBodyParts() added + * void humanoid::CreateHead() added + * void humanoid::CreateRightArm() added + * void humanoid::CreateLeftArm() added + * void humanoid::CreateRightLeg() added + * void humanoid::CreateLeftLeg() added + * head* humanoid::GetHead() const added + * void humanoid::SetHead(head* What) added + * arm* humanoid::GetRightArm() const added + * void humanoid::SetRightArm(arm* What) added + * arm* humanoid::GetLeftArm() const added + * void humanoid::SetLeftArm(arm* What) added + * leg* humanoid::GetRightLeg() const added + * void humanoid::SetRightLeg(leg* What) added + * leg* humanoid::GetLeftLeg() added + * void humanoid::SetLeftLeg(leg* What) added + * charde.h + * see charde.cpp + * material* humanoid::CreateHeadFlesh(ulong Volume) const added + * material* humanoid::CreateHeadBone(ulong Volume) const added + * material* humanoid::CreateRightArmFlesh(ulong Volume) const added + * material* humanoid::CreateRightArmBone(ulong Volume) const added + * material* humanoid::CreateLeftArmFlesh(ulong Volume) const added + * material* humanoid::CreateLeftArmBone(ulong Volume) const added + * material* humanoid::CreateArmFlesh(ulong Volume) const added + * material* humanoid::CreateArmBone(ulong Volume) const added + * material* humanoid::CreateRightLegFlesh(ulong Volume) const added + * material* humanoid::CreateRightLegBone(ulong Volume) const added + * material* humanoid::CreateLeftLegFlesh(ulong Volume) const added + * material* humanoid::CreateLeftLegBone(ulong Volume) const added + * material* humanoid::CreateLegFlesh(ulong Volume) const added + * material* humanoid::CreateLegBone(ulong Volume) const added + * material* humanoid::CreateTorsoFlesh(ulong Volume) const added + * uchar humanoid::HeadBonePercentile() const added + * uchar humanoid::TorsoBonePercentile() const added + * uchar humanoid::RightArmBonePercentile() const added + * uchar humanoid::LeftArmBonePercentile() const added + * uchar humanoid::ArmBonePercentile() const added + * uchar humanoid::RightLegBonePercentile() const added + * uchar humanoid::LeftLegBonePercentile() const added + * uchar humanoid::LegBonePercentile() const added + * ulong humanoid::RightArmVolume() const added + * ulong humanoid::LeftArmVolume() const added + * ulong humanoid::RightLegVolume() const added + * ulong humanoid::LeftLegVolume() const added + * uchar humanoid::BodyParts() const added + * nearly infinite amount of ulong xxx::GetDefaultVolume(ushort Index) consts removed + * equally many respective ulong xxx:TotalVolume() consts added + * InitMaterials line removed from macro and class definitions + * nearly infinite amound of material* xxx::CreateTorsoFlesh(ulong Volume) consts added + * Gods prototyped + * They now are derived from type and use the same macro system as characters, items etc. + * Modified files: + * game.cpp + * void game::CreateGods() added + * game.h + * void game::SetGodNumber(uchar What) removed + * uchar game::GetGodNumber() removed + * uchar game::GetGods() added (note: this returns former GetGodNumber() + 1) + * Removed files: + * god.cpp + * god.h + * Added files: + * godba.cpp + * godba.h + * these files are reserved for base god layer routines + * godde.cpp + * godde.h + * these files are reserved for derived gods' routines + * Routine transportation + * Some name routines moved temporarily from object to entity to make them available to the non-object character class + * Routines: + * std::string Name(uchar Case) const + * std::string GetNameSingular() const + * std::string GetNamePlural() const + * std::string NameSingular() const + * std::string NamePlural() const + * std::string object::NameNormal(uchar Case, std::string Article, std::string Adjective) const + * std::string object::NameProperNoun(uchar Case) const + * Modified files: + * object.cpp + * object.h + * entity.cpp + * entity.h + * Name changes + * Modifications: + * typeable -> type + * levelsquare -> lsquare + * worldmapsquare -> wsquare + * groundterrain -> gterrain + * overterrain -> oterrain + * levelterrain -> lterrain + * worldmapterrain -> wterrain + * groundlevelterrain -> glterrain + * overlevelterrain -> olterrain + * groundworldmapterrain -> gwterrain + * overworldmapterrain -> owterrain + * partially capitalized and fully capitalized versions of the preceding + * changed igraph.h's graphics file #define prefixes from G to GR (e.g. GLTERRAIN -> GRLTERRAIN) + * Modified files: + * wterraba.cpp + * wterraba.h + * wterrade.cpp + * wterrade.h + * wsquare.cpp + * wsquare.h + * worldmap.cpp + * worldmap.h + * terra.cpp + * terra.h + * stack.cpp + * stack.h + * square.cpp + * square.h + * script.cpp + * script.h + * roomba.cpp + * roomba.h + * roomde.cpp + * roomde.h + * materba.cpp + * materba.h + * materde.cpp + * lterraba.cpp + * lterraba.h + * lterrade.cpp + * lterrade.h + * level.cpp + * level.h + * itemba.cpp + * itemba.h + * itemde.cpp + * itemde.h + * igraph.h + * game.cpp + * entity.cpp + * entity.h + * cont.cpp + * cont.h + * charba.cpp + * charba.h + * charde.cpp + * dungeon.dat + * Removed files: + * typeable.cpp + * typeable.h + * Added files: + * type.cpp + * type.h + * EntityID removed and ItemID added. Names changed everywhere. + * Modified files: + * itemba.h + * itemba.cpp + * game.h + * game.cpp + * entity.h + * entity.cpp + * itemde.h: Genie possibility increased + * fluid.cpp: Fluid degeneration rate is now lower + * Symbol.pcx: mounted symbol added by Kahvi + * LTerrain.pcx: stunt trees added by Kahvi + * Human.pcx: Cossack added by Kahvi and mistress teared apart by Kahvi + * Char.pcx: raven, lion, carnivorous plant, bison, horse and dragon added by Kahvi + +Sunday, March 03 2002 Heikki Sairanen + + * Main/Include/itemba.h: Teleport(stack*) function added + * Main/Include/itemde.h: Wand of Teleportation added + * Main/Include/roomba.h: TeleportSquare function added + * Main/Include/roomde.h: TeleportSquare function added to catedral and shop + * Main/Include/charba.h: Teleport function added + * Main/Include/lsquare.h: Added TeleportEverything + * Main/Source/stack.cpp: Teleport function added + * Main/Source/itemba.cpp: Teleport(stack*) function added, which currently uses Player's character to determine walkability of square. This probably should be still fixed. + * Main/Source/itemde.cpp: Wand of Teleportation added + * Main/Source/lsquare.cpp: Added TeleportEverything + * Main/Source/roomde.cpp: TeleportSquare function added to catedral and shop + * Main/Source/stack.cpp: Teleport function added + +Friday, March 01 2002 Heikki Sairanen + + * Main/Source/god.cpp: silva::PrayGoodEffect now also ImpactDamages all items in the level + * Main/Source/charba.cpp: character::Kick() added a burden state checker + +Friday, March 01 2002 Heikki Sairanen + + * Main/Source/charba.cpp: Added a burdenstate checker to Hit function + * Main/Source/lterrade.cpp: Added private function Break() to door to reduce duplicated code. + * Main/Include/lterrade.h: Added function Break() to door + +Thursday, February 28 2002 Heikki Sairanen + + * Main/Include/itemde.h: Added scroll of taming + * Main/Source/itemde.cpp: Added scroll of taming + +Wednesday, February 27 2002 Heikki Sairanen + + * Main/Include/Makefile.am: Added entity.h and fluid.h (somehow these changes have been lost from CVS) + * Main/Source/Makefile.am: Added entity.cpp and fluid.cpp (somehow these changes have been lost from CVS) + * Main/Source/itemde.cpp: bananapeals::GetStepOnEffect for fall effect, Wand of Striking added. All BeamEffect functions changed to returing bool that tells the Beam function wheter the beam continues after the square in question. + * Main/Include/lsquare.h: levelsquare::HasBeenHitBy added + * Main/Source/lsquare.cpp: levelsquare::HasBeenHitBy added + * Main/Include/lterraba.h: overlevelterrain::HasBeenHitBy added + * Main/Include/lterrade.h: door::HasBeenHitBy, brokendoor::HasBeenHitBy added + * Main/Source/lterrade.cpp: door::HasBeenHitBy, brokendoor::HasBeenHitBy. That open or damage the door when an item hits it. + * Main/Include/itemde.h: Wand of strikings color changed from yellow to white. Added Wand of fireballs + * Main/Source/charba.cpp character::WizardMode changed so that the player now receives 5 scrolls of wishing also after activating the wizard mode. diff --git a/Doc/Obsolete/ChangeLog.0401 b/Doc/Obsolete/ChangeLog.0401 new file mode 100644 index 0000000..019b74e --- /dev/null +++ b/Doc/Obsolete/ChangeLog.0401 @@ -0,0 +1,267 @@ +------------------------------------------------------------------------------------ + +>>> NOTICE!!! <<< + +This file contains SPOILERS, which might ruin your IVAN experience totally. The file +is also provided AS IS and is probably completely unreadable. + +------------------------------------------------------------------------------------ + +February 5 2003 Timo Kiviluoto + + * Rewizarded '%' and '9' + * Angels and genies no longer crawl + +February 5 2003 Heikki Sairanen + + * Spiders cannot lock doors anymore + +February 5 2003 Timo Kiviluoto + + * Removed the silly console window from the MinGW port + * Reduced floating eyes' effect a bit to reduce the possibility of insta deaths + +February 4 2003 Timo Kiviluoto + + * FeLib interface is finally OS independent + * Ivan now compiles on MinGW and DJGPP environments correctly + * Ctrl + Print Screen now takes a screenshot in the DOS port + * Print Screen works again correctly in both Win32 ports + * Ctrl + Alt + E is now interpreted as a euro symbol in all SDL ports + * Corrected many bugs in game::HandleQuitMessage() + * Many new files added to Doc/Obsolete and Doc/Work + +February 2 2003 Timo Kiviluoto + + * Hitting a non-polymorphed chameleon no longer crashes + +February 2 2003 Timo Kiviluoto + + * Corrected an abusable bug in the item polymorph code + * Polypiling may no longer create scrolls of charging + * Wands of polymorph may no longer be cloned + * Being satiated no longer abuses agility, since even the minimum effect was way + too strong; however, bloated and overfed effects remain unchanged + * Bone now spoils slower + * Disabled Mondedr + * The library is now better protected + * Experiment ZQ-29 and blood golems no longer appears in the golem room + * Added ConsumeWisdomLimit material database value which prevents monsters with + enough wisdom to avoid eating the specified material (this replaces the + IsBadFoodForAI value) + * Monsters with >= 10 wisdom will not attack a floating eye anymore + * Monsters with >= 15 wisdom will not wear rings of teleport/polymorph + * Elpuri can no longer faint + * Corrected a memory leak in Mellis's pray effect + * Skeletons, Bill's wills, golems, imps and floating eyes no longer eat nor get + hungry + * Eating and drinking items in the player's inventory is again allowed in the + cathedral and shops + * Dark frog flesh's effect decreased somewhat + * Poison no longer affects non-living bodyparts + * Completely non-living monsters can no longer be paratisitized + * Ommel urine + damaged non-living bodyparts works now correctly + +February 02 2003 Heikki Sairanen + + * Space is also accepted as enter-key in scroll bar questions + * . = keypads 5 in direction questions + +February 01 2003 Heikki Sairanen + + * Mellis now exchanges maximum of 5 items + +January 28 2003 Heikki Sairanen + + * Chests can be offered now + +January 28 2003 Timo Kiviluoto + + * Corrected an odd bug caused by RNG inlining + * Atavus's pray effect tweaked a little + +January 28 2003 Heikki Sairanen + + * Small typo in elite guard's talk corrected. + * Atavus healing bodyparts works now + +January 27 2003 Heikki Sairanen + + * Shattering bottles with poison cause less damage to player. + * Healing potions give always one random missing bodypart. + +January 23 2003 Heikki Sairanen + + * Eating in cathedral and shop prohibited again (I found or made and found this bug) + * Kicking items in certain rooms requires saying Yes to a bool question. + +January 19 2003 Timo Kiviluoto + + * Explosions are now stopped by walls + * Added PLAYER macro which expands to game::GetPlayer() + * Removed the 3 letter limit of DefaultName, too + +January 19 2003 Heikki Sairanen + + * Fountains should never give permanent lycanthropy-state. + * Nefas shouldn't confuse plants. (maybe they should hit randomly characters around them) + * Healing should be terminated if player enters faint state. + * Remove the 3 letter limit for names. + * Amulet of Life saving generates high score entry. + +January 18 2003 Heikki Sairanen + + * Lycanthropy cannot be randomly activated anymore. + +January 18 2003 Timo Kiviluoto + + * Corrected some bugs in the Win SDL port + * The DOS port compiles again + * Compiling is now twice as fast as before + * Monoheaded and two two-headed mooses and two giant mushrooms added to char.pcx + by Kahvi + * Breaking and fixing an item no longer destroys accustomization + * Something that I forgot + +January 18 2003 Heikki Sairanen + + * Smith now fixes damaged bodyparts if they are made from a metal + * Bug with highscores corrected (makefiles) + * CanThrow added to script + * Handling Home,save and data directories changed + * chameleon whip added + * whip of thievery can be cloned + +January 9 2003 Timo Kiviluoto + + * Fixed a bug that sometimes prevented Haedlac from being teleported to aid + Petrus ere the final battle + * Removed bulimia damage type + * Greatly optimized the creation of sparkling and lightning-shooting objects + * IVAN's total RAM usage on my system reduced from 30 Mb to 10 Mb + * Binary initialization speeded up by 25% + * The script now supports cumulative comments + +January 5 2003 Heikki Sairanen + + * Player can no longer choke on his/her food + +January 5 2003 Timo Kiviluoto + + * A floating eye can't make enemies faint if the they don't see it + * Corrected a rounding error bug in the LOS code + * Endurance changes affect current HP a little differently + +January 5 2003 Heikki Sairanen + + * Lookmode now shows if a monster has panicked. (now works) + +January 5 2003 Heikki Sairanen + + * Lookmode now shows if a monster has panicked. + +January 5 2003 Timo Kiviluoto + + * Intact and broken battle-axe, bastard sword, scythe and club pictures added + to item.pcx by Kahvi + * Ghost of Brezhnev added to char.pcx by Kahvi + * New basic weapons added: bastard sword, battle-axe and scythe + * Implemented an AD&D style weapon categorization, which I found much more + balanced than our old one + * If the handle of a weapon is made of same material as the edge, SoCM alters + it, too + * Added a gum solution to prevent whips of thievery from stealing the + Justifier; this was a too easy way to get rid of Petrus + * For clarity, polymorphed monsters now leave the corpse of the original form + * Seges and Petrus no longer cure non-living bodyparts + * Gods, Petrus and angels no longer put back non-living bodyparts + * When trying to use a stethoscope without hands, the game now behaves a + little less idiotically + * Restructured all wand code: beam colors and beam effect & style indices now + come from the script and the actual effect code is located in level.cpp and + lsquare.cpp + * All wand specializations are now configurations of wand + * Removed wands of locking; they were completely useless + * Wand of invisibility's beam is now invisible + * Wand of polymorph's beam now changes color all the time + * Wand of door creation can now create doors to walls + * Added wand of lightning which uses a special lightning beam which bounces + from walls + * Rewrote the (formelly unused) lightning animation code + * Added thunder hammer which uses lightning animation and occasionally shoots + lightning bolts + * Added ring of electricity resistance + * Removed several unused and useless resistances like bulimia resistance + * Added drain and electricity damage types + * Neerc Se-ulb now drains HPs from living enemies every now and then + * Removed most magical benefits from broken items + * Added short sword named Saal'thul which makes its master invisible + * Pet floating eyes now follow the player correctly + * Floating eyes no longer crash on world map or on level borders + * Floating eyes now "levitate", not "stand" + * Saving dead script-generated monsters no longer crashes + * Golems' agilities and dexterities doubled + * Modified hit chance constants in favour of low accuracy hits + * Decreased weight's effect on weapon accuracy + * Script optimizations + * Boolean value retrieval functions of script now honor the IvanDev Naming + Standard + * Changed RAND() algorithm from Nishimura's & Mashumoto's mammoth function + to a simpler linear congruential RNG; I know Hex doesn't like these so + I feel I must present a few arguments to justify my decision: + * I ensured that it doesn't have the low-order bit problems abundant in the + 32-bit versions by using a 64-bit seed and discarding the possibly cycling + bits + * It is easier to understand + * It is a one line function so it can be inlined + * It is three times faster + * It's seed can be retrieved unlike the old one's + * No independent observer did notice any positive effect on gameplay when + we switched the RNG in ancient times (well, there weren't any players :) + * I dare to suspect that Nishimura's & Mashumoto's RNG may be responsible + for the odd improbabilities happening all the time in item generation, + increasing the gameplay's dependance on luck considerably + * Corrected many gcc warnings given by -pedantic + * Shortened the names of terrain graphics files because the old ones were + too long for the DOS version + * Decreased AoGH's InElasticityPenaltyModifier and Possibility + * Broken items' inventory entries no longer show durability + * Corrected a fatal bug in the high score display code + +December 30 2002 Heikki Sairanen + + * Eating Floating eye corpses gives ESP + * Headless creatures no longer hit their head on walls + * Creatures with no legs don't stand in look mode anymore + +December 27 2002 Heikki Sairanen + + * Added floating eye + +December 22 2002 Timo Kiviluoto + + * Teleport control is now more user-friendly + * Halved the strength required to use two-handed weapons + * Gcc 2.9x now compiles the game correctly + +December 18 2002 Timo Kiviluoto + + * Doubled nutrition level limits + * Increased NP values by 50% + * Adjusted the probability distribution of kiwi amounts + * Further decreased the amount of wolves in the special level + * Decreased gauntlets' InElasticityPenaltyModifier from 30% to 20% + * Severed zombie bodyparts are now initially spoiled like the corpse + * Plants no longer stand but are rooted + * Tweaked the danger algorithm so that monsters value helmets more + * Added two random pieces of armor to UT level 2 + * Greater carnivorous plants are now a bit easier + * Torso must now be in bad condition before head or groin can be destroyed + +December 16 2002 Timo Kiviluoto + + * Corrected a fatal bug in the door breaking code + * Legs swapped + * Fixed the Evil Boulder Bug of the Insane Environmentalist once and for all + * You can no longer 'l'ook at walls to see if they contain jewels + * Kobolds are little less ugly now diff --git a/Doc/Obsolete/ChangeLog.0410 b/Doc/Obsolete/ChangeLog.0410 new file mode 100644 index 0000000..a908d6a --- /dev/null +++ b/Doc/Obsolete/ChangeLog.0410 @@ -0,0 +1,383 @@ +------------------------------------------------------------------------------------ + +>>> NOTICE!!! <<< + +This file contains SPOILERS, which might ruin your IVAN experience totally. The file +is also provided AS IS and is probably completely unreadable. + +------------------------------------------------------------------------------------ + +March 27 2003 Timo Kiviluoto + + * Adventure information is now shown when the player wins, too + +March 27 2003 Heikki Sairanen + + * Infravision added to magpie so that AI doesn't do stupid things when player is invisibile + +March 27 2003 Timo Kiviluoto + + * The slave is now better and his picture changes due to armor like the player's + +March 27 2003 Heikki Sairanen + + * Unicorn sometimes teleport when they take hits (so that killing them when invisible isn't too easy) + +March 27 2003 Timo Kiviluoto + + * Throwing healing liquid or some other good potion to people doesn't make them + angry + * The holy banana of Liukas Vipro renamed as the holy banana of Oily Orpiv + * The cursor is now bigger + * Skeletons and jackals are now easier to see + +March 27 2003 Timo Kiviluoto + + * Metallic hands are again shown correctly on the game screen + * Corrected a bug in the messages after teleport + * Doors created by a wand of door creation are now sometimes open + +March 27 2003 Heikki Sairanen + + * bug corrected: If multiple chests are stacked, only the top one can be opened with key. + +March 27 2003 Timo Kiviluoto + + * (u)longlong type is no longer used + +March 27 2003 Timo Kiviluoto + + * Slow is now a hostile action + * The encrypted scroll is now removed when the player talks to Petrus + * GC levels 8 and 9 are now more difficult + * The secret vault is now on a random level + * RestoreBodyParts() now handles genies and angels correctly + * Corrected a bug in the bodypart vanish code + * Bodyparts of polymorphed monsters now vanish when severed + * Being invisible to the enemy now doubles relative danger; this makes + the state more balanced since generated enemies will be more powerful + * Weapons are now more balanced + * Corrected a fatal bug in the thunder hammer & Turox codes + * Fixed a horrible bug in the consuming code that could cause unreproducable + crashes + * Damage types are now flagged + * Thrown potions now break easier + * Dipping to corpses is again possible + * Removed the strange item NPModifier gum solution which became abusable due + to the latter (you got 100 times more NP by putting flesh first in potions) + * Battle-axe, scythe, sickle and halberd InHandsPics added to humanoid.pcx + by Kahvi + * Wielded pick-axe can now be applied + * Planting traps in shops or in the cathedral is now a hostile action + * Mondedr removed temporarily from the script so it won't confuse players of + v0.410 + +March 24 2003 Timo Kiviluoto + + * Talking now trains charisma a little + * Corrected a small bug in the lantern animation + * Mushrooms no longer panic + * Skunks are now a bit easier + * Corrected a typo in the mine discovery message ("thumb" -> "thump") + * Smokes are now a little less repetitive + * A problem in the attack interrupt message fixed + * Corrected a fatal bug in the wand of teleport's and polymorph's break code + * Reduced the amount of kiwis in level 3 to make exping more difficult + +March 23 2003 Timo Kiviluoto + + * Corrected some bugs concerning the new kill lists + * The massacre lists now show the total amounts of killed monsters in each + category + * The player's representation on the game screen is now influenced by his armor + * Some graphics needed to make the latter possible added to humanoid.pcx by me + * Gas mask added to item.pcx by Kahvi + * Steam tank added to char.pcx by Kahvi + * Smokes are now in effect.pcx as they should + * Ivan now wears the Gorovits family gas mask + * Ivan now never willingly drops one of the family items and equips them always + when they are taken from him and he founds them afterwards, even if he has + got something better by then + * Corrected a small bug in the reply randomization system + * Added proper replies for player clones + * Reimplemented the old Mersenne Twister RNG with seed save ability + * Disabled Mondedr again + * Eddies and mommos are now animated in the SecretKnowledge screens, too + * The new powerful materials can no longer be wished, except for meteoric steel + +March 22 2003 Timo Kiviluoto + + * Corrected a few gcc warnings + * Contents of containers in containers and etc. are now shown correctly upon + death + * Lists of monsters killed by you, by pets and by other reasons during the + adventure are now shown upon death + * Adventure information may now be shown when the player quits, too + +March 21 2003 Timo Kiviluoto + + * Kicking mines no longer crashes + * Added CanBeConfused and CanAttack character database booleans + * Most special monsters can no longer be confused (this made them too easy) + * ESP-capable monsters are now more intelligent + * Only ESP-seen monsters, non-movable non-adjacent creatures and floating eyes + no longer trigger action deactivation requests + * Spiders and Petrus's wife number 4 are no longer seen by ESP + * Offering an item to its AttachedGod is now especially pious + * Other offers are no less valuable, for balance + * Corrected a fatal bug in the gas loading code + * Polymorph targets are again more balanced + * Broken bear traps' names are now shown correctly + * Smoke is now shown in the lookmode + * Lookmode pile messages are now more informative + * Dropping is now more convenient + * Magic vapour and skunk smell work now a little better + * Fixed a terrible bug in the altar polymorph code + * Corrected a horrible error in the danger system initialization code + * Added Janne Miettinen's Mondedr to CVS + * Added swamp terrain and prison doors + * Added lava and swamp materials + * Added a couple of temporary bad guys to the town + * Prison doors can now be seen through + * Dipping into an empty bottle no longer crashes + * It is no longer possible to dip a potion to the ice fountains of Attnam + * One may now dip potions to the dolphin pool and die + * Falling roofs now destroys smoke + * Earthquake now damages doors + +March 14 2003 Heikki Sairanen + + * Made skunk easier + +March 13 2003 Timo Kiviluoto + + * Mage tweaks + +March 13 2003 Timo Kiviluoto + + * Mushroom spawning rate tuned down + * Skunks no longer release their smell inside walls + * Step on pile messages are now more informative + * Random polymorph targets are now somewhat limited + * Flying monsters are now drawn over smoke + +March 13 2003 Heikki Sairanen + + * Floating eyes' attackwisdom limit changed from 10 to 8 + * Ivan's wisdom from 5 to 6 + * Added a puff of smoke to Genie creation + +March 13 2003 Timo Kiviluoto + + * Empty and atmosphere terrains have been removed; null pointers are used in + their place + * Corrected a fatal bug caused by the flagization of team relations + * Fixed a fatal bug in the new explosion code + * Explosions now leave smoke + * Smoke now blocks light + * The patrol guard in the Cathedral's garden replaced by a floating eye + * Life saving now resets states + * It is now possible to wish for a holy book of a specific god + * Wish parser is now less annoying + * Go doesn't walk on traps anymore + * Added AskKeyPress to trap detection + * Balancing of new monsters + * New monsters now look somewhat better + * Zombies now pick up heads from corpses, too + * Frequency system works again correctly + * A fatal bug in the corpse cloning code fixed + * Exploding wands now cause their specific effect to a certain area instead of + a dull fireball + * Staff pic added to item.pcx by me + * Added quarterstaff item + * Added meteoric steel, dragonhide, arcanite, illithium and adamant materials + * Added many new material configurations to weapons and armor etc. + * Dark wizard renamed as dark mage and divided to four configs: apprentice dark + mage, dark battlemage, elder dark mage and dark archmage (unique) + * Added many new effects and more intelligence to dark mages + * Mushrooms reproduce in more interesting patterns + * Mutated mushroom renamed as magical mushroom + * Added raw magical vapour gas which activates random states when breathed + * Magical mushrooms now occasionally release raw magical vapour + * Eddys now occasionally engulf items under them and sometimes even themselves + * Moose messages work better now + +March 12 2003 Heikki Sairanen + + * Added GAS_IMMUNITY state (for skunk) + +March 10 2003 Heikki Sairanen + + * Bear traps snap shut when kicked. And sometimes even break + +March 10 2003 Heikki Sairanen + + * Corrected brain-dead AddDeathExplanation() system + +March 10 2003 Heikki Sairanen + + * Skunk looks better + +March 08 2003 Heikki Sairanen + + * Skunk added + * Smoke improved + +March 08 2003 Heikki Sairanen + + * Smoke added + +March 7 2003 Timo Kiviluoto + + * A terrible bug in the door breaking code fixed + +March 7 2003 Timo Kiviluoto + + * Searching now works correctly + * Restrictions of permanent states activated by fountain updated + +March 07 2003 Heikki Sairanen + + * added magpie + +March 6 2003 Timo Kiviluoto + + * Special monsters and items now determine their AttachedGod correctly + * Terrains now also have AttachedGods (mostly depends on material) + * Horn of fear is now much more powerful + * Many unfinished things which I do not remember + +February 28 2003 Heikki Sairanen + + * When player is killed by something when he is fainting the death message now says "killed by a X while helpless". + +February 28 2003 Heikki Sairanen + + * Polymorph question should default to the monster that the player last polymorphed to. + +February 26 2003 Heikki Sairanen + + * Zombies are more picky about the brains they want (atleast that's what they say now) + * Petrus's, angels' and genies' bodyparts disappear when severed. + * Zombies pick up and use heads if they lack one + +February 25 2003 Heikki Sairanen + + * Added two-headed moose + +February 24 2003 Heikki Sairanen + + * Added ghosts + +February 20 2003 Timo Kiviluoto and + Heikki Sairanen + + * All items, characters and materials have been attached to some god + +February 20 2003 Timo Kiviluoto + + * Added support for transparent non-walkable tiles + * Added support for monsters walking through walls + * Monster generation works now a little better (maybe) + +February 20 2003 Heikki Sairanen + + * Added cans filled with mushroom flesh. + +February 17 2003 Heikki Sairanen + + * Dark wizards added + +February 20 2003 Timo Kiviluoto + + * Gcc 3.2.1 now compiles the game correctly and without warnings + * Optimization + * Removed subbitmap support from FeLib; it was complicated and useless + * Added some info about materials to SecretKnowledge + * Diamond plate mails and other objects made of transparent materials now look + better in inventory and panel + +February 17 2003 Timo Kiviluoto + + * A small bug in the key handling code fixed + * A fatal bug in the script code corrected + +February 17 2003 Timo Kiviluoto + + * A fatal bug added by the last commit corrected + * A fatal bug in Mellis's pray effect corrected (thanks to the anonymous + SourceForge user who reported it) + * Created a much better way of handling random script variables; they are now + presented in form "min:max" instead of "min + rand%(max-min+1)" and are + calculated when needed instead of during the parsing + * Finally fixed the Ancient Mad Script Error of Invading Shopkeeper Clones + * Dark wizard added to humanoid.pcx by Kahvi + +February 17 2003 Heikki Sairanen + + * Mushrooms added + +February 14 2003 Timo Kiviluoto + + * Corrected a bug in the new C++ alpha blit code + * Added an alpha halo to lanterns + * Wave animations for both mommos added and integrated + * A new whistle drawn + +February 13 2003 Timo Kiviluoto + + * Removed asm + +February 12 2003 Timo Kiviluoto + + * Configuration can again be saved + +February 12 2003 Timo Kiviluoto + + * Corrected a bug in the Igor's savefile code + * GetFlyAmount() S&R:ed by GetSpoilLevel() + * Eddies are now animated + +February 12 2003 Timo Kiviluoto + + * New eddy animation added to char.pcx by Kahvi + * Added makefile for DJGPP port of Igor + * Igor now works if the graphics are in the same directory as the binary + +February 12 2003 Heikki Sairanen + + * Wrong version number in configure.in of igor corrected + +February 10 2003 Timo Kiviluoto + + * Corrected some bugs in Igor's Linux port + * Igor works again under Win32 + * ESC now works in Igor's file menu + * Project line etc. stats updated + +February 9 2003 Heikki Sairanen + + * Monsters appearing from fountains are sometimes friendly. + +February 9 2003 Heikki Sairanen + + * Igor portated to Linux + * Igor added to CVS + * Graphics/ can now be in any directory on the machine with igor. + +February 8 2003 Heikki Sairanen + + * Tweaked eddies in space-time continuum + +February 8 2003 Timo Kiviluoto + + * Floors are now a bit brighter + * Added comprehensive graphics file format documentation to Doc/Data + +February 8 2003 Heikki Sairanen + + * Added eddies in space-time continuum + +February 6 2003 Heikki Sairanen + + * Merged Miles Bader's patch to correct a bug that "prevents ivan from being built in a separate build directory". diff --git a/Doc/Obsolete/ChangeLog.0420 b/Doc/Obsolete/ChangeLog.0420 new file mode 100644 index 0000000..2d35c33 --- /dev/null +++ b/Doc/Obsolete/ChangeLog.0420 @@ -0,0 +1,516 @@ +------------------------------------------------------------------------------------ + +>>> NOTICE!!! <<< + +This file contains SPOILERS, which might ruin your IVAN experience totally. The file +is also provided AS IS and is probably completely unreadable. + +------------------------------------------------------------------------------------ + +May 11 2003 Timo Kiviluoto + + * Corrected a minor bug in the bodypart mutation code + * README, NEWS and INSTALL updated + +May 11 2003 Timo Kiviluoto + + * Decreased old walls' HPs by 40%, so that the steel vault is easier to open + with explosives + * Corrected a bug that caused the player to die on the first turn if he had + starved in the world map at the end of the previous game + * Weaponskills now don't care whether the weapon is poisoned or not + * "X looks very agile." etc. messages are now only shown for pets (they were + annoying for instance in Attnam) + * Ivan is now a lot tougher + * Forbade the Grisly Game-Reaping Abuse of Arbitrary Brain Weight + * The final battle against Petrus is now much more difficult + +May 10 2003 Timo Kiviluoto + + * Cloaks break less often now + * Ghosts are no longer normally generated + * Corrected a strange bug concerning partly eaten bodyparts + * Fixed a small bug in the original bodypart id code + +May 10 2003 Timo Kiviluoto + + * Reduced global difficulty values by 5-10 danger points + +May 10 2003 Timo Kiviluoto + + * Corrected a bug in the reload datafiles command which made it useless + * The locks of the cathedral have been changed, and the master guard holds the + only key + * The secret equipment room of Attnam now contains more powerful items, if + someone wants to steal them + * Removed a test in wskill.cpp which I had totally forgotten and which looked + like a symptom of a really horrible bug + * Gravel is now somewhat brighter so steel weapons items are easier to see + * Adjusted various rings' materials so that they are more visible on gravel and + marble floors + * Items on lower levels are now somewhat more valuable (the difference is not + great since I don't want to ruin game balance now) + * Corrected a bug in the sizes of broken items generated on the ground that + most likely had existed since v0.40 + * Magical mushrooms teleport a little less often now + * Thunder hammer is bit less überpowerful now + * Bonefiles are loaded less often now + +May 10 2003 Heikki Sairanen + + * Added possibility loafs by 25 % to improve the food situation. + +May 9 2003 Timo Kiviluoto + + * Bonefiles aren't now *always* generated when it's possible + +May 9 2003 Timo Kiviluoto + + * Corrected a minor bug in the whip of thievery code + * Lowered orcs' danger modifier from 100 to 75 + * Reduced NPC exp by 50% (it was a *bit* too high) + +May 9 2003 Heikki Sairanen + + * Primary and secondary wielded are highlighted in '@' menu + * Msg for no zappable items when zapping works now. + * New insults + +May 9 2003 Timo Kiviluoto + + * Magical whistle now teleports only up to 10 monsters + * NPC:s now get four times more exp + * All team members now follow from (New) Attnam if the town isn't hostile + +May 9 2003 Timo Kiviluoto + + * Bill's wills' psi waves and ghosts' touches no longer damage armor + * Bill's wills are now immune to gas and can walk through walls + * Petrus now wears a cloak of electricity resistance and is intrinsically + energy resistant + * Adjusted mages' inventories a little + * Healing now consumes NP + +May 8 2003 Timo Kiviluoto + + * Banana growers no longer attack people 'g'oing on the landing platform + * Highscore now shows only 40 entries per page, to prevent felist crossing + the screen borders + * Corrected a bug that allowed unarmed attackers to hit a floating eye with + another arm even if they had been fainted + * Golems' bodyparts and corpses now vanish, because they were too expensive + * Gods are now more demanding + +May 8 2003 Heikki Sairanen + + * Action is terminated when life is saved + +May 8 2003 Heikki Sairanen + + * Go command stops when entering or leaving rooms + * Explosions don't destroy the edge tiles anymore. + +May 7 2003 Timo Kiviluoto + + * Getting killed by an own thunder hammer or Turox now gives a unique message + * A voluntary action is now terminated if a mage attacks from long range + * Corrected a bug that caused bone to spoil way too fast + +May 7 2003 Heikki Sairanen + + * Documented 1 idea and 1 bug + +May 6 2003 Timo Kiviluoto + + * Biting monsters now capture the severed bodyparts of the enemy + * Eddies are no longer charmable as this could be abused + * Corrected a small bug in the square memorized descriptions + * Cloned weapon accustomizations are now separated, so that using two + cloned items doesn't increase one common sweaponskill but two + (this was unbelievably difficult, so there might be obscure bugs) + * Cloaks now *really* can break + * Corrected a small bug concerning the handling of special monsters during + bonefile creation + * Version updated to 0.420 + * Removed lots of debug tests + * Corrected a fatal bug in the door booby trap code that occurred often + after earthquakes + * The enner beast is now a little less insane + * Doubled the time bone needs to spoil + +May 5 2003 Heikki Sairanen + + * Hands no longer tingle when praying to Loricatus handless + * Monsters don't walk to smoke for fun + * Sickle and scythe are better with plants than other items + +May 5 2003 Timo Kiviluoto + + * Added version information to bone files + * Broken and intact war hammers, a torn cloak and a vacuum cleaner added to + item.pcx by Kahvi + * Broken and intact war hammer wielded pictures added to humanoid.pcx by me + * Added hammer item (worse than war hammer) + * Ikiros now uses a normal hammer instead of war hammer + * Cloaks can now break + * Broken cloaks' and flexible armors' adjectives changed from "broken" to + "torn" + * Broken items now have unique material configurations, which are usually + much better than intact items'; this might make repairing techniques more + important + +May 3 2003 Timo Kiviluoto + + * Divided wood to the following materials: balsa, pine, fir, birch, oak, teak + and ebony + * All walls in New Attnam and in the underwater tunnel are now made of balsa, + and fir dominates the northern architecture + * Arcanite's density reduced from 4000 to 800 g/cm^3 to make it suitable for + staves + * Added octiron, which is a light but extremely hard staff material + * Added staff of wondrous smells + * The dark archmage is no longer burdened + * Added cloaks of fire and electricity resistances + * Corrected a bug that allowed bonefiles to to be generated in levels where + the player had already been + * Thunder hammer was perhaps little too good, so I increased its lightnings' + range to 4, so that it will hit the player in some circumstances, which are + however always easily recognisable and avoidable + +May 2 2003 Timo Kiviluoto + + * Corrected a truly horrible bug in the sparkle position randomization code + that only appeared when compiled with gcc + * Monster equipment choosing code now doesn't update pictures, lights, memorized + bitmaps, descriptions and ground stack's weight and volume if the gear doesn't + actually change -> suberb speed increase to monster equipping AI + +May 1 2003 Timo Kiviluoto + + * Fixed a few bugs added in the last commit + * Squashed a minor bug that caused messages to be concatenated oddly for some + turns after the player had died and started immediately a new game + * Deleted a fatal bug that allowed polymorphed monsters to crash during the + creation of bone files + * Object graphic creation system optimized yet more + +May 1 2003 Timo Kiviluoto + + * colorbit.cpp and allocate.h are now cleaner + * Inline functions inside the material colorization routines have been + expanded to increase non-optimized binary speed + * Corrected the Mischievous Memory Leak of Forbidden Knowledge + * Corrected the Sinister Memory Leak of Planetary Oblivion + * Corrected the Malign Memory Leak of Forgotten History + * Corrected the Perverse Memory Leak of Spiritual Weapon Skills + * Corrected the Ugly Memory Leak of Many Names + * Corrected the Nasty Memory Leak of Twisted Dungeons + * Corrected the Terrible Memory Leak of Sliced Sentences + * Decreased the RAM usage of IVAN by 1-2 megs by storing a tilemap iterator + array in objects instead of graphicids + * Corrected a shameful bug in festring's loading routine + * Configuration indices are now stored in databases instead of objects + * Reload datafiles WMode command works again + * Fixed the Notorious Mirage Puppy Spawning Error + * Some graphic routine optimization + * Resurrected creatures no longer change team if they aren't charmable + * Invisible stalkers are now tougher + * Removed a fatal bug in the explosion code which occured if a monster + polymorphed during an explosion + * Eliminated a fatal and extremely rare bug in zombie::SevereBodyPart() + * Added new material database booleans UseMaterialAttributes and + CanRegenerate, whose effects are no longer determined by IsAlive(); + this removes numerous illogicalities like poisoned ghosts etc. + * Corrected a rare bug that allowed very chaotic and complex series of + wand explosions to change the team of the player + +April 30 2003 Heikki Sairanen + + * Wielded items can be zapped + +April 30 2003 Heikki Sairanen + + * Fainted monsters no longer talk + +April 30 2003 Heikki Sairanen + + * Skeletons and other monster made from cold materials do not show in infravision + +April 30 2003 Heikki Sairanen + + * Bug in bodypart::SpillBlood(ushort) squashed + +April 30 2003 Heikki Sairanen + + * Added invisible stalker + * Bone files work in Linux + +April 28 2003 Heikki Sairanen + + * Slight improvements to makefiles + +April 28 2003 Timo Kiviluoto + + * Golems can now read + * Death message now shows if the player is polymorphed + * Flying items are no longer slowed by code if they can't be seen, which + makes AI battles faster + +April 28 2003 Timo Kiviluoto + + * Ghosts with >= 100 HPs can no longer be polymorphed + * Carrots' weights and NP values are now more realistic + * Carrot flesh plate mails are no longer allowed since eating them gave 10 + perception + * Metal bodyparts are now resurrected correctly + * Potions shattering inside damaged chests now give a message + * Lanterns' weight decreased to 1 kg + * Stethoscope is again useless + * The player can no longer talk if he is a golem + +April 28 2003 Timo Kiviluoto + + * Vomiting on the altar of Valpurus in Attnam is now punished with death + +April 28 2003 Timo Kiviluoto + + * Ghosts now ignore sound damage + * Cloning a monster along with a stack now reduces the amount of copied items + by one + * Corrected two small bugs in the lookmode code + +April 28 2003 Timo Kiviluoto + + * BusyAnimations are now cached + +April 28 2003 Timo Kiviluoto + + * Bonefiles are now working + * Mellis now changes 10 items instead of 5 + * Replaced many Id-ending terms with *ID + * Corrected yet another many bugs in the bear trap code + * Panicked monsters no longer start eating or equipping things + * Resurrected creatures now always become members of the raiser's team + * Added Possess WMode command ('{') which allows one to control monsters + * Cloned items now share accustomizations of their mothers + * Accustomizations are now dependent on weight + * Corrected the Dreadful Crash of Forgetful Amateur Weapon Swingers + * The danger system is now a little more sane when throwing unique monsters + at the player + * Stethoscope info now shows states and a verbal description of danger + * Corrected a minor problem in the block messages of invisible enemies + * Shop and elite guards and the master guard are now more powerful, because + killing them was too easy a way to get excellent equipment + * Adjusted certain unique monsters' equipment + * Resting with the 'h'eal command is now twice as fast, to balance the fact + that it yields no exp bonuses, like for instance 'g'o + * If the player can't heal more, rest asks how many turns to wait + * Corrected a small bug in ghosts' and bill's wills' hit messages + * The enner beast is now tougher on long range, like in good old times + * Lanterns' weight decreased from 2500 to 1500 grams + * Shields are now better + * Inline functions inside critical blit loops of bitmap.cpp have been + converted to ugly macros which are enormously faster if optimizations + are disabled + +April 25 2003 Heikki Sairanen + + * Hyper new great (even greater than great actually) feature: NOW COMPILES! + +April 25 2003 Heikki Sairanen + + * Fixing not alive bodyparts with scroll of repair is now possible + * Only monsters that can read can read engraved messages + +April 23 2003 Heikki Sairanen + + * Now compiles in Linux. (some problems with festring) + * Engraved messages can be viewed in LookMode. + * Low dexterity characters can get caught in setting bear traps + * Cloning elpuri's head abuse prohibited + * Golems now engrave and don't chat + * Mellis gives just new bottles, that can be filled with other materials than ommel urine and banana flesh too. + +April 23 2003 Timo Kiviluoto + + * Silva's boulders now destroy neither altars, stairs nor any other important + terrain + * Eddies no longer spit couches over irreplacable terrain + * Decreased the HP requirement of every mage by 100 since they were so rare; + this change's effect must be tested carefully in game + * Corpses made of precious materials now sparkle + * Removed the insult for pressing an unrecognized key + * Kicking now uses a random leg so that both legs are trained + * Hit order using two weapons or fists is now unpredictable + * Fixed a rounding error that freezed leg strength when burdened and doubled + agility exp minus + * Hitting or blocking with a two-handed weapon now gives strength exp to both + arms + * Reduced exp gained in battle to counterbalance these changes + * Being satiated (and not moving) again reduces agility exp; I don't know + if this makes agility too difficult to train, but let's test it + * Nonhumanoids now gain strength exp from biting + * Kicking now gives no exp if there is no character in the kicked square, + since this could be abused + * Added many new material configs for thunder hammers + * The holy banana of Oily Orpiv is now more accurate, does greater damage, + burns enemies, shoots fireballs when zapped (that's "oily" after all) + and explodes with the power of an atomic bomb when damaged, making it a + fierce weapon of mass destruction + * The holy banana now emits bright light + * The holy banana now greatly trains all attributes when eaten + * Broken shield's wielded picture added to humanoid.pcx by Kahvi and + later integrated + * Visible invisible stalker added to char.pcx by Kahvi + +April 21 2003 Timo Kiviluoto + + * Corrected a bug that prevented floating eye avoiding AI from working + * Increased the difficulty penalty for being invisible by 25% + * Decreased the time needed to read a scroll of teleportation by 75% + * Decreased the range of thunder hammer's lightnings from 10 to 3 so they + cannot hit the user (well, unless he is fighting a ghost inside a wall) + * Difficulty now cannot decrease even if the player becomes lousier than + before, since this could be abused by running naked around locked rooms + to cheat the monster generation code + * Quickdeath prevention system no longer affects unique monsters, and + their danger modifiers have been decreased; these changes seem to make + them appear roughly in the right time + * Added several new material configs for armors + +April 18 2003 Timo Kiviluoto + + * Carrots now look better + * A wielded item is now animated if the original item is (ie. it is glittering, + spoiled, outlined, flaming or shooting lightnings) + * Golems made of precious metals now sparkle, as do elite guards' and Haedlac's + armor and the goblin king's crown + * Shining armor now sparkles on the game screen if worn by the player + * The player's head looks correct if he wears the Gorovits family gas mask + +April 16 2003 Timo Kiviluoto + + * Corrected many bugs in the bear trap code + * Slaves no longer carry encrypted scrolls + * Wielded items are again shown on the game screen + +April 14 2003 Timo Kiviluoto + + * Arms are again shown correctly on the game screen + * Replaced std::string with festring, which is about twice as fast, compiles + faster and doesn't crash randomly when compiled with VC++ 5.0 + * Bill's wills and ghosts are no longer überpowerful in the DOS port + * Greater carnivorous plants are now tougher + * Key names again work correctly + * Magpies are now more intelligent + +April 7 2003 Heikki Sairanen + + * Added carrots + +April 7 2003 Timo Kiviluoto + + * Carrot added to item.pcx by Kahvi + * Broken InHandsPics and head with gas mask added to humanoid.pcx by Kahvi + +April 7 2003 Heikki Sairanen + + * Automatic start pet naming works now. And I'm not kidding. + +April 7 2003 Heikki Sairanen + + * Automatic start pet naming works now + +April 7 2003 Heikki Sairanen + + * Certain function added to hscore.cpp for Mihail + +April 7 2003 Timo Kiviluoto + + * Setting the pet default name is now possible from conf + * This default is initialized to Kenny + * Puppies now have more replies + +April 7 2003 Timo Kiviluoto + + * Extremely powerful kickers like adamant golems can now kick down walls + * It is now possible to break lanterns on walls by kicking them + * The faster monster now gets a 25% bonus when calculating relative danger, + since it can potentially flee from the battle + * Remade database.cpp + * Converted a lot of std::strings to const char*s in places where it decreased + compile time + * Removed the ShowBattleInfo command which was nearly useless and took months + to compile + * Compile time (full optimization) decreased by about 30-40% + * The game maybe compiles on machines with a lot less memory now + * Added WIZARD define without which wizard-mode commands are not compiled + * You can now feel walls around you even if you don't see them + +April 4 2003 Timo Kiviluoto + + * Version updated to 0.411 + * SeeWholeMapCheat is now more versatile + * Corrected a bug that prevented doors to be created on the right and left sides + of a room, so they appeared only on the top and bottom (it's quite interesting + that no one ever noticed) + * Fixed a strange bug that caused lanterns to be generated inside the walls of + the starting room + * If less than four items are on the ground, stepping on them says "A few items + are lying here" instead of "several" + * State information is again better categorized and documented + * Again corrected a bug in the messages after teleport (alas, this slowed down + the game...) + +April 3 2003 Timo Kiviluoto + + * Docs have been cleaned up a bit + * _Lots_ of ideas added + * Value plan updated + +April 2 2003 Heikki Sairanen + + * CanApply value added for monsters + * Werewolves' KillMessage corrected + * Go command no longer walks through smoke + * Probably something completely different... + +April 1 2003 Timo Kiviluoto + + * Corrected a small abusable bug in the monster AI + * "Collected conversations of Ivan Gorovits and the Leader of Citizens" added + to Doc/Data + +March 31 2003 Timo Kiviluoto + + * Severed bodyparts of humans look again correct + * The dificulty penalty for being invisible is now insane + * Wishing invisiblity-confering items is again allowed + * Reduced the penalty for eating frog flesh (it was worse than committing + suicide) + * Bunny, large bunny and Vladimir (Vile Lurking Agressive Devourer with Infernal + Muscles Intended to Rampage) the gigantic carnivorous mutant bunny added to + char.pcx by Kahvi + * The game now compiles correctly on gcc 2.952 without special switches + +March 31 2003 Timo Kiviluoto + + * Gauntlet armor pictures are again shown correctly + +March 31 2003 Timo Kiviluoto + + * State data has been structuralized to make it more readable + * Magical vapour no longer activates lycanthropy, I think + +March 30 2003 Timo Kiviluoto + + * Corrected a bug in the banana growers' former profession system + * Fixed the "Overgrowth of square population!" error of Attnam + * Corrected a minor bug in the wand of door creation and earthquake codes + * Destroying or polymorphing items or using a wand of door creation in shops and + in the Cathedral is now prohibited + * Kicking shop doors doesn't make shopkeepers mad anymore if you don't actually + damage them + * Polymorphing the altar in Attnam no longer changes the divine owner of the + whole Cathedral and make the priest say "Not currently implemented." + * The banana grower encourager no longer mutilates the village elder diff --git a/Doc/Obsolete/ChangeLog.0430 b/Doc/Obsolete/ChangeLog.0430 new file mode 100644 index 0000000..d2ffbf1 --- /dev/null +++ b/Doc/Obsolete/ChangeLog.0430 @@ -0,0 +1,344 @@ +------------------------------------------------------------------------------------ + +>>> NOTICE!!! <<< + +This file contains SPOILERS, which might ruin your IVAN experience totally. The file +is also provided AS IS and is probably completely unreadable. + +------------------------------------------------------------------------------------ + +August 5 2003 Timo Kiviluoto + + * Now compiles with gcc 2.952 + +August 5 2003 Heikki Sairanen + + * No warnings with gcc anymore. + +August 5 2003 Timo Kiviluoto + + * Corrected a bug in the bone player resurrection code + * More test removals + +August 5 2003 Timo Kiviluoto + + * Some frequency and danger modifier changes + * Test removals + +August 5 2003 Timo Kiviluoto + + * UT3 is now a little easier + +August 5 2003 Timo Kiviluoto + + * Polymorphing cloned multitiled items no longer crashes + * Vladimir can no longer be cloned + * Bear traps are no longer polymorphable, since this happening when they were + trapping someone caused a crash + * Corrected two Yet Another Teleport on Mine, Polymorph and Crash Bugs + * Teleporting away from bear traps works again correctly + * Saving and loading while transporting Vladimir through the wilderness no + longer crashes + * Highscores now store system time and a random 31-bit integer along with + score information, which may be useful for MIHAIL + * Purified the source from a fatal bug which could cause removing of + multitiled creatures from prescripted strategic locations to abort the game + +August 5 2003 Heikki Sairanen + + * When multi-squared monsters enters a square with a door. The door is destroyed. + +August 5 2003 Heikki Sairanen + + * typo corrected "blah are/is too large to big up" -> pick up + +August 5 2003 Timo Kiviluoto + + * Resurrecting monsters will now trigger StepOn effects under them + * Corrected a typo in Vladimir's description + * Vladimir can no longer be polymorphed + +August 4 2003 Timo Kiviluoto + + * The alternative key settings now work in felists, for instance inventory + * Imps are now immune to fire + * Gibberlings now gibber instead of grunting + * Ikiros can now fix any metal item even if it's harder than mithril; the + former restriction was really unrealistic + * NEWS and AUTHORS updated + * Corrected a bug that could crash the game when placing multitiled characters + in the script + * Attempting to kick or teleport multitiled corpses out of the level no longer + crashes + * character::MoveRandomlyInRoom() no longer crashes near the level borders + * Corrected a fatal bug in all retreating AIs which occasionally caused a + crash near level edges and a very rare bug that could cause them not to be + able to escape near the right side of a level + * The same AIs are now somewhat more intelligent near unwalkable terrain + * Resurrecting the headless corpse of Elpuri no longer crashes + * Planting mines, creating doors etc. on the ostrich landing site is now + prohibited + * Exiled the Bloodthirsty Killer Bug of the Shattered IvanDev Emblem + * Vladimir no longer flies + * Fixed a rare bug that could cause Elpuri's area attack to crash if + he triggered item explosions which killed him and polymorphed his corpse + before the attack was completed + * Wand of door creation can no longer be used to reach level borders in + dungeons + * Defeated the Mysterious Chaos Bug of Game-Eating Ghost Items + * Squashed a fatal bug in the NodeMap initialization code that could cause + multitiled monsters' pathfinding to crash + * Plugged a minor memory leak in the level destruction code + +August 3 2003 Timo Kiviluoto + + * Removed the obsolete character and item outline codes + * Corrected some gcc warnings + * Walls in GC are now made of stone, for variety + +August 3 2003 Timo Kiviluoto + + * Corrected a fatal bug in the bonefile code + * The holy banana's big explosion damage reduced to half + * Added some magic resistance to powerful named monsters + * The AI is now able to use teleport control + * The AI now uses ESP more efficiently + * Sherarax and Ischaldirh are again several times more insane + * Plants etc. again can't follow or be displaced + * Corrected a minor outlook bug in the explosion code + * Kamikaze dwarves and mines are now more powerful + +August 2 2003 Timo Kiviluoto + + * Fly symbols are again displayed correctly + +August 1 2003 Heikki Sairanen + + * Bunnies no longer can open doors + +July 31 2003 Heikki Sairanen + + * Nobody can walk in dungeon to the edge tiles. + +July 31 2003 Timo Kiviluoto + + * Vladimir now changes color all the time + * The banana growers no longer defend Decos + +July 31 2003 Heikki Sairanen + + * Opening book cases is easier (just walk towards one) + +July 30 2003 Timo Kiviluoto + + * Adjusted Legifer's, Cleptia's, Nefas's and Infuscor's pray effects somewhat + +July 30 2003 Heikki Sairanen + + * Mistress' weaponskill increased + +July 30 2003 Timo Kiviluoto + + * Corrected a fatal bug in the dig code + * Fixed a small bug in the consuming code which could create fleshless + holy bananas + * Removed a tiny abusable bug in the teleport control code + * Broken thunder hammers no longer emit light + * Eliminated a minor bug in the clone experience behaviour + * Destroyed an ugly bug that occurred when teleporting vesana + +July 30 2003 Heikki Sairanen + + * Stupidity in my bug correction (zombies eating headless) corrected + +July 30 2003 Timo Kiviluoto + + * Entering Corey's dungeon is now more difficult + +July 30 2003 Timo Kiviluoto + + * Action system implemented in a slightly different way, which corrects + some strange bugs appearing when eating multi-tiled corpses + +July 30 2003 Heikki Sairanen + + * Temporary states (such as poisoned) are deactivated when monster is resurrected. + +July 30 2003 Heikki Sairanen + + * Pets and player killed monsters are no longer handled seperatly for score calculation + * Bug with digging earth tiles + +July 30 2003 Heikki Sairanen + + * Headless zombies can no longer eat or drink. + +July 30 2003 Heikki Sairanen + + * Menu picture changed. + +July 30 2003 Timo Kiviluoto + + * Fixed a fatal bug in the character::PutNear() + +July 30 2003 Timo Kiviluoto + + * StepOn effects are now applied when changing areas, too + +July 30 2003 Timo Kiviluoto + + * Added Vladimir the gigantic carnivorous mutant bunny, which is a loyal + companion of Ivan and occupies 2x2 squares at once + +July 30 2003 Timo Kiviluoto + + * Corrected a bug which caused female cowards of Attnam to scream impolite + personal pronouns if their pursuer wasn't seen by the player + * Hattifattener added to char.pcx by Kahvi + * Cactus added to item.pcx (a completely wrong place) by Kahvi + * New monsters added: + * Giant dark frog, giant light frog, giant carnivorous plant, black bear, + grizzly bear and cave bear + * Hedgehog, of which spines do damage to unarmed attackers + * Hattifattener, which walks around randomly and shoots lightning bolts at + unexpectable directions and explodes in a large burst of electricity when dies + * Adult male and female carnivorous mutant bunnies, which are generated in pairs, + seek food and bunnies of opposite sex aggressively, mate and produce babies + * Baby male and female carnivorous mutant bunnies, which find food to grow big + * level::FindRoute now uses real A-star pathfinding + * Sober monsters with intelligence of at least ten now use the new pathfinding + when the old one isn't sufficient and are capable of some slight teamwork + (ie. calling for nearby friends' attention if they detect an enemy) + * The AI will now kick down locked doors if it can and presumes an enemy is + hiding behind them + * Genetrix vesana now spawns more and more powerful plants as time passes + * Decreased global danger values, monster amounts and and spawn rates considerably + +July 30 2003 Heikki Sairanen + + * Added CanBeSeen checks to Elpuri's code. + * Added weird reference to Ivan's talk + +July 28 2003 Heikki Sairanen + + * Elpuri goes through walls and destroys them + +July 22 2003 Heikki Sairanen + + * Score system simplified + +July 15 2003 Timo Kiviluoto + + * Removed some ambiguous material definitions + * Corrected a small bug in the olterrain changing code + * Earth pictures modified somewhat by Kahvi + +July 14 2003 Timo Kiviluoto + + * Monster invisibility works again correctly + * Fixed a small border tile bug + +July 14 2003 Heikki Sairanen + + * Bug related to multisquared characters corrected + * Now compiles with GCC again. + +July 11 2003 Timo Kiviluoto + + * Added border tiles to earth + * Wand of door creation no longer crashes + * Corey's level no longer crashes, I think + +July 9 2003 Timo Kiviluoto + + * StepOn effects work again + +July 9 2003 Timo Kiviluoto + + * Added support for multi-tile characters and items + * 32x32 genetrix vesana and Elpuri added to char.pcx by Kahvi + * Genetrix vesana, Elpuri and their corpses are now 2x2 squares large + * Earth with border tiles added to olterra.pcx by Kahvi + * Sumo wrestler added to humanoid.pcx by Kahvi + * Removed boots of the (Master) Porter + * Corrected a bug in the lock configuration creation code + * The enner beast is now quite funnily animated + * The holy banana can no longer be cloned + * Mages are now a little less insane opponents + * Corrected a highly abusable problem in the polymorph control code + * Added numerous bugs + +July 9 2003 Heikki Sairanen + + * Chests in inventory can be unlocked/locked. + * Possible abuse prohibited in key-code + + 1 : unlock chest + 2 : lock chest + 3 : goto 1 + (gives dex) + +July 8 2003 Timo Kiviluoto + + * Corrected a fatal bug in the level loading code + +July 5 2003 Heikki Sairanen + + * No more funny snow covered boulders in weird places like New Attnam + +July 4 2003 Timo Kiviluoto + + * A small bug in save.cpp corrected + * A great bug in rooms.cpp corrected + * You can now easily add new lock types + * Disabled several of Hex's tests + * 'K' now kicks instead of 'k' + * Lyre picture added to item.pcx by Kahvi + * Animated enner beast head and lyre's wielded picture added to humanoid.pcx + by Kahvi + * Impenetrable jungle terrain + border tiles added to olterra.pcx by Kahvi + * Added lyre of charm which can tame weak monsters + +July 01 2003 Heikki Sairanen + + * Support for keyboards without number pad added + * Some keys changed (because of the above change). Probably very annoying. + +June 05 2003 Heikki Sairanen + + * Very basic worldmap tile types added. (much work needed still) + +May 24 2003 Heikki Sairanen + + * The new path-finding (or actually checking whether a path exists...) algorithm added + * Few spelling mistakes in char.dat corrected + +May 21 2003 Heikki Sairanen + + * Dungeons work again. + * A pathfinding algorithm. (That is not yet finished) + +May 20 2003 Timo Kiviluoto + + * Added second version of Corey's crystal level + * Btw, this version doesn't work + +May 20 2003 Timo Kiviluoto + + * Added first version of Corey's crystal level + * Btw, Hex's code doesn't compile + +May 19 2003 Heikki Sairanen + + * Compiles with gcc 3.3 + * Almost everything crashes. (started to make new world map idea) + +May 13 2003 Heikki Sairanen + + * interupt -> interrupt. We suck in English. Thanks Tuukka! + * No more weird msgs from unseen bear traps. + * Bugs with many rooms and no-guy-who-is-responsable-for-the-attack fixed + +May 12 2003 Timo Kiviluoto + + * Docs (especially bugs.txt) have been cleaned up a bit diff --git a/Doc/Obsolete/ChangeLog.050 b/Doc/Obsolete/ChangeLog.050 new file mode 100644 index 0000000..1fdb4a7 --- /dev/null +++ b/Doc/Obsolete/ChangeLog.050 @@ -0,0 +1,484 @@ +----------------------------------------------------------------------------- + + >>> NOTICE!!! <<< + + This file contains SPOILERS, which might ruin your IVAN experience + totally. The file is provided AS IS and is probably completely unreadable. + + Note also that the file contains only a fraction of the changes between + versions 0.430 and 0.50, since Timo Kiviluoto did not update ChangeLog + during the period between January and September 2004 + +----------------------------------------------------------------------------- + +December 10 2004 Timo Kiviluoto + + * Corrected a bug in the genie hostility code + +December 10 2004 Timo Kiviluoto + + * The annoying question "Do you want to ERASE..." whatever which appeared + after dying if the highscore file was empty or old is now less annoying + and doesn't appear unless it's really necessary + +December 10 2004 Timo Kiviluoto + + * BugFix: Very complex chain explosions caused by kicking could crash + the game + * BugFix: Polymorphing to Petrus in WMode crashed + * Great War in Attnam is inactive + * BugFix: Fleeing monsters using teleport control could try to teleport + outside the level, causing a crash + * You can no longer use your vital bodyparts for golem creation + * Invisible creatures can no longer block light + * BugFix: There was no way to use stairs covered by slime + * Flying creatures no longer get stuck to slime, at least if it's not + vomited at them + * You can give no more commands to unconscious pets + * BugFix: "Don't consume anything valuable" command flag wasn't activated + by default due to an obscure problem + * Increased Shirt of the Golden Eagle's AV from 35 to 40 + * The player no longer autovomits on altars unless he has to or it + belongs to Scabies + * Removed the "temporary" comments that increased the probability of bone + files being created/loaded + * The veteran kamikaze dwarf of GC6 is no longer always on the downstairs; + this was too nasty for those players unfortunate enough to come up + from GC7 after being sucked there through a fountain + * An extra confirmation is needed before the game allows the player to + vomit at a neutral NPC; I've died too many times because of pressing + 'V' instead of 'C' + * There are no doors in UT4 now; it is a natural cave + * The player is no more allowed to change the equipment of archangels + * Archangels are now less invincible + * Cut the time archangels exist to one tenth + * Added some extra monsters to Oree's lair + * It now rains blood in Oree's lair + +December 10 2004 Timo Kiviluoto + + * Broken gauntlet pic modified by Kahvi + * BugFix: Killing a unicorn could crash + * Great War in Attnam is active + +December 10 2004 Timo Kiviluoto + + * Fixed some minor "problems" reported by Valgrind + * Thaumic bomb and empty/full dwarven gas grenades added to item.pcx + by Kahvi + * Adjusted the InElasticityPenaltyModifiers of broken items upwards + * BugFix: No message was printed when levitation was activated, except + if due to a BoL + * Decreased the possibility of attribute increase and polymorph + effects of fountains + * Groups of monsters can appear from a fountain, not just one; + the amount depends on the species and luck + * Removed some obsolete commented code + +December 03 2004 Heikki Sairanen + + * unconscious shopkeepers don't trade + +November 17 2004 Timo Kiviluoto + + * Science talks can now require and train charisma + * Richel Decos has his own unique science talk particles + +November 15 2004 Heikki Sairanen + + * small typo corrected + +November 12 2004 Heikki Sairanen + + * the incompatability of old highscore files is handled like it should + +November 11 2004 Heikki Sairanen + + * old highscore files incompatable + +November 11 2004 Timo Kiviluoto + + * New death messages "beheaded by X" and "killed by X's dirty attack below + the belt" added + * Decreased the effect of TALENT_CLEVER; I hadn't taken into account the + true power of nonorganic limbs when defining it + +October 29 2004 Timo Kiviluoto + + * Corrected an annoying bug in DoEvilDeed + * Every generated monster decreases the approximate monster generation + interval of the level slightly, so infinite exping on one area becomes + less effective over time + * More messages are now generated when teleporting + * BugFix: the weight of a mammoth's corpse was negative + * BugFix: mushrooms were able to grunt + * TypoFix: one space was missing from rusted golems' names + * Changed the basic terrain materials of GC levels 6, 9, 10 and 11 + mainly for clarity reasons + +October 22 2004 Timo Kiviluoto + + * BugFix: offering on the Silva's altar in UT4 crashed + * BugFix: endurance exp wasn't gained if the player had a non-living + bodypart and his binary was compiled with VC, due to a bug in + the latter + * Corrected many problems in bodypart special effect draw order + * Angels might be a little more intelligent now + * Gods may be a little wiser when choosing gift bodypart materials + * Probably fixed an additional bug or two which I can't quite recall now + +October 13 2004 Timo Kiviluoto + + * Decreased the amount of Attnam's polar bears to one, because their + shadows had a noticable negative effect to performance + * BugFix: charming a tourist and leaving New Attnam crashed + * Halved the speed materials spoil + * Spoil and mirror item vanish messages are now more informative + * BugFix: equipment slots were shown in the panel when polymorphed into + a golem + * BugFix: removed the stairs to not-yet-done UT5 + * Webs of giant spiders are a little weaker + * The big trap room of GC changed into a dwarven mine field + * Monsters are no longer generated in the mine field or vault rooms + * Corrected a bug in the lantern code which could prevent items on the + ground from being animated if the square once had a wall & attached + lantern + * Fixed many problems with messages + +October 8 2004 Timo Kiviluoto + + * BugFix: items on the ground were sometimes not animated + * BugFix: armor pictures were not always updated correctly when + polymorphing + * BugFix: polymorphing into a nonhumanoid while wearing a belt of + levitation crashed + * Founders.jpg added to Doc/Data + * Hunters now have unique science talk particles + * The body of science talk msgs ("You have a rather pleasant chat...") + is now more vivid, having ten different forms + * Removed the scroll of create monster; it was never used for anything + sensible save abuse + * The game now displays more informative break/destroy msgs, eg. + "something on the ground explodes", "the slave's leather armor breaks", + etc. + * The game now says "xxx stabs/strikes/slashes" instead of "xxx hits" + if xxx is armed; the verb depends on the weapon + * Some other msgs changed, but there are still problems with these + +October 3 2004 Timo Kiviluoto + + * Some mind worms added to char.pcx by Kahvi + * Corrected a rather nasty bug, which eg. made infravision useless + * Enemies might retreat from invisible creatures a bit more effectively + (again, there's bugs in this also; I'll fix them later) + +September 31 2004 Timo Kiviluoto + + * Fixed a typo in the anti-explosive magical field code + * Added distinct break messages for wands (this probably has bugs now, + I'll fix them later) + +September 30 2004 Timo Kiviluoto + + * Corrected a minor bug in characters' special effect draw order + +September 30 2004 Timo Kiviluoto + + * My sincerest apologies for not updating ChangeLog for a loooooooooong + while. Henceforth, however, I wow to resume this healthful habit + * Mistresses and dogs now have unique random science talk particles + * For in-house testing sessions, there's now a BONUS_LIVES #define in + define.dat which determines how many bonus amulets of life saving + the player gets when he starts a game + * Disabled the time test in the panel + * Spiders' flesh and blood are now poisonous + * The game no longer crashes if an NPC wearing a belt of levitation steps + over another belt floating near the edge of a lake or pool + * Corrected some small problems in certain god effect messages + * Pets are now killed when generating bonefiles even if they have + lifesaving + * Corrected a typo in the standard file copyright notice (oops :) + +August 25 2004 Heikki Sairanen + + * Timing works. Although fetime:: is still a bit incomplete + +August 20 2004 Heikki Sairanen + + * Monsters think webs are dangerous + +August 20 2004 Heikki Sairanen + + * Sophos changed to also god of handicrafts + +August 20 2004 Heikki Sairanen + + * panel changes + +August 20 2004 Heikki Sairanen + + * more webs + +August 19 2004 Heikki Sairanen + + * webs added... atleast some fraction of them + +August 17 2004 Heikki Sairanen + + * smith and tailor work with equiped items now too. + * changed tailor replies + +August 6 2004 Heikki Sairanen + + * blue color cursor for run mode + * some typo corrected + * bananapeels can be resurrected + * monster portal in Oree level + * X swimming here now ok + +August 5 2004 Heikki Sairanen + + * bomb displacing energy field in Attnam + +August 5 2004 Heikki Sairanen + + * Food added to UT + * wskill with no exp that are used are shown on wskill screen + +July 14 2004 Heikki Sairanen + + * Vladimir works + +July 14 2004 Heikki Sairanen + + * Vladimir works better, but far from perfect + +July 13 2004 Heikki Sairanen + + * Makefile corrections (couple of missing files) + * Moved Polymorph command in cpp to only wizard mode compiles.. + +July 8 2004 Heikki Sairanen + + * Minor correction to fountains... + +July 8 2004 Heikki Sairanen + + * Added wizard mode command Polymorph (key '[') + +July 8 2004 Heikki Sairanen + + * Changed license notices a bit + +July 8 2004 Heikki Sairanen + + * levitating plants and mushrooms move about + +July 7 2004 Heikki Sairanen + + * Vomit messages work... + +July 7 2004 Heikki Sairanen + + * "You look down. You see an active mine." in the dark. no more... + * Headless zombies can no longer vomit. + * ?,*,|,<,>,/,\,: are no longer acceptable for names (windows doesn't like them) + * ForcedVomit messages are different for mommos and dolphins + * 'If player tries to apply when he has no arms, the message says that there are no items to apply.' bug corrected.. + * When you don't get to the highscore list your reason of death is shown + +July 7 2004 Heikki Sairanen + + * Copyright notices added to sources, headers and script + +Heikki Sairanen + + * fountain test removed. sorry + +Heikki Sairanen + + * Fountains can now throw player to other fountains in near levels or same level. + +Heikki Sairanen + + * Leprosy improved a lot and is cureable + +Heikki Sairanen + + * aclocal.m4 updates or something strange like that anyway :) + + + +??? Timo Kiviluoto + + + + * It is no longer possible to kick stones under walls + * It is no longer possible to detect traps by floating over them, dropping + items and checking if the total weight of the stack is greater than the + items' weights' sum + +January 6 2004 Heikki Sairanen + + * Now compiles in linux. The corrections are _bad_ so checking them wouldn't be a bad idea. + +January 1 2004 Timo Kiviluoto + + * Corrected a fatal bug in the cloning code + * Fixed a problem that caused the fluid system to run occasionally very slowly + * Decreased the banana peel trap damage a little + * Pressing ESC when choosing a teleport control target no longer crashes + * Prevented crystals from spawning over stairs in UT4 + +January 1 2004 Timo Kiviluoto + + * Corrected an extremely fatal bug in the square destruction code (it may + have caused random crashes when changing levels) + * Fixed a harmless light update absence bug in the cloning code + * N simultaneously bought items in a shop can no longer cost more than N + times one item's price (formelly this was possible since all arithmetic + operations during the price calculations were rounded down) + * Monsters now avoid needlessly stepping on banana peels and broken bottles + * The nationality adjective "Attnamian" is now written "Attnamese" + * Gear pictures are now updated correctly when an equipped item spoils + * Adjusted the word order of sword names (based on our weapon expert Kahvi's + opinions) + * The game no longer enters an infinite loop when blood is spilled on an + enner beast's groin + * It is now possible to determine the leader of a team in the script + * It is now possible to determine how spoiled items are in the script + * Adding several identical items in the script is now more elegant + * Remade the consume system: the game now handles correctly situations + where the player eg. eats an iron sword with a banana flesh handle + * Ikiros can now remove rust from equipment + * Fixed two bugs in Loricatus's pray effect + * Optimized the generation of terrains and items somewhat + * Added many new features to New Attnam + * Banana shop for the hungry (the products start to spoil within a few + turns, though) + * Priestess of Silva who offers the same services as the priest in + Attnam but is cheaper + * Tourist family who follow Kaethos around the town + * Underground sumo wrestling arena, where the player can compete with + the local sumo champion Huang Ming Pong (I will not list all the + meticulous details of the associated events here) + * Added two new armors which the player gets after winning the sumo + fight + * The banana growers now feed the sumo wrestler regularily + * It now rains in the village occasionally + * Lowered the player's starting money + * Zombies raised by necromancers now inherit the attributes of their + past lives; also their names now depend on them + * Master necromancers can now sometimes raise Xinroch, if he hasn't + been generated yet + * Apprentice necromancers can now raise any corpse he sees as a zombie + (not just an adjacent one), and masters any corpse in the level + * Necromancers are now somewhat more intelligent + +December 16 2003 Timo Kiviluoto + + * Adjusted the colors of menus and lists so that the selected item is + more clearly visible on dark monitors + * Large spider, giant spider and Lada added to char.pcx by Kahvi + * Expensive camera and miniature mummy of Lenin added to item.pcx by Kahvi + * Necromancer, bourgeois tourist, his wife and child, and Lenin added to + humanoid.pcx by Kahvi + * Necromancer graphics integrated + * Two spider webs added to effect.pcx by Kahvi + * Remade felist entry picture drawing system; browsing an enormous inventory + (for instance after using the gain all items cheat) is now a lot faster + * A small typo in the look description of neutral panicked NPCs corrected + * Added a super-fast system for retrieving random pixels for which a certain + predicate (say, non-transparency) is true; I plan to use a similiar + algorithm for level::RandomSquare in the future + * Documented stack.h/cpp and fluid.h/cpp + * Added ironalloy material category which currently consists of iron, steel, + meteoric steel and adamant + * Iron alloys can now rust; names and strength values are affected + * Rusting is now displayed graphically as random brown pixels + * Added RustModifier material database member which determines the ability + to rust for liquids and how easily rusting happens for ironalloys + * Golems and terrains made of ironalloys can also rust + * Scroll of repair can now remove rust + * Renamed class human to playerkind + * Rewrote the player's armor drawing code; it was probably the most horrible + gum solution in the entire game + * Added several item database members to control which armor pictures + are to be drawn + * Added (half-accidentally) support for armor picture animations + * The Shirt of the Golden Eagle now gives the player an appropriate blue aura + when worn + * The priority map trickery which is used to determine the special effect + drawing order of humanoids is now completely incomprehensible + * Remade the whole fluid code: it now utilizes the full power of IVAN's + material system + * Liquids on ground can now cause effects to legs/other bodyparts + * An arbitrary number of fluids can now cover weapons, armor and + bodyparts; graphical effects added to item pictures + * Fluids can now affect the items and bodyparts they cover + * Removed the obsolete ContainedMaterial of weapons + * Fluid is now displayed over wielded, armor and bodypart graphics + for playerkinds; however I chose not to show them for other monsters + since it could cause many characters to be drawn very unclearly + from the user's perpective + * Added a dripping animation for all fluids not on ground + * Damaging a monster with a weapon now causes blood to be spilled + over the item used + * Dipping to solid materials is alas no longer possible; however, I + haven't done this since 0.311 so it isn't so great a loss + * Liquids can now exist on non-walkable terrain and affect them. + For instance you can destroy walls using acid (see below). + However, there is a small problem that liquids can be seen from + all sides of the terrain which can be strange if it is a part of a + long wall separating parallel tunnels + * Fluids can now emit light (but this feature is not yet used) + * A potion which breaks due to hitting an object now spills half of + its contents to the next square on its theoretical line of flight + * Fluids affected by paranormal gravitational distractions now land + correctly on the players face, if he/she/it has one + * Corpses now spill blood, not just severed bodyparts + * Added acid effects which are based on the Acidicity material database + member; acidous fluids cause items to break and can destroy very weak + broken items, and they also tend to be very rusting + * Added IsImmuneToAcid material database boolean + * Dark frog blood and pepsi are now acidous + * Remade the vomit system; the amount of liquid now depends on the body + volume of the character and it can be targeted to adjacent squares + and used as a very poor acid-based weapon + * Added BloodMaterial and VomitMaterial character database members + * Mommos now really attack by vomiting acidous slime, which also has + a high RustModifier + * Oree's vomit attack now really spills acidous blood over the player + * M-color 3 of arm pictures is henceforth allocated for gauntlets; + some humanoid graphics updated due to this + * Zombies now spoil + +November 8 2003 Heikki Sairanen + + * feio.cpp documented + +August 31 2003 Heikki Sairanen + + * Now compiles with gcc + * Added necromancers + * Mushrooms inherit their stats. + * Atavus gives player a banana at XMas. Banana should probably be changed to something else. + +August 14 2003 Heikki Sairanen + + * Certain small changes to mihail so it know finds it's missing FeLib... + + Added instructions to create symbolic link to FeLib, if it isn't distributed for the user. + +August 14 2003 Timo Kiviluoto + + * Added some comments to the Febot code + * Hex's new MIHAIL version added to CVS + +August 14 2003 Timo Kiviluoto + + * iosystem::StringQuestion now takes the destination string as an argument + and returns whether the user aborted the question or not + * Configuration system has been standardized and moved largely to FeLib + * Added Febot engine which can be used by the Oracle of the Holy Haven of Gurus + when the time comes diff --git a/Doc/Obsolete/Changes.txt b/Doc/Obsolete/Changes.txt new file mode 100644 index 0000000..da34a8b --- /dev/null +++ b/Doc/Obsolete/Changes.txt @@ -0,0 +1,101 @@ +------------ +| | +| Changes | +| | +------------ + + + +Date: 3.18.01 +Files Changed: area.cc/h, square.h, character.cc/h, game.cc, item.cc/h +Functions Changed: area = (HandleCharacters, CIdealPopulation, CPopulation, GenerateNewMonsters), square = (CPopulation) character = (Consume), humanoid(humanoid, draw), perttu(perttu) game = (panel::Draw), can = (Consume, GetConsumeType), potion = (Consume) +Things Added: Population counter, Consume from the ground, More Monsters are created during the game, More info in Panel, dropping potions after drinking +Bugs Corrected: "Dying" in hunger in Wizard mode, no crashing while trying to consume empty cans or empty bottles, random humanoids, REALLY! +Comments: First change to this file! Yepee! +Author: Hex, Nuteater + + +Date: 3.20.01 +Files Changed: game.cc/h, charachter.h/cc +Functions Changed: game = (CCommandDescription), character = (ShowKeyLayout) +Things Added: ? key shows the current keylayout (which is currently unchangable) +Bugs Corrected: - +Comments: shard (also sherd) a broken piece of clay, glass, metal, etc: shard of pottery. (random word from a dictonary) +Author: Hex + +Date: 3.8.3 jF +Files Changed: All +Functions Changed: So many functions that I won't list them + because it would cause instant death to numerous + species in Afganistan +Things Added: Ligth & draw optimization +Bugs Corrected: Amount most likely negative +Comments: My brains are melting... Coke... +Author: DPV + +Date: 3.8.3 jF +Files Changed: character.cc +Functions Changed: character::Consume +Bugs Corrected: Worn/wielded items are no longer edible. +Comments: All your base are belong to us. + +Data: 10.8 jF +Files Changed: character.cc/character.h, game.cc, game.h +Functions Changed: golem = (kaikki), game = (BalancedCreateMonster, CalculateMonsterDifficulty(void), CurrentDifficulty(void) +Things Added: Golems' meleestrength depends on its material. And no longer are too hard monsters generated for stupid players.. +Comments: I hate this !!! I f***ing hate this!!1 aaaaaaarfg!!128312 +Author: Hex... + +Date: 15.8.3 jFEP +Files Changed: character.cc +Functions Changed: Consume +Things Added: Bulimia prohibited +Comments: Joku terveystieto 10 voi tasapainottaa noita ravintomääriä, + jos häiritsee... +Author: PMGR Castus Petrus I + +Data: 17.8.3 jF +Files Changed: character.cc/character.h, item.cc +Functions Changed: character::Move, scrollofteleport::Read +Things Added: - +Bugs Corrected: Teleport works even when overburdened +Comments: Not only does God play dice, he sometimes throws them + where they can't be found ---Stephen Hawking +Author: Hex + +Data: 17.8.3 jF (and maybe earlier) +Files Changed: terrain.cc/h, square.h, game.cc/h, character.cc +Functions Changed: something and something read below +Things Added: buggy lookmode and no longer will a character be able to eat + too much.... +Comments: I'm correcting Castus Petrus' code and I must state that he is not + worthy of his name. Lookmode is extremely buggy and most of all + he erased changes.txt for it. I was blind, but still I could make + this in to a release. Thank you PMGR Castus Petrus I. +Author: PMGR Castus Petrus I with *slight* help from Hex + + +Data: 19.8.3 jF +Files Changed: character.cc/h, game.cc/h, square.h +Functions Changed: (kaikki tekemisen lisäämisen liittyvät... characterin konstruktori + jne) ja character = (WhatToEngrave, Engrave, Move), square = (CEngraved, Engrave), game::(KeyIsOK, StringQuestion) +Things Added: Engraving and related functions. +Comments: I can't say anything. +Author: Hex + +Data: 23.8.3 jF +Files Changed: Almost all files. +Functions Changed: 75 % of all functions, so I won't list them because animals in + Afganistan etc etc etc. +Things Added: You are a [species name] thing for debuging (try playing with a golem) + and more inside class fuction calls. Golem's ArmorModifier is now based + on its material. +Comments: My back hurts. +Author: Hex + +Data: 24.8.3 +Files Changed: character::(GetAICommand, MoveRandomly), golem::MoveRandomly +Things Added: Golems write a message... +Comments: Brothers where art thou? +Author: Hex + diff --git a/Doc/Obsolete/Creation.rtf b/Doc/Obsolete/Creation.rtf new file mode 100644 index 0000000..9c237d2 --- /dev/null +++ b/Doc/Obsolete/Creation.rtf @@ -0,0 +1,77 @@ +{\rtf1\ansi\ansicpg1252\uc1\deff0\stshfdbch0\stshfloch0\stshfhich0\stshfbi0\deflang1035\deflangfe1035{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f36\froman\fcharset238\fprq2 Times New Roman CE;} +{\f37\froman\fcharset204\fprq2 Times New Roman Cyr;}{\f39\froman\fcharset161\fprq2 Times New Roman Greek;}{\f40\froman\fcharset162\fprq2 Times New Roman Tur;}{\f41\froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\f42\froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f43\froman\fcharset186\fprq2 Times New Roman Baltic;}{\f44\froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255; +\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0; +\red128\green128\blue128;\red192\green192\blue192;}{\stylesheet{\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs24\lang1035\langfe1035\cgrid\langnp1035\langfenp1035 \snext0 Normal;}{\*\cs10 \additive \ssemihidden +Default Paragraph Font;}{\*\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tscellwidthfts0\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv +\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs20\lang1024\langfe1024\cgrid\langnp1024\langfenp1024 \snext11 \ssemihidden Normal Table;}}{\*\rsidtbl \rsid13858233}{\*\generator Microsoft Word 10.0.4219;}{\info +{\author User name placeholder}{\operator User name placeholder}{\creatim\yr2004\mo12\dy14\hr20\min13}{\revtim\yr2004\mo12\dy14\hr20\min18}{\version2}{\edmins5}{\nofpages2}{\nofwords943}{\nofchars7647}{\*\company Your Company Name}{\nofcharsws8573} +{\vern16469}}\margl1134\margr1134\margt1417\margb1417 \widowctrl\ftnbj\aenddoc\hyphhotz425\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\hyphcaps0\horzdoc\dghspace120\dgvspace120\dghorigin1701\dgvorigin1984\dghshow0\dgvshow3 +\jcompress\viewkind4\viewscale100\nolnhtadjtbl\rsidroot13858233 \fet0\sectd \linex0\headery708\footery708\colsx708\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang +{\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang +{\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain +\ql \li0\ri0\nowidctlpar\faauto\rin0\lin0\itap0 \fs24\lang1035\langfe1035\cgrid\langnp1035\langfenp1035 {\fs28\lang2057\langfe1035\langnp2057\insrsid13858233\charrsid13858233 The Eight Eggs of Valpuri}{ +\fs20\lang2057\langfe1035\langnp2057\insrsid13858233\charrsid13858233 +\par +\par Rather difficult indeed is to converse about the beginning, since the Time itself is a product of the Creation. Only Valpuri knows for sure, and yet to this day he has not revealed all of the Great Mystery, even to other gods. +It is said that during the + First Age the Duchal Library of Omaktos contained the most detailed description of these events and the elven folk of Artami long singed the Song of Fate itself, but both took their secrets with them during their fall after the Incursion. +This story is mo +stly based on researches of Suetomit Sutsac, High Priest of Otoul'iv Ik-Omit and the current Grand Duke of the rebuild Omaktos, and much of it has been verified also by Surtep Eanar Sidnarg Sumixam Xefitnop, High Priest of the Great Frog Himself and the r +uler of the Empire of Attnam. +\par One thing is sure: In the beginning, there was Valpuri. Valpuri, the Creator and the Father of the Elder Gods. But He was, and is, actually much more than His physical form we all know. It is only one of His countless manife +stations. Sutsac theorises that it is even possible that this physical form was His own creation on the beginning of the First Day. More important, in his opinion, is His manifestation as the Void, also and better known as the Sea of Eternity. +This he says to be the true form of the Great Frog. The Sea is boundless unlike any physical element and before the Awakening contained nothing, absolutely nothing. +But, somehow, someway, It became sentient and became He. And this event gave birth to time, and the First Day began. +\par After the Awakening Valpuri looked around and saw only the nothingness of the Void. But, He wanted something other than nothing to become existent, because... Why? This is one of the biggest questionmarks on the Great Mystery. Sutsac canno +t answer this, nor does any other High Priest. All we can give are guesses; Since nothingness is quite boring, perhaps it was just for fun? But this supposes Valpuri would think like a man, lusting for enjoyment, and we can't trust that. +Perhaps He had a mind of an artist like Neno Sokokin, only creating because of the deligth of creation? Better guess, but still supposes Valpuri to think with a mind of a lesser being, which is not appropriate. +Sutsac trusts there is some greater meaning behind this, some yet not unraveled and perhaps never understandable to any lifeform lesser than the Great Frog Himself. +\par By the way, some people simply ignore this question, saying why to wonder about such, if Valpuri wouldn't have created anything, there would not be us to wonder why we weren't existent! +\par Even if the reason is yet to this day a complete mystery, the result is better known: The Elder Gods, the Okkammas and the Nenimhi, who were to shape the World. Okkammas, the Frog Gods, were the first to be born, during the Initial Day. +Each one He decided a task, layed an egg, helped it to hatch and gave the young deity a unique mind and soul, with fundamental tasks on the World. Over time, their minds have been altered, but this list shows what they were at first: +\par +\par The first Child born was Okkuu'lu T'trep, God of Life, 1/3 days after the Awakening. +\par The second was Otoul'iv Ik-Omit, God of Knowledge, 2/3 days after the Awakening. +\par The third was Okrepatri Vakk'uut, God of Change, at the end of the First Day after the Awakening. +\par +\par Next day he gave birth to the Nenimhi, the man-like gods. No one knows why he so drastically changed the nature and outlook of the gods between the first two days, perhaps because the Okkammas also achieved sentience during this day, and + wanted to have their mark on the work also. +Nonetheless while the Okkammas looked and thinked more like Valpuri, the Nenimhi were more like mankind mortals, humans, elves, dwarves and such of the nowaday world, and completed tasks of lesser value, but more specialized than the Okkammas. +As we know, this two-tree division of gods does not apply anymore, since the Chaos has altered everyone more or less (for example, a vigilant reader may find Vakk'uut's profession quite a bit different and Senner's complete +ly contrarian compared to what they are today). +\par The Nenimhi were: +\par +\par Nenitra Akirali, God of Machines, born 1 1/3 days after the Awakening. +\par Nenaria Si'kkieh, God of Money, born 1 2/3 days after the Awakening (twin of Senner). +\par Nenaria Senner, Goddess of Beauty, born 1 2/3 days after the Awakening (twin of Si'kkieh). +\par Neno Sokokin, Goddess of Music, born at the end of the Second day after the Awakening (twin of T'lepasev). +\par Neno T'lepasev, Goddess of Imagination, born at the end of the Second day after the Awakening (twin of Sokokin). +\par +\par It is said T'trep had great influence on the creation of Senner (since he already planned how the mortals would breed) and also on lesser degree to the Neno sisters since their tasks were advantageous for a happy life. +However he actually opposed the creation of Akirali and Si'kkieh for he foresaw they were not good for nature. +\par Ik-Omit influenced the making of Akirali (no need to say why), Sokokin (music is mathemacy, and mathemacy is science) and T'lepasev (source of inventions come from an imaginative mind) and taught them many things in their initial hours. +At first, Si'kkieh he didn't, for he saw not how he could help the goals of science, until this cunning young one wired up promises of future sponsoration, and gained what knowledge he needed. +Senner was the only one left without proper teaching, and became the least intelligent. Only thing she truly learnt was Sokokin's notes, for the Neno goddess needed a singer for her brilliant melodies. And how lovely + was Senner's voice those days... And how sad, that today it is forever lost. +\par Vakk'uut gave much ideas to Akirali, for machines must move and their state change in order to work, the quicker it happens the better. Si'kkieh he gave the thougth of circula +ting economy, which allowed those who wanted to collect as much they wanted, and those who didn't to spend as much as they could. T'lepasev he gave her everchanging mind which always opens paths to new portraits of imaginative events. +Particulary he enjoyed the songs of Sokokin, and made even some of that kind himself, for speed and changing are major parts of music. +\par Thus were the Eigth Elder Gods alive, the three Okkammas and the five Nenimhi, after the First Two Days of Creation. +\par +\par }{\fs28\lang2057\langfe1035\langnp2057\insrsid13858233\charrsid13858233 The Forming +\par }{\fs20\lang2057\langfe1035\langnp2057\insrsid13858233\charrsid13858233 +\par And the First Gods had been born and swimmed in the Sea of Eternity. The Great Task of Building the World was to begin. At first, T'lepasev imagined the Earth how it would be once completed. +She saw the mountains, rivers, birds of the sky, the sentient races that would dominate the lands, events that would shape the destiny of nations. +Since physical writable surfaces did not yet exist, she dictated her dream to Sokokin, whom which composed it as a song, the most eternal of all forms of information. And at the e +vening of the Third Day Senner was ready to sing the Song of Fate to the other Elders and the Great Frog Himself. And all they noded to the Neno as a sign of acceptance. And the Plan was complete. +\par In the beginning of the Fourth Day Otoul'iv wrote the Laws of Physics which would determine the ways of energy's and it's many forms' behavior. +It is said, especially on the earlier ages, that there is virtually an infinite amount of these Laws, just so that the mortal could never unravel every of them. +This world is complex indeed; many of the Laws we all know well, like the Law of Gravity or the Law of Friction, some are abstracter and needed only in very special occasions, like the Law of Relativity or the Law of Thermodynamics. +But as we speak with Suetomit about this, he shakes his head and shows his discustion towards this old-fashioned and worn-out set of theories. Today among the top-ranking scientific circles of Omaktos there is no true belief of + a law collection like this, but of one Universal Law, from which all others are derived from, and continuous effort is made for it's final discovery. Whatever the truth, it wasn't an easy job for Ik-Omit. +\par And in the evening of the Fourth Day Valpuri th +e Great Frog casted the Spell of Creation, the most suberb magical act of all of Time, and summoned the Law(s) into existence, to rule upon the World, to which he allocated a space from the Void, a flat cylinder, said to be 4242 miles wide and 256 miles i +n height, and placed it above His own Back, where it now resides. It is said the latter was done for otherwise "the Earth would sink into the Sea", whatever that would mean.}{\fs20\lang2057\langfe1035\langnp2057\insrsid13858233 +\par }{\fs20\lang2057\langfe1035\langnp2057\insrsid13858233\charrsid13858233 +\par }} \ No newline at end of file diff --git a/Doc/Obsolete/Danger.txt b/Doc/Obsolete/Danger.txt new file mode 100644 index 0000000..f2c8e61 --- /dev/null +++ b/Doc/Obsolete/Danger.txt @@ -0,0 +1,19 @@ +Iter Vehemens ad Necem combat danger levels. + +Random monsters are usually 1-2 levels less dangerous than the player. +Bosses are usually as dangerous as the whole player team when they are met. +Player starts from level 1. + +Level Danger points Description Monsters +0 0 harmless spiders, kobolds, donkeys, zombies +1 100 mostly harmless bats, rats, gibberlings, skeletons, dogs, leaf/cloth golems +2 500 goblins, cats, mommos, frogs, wolves, bone/wood/gold/glass golems, werewolves (h-form) +3 2500 imps, Bill's wills, marble/sulfur/stone golems +4 10000 dark knights*, werewolves (w-form), k-dwarves, iron golems +5 50000 Ivan, mistresses, mammoths, mithril/diamond/valpurium golems, angels +6 250000 Elpuri +7 1000000 Oree +8 5000000 Petrus +9 25000000 demigod - + +* Level 5 dark knigths might also be occasionally spawned. \ No newline at end of file diff --git a/Doc/Obsolete/Demon.txt b/Doc/Obsolete/Demon.txt new file mode 100644 index 0000000..78c252e --- /dev/null +++ b/Doc/Obsolete/Demon.txt @@ -0,0 +1,101 @@ +/* Kommentit merkitään tällä tavalla */ + +Level 9; +{ + /* Mitä näytetään alapalkissa */ + Description=OreeLair; + + /* Viesti, joka tulostetaan kun levelille ekan kerran tullaan */ + LevelMessage="\"Welcome to my lair, mortal! There's no escape now!\" Suddenly the stairs disappear."; + + /* Mikä on kentän pohjamaasto */ + FillSquare=floor, iron wall; + + /* Montako huonetta levelille luodaan */ + Rooms=1; + + /* Luodaanko kentässä pelin aikana satunnaisesti uusia monsuja */ + GenerateMonsters=false; + + /* Jos nämä on true, peli arpoo jonkun ruudun kentästä, pistää siihen portaat ja vetää sinne tunnelin jostain ovesta */ + /* Jos haluaa itse määrittää portaiden paikan (kuten nyt), nämä asetetaan falseksi ja paikka pitää asettaa itse kuten alla */ + GenerateUpStairs=false; + GenerateDownStairs=false; + + /* Montako satunnaista itemiä levelille luodaan */ + Items=0; + + /* Kirjoitetaan huoneelle oma skripti. Muuten siitä tulisi samanlainen tylsä huone kuten kaikki muutkin */ + + Room 0; + { + /* Huoneen koko */ + Size=7,7; + + /* Voidaanko luoda satunnainen alttari */ + AltarPossible=false; + + /* Huoneen seinien ja lattian maasto */ + WallSquare=floor, iron wall; + FloorSquare=parquet,empty; + + /* Luodaanko satunnainen ovi */ + GenerateDoor=false; + + /* Jos huone on pyhitetty jollekin jumalalle, niin kelle */ + /* Pyhitetyssä huoneessa ei voi rukoilla muille kuin "omistaja" jumalalle */ + /* Tämä on numeroilla toimiva tapa on tosin vanhentunut. Seuraavassa versiossa kirjoitetaan DivineOwner=erado; tms. */ + DivineOwner=17; + + /* Voidaanko luoda satunnainen suihkulähde */ + GenerateFountains=false; + + /* Asetetaan Oreen alle alttari. Alttari on pyhitetty samalle jumalalle kuin huonekin */ + + Square, Pos 3,1; + { + OTerrain = iron altar; + } + + /* Pelaaja tulee kenttään tässä. Ei välttämättä tarkoita, että ruudussa olisi portaat... */ + + Square, Pos 3,3; + { + IsUpStairs=true; + } + + /* ...vaan oikeat portaat voivatkin olla esim. tässä */ + + Square, Pos 3,5; + { + OTerrain=iron stairsup; + } + + /* Jos haluaa visuaalisen käsityksen siitä, mitä kenttään pistää, voi käyttää tällaista karttaa */ + /* Ihan samoin onnistuisi myös ItemMap, OverTerrainMap yms. */ + + CharacterMap + { + /* Kartan yläkulman koordinaatit ja koko */ + Pos=1,1; + Size=5,5; + + /* Tässä määritellään, mitä oliota vastaa mikäkin kartan kirjain */ + Types + { + O=oree; + B=darkknight; + G=blood golem; + } + } + { + /* Ja tässä on itse kartta. Peli osaa lukea sen tällaisenaan */ + + ..O.. + .BBB. + GB.BG + .BBB. + ..G.. + } + } +} \ No newline at end of file diff --git a/Doc/Obsolete/ExampleChanges.txt b/Doc/Obsolete/ExampleChanges.txt new file mode 100644 index 0000000..f5f0eec --- /dev/null +++ b/Doc/Obsolete/ExampleChanges.txt @@ -0,0 +1,16 @@ +------------ +| | +| Changes | +| | +------------ + + + +Date: 3.12.01 +Files Changed: character.cc/h +Functions Changed: Die, ChangeColorOfFace +Things Added: Character Blushes if complimented. +Bugs Corrected: Beards don't burn when you eat rubber. +Comments: Beard r0xxxx + + \ No newline at end of file diff --git a/Doc/Obsolete/Exp.txt b/Doc/Obsolete/Exp.txt new file mode 100644 index 0000000..41c6986 --- /dev/null +++ b/Doc/Obsolete/Exp.txt @@ -0,0 +1,37 @@ +IVAN action experience chart + + Strength Endurance Agility Perception NP AP +*Hit 50 0 25 0 -50 0 +*Scream 100 0 0 0 -100 0/+1000 +*Miss 0 0 25 0 -50 0 +*Dodge 0 0 50 25 - - +*Block 50 50 0 0 - - +*Move (base) 0 0 1 0 -10 0 +*Burden 1* 0 -1* 0 -1* + +*Overload 3* 0 -3* 0 -3* + +*Hungry -1* 0 0 0 - - +*Faint -3* 0 0 0 - - +*Open/Close 0 0 25 0 -10 -500 +*Up 50 0 0 0 -20 -1000 +*Down 0 0 25 0 -10 -1000 +*Regen 0 100 0 0 - - +*Dig 5* 0 -5* 0 -5* - +*Kick 25 0 50 0 -50 0 +*Look 0 0 0 1 - +900 +Rest/NOP/Sit +-couch 0 0 -2* 0 0 0 +-bed 0 0 -5* 0 0 0 +-normal 0 0 -1* 0 0 0 +*Throw 25 0 25 25 -50 0 +*Vomit -50 50 0 0 -200-400 0 +*Zap 0 0 0 50 0 +500 +*Chat 0 0 0 0 0 0 +*Dip 0 0 0 0 0 0 +*Offer 0 0 0 0 0 0 +*Pray 0 0 0 0 0 0 +Read 0 0 0 0 0 ? +-poor light 0 0 0 -25 0 ? +Wear 0 0 0 0 0 -5000 +Wield 0 0 0 0 0 0 + +* per tick \ No newline at end of file diff --git a/Doc/Obsolete/FEEL.txt b/Doc/Obsolete/FEEL.txt new file mode 100644 index 0000000..4af1553 --- /dev/null +++ b/Doc/Obsolete/FEEL.txt @@ -0,0 +1,66 @@ +---------------------------------------------------------------------------- + + Fatal Error Exception Library (FEEL) documentation + +---------------------------------------------------------------------------- + +Overview: +--------- + +A project-independent error handling interface, which acts as a base for +development environment and handles all exceptions and abortion/termination +calls generated by other projects. + +Classes: +-------- + +globalerrorhandler (error.h) +---------------------------- + +All errorhandling routines are gathered here as static members. Also +holds some backup routines which are installed when the program exits. +Win32 variant has also a backup copy of the window handle. + +Functions: + +Install() + + Installs the class. Normally this is called automatically + when the program starts, in main() or WinMain(). + +DeInstall() + + Deinstalls the error handler. Normally called automatically + when the program closes, in exit(). + +Abort(const char* Format, ...) + + Generates an error message using Format and other arguments + as printf() does, prints the message with a method most suitable + for the current OS and terminates the program. This is a rather + common function to be called, so a macro named ABORT is provided + to ensure convenient using. + + Example of usage: + + ABORT("Fatal Error: Detected a %d-eyed Enner Beast!", 3); + + Result: Program closes and a messagebox with text "Fatal Error: + Detected a 3-eyed Enner Beast!" is displayed. + +SetWindow(HWND* NewhWnd) + + Win32 only. Replaces the current window handle with NewhWnd. + +Globals: +-------- + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR + lpCmdLine, int nCmdShow) + + Win32 only. The starting procedure of the program. Not to be + called from anywhere else. + +---------------------------------------------------------------------------- + +End of document. \ No newline at end of file diff --git a/Doc/Obsolete/FELL.txt b/Doc/Obsolete/FELL.txt new file mode 100644 index 0000000..ee2db47 --- /dev/null +++ b/Doc/Obsolete/FELL.txt @@ -0,0 +1,128 @@ +---------------------------------------------------------------------------- + + Fatal Error Listing Library (FELL) documentation + +---------------------------------------------------------------------------- + +Overview: +--------- + +A simple system for handing different types of list displays, like the +inventory screen of the game or a high score list. + +Classes: +-------- + +highscore (hscore.h) +-------------------- + +A class that loads, saves and handles highscore files. + +highscore(std::string File = "HScore.dat") + + Creates the class and loads a highscore file with specified + filename. + +Add(long NewScore, std::string NewEntry) + + Adds a new score entry. The class automatically places the + current score to its appropriate position in the list and + resizes the list size to one hundred if its longer than that. + NewEntry should contain the name of player and the cause of + death/victory. + + Example of usage: + + highscore HScore; + HScore.Add(-1234, "Bill, died of bloodloss after Perttu ate his nuts"); + HScore.Save(); + HScore.Draw(); + + Result: Bill is (most likely) placed at the bottom of the score list + and the list is drawn onto the screen. + +Draw() + + Draws the highscore list and waits for keypress. + +Save(std::string File) + + Saves the highscore list. + +Load(std::string File) + + Loads a highscore list. Normally called only by the constructor. + +list (list.h) +------------- + +A simple list class containing list data and draw-to-screen functions. + +list() + + Creates an empty list with no topic. + +list(std::string Topic) + + Creates an empty list with topic Topic. + +AddString(std::string S) + + Adds an element to the list. + +AddDescription(std::string S) + + Adds a description to the list. Descriptions are drawn above other + list items and they cannot be selected by alphabet keys. Topic + counts as the first description. + +std::string GetString(ushort Index) + + Returns a previously added element of the list. + +ushort Length() + + Returns the total amount of previously added elements. + +dynarray* CString() + + Returns a pointer to the dynamic element array. + +ushort Draw(bool WillDrawNumbers) + + Draws the list onto the screen above the current DoubleBuffer in + 20 element sections and waits for keypress. WillDrawNumbers + determines whether individual alphabet letters are drawn before + every element. Pressing the key of element's letter causes the + function to terminate and return the index of the element selected, + or 0xFFFE if '-' was pressed, 0xFFFD if Escape was pressed and + 0xFFFF if an illegal key (not of any element nor '-' or Esc) was + pressed. + + Example of usage: + + list List("Choose your lord:"); + List.Add("Bill"); + List.Add("Valpuri"); + List.Add("Santa Claus"); + if(List.Draw() == 0) + { + ADD_MESSAGE("Suddenly the dungeon just crashes."); + game::CPlayer()->CHP(-98); + game::CPlayer()->CheckDeath("crashed horribly"); + } + + Result: Guess twice. + +DrawDescription() + + Draws the description strings. Only used by Draw(). + +Empty() + + Removes all previously added string elements from the list. + Descriptions, however, are not affected. + +---------------------------------------------------------------------------- + +End of document. \ No newline at end of file diff --git a/Doc/Obsolete/FeDX.txt b/Doc/Obsolete/FeDX.txt new file mode 100644 index 0000000..93be280 --- /dev/null +++ b/Doc/Obsolete/FeDX.txt @@ -0,0 +1,277 @@ +---------------------------------------------------------------------------- + + Fatal Error DirectX Graphics Library (FeDX) documentation + +---------------------------------------------------------------------------- + +Overview: +--------- + +Implements the Fatal Error Standard Graphics Interface (FESGI) using +Microsoft DirectX 8.0a. As FESGI requires, it handles both resolution and +colordepth changes as well as various bitmap handling routines. + +Classes: +-------- + +bitmap (bitmap.h) +----------------- + +The basic bitmap structure. Includes all data of the bitmap in a DirectX +surface, and various functions for its efficient usage. + +Functions: + +bitmap(const char* FileName) + + Loads contents from specified file and creates a bitmap to hold + them. The file must use pcx-style RLE-compression. + +bitmap(ushort XSize, ushort YSize) + + Creates a bitmap of specified size. Initial contents are + undefined, so you must fill it with data before using. + +bitmap(IDirectDrawSurface7* DDSurface) + + Special constructor used only for the DoubleBuffer bitmap in Win32. + +~bitmap(void) + + Destroys the bitmap object. + +Save(std::ofstream* SaveFile, ushort XPos, ushort YPos, ushort XSize, + ushort YSize) + + Saves a rectangle specified by parameters to SaveFile as an + uncompressed bytestream. + +Load(std::ifstream* SaveFile, ushort XPos, ushort YPos, ushort XSize, + ushort YSize) + + The reverse function of the preseding. + +Save(std::string FileName) + + Saves the entire bitmap as a dib-format bmp-file. + +PutPixel(ushort X, ushort Y, ushort Color) + + Quite self-explaining. Color must be in 16-bit format. + +ushort GetPixel(ushort X, ushort Y) + + The reverse function of the preseding. + +ClearToColor(ushort Color = 0) + + Fills the entire bitmap surface with the given color, which must + be in 16-bit format. + +ClearToColor(ushort DestX, ushort DestY, ushort SizeX, ushort SizeY, + ushort Color = 0) + + Fills a rectancle bound by the parameters with the given 16b-color. + +Blit(bitmap* Bitmap, ushort SourceX, ushort SourceY, ushort DestX, ushort + DestY, ushort Width, ushort Height, uchar Flags = 0) + + A bit block transfering procedure, which copies a source rectancle + from this to Bitmap. If Flags is zero, all pixels are overwritten + with their exact counterparts of the source. Otherwise if the + MIRROR (1) flag is set, the bitmap is mirrored (flipped over the + Y-axis), if FLIP (2) flag is set, it is flipped (over the X-axis), + if ROTATE_90 (4) flag is set, it is rotated by 90 degrees + clockwise. The Flags can be mixed in eight different combinations + (including zero) as wanted. + + Warning: ensure that borders of both bitmaps are respected, or + the application may crash, since there's no clipping. This applies + also to all sister Blits. + +Blit(bitmap* Bitmap, ushort SourceX, ushort SourceY, ushort DestX, ushort + DestY, ushort Width, ushort Height, ushort Luminance) + + As a luminated variant of the preseding, this function allows + lightning effects with the bit block transfer. Luminance must be in + the range of 0-511, and it is applied to all pixel color components + copied to the destination, according to the following formula: + ColorComponent += Luminance - 256. So 256 means the lightning level + is average, 0 that there's no light, and 511 is (almost) as brigth + as Valpuri Himself. Notice that there's no Flags, and so no + mirroring, flipping or rotating is possible with this method. + + Example of usage: + + bitmap Picture("Char.pcx"); + Picture.Blit(DOUBLEBUFFER, 92, 128, 0, 0, 16, 16, 400); + graphics::BlitDBToScreen(); + + Result: A highly radioactive-looking Enner Beast is displayed in + the top left corner of the display. + +MaskedBlit(bitmap* Bitmap, ushort SourceX, ushort SourceY, ushort DestX, + ushort DestY, ushort Width, ushort Height, uchar Flags = 0) + + Same as Blit of same parameters, except that transparent pixels + (those of brigth pink color 0xF81F) are not copied. This allows + non-rectangular sprites (characters, items etc.) to be displayed + on the screen on top of each other. + +MaskedBlit(bitmap* Bitmap, ushort SourceX, ushort SourceY, ushort DestX, + ushort DestY, ushort Width, ushort Height, ushort Luminance) + + Same as above, except that Luminance is supported as in the + corresponding Blit. Notice that there's no Flags, and so no + mirroring, flipping or rotating is possible with this method. + +BlitToDB(ushort SourceX, ushort SourceY, ushort DestX, ushort DestY, ushort + Width, ushort Height) +BlitToDB(ushort SourceX, ushort SourceY, ushort DestX, ushort DestY, ushort + Width, ushort Height, ushort Luminance) +MaskedBlitToDB(ushort SourceX, ushort SourceY, ushort DestX, ushort DestY, + ushort Width, ushort Height) +MaskedBlitToDB(ushort SourceX, ushort SourceY, ushort DestX, ushort DestY, + ushort Width, ushort Height, ushort Luminance) + + All of these are equalent to calling the most similiar blit or + maskedblit with DOUBLEBUFFER as the first argument. Note: + you shouldn't use these functions anymore. They are provided + just for backwards compability. + +FastBlit(bitmap* Bitmap) + + Just a raw fast blit to the destination bitmap without any luminance + or masking. The destination *must* be of same size, because although + FeDX doesn't crash if it isn't, the DOS-port of FESGI, FeVesa, does. + +FastMaskedBlit(bitmap* Bitmap) + + Same as above, except that transparent pixels are again ignored. + +Printf(bitmap* Font, ushort X, ushort Y, const char* Format, ...) + + Prints text to *this* bitmap, using Font as the font bitmap. + Font bitmap is a 246x216x16-bitmap containing ASCII-codes 32-255 + as little 8x8-subbitmaps, 16 in every line, with 8 pixels of + empty space between each character and same amount between + each line. The string printed is generated using Format as + printf() does, and placed according to given X & Y coordinates. + + Example of usage: + + bitmap Font("Font.pcx"); + DOUBLEBUFFER->Printf(&Font, 0, 0, "Oree has taken %d SWAT commandos + as gay-slaves.", 8); + graphics::BlitDBToScreen(); + + Result: "Oree has taken 8 SWAT commandos as gay-slaves." is printed + in the upper left corner of the screen. + +ReadFromDB(ushort X, ushort Y) + + Obsolete function that copies contents of DOUBLEBUFFER to this. + +WriteToDB(ushort X, ushort Y) + + Reverses the preseding. + +PrintfToDB(ushort X, ushort Y, const char* Format, ...) + + Just calls DOUBLEBUFFER->Printf(this, X, Y, Format, ...). + +Backup(ushort X = 0, ushort Y = 0, bool DestroySurface = true) + + Backups the contents of the DirectX surface into a bytestream + and releases the surface if DestroySurface is true. Used only by + graphics::SwitchMode(). + +Restore(ushort X = 0, ushort Y = 0, bool CreateSurface = true) + + Reverses the preseding. CreateSurface must be true if surface is + known to be destroyed. Used only by graphics::SwitchMode(). + +AttachSurface(IDirectDrawSurface7* DDSurface) + + Attaches the given surface to the bitmap. Used only by + graphics::SwitchMode(). + +ushort GetXSize() +ushort GetYSize() + + Self-explanatory. + +CDisplay (ddutil.h) and CSurface (ddutil.h) +------------------------------------------- + +Two wrapper classes copypasted from DirectX SDK samples :) More or less +temporary, so no documentation is available. + +graphics (graphics.h) +--------------------- + +A purely static class that contains functions dealing with the doublebuffer +and screen display mode. + +Functions: + +Init() +DeInit() + + Obsolete and do nothing. + +SetMode(HINSTANCE hInst, HWND* phWnd, const char* Title, ushort NewXRes, + ushort NewYRes, uchar NewColorDepth, bool FullScreen) + + Calls globalwindowhandler::Init() and sets up a new display mode. + The first parameter is provided by Windows to WinMain, second + by FEEL to Main, so don't lose them if you plan to change modes. + Title is the text displayed on top of the window and at the + toolbar. Other parameters are self-explanatory. + +BlitDBToScreen() + + Blits the DoubleBuffer to the screen surface, effectively to + be shown on the monitor display. + +ClearDBToColor(ushort Color = 0) + + Just calls DOUBLEBUFFER->ClearToColor(Color); + +ClearDBToColor(ushort X, ushort Y, ushort XSize, ushort YSize, + ushort Color = 0) + + A more sophisticated variant of the preseding. + +UpdateBounds() + + Updates the Window bounds stored in the CDisplay class. Used + only by globalwindowhandler::WndProc(), when the window is resized + or moved. + +SwitchMode() + + Switches between fullscreen and windowed modes. + +ushort CXRes(void) +ushort CYRes(void) + + Return the resolution width and height. + +bitmap* GetDoubleBuffer(void) + + Return a pointer to the DoubleBuffer bitmap, which is attached + to the BackBuffer DirectX surface. All data must be blitted + or otherwise drawn to this bitmap in order to be shown on + the screen. This is a common function, so the macro DOUBLEBUFFER + is provided to ease usage. + +CDisplay* GetDXDisplay(void) { return DXDisplay; } + + Returns the display structure. Used only by bitmap member functions. + +---------------------------------------------------------------------------- + +End of document. + + diff --git a/Doc/Obsolete/FeVesa.txt b/Doc/Obsolete/FeVesa.txt new file mode 100644 index 0000000..2d334ef --- /dev/null +++ b/Doc/Obsolete/FeVesa.txt @@ -0,0 +1,66 @@ +Fatal Error Vesa 2.0 graphics library, ohje: + +Linkitys: + +#include & option -lvesa + +Installaatio: + +Staattinen graphics initialisoidaan automaattisesti. + +void graphics::SetMode(USI Mode) +Initialisoi Vesa-näyttötilan Mode. + +Käyttö: + +bitmap::bitmap(USI XSize, USI YSize, USI BytesPerLine, ULI PhysicalAddress) +Luo bittikartan BytesPerLine * YSize osoitteeseen PhysicalAddress. Vain guruille. + +bitmap::bitmap(USI XSize, USI YSize, UC BitsPerPixel) +Varaa keosta bittikartan BytesPerLine * XSize * YSize. + +bitmap::bitmap(USI XSize, USI YSize) +Varaa keosta bittikartan XSize * YSize nykyisellä värikoolla. + +bitmap::bitmap(void) +Varaa keosta screenin specsit omaavan bittikartan. + +bitmap::bitmap(CC* FileName) +Lataa pcx-kuvan ja varaa sille keosta tilan. + +bitmap::~bitmap(void) +Tuhoaa bittikartan. Ajetaan deleten yhteydessä automaattisesti. + +void bitmap::PutPixel(USI X, USI Y, ULI Color) +Piirtää pikselin kohtaan X:Y (tällä hetkellä toimii vain 16b-tilassa). + +void bitmap::ClearToColor(USI Color) +Puhdistaa bittikartan väriin Color. + +void bitmap::Blit(bitmap* Target, USI SourceX, USI SourceY, USI DestX, USI DestY, + USI XSize, USI Ysize) +Blittaa bittikartaan Targettiin parametrien määrittemästä kohdasta parametrien +määrittelemään kohtaan parametrien määrittämällä koolla. + +void bitmap::MaskedBlit(bitmap* Target, USI SourceX, USI SourceY, USI DestX, USI DestY, + USI XSize, USI Ysize) +Sama kuin edellinen, mutta jättää värin 255:0:255 transparentiksi. + +void bitmap::MaskedBlit(bitmap* Target, USI SourceX, USI SourceY, USI DestX, USI DestY, + USI XSize, USI Ysize, USI Luminance) +Sama kuin edellinen, mutta lisää clipaten jokaiseen värikomponenttiin arvon 256-Luminance. + +void bitmap::Printf(USI X, USI Y, CC* Format, ...) +Analysoi formaattilauseen täydentäen sen optionaalisilla parametreilla ja maskedblittailee +graphics::Fontista asiaankuuluvat karakterit (koko 8x8) bitmappiin alkaen kohdasta X:Y. + +Tärkeitä definitioita: + +#define SCREEN graphics::GetScreenBitmap() //näyttöbitmappi +#define DOUBLEBUFFER graphics::GetDoubleBuffer() //tuplabufferi +#define FONT graphics::GetFont() //default fontti +#define XRES graphics::CXRes() //... +#define YRES graphics::CYRes() //... +#define BITSPERPIXEL graphics::CBitsPerPixel() //värimoodi käytännössä +#define BYTESPERLINE graphics::CBytesPerLine() //rivin tavupituus (guruille) +#define BUFFERSIZE graphics::CBufferSize() //bufferin koko (guruille) \ No newline at end of file diff --git a/Doc/Obsolete/FeWin.txt b/Doc/Obsolete/FeWin.txt new file mode 100644 index 0000000..a48dc76 --- /dev/null +++ b/Doc/Obsolete/FeWin.txt @@ -0,0 +1,43 @@ +---------------------------------------------------------------------------- + + Fatal Error Window Operator Library (FeWin) documentation + +---------------------------------------------------------------------------- + +Overview: +--------- + +A Win32 library designed to handle various window-bound routines, mainly +creating it and getting input through it. + +Classes: +-------- + +globalwindowhandler (whandler.h) +-------------------------------- + +All routines of the project are stored here as static members. + +Functions: + +LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM + wParam, LPARAM lParam) + + Windows calls this function every time it wants to say something + concerning the window, like when the user presses a key while it's + focused or resizes it and etc. Not to be called from anywhere + else! + +int GetKey() + + Waits for a keypress and return it once detected as an ASCII-code + like standard getkey() does. + +Init(HINSTANCE hInst, HWND* phWnd, const char* Title) + + Creates a window and initializes the keyhandler. Not to be called + from anywhere else than graphics::SetMode()! + +---------------------------------------------------------------------------- + +End of document. \ No newline at end of file diff --git a/Doc/Obsolete/Format.txt b/Doc/Obsolete/Format.txt new file mode 100644 index 0000000..94a0442 --- /dev/null +++ b/Doc/Obsolete/Format.txt @@ -0,0 +1,30 @@ +Humanoid.pcx:n uusi formaatti: + +Ruumiinosat tulee pystyriveille. Jokaisella rivillä on korkeintaan 32 kuvaa +ja jokaiselle ruumiinosatyypille on varattu neljä riviä. Vasemmassa reunassa +ovat päät, sen jälkeen tulee torsot, sitten kädet, sitten jalat, sitten +kilvet ja oikeimpana inhandspicit. + +Normaalisti tiedostoon piirretään vain karakterin oikea käsi ja jalka +(piirtäjästä katsottuna siis vasemmalla oleva). Se peilataan sitten +toisellekin puolelle. Näin siksi, että jos käsi/jalka on irronnut, sitä ei +piirretä. Jaloissa tämä koskee vain lahjetta, housujen yläosan piirrät +kokonaisena, koska muuten toisen jalan irrotessa strategiseen kohtaan +tulisi pystysuora raja. Jos karakterin raajat ovat epäsymmetriset, piirrä +molemmat niistä allekkain oikeina raajoina ja kerro meille, niin käytämme +vasempaan raajaan eri kuvaa kuin oikeaan. + +Ruumiinosat on materiaaliväritettyjä. Iho, aseiden terät ja kilvet on aina +m-väriä 1. Vaatteiden päävärit ja aseiden kädensijat on m-väriä 2. Useimmat +muut värit kuten hiukset, vaatteiden koristukset yms. on perusvärejä. Tämä +itseasiassa helpottaa uusien ruumiinosien lisäystä, koska esim. käsiä ei +yleensä tarvitse tehdä uudelleen, vaan useimmiten voidaan käyttää +standardikuvaa, jossa käsi on m-väriä 1 ja hiha m-väriä 2. + +CW ei pysty näyttämään tämän formaatin kuvia, eikä tule niin tekemäänkään. +Sen sijaan olen määrännyt toveri Heikin kehittelemään uutta +kuvankäsittelyohjelmaa nimeltä Ivan Graphics editOR eli IGOR. Tämän +ominaisuuksiin kuuluu mm. humanoidien kokoaminen näiden ohjeiden mukaan +sekä tehokas materiaaliväritysjärjestelmä. Tarvitsemme kuitenkin uuteen +formaattiin konvertoidun kuvan jo ennen IGOR:n valmistumista, joten se +tulee helpottamaan vasta uusien kuvien lisäystä. \ No newline at end of file diff --git a/Doc/Obsolete/Future.txt b/Doc/Obsolete/Future.txt new file mode 100644 index 0000000..6a2e44b --- /dev/null +++ b/Doc/Obsolete/Future.txt @@ -0,0 +1,34 @@ +Future plans: + +Version 0.301 alpha +Release time: within some weeks + +Goals: + +-bugfixes +-balancing issues +-ergonomic modifications +-DOS port (could be left to 0.31a if too difficult) +-some in-code documentation + +Version 0.31 alpha +Release time: before the end of the std::year + +Goals: + +-many new items and monsters +-block AI system +-renewed god system +-fixing the Great Surface Destruction Bug by remaking FeDX +-a second city that will act as a haven for chaotics +-completely comprehensive in-code documentation + +Version 0.32 alpha +Release time: winter 2002 + +Goals: + +-identify system +-genetic algorithms +-magic system +-random towns and dungeons (perhaps even *very* simple quests?) \ No newline at end of file diff --git a/Doc/Obsolete/GenePoints.txt b/Doc/Obsolete/GenePoints.txt new file mode 100644 index 0000000..69b1702 --- /dev/null +++ b/Doc/Obsolete/GenePoints.txt @@ -0,0 +1,14 @@ +Killing player: 1000 +Escaping dungeon: 25 + +NPs eaten: 0.3 * NP +Damaging players: 10 * damage +Surviving in the same level with player: 0.1 * Turns +Causing player's danger level to drop: --- +Increasing own danger value: --- + +1400 +464 +432 +423 +230 \ No newline at end of file diff --git a/Doc/Obsolete/Genetest.txt b/Doc/Obsolete/Genetest.txt new file mode 100644 index 0000000..f1b6e9a --- /dev/null +++ b/Doc/Obsolete/Genetest.txt @@ -0,0 +1,27 @@ +JOS pelaaja paikalla NIIN käy päälle + +JOS pelaaja ei paikalla mene satunnaiseen suuntaan + + +1(void) = vihollinen paikalla +2(x) = oma HP < x +3(void) = vihollinen vuotaa +4(x) = oma agility - vihollisen > x +5(void) = vihollinen vieressä +6(void) = vihollinen pitkänmatkan aseen kantamalla ja ok suunnassa + +a = liiku kohti vihollisen +b = liiku satunnaiseen suuntaan +c = peräänny +d = ammu pitkänmatkan ase + +& = ja +| = tai +! = not + +if +if !4(0)|5 then b +if 2(3) then a +if !4(0)&!5 then d +if !2(-3) then c +if 6 then a diff --git a/Doc/Obsolete/Goals 0-41.txt b/Doc/Obsolete/Goals 0-41.txt new file mode 100644 index 0000000..4a06151 --- /dev/null +++ b/Doc/Obsolete/Goals 0-41.txt @@ -0,0 +1,9 @@ +Confuse effects for scrolls. +Attaching each item to some god. +A* route code. +//Floating eye. +Verbalized danger to stethoscope info. +//Explosions should be stopped by walls. +//Different effects for breaking different wands. +//Removing asm. +//Removing MS-code. diff --git a/Doc/Obsolete/Goals.txt b/Doc/Obsolete/Goals.txt new file mode 100644 index 0000000..bab2fa5 --- /dev/null +++ b/Doc/Obsolete/Goals.txt @@ -0,0 +1,31 @@ +////*Pentagrammi menuun. +////*Monsujen väheneminen. +////Change material scroll. +////Syönnin keskeytys kun monsu tulee näköpiiriin. +////Dark frogeista plussaa kaaottisille jumalille. +////Kannibalismi. +////Ennerscream hajottamaan potioneita sekä maasta että inventorioista. +////(läheltä huutaminen voisi hajottaa myös plate maileja) +Pimeydelle vaikutuksia. +////Kickille vielä muutakin käyttöä = ovien potkiminen. +////Avatut cannit. +////%Asetaidot. +////%Tiimit. +////*Script. +//%*Attnam. +////*Player must not be the Only Son anymore. +////Garbage monsters. +////%*New material color system. +////Ääriviivat itemeille ja monsuille. +////Space should = esc in the inventory. +////Help should be organised. +////*Do you really want to pray to somebody. +////Pick-axejen sääteleminen (kesto, expat, valot). +////*Mellis abuse. +////%*Alpha blend. +////Rest until fully healed. +////Messagebuffer should be reordered. +////Messagebuffer should be saved. +%*DOS-portti. +DX lack warning. +////Last game should be highlighted in the high scores. \ No newline at end of file diff --git a/Doc/Obsolete/Gods.txt b/Doc/Obsolete/Gods.txt new file mode 100644 index 0000000..431c555 --- /dev/null +++ b/Doc/Obsolete/Gods.txt @@ -0,0 +1,82 @@ + + IVAN deity chart + + /----------------------------------------\ + | Valpurus | Atavus | Dulcis | + | creator | charity | love | + altruism | | | | + | Seges | Sophos | Verax | + | health | good magic | truth | + |----------------------------------------| + | Legifer | | Silva | + | law | | nature | + | | | | + | Loricatus | | Cleptia | + | machines | | crime | + |----------------------------------------| + | Mellis | Infuscor | Scabies | + | trade | dark magic | disease | + egoism | | | | + | Cruentus | Nefas | Mortifer | + | war | greed | death | + \----------------------------------------/ + + order chaos + +Perfect harmony: + +Two gods per alignment. +Eight males, eight females. +Each god has an arch enemy of opposite alignment. + +Enemies are: +Valpurus - Mortifer +Seges - Scabies +Atavus - Nefas +Sophos - Infuscor +Dulcis - Cruentus +Verax - Mellis +Legifer - Cleptia +Loricatus - Silva + +Working sets of worshipped gods (total 8 possibilities): + +Benevolent king/paladin (LG) set +Friends: Valpurus, Seges, Atavus, Sophos, Legifer, Loricatus +Neutrals: Dulcis, Verax, Mellis, Cruentus +Enemies: Silva, Cleptia, Infuscor, Nefas, Scabies, Mortifer + +Paragon of good/Santa Claus/GNU coder (NG) set +Friends: Valpurus, Seges, Atavus, Sophos, Dulcis, Verax +Neutrals: Legifer, Loricatus, Silva, Cleptia +Enemies: Mellis, Cruentus, Infuscor, Nefas, Scabies, Mortifer + +Robin Hood (CG) set +Friends: Atavus, Sophos, Dulcis, Verax, Silva, Cleptia +Neutrals: Valpurus, Seges, Scabies, Mortifer +Enemies: Legifer, Loricatus, Mellis, Cruentus, Infuscor, Nefas + +Judge/sheriff (LN) set +Friends: Valpurus, Seges, Legifer, Loricatus, Mellis, Cruentus +Neutrals: Atavus, Sophos, Infuscor, Nefas +Enemies: Dulcis, Verax, Silva, Cleptia, Scabies, Mortifer + +Druid/environmentalist/anarchist (CN) set +Friends: Dulcis, Verax, Silva, Cleptia, Scabies, Mortifer +Neutrals: Atavus, Sophos, Infuscor, Nefas +Enemies: Valpurus, Seges, Legifer, Loricatus, Mellis, Cruentus + +Merchant/politician/diplomat (LE) set +Friends: Legifer, Loricatus, Mellis, Cruentus, Infuscor, Nefas +Neutrals: Valpurus, Seges, Scabies, Mortifer +Enemies: Atavus, Sophos, Dulcis, Verax, Silva, Cleptia + +Evil warrior/necromancer (NE) set +Friends: Mellis, Cruentus, Infuscor, Nefas, Scabies, Mortifer +Neutrals: Legifer, Loricatus, Silva, Cleptia +Enemies: Valpurus, Seges, Atavus, Sophos, Dulcis, Verax + +Evil bandit/terrorist (CE) set +Friends: Silva, Cleptia, Infuscor, Nefas, Scabies, Mortifer +Neutrals: Dulcis, Verax, Mellis, Cruentus +Enemies: Valpurus, Seges, Atavus, Sophos, Legifer, Loricatus diff --git a/Doc/Obsolete/Graphics.txt b/Doc/Obsolete/Graphics.txt new file mode 100644 index 0000000..74cc297 --- /dev/null +++ b/Doc/Obsolete/Graphics.txt @@ -0,0 +1,52 @@ +hp - high priority +lp - low priority +// - done +(r) - rejected + +//Stethoscope. (hp) +(r) Village Elder. +//Thief. +//Black Mage. +//Grave digger. +//Short sword + broken picture. +//Dagger + broken picture. +(r) Halberd + broken picture. +//Hammer and sickle + broken pictures. +//Broken shield. +//Morning star + broken picture. +//Flail + broken picture. +//Quarterstaff + broken picture. +//Battle axe + broken picture. +//Scythe + broken picture. +//Bastard sword + broken picture. +//Skull. +//Elpuri's head is of wrong color. +//Broken chain mail. +//Jackals on gravel floor. +//Ring of teleportation + gravel floor. +//Smith. +//Anvil. (lp) +//Levitating ostrich. +//M-color for boots. (hp) +//Animation for mommos. +//Teleport. +//Golden Eagle Shirt's animation is really gay. +//Better helmet. +//Ring of poison resistance, triangular key on a gravel floor. +//Corpse of a black unicorn looks odd. +//Iron dagger on a gravel floor. +//Floating eye. +//Bunny, large bunny and mega-large bunny. +//Inhandspics of broken weapons. (lp) +//Visible invisible stalker. +//Vacuum cleaner. +//Lada. +//Web. + +//Fabrick belts and whips seem to not be clearly visible from a wooden parquet. (might have something to do with my red-green color blindness though) +//Small, medium and large chests (one is probably already done...) +//The picture in the menu should be cleaned from all compression related problems. (small color variation in the places) +//Keys are hardly visible on wooden parquet +//Broken axe is nearly invisible on gravel floor +//Steel halberd on a gravel floor. +//Steel scimitar on a gravel ground. diff --git a/Doc/Obsolete/Great Jobs.txt b/Doc/Obsolete/Great Jobs.txt new file mode 100644 index 0000000..d27ca24 --- /dev/null +++ b/Doc/Obsolete/Great Jobs.txt @@ -0,0 +1,25 @@ +The greatest tasks that need to be done, in order of priority: + +For version 0.301a: + +1. Gcc port. +2. In-code documentation. + +For version 0.310a: + +3. Block AI system and genetic AI algorithms. +4. Renewed god system. +5. Traps. +6. Identify system. +7. Player races and classes. + +For versions after that: + +8. Bodyparts. +9. Wishing parser. +10. Skill system. +11. Magic system. +12. Dungeon water tiles and swimming. +13. Wilderness encounters and other similiar events. +14. Random towns, dungeons and quests. +15. Intercontinental transportation. \ No newline at end of file diff --git a/Doc/Obsolete/Hexhit.txt b/Doc/Obsolete/Hexhit.txt new file mode 100644 index 0000000..3a8e47a --- /dev/null +++ b/Doc/Obsolete/Hexhit.txt @@ -0,0 +1,92 @@ +Material Hit Values: + +iron 70 +stone 60 +gravel 30 +wood 30 +schoolfood 20 +bananaflesh 10 +air 0 +default 0 + +Weapon Strength Base Values: + +sword 0,234375 * MHV +default 0 + +True Values: + +melee 25 +iron sword 16 +any other item 1 + +Weigth: + +iron sword 2800 to 3199 +can of bf / sf 170 +banana 150 +lump of bf / sf 150 +empty can 25 + +Size: + +lump of bf / sf 123 +iron sword 100 to 149 +banana 20 to 29 +empty can 7 +can of bf / sf 7 + +Strength: + +billswill 70 +others 20 + +Agility: + +billswill 90 +player 20 +others 10 + +Size: + +ennerbeast 700 3 +human 700 3 +player 700 3 +darkfrog 50 1 +billswill 20 1 + +Speed: + +billswill 251 to 349 -> 100 +player & can 151 to 249 -> 100 +all melee figthers 51 to 149 -> 51 to 100 +player & other than can -49 to 49 -> 0 to 49 + +Success: + +billswill 90 to 139 -> 90 to 100 +player 20 to 69 -> 20 to 69 +others 10 to 59 -> 10 to 59 + +Dodge: + +billswill vs. anything -198 to 60 -> 0 to 60 +player can vs. anything -169 to 79 -> 0 to 79 +player melee vs. anything -167 to 130 -> 0 to 100 +others vs. anything -157 to 140 -> 0 to 100 +player iron sword vs. anything -117 to 180 -> 0 to 100 + +Block: + +billswill vs. 3 0 to 498 +others vs. 3 0 to 398 +billswill vs. 1 0 to 298 +others vs. 1 0 to 198 + +HPLoss: + +billswill vs. anything 0 to 12 +player melee vs. anything 0 to 8 +others vs. anything 0 to 8 +player iron sword vs. anything 0 to 4 +player other vs. anything 0 \ No newline at end of file diff --git a/Doc/Obsolete/Hierarchy.gif b/Doc/Obsolete/Hierarchy.gif new file mode 100644 index 0000000..e8e17b0 Binary files /dev/null and b/Doc/Obsolete/Hierarchy.gif differ diff --git a/Doc/Obsolete/Include.txt b/Doc/Obsolete/Include.txt new file mode 100644 index 0000000..6b4faee --- /dev/null +++ b/Doc/Obsolete/Include.txt @@ -0,0 +1,162 @@ +---------------------------------------------------------------------------- + + Documentation of miscellaneous shared include files + +---------------------------------------------------------------------------- + +Overview: +--------- + +Located in IVAN/Include, these files contain some standard classes and +small, convenient inline routines that are commonly used in every IvenDev +project. + +Classes: +-------- + +dynarray +--------------------------------------------- + +A class template that was a kind of substite for vector when IvanDev +knew not how to use it :) Type is the type of one element, SizeType +is the type in which the size of the array is measured. + +Functions: + +dynarray() + + Creates a new, empty dynarray. + +dynarray(const dynarray* Array) + + Creates a new dynarray and copies its initial data from Array. + +~dynarray() + + Detroys dynarray and its elements. + +Type& Access(SizeType Index) + + Access element of Index. May crash if Index greater than or equal + to Length(). + +SizeType Length() + + Returns the current number of elements. + +Add(Type Element) + + Adds Element to the end of dynarray. + +Add(const dynarray* DynArray) + + Adds the entire contents of DynArray to the end of dynarray. + +Put(Type Element, SizeType Position) + + Makes Access[Position] henceforth return Element. All elements + beyound Position are pushed forward. May crash if Position + is greater than or equal to Length(). + +Type Remove(SizeType Index) + + Erases the Element of Index and returns it. All elements beyound + Index are pulled backwards. + +Type& operator [] (SizeType Index) + + Same as Access(). + +Type& operator << (Type& Element) + + Same as Add(). + +SizeType Search(Type Element) + + If an element equal to Element is found in the dynarray, returns + its index, otherwise returns -1 converted to SizeType. Type + must support the logical == operator. + +Resize(SizeType NewSize) + + Forces Length() henceforth return NewSize. All elements beyound + this length are erased. If Length() is less than NewSize, new + elements are filled with rubbish. + +vector (vector.h) +----------------- + +The basic two-dimensional vector class, i.e. a mathematical object with +scalar X and Y elements. + +Functions: + +vector() + + Constructs a vector with undefined elements. + +vector(ushort X, ushort Y) + + Constructs a vector with specified elements. + +vector operator + (const vector& H) +vector& operator += (const vector& H) +vector& operator = (const vector& H) +bool operator == (const vector& H) +bool operator != (const vector& H) + + Self-explanatory overloaded math operators. + +Globals: +-------- + +allocate.h: +---------- + +template Type** Alloc2D(ulong XSize, ulong YSize) + + Allocates a two-dimensional block of type Type and size of + XSize * YSize. The Block can be deleted via a simple delete [] + operator, but it cannot be resized without reallocation. + + Example of usage: + + int** Map = Alloc2D(3,4); + Map[2][3] = 666; + std::cout << Map[2][3] << std::endl; + delete [] Map; + + Result: "666" is output. + +femath.h: +---------- + +template Type GetHypotSquare(Type X, Type Y) + + Returns the hypotenuse of a rigth-angle triangle of short sides + X & Y raised to the power of two, i.e. X*X + Y*Y. + +strover.h: +---------- + +std::string operator+ (std::string String, const int& Int) + + Overloaded string operator that transforms numeric Int into + a string and adds it to String temporarily, returning the result. + +std::string& operator+= (std::string& String, const int& Int) + + Transforms numeric Int into a string and adds it to String + permanently, returning the result. + +std::ofstream& operator+= (std::ofstream& File, const std::string& String) + + Saves String into a File. + +std::string& operator-= (std::ifstream& File, std::string& String) + + Loads String from a File. + +---------------------------------------------------------------------------- + +End of document. \ No newline at end of file diff --git a/Doc/Obsolete/Item ideas II.txt b/Doc/Obsolete/Item ideas II.txt new file mode 100644 index 0000000..b787802 --- /dev/null +++ b/Doc/Obsolete/Item ideas II.txt @@ -0,0 +1,34 @@ +Misc: + +wand of petrification - has a chance of changing the material of every item and monster it hits to stone +is - these things are expensive but their amount is every now and then cut to half (mockery of Adom's si :) +ring of conflict +vodka bottle of plenty +knuckles + +Weapons: + +baseball bat +steam-powered flamethrower +machete + +Armor: + +cloak of displacement - when the owner is being hit, this occasionally displaces him +reflector shield - reverses the direction of beams +helm of ESP +sandals of Hermes - flying +amulet of improbability - causes odd improbabilities +spiked gauntlets +night vision googles - infravision +mutant armor - acid immunity, spills acid, polymorphs monsters around, permanent lycanthropy + parasitized + leprosy + +Other equipment: + +amulet of unchanging - stats never change, immunity to polymorph (juts like in Nethack) + +Inside jokes: + +Blessed Boxer-shorts of Protection from Elpuri +Holy Toesocks +Windows 95 Master CD diff --git a/Doc/Obsolete/Item ideas.txt b/Doc/Obsolete/Item ideas.txt new file mode 100644 index 0000000..ff7a26b --- /dev/null +++ b/Doc/Obsolete/Item ideas.txt @@ -0,0 +1,25 @@ +Misc: + +//horn of bravery - cures panic of friendly creatures +//horn of fear - causes panic to enemies + +Weapons: + +//hammer of thunderbolts - lightning animation, electricity damage +//poleaxe of unholy power - energy damage (renamed to Poleaxe named Mjolak) +//wormhole spear - teleports the enemy (renamed to Spear named Vermis) +//mace of hellfire - causes an explosion (renamed to Mace named Turox) +//whip of thievery - disarms the enemy +//staff of archmagi - randomly polymorphs/teleports/slows the enemy + +Armor: + +//gauntlets of strength - +x arm str +//gauntlets of dexterity - +x dexterity +//armor of great health - +x end +//helm of piercing perception - +x per +//helm of understanding - +x wisdom +//helm of innovation - +x intelligence +//helm of attractivity - +x charisma +//boots of kicking - more kicking damage +//girdle of carrying - +x% carrying strength diff --git a/Doc/Obsolete/Items.txt b/Doc/Obsolete/Items.txt new file mode 100644 index 0000000..2741364 --- /dev/null +++ b/Doc/Obsolete/Items.txt @@ -0,0 +1,37 @@ +Item chart for RL5 by Fatal Error Productions 2001: + +item default material size armor value chance volume weigth speed fm ws dam offerv +banana bananapeal, -flesh 20 - 50 30,150 195 72 25 221 0-1 1 +holy banana of Liukas Vipro bananapeal, -flesh 30 - 1 40,300 380 51 35 365 0-1 40 +lamp glass 30 - 10 850 2125 22 30 1383 0-1 1 +can iron, school food 10 - 100 50,600 1300 28 20 1612 0-2 1/2 +lump school food 10 - 0 600 900 33 10 212 0-1 1/2 +sword iron, iron, - 150 - 0 2500,100,0 20800 7 100 14422 5-9 1/5 +two-handed sword iron, iron, - 175 - 3 5500,250,0 46000 5 125 23979 8-16 1/5 +curved two-handed sword iron, iron, - 175 - 1 5500,250,0 46000 5 150 26268 9-17 1/4 +Valpuri's Justifier valpurium, valpurium, - 200 - 0 6500,300,0 20400 7 400 57131 20-35 1/4 +axe iron, wood, - 125 - 25 450,900,0 4050 16 150 7794 1-3 1/2 +pick-axe iron, wood, - 150 - 10 1000,1050,0 8525 11 150 11308 1-2 1/2 +spear iron, wood, - 200 - 25 150,1500,0 1950 23 200 6245 0-1 1 +plate mail iron 75 50 3 4000 32000 6 15 6928 2-5 1/2 +chain mail iron 75 70 10 2000 16000 8 15 4899 1-4 1/2 +Maakotka shirt cloth 60 20 0 1000 100 100 15 150 0-1 N/A +corpse - - - 0 - - - 20 - - 1/100 +potion glass, omle urine 30 - 10 50,1500 1625 25 40 1396 0-1 1/10 +banana peals banana peal 20 - 25 30 16 250 20 40 0-1 0 +broken bottle glass 10 - 25 50 125 89 60 474 0-1 0 +scroll of create monster parchment 30 - 25 200 120 91 30 232 0-1 5 +scroll of teleport parchment 30 - 25 200 120 91 30 232 0-1 5 +head flesh - - 0 1500 1800 24 10 520 0-1 1/10 +head of Elpuri elpuri's flesh 60 - 0 100000 240000 2 10 8485 0-2 N/A +nut flesh - - 0 - - - 10 - - 10 +left nut of Perttu human flesh 10 - 0 150 180 75 10 164 N/A +bone bone 50 - 50 2000 4000 16 50 2236 1/10 +poleaxe iron, wood, - 225 - 15 1500,3000,0 13500 9 100 11619 1/4 +spikedmace iron, wood, - 150 - 5 5000,3000,0 41500 5 75 17642 1/8 +H'taed Foneer Cse-ulb mirthril, iron, - 200 - 0 15000,8000,1000 140500 3 100 53009 1/4 +loaf pork 40 - 200 2000 2400 20 15 735 1/8 + +Speed = 1000 * sqrt(1 / Weight) +Weaponstrength = sqrt(Form Modifier * Material Strength * Weight); +Damage = (5 * Weaponstrength) / 100 \ No newline at end of file diff --git a/Doc/Obsolete/Items2.txt b/Doc/Obsolete/Items2.txt new file mode 100644 index 0000000..92257c0 --- /dev/null +++ b/Doc/Obsolete/Items2.txt @@ -0,0 +1,59 @@ +Name Prim. material HV FM Weight g WeaponStrength Damage AV Price + +nothing - - - - 1000 0-2 100 - + +banana bananaflesh 5 50 200 316 0-1 - ? +holybanana bananaflesh 5 200 2000 2000 1-3 - 1000 +holybanana mithril 5 200 3800 12329 9-16 - 1000 +lamp glass 30 30 2500 1500 1-2 - 50 +can iron 100 30 1150 1857 1-3 - ? +can iron 100 30 1000 1732 1-3 - ? +empty can iron 100 30 400 1095 0-2 - - +lump bananaflesh 5 15 600 212 0-1 - ? +lump schoolfood 5 15 750 237 0-1 - ? +*longsword iron 100 150 20000 17321 12-22 - 259 +*twohandedsword iron 100 175 46000 28372 21-36 - 496 +*twohandedsword mithril 200 175 29500 32132 24-41 - 1124 +*curvedtwohandedsword iron 100 200 46000 30331 22-38 - 606 +*curvedtwohandedsword mithril 200 200 29500 34132 25-43 - 1347 +*curvedtwohandedsword valpurium 200 200 18500 38470 28-49 - 3077 +valpurijustifier valpurium 400 400 21000 57965 43-73 - 9274 +*axe iron 100 150 12000 13416 10-17 - 189 +*axe mithril 200 150 7800 15297 11-20 - 458 +pickaxe iron 100 100 13000 11401 8-15 - 113 +pickaxe mithril 200 100 8500 13038 9-17 - 260 +*spear iron 100 200 2400 6928 5-9 - 138 +*spear mithril 200 200 1800 8485 6-11 - 339 +platemail iron 100 20 32000 8000 6-11 50 1600 +platemail mithril 200 20 20000 8944 6-12 37 3948 +chainmail iron 100 20 16000 5656 4-8 70 583 +chainmail mithril 200 20 10000 6324 4-8 61 881 +maakotkashirt cloth 5 20 100 100 0-1 10 200000 +elpuriflesh corpse elpuriflesh 30 15 666000 17311 12-22 - ? +mithril corpse mithril 30 15 1387500 64517 48-81 - ? +valpurium corpse valpurium 400 15 300000 42426 31-54 - ? +empty potion glass 30 40 150 424 0-1 - - +Omle urine potion glass 30 40 1650 1407 1-2 - ? +bananapeals bananapeal 10 70 20 118 0-1 - - +brokenbottle glass 30 100 150 670 0-1 - - +scroll parchment 15 40 150 300 0-1 - 20-2000 +head humanflesh 10 20 2400 692 0-1 - - +headofelpuri elpuriflesh 30 20 60000 6000 4-8 - - +nut humanflesh 10 20 30 77 0-1 - - +leftnutofperttu humanflesh 10 20 600 346 0-1 - - +abone bone 30 70 3000 2509 1-4 - - +*poleaxe iron 100 150 21000 17748 13-23 - 266 +*poleaxe mithril 200 150 13500 20124 15-26 - 603 +*spikedmace iron 100 125 41000 22638 16-29 - 282 +*spikedmace mithril 200 125 26000 25495 19-32 - 637 +*neercseulb mithril 200 150 93000 52820 39-67 - 1584 +loaf pork/beef 10 30 600 489 0-1 - ? +cheapnutcopy glass 30 20 1250 866 0-2 - 500 +wand glass 30 80 250 774 0-1 - 500 +wand marble 50 80 300 1095 0-2 - 500 +arrow wood 20 50 50 273 0-1 - ? +headofennerbeast ennerbeastflesh 30 20 9000 2323 1-3 - 100 +brokenlamp glass 30 80 2500 2449 1-4 - - +avatarofvalpuri valpurium 400 20 750 2449 1-4 - - +brokenplatemail iron 100 30 32000 9797 7-13 - - +bow wood 20 40 2000 1549 1-2 - ? \ No newline at end of file diff --git a/Doc/Obsolete/Jumal-auta.txt b/Doc/Obsolete/Jumal-auta.txt new file mode 100644 index 0000000..83a7033 --- /dev/null +++ b/Doc/Obsolete/Jumal-auta.txt @@ -0,0 +1,23 @@ +******** +L++King of Gods, Valpuri the Great Frog + +L+ Sd'Lavrot Sun-il, light god of paladins and holy fire. +L+ Ikk'upu Luojäh'yp, light god of gifts. +L Neno Sokokin, light goddess of music. +L Neno T'lepasev, light goddess of prophecies and dreams. +L- Otou Livik-omit, light god of knowledge, wisdom and understanding. + +******** +N= Okkuu'lu T'trep, Father Nature. +N= Nenitra Akirali, god of fire, machines and weaponry. +N- Nenaria Si'kkieh, god of money, trade and politics. +N- Okrepatri Vakkuut, god of assasins, thieves and other secretive activities. + +******** +C Nenaria Senner, chaos god of pain, misery and annoying noise. +C Reml Labevets, chaos god of greed and forbidden pleasures. +C Irasse Vukkris, chaos goddess of mutations and deseases. +C- Swodniwt-fo Sorcim, chaos goddess of wrong knowledge and vile magic. +C- Rerolp Xetenretni, chaos god of war and blood. + +C--Destroyer of worlds, Seta Gl'lib \ No newline at end of file diff --git a/Doc/Obsolete/Kivipaperisakset.txt b/Doc/Obsolete/Kivipaperisakset.txt new file mode 100644 index 0000000..4d7db76 --- /dev/null +++ b/Doc/Obsolete/Kivipaperisakset.txt @@ -0,0 +1,16 @@ +IVAN taisteluturnaus, osanottajat: + +A: keskiverto osuminen, kova lämä, tosi kova armori, mutta pää suojaton +B: tosi huono osuminen, tosi kova lämä, keskiverto armori, ei heikkoja kohtia +C: tosi kova osuminen, huono lämä, kova armori, ei heikkoja kohtia + +A tekee vahinkoa B:hen, mutta B ei pysty läpäisemään A:n armoria eikä osu päähän. +-> A voittaa B:n. + +B tekee vahinkoa C:hen, mutta C ei pysty läpäisemään B:n armoria. +-> B voittaa C:n. + +A ei pysty läpäisemään C:n armoria, eikä C A:n. C kuitenkin helposti osuu A:ta tappavasti päähän. +-> C voittaa A:n. + +Kenellä on oltava korkein danger level? \ No newline at end of file diff --git a/Doc/Obsolete/LibTest.txt b/Doc/Obsolete/LibTest.txt new file mode 100644 index 0000000..c1fad10 --- /dev/null +++ b/Doc/Obsolete/LibTest.txt @@ -0,0 +1,29 @@ +---------------------------------------------------------------------------- + + Fatal Error Library Test (LibTest) documentation + +---------------------------------------------------------------------------- + +Overview: +--------- + +A low-level program designed to view the capabilities of project libraries +and to detect possible bugs in them with ease. + +Classes: +-------- + +None. + +Globals: +-------- + +int Main(HINSTANCE hInst, HINSTANCE hPrevInst, HWND* hWnd, LPSTR pCmdLine, + int nCmdShow) + + The only function of the project, called by FEEL's WinMain. All + testing code should be placed here. + +---------------------------------------------------------------------------- + +End of document. \ No newline at end of file diff --git a/Doc/Obsolete/LinesOld.txt b/Doc/Obsolete/LinesOld.txt new file mode 100644 index 0000000..8334e4a --- /dev/null +++ b/Doc/Obsolete/LinesOld.txt @@ -0,0 +1,17 @@ +RL daily build 04.05.1999 1262 +RL3 daily build 28.07.1999 1425 +RLPMM 0.100 alpha 10.12.1999 3261 +RLPMM 0.190 alpha 24.12.1999 4070 +RLPMM 0.191 alpha 26.12.1999 4175 +RLPMM 0.192 alpha 26.01.2000 4397 +RLPMM 0.193 alpha 20.02.2000 5139 +RL5 0.200 alpha 11.02.2001 7129 +RL5 0.210 alpha 25.03.2001 8876 +RL5 0.220 alpha 23.04.2001 10567 +RL5 daily build 27.04.2001 10733 +IVAN 0.230 alpha 01.05.2001 12086 +IVAN 0.240 alpha 27.07.2001 14547 +IVAN 0.300 alpha 20.11.2001 25001 +IVAN 0.301 alpha 10.12.2001 26583 +IVAN 0.310 alpha 04.02.2002 31924 +IVAN 0.311 alpha 25.02.2002 32209 \ No newline at end of file diff --git a/Doc/Obsolete/Main.txt b/Doc/Obsolete/Main.txt new file mode 100644 index 0000000..a1f8fae --- /dev/null +++ b/Doc/Obsolete/Main.txt @@ -0,0 +1,2477 @@ +---------------------------------------------------------------------------- + + Iter Vehemens ad Necem main executable source documentation + +---------------------------------------------------------------------------- + +Classes: +-------- + +area (area.h) +------------- + +Basic container of squares. In addition to square data, this hold FlagMap, +a 2D-map containing flags used for level generation and Memorized, a bitmap +with all memorized terrain blitted here for safekeeping. Base class for level +and worldmap. + +Functions: + +area(ushort XSize, ushort YSize) + + Creates a new area of given size. The squares are not initialized + in any way, however. + +~area() + + Destroys the area and its components. + +Save(std::ofstream* SaveFile) + + Saves the area and its contents to a file as an uncompressed + bytesream. + +area(std::ifstream* SaveFile) + + Creates a new area and loads its contents from a file. + +RemoveCharacter(vector Pos) + + Removes a character from the square pointed by Pos. + +AddCharacter(vector Pos, character* Guy) + + Adds Guy to square in Pos. + +vector RandomSquare(const bool Walkablility) + + Returns coordinates of a random square with overterrain + of specified Walkability. Crashes horribly if one is not found. + +ushort CFlag(const vector Pos) + + Return contents of FlagMap in position Pos. Used mainly + by level generation code. + +square* operator [] (vector Pos) +square* CSquare(vector Pos) + + Returns square in position Pos. + +ushort CXSize() +ushort CYSize() + + Return the width and height of the area map. + +bitmap* CMemorized() + + Return a pointer to the Memorized bitmap. + +character (char.h) +------------------ + +The base class for all character types, derived from object. + +Functions: + +character(material** Material, vector BitmapPos, ushort Size, + ushort Agility, ushort Strength, ushort Endurance, + ushort Perception, uchar Relations); + + Constructs the character. Material must point to a pointer to the + correct material. BitmapPos is discarded. Relations must be in + the range either HOSTILE (0), NEUTRAL (1) or FRIEND (2), for + it determines the initial relation to the player character. + Note: "character" is an abstract class. You must call this + constructor from derived concrete classes only! + +~character() + + Destructs the character. + +character(std::ifstream* SaveFile, ushort MaterialQuantity = 1) + + Loads a character from a savefile. MaterialQuantity is discarded. + +DrawToTileBuffer() + + Draws the character to igraph's TileBuffer, from where it's + blitted to DOUBLEBUFFER once time is rigth. + +Act() + + The basic command for character to do something. Called every + tick. + +bool Hit(character* Enemy) + + Commands the character to hit someone. Normally called only by + TryMove(), when the character tries to move over someone. + Return false if hit was canceled by some reason. + +uchar TakeHit(ushort Speed, short Success, float WeaponStrength, + character* Enemy) + + Called by Hit(). This determines the success of the hit and + damage caused based on parameters and characters' attributes. + Returns either HAS_HIT (0), HAS_BLOCKED (1), HAS_DODGED (2) or + HAS_DIED (3). + +bool Consume() + + Called by GetPlayerCommand(), asks the player what he wants + to consume and such. Returns false if action was cancelled. + +Hunger() + + Called by Act(), this function handles nutrition consuming, + "You are getting hungry." messages and starvation death. + +bool TryMove(vector MoveTo) + + Tries to move somewhere or hit something. Returns false + on cancel. + +bool Drop() + + Displays the drop dialog. Returns false if nothing was dropped. + +bool ConsumeItem(int ToBeEaten, stack* ItemsStack) + + Tries to consume an item. Called by Consume(). False means cancel. + +Regenerate() + + Called by Act(), this handles turnly HP regeneration. + +Move(vector MoveTo, bool TeleportMove = false) + + An actual move, this handles lightning and LOS changes and + replacing the character. However this does nothing if + the character is overburdened and TeleportMove is false. + +bool ShowInventory() + + Shows the inventory list. Always returns false. + +bool PickUp() + + Shows the pickup dialog. Returns false on cancel. + +bool Quit() + + Asks whether the player wishes to quit. If no, returns false. + If yes, exits to main menu. + +bool Wield() + + Shows the wield dialog. Returns false on cancel. + +Die() + + Drops all items, creates a corpse of the character, possibly + updates lights, sends the character to game::Hell and exits + if this == Player and no cheats prevent this. Does nothing + if character is already dead. + +bool OpenItem() + + Shows the open item dialog. Returns false on cancel. + +ReceiveSound(char* Pointer, short Success, float ScreamStrength) + + Adds string pointed by Pointer to messages if this == Player + and causes appropriate damage to the character based on the + parameters. + +item* CWielded() + + Return pointer to currently wielded item or 0 if character is in + melee mode. + +stack* CStack() + + Return pointer to the character's inventory stack. + +ushort CEmitation() + + Calculates the character's light emulation in range 0-511. + +vector CPos() + + Returns the position of the square under the character. If such + doesn't exist, crashes terribly. + +bool CHasActed() + + Has Act() of this character been called during this tick? + +ushort CStrength() +ushort CAgility() +ushort CEndurance() +ushort CPerception() +ushort CSize() + + Returns the attribute in question. + +short CHP() +long CNP() + + Returns Hit Points and Nutrition Points (how far the character is + from starvation). + +SSquareUnder(square* Square) + + Set SquareUnder to Square. + +SHasActed(bool HA) + + HasActed determines whether the character has acted during this + tick. This sets it to HA. + +bool WearArmor() + + Shows the wear armor dialog. Return false if not applicable + to the class or cancellation was detected. + +item* CTorsoArmor() + + Return the item worn over torso, or 0 for nothing. + +bool ConsumeItemType(uchar Type) + + Returns whether this individual character can consume an item + of consume type Type. (like MEAT etc.) + +ReceiveBulimiaDamage() + + Calculates an NP limit based on character's size and causes + overeating damage based on how much NP value is over it. + +ReceiveNutrition(long SizeOfEffect) +ReceiveFireDamage(long SizeOfEffect) +ReceiveSchoolFoodEffect(long) +ReceiveOmleUrineEffect(long) +ReceivePepsiEffect(long SizeOfEffect) +Darkness(long SizeOfEffect) + + Various effect functions called from material::EatEffect(). + +uchar CRelations() + + Returns current relation towards the player character, + either HOSTILE (0), NEUTRAL (1) or FRIEND (2). + +AddBlockMessage(character* Enemy) + + Adds a message that Enemy has blocked the this character's attack, + i.e. hit was successful but did zero damage. + +AddDodgeMessage(character* Enemy) + + Adds a message explaining that Enemy has dodged this character's + attack, i.e. the character missed and did zero damage. + +AddHitMessage(character* Enemy, const bool Critical = false) + + Adds a message informing the that this character has hit the + Enemy succesfully and did a positive amount of damage. + Critical must determine whether the hit was critical (double + damage, automatic hit) or not. + +uchar GetSex() + + Returns whether character is MALE (1) of FEMALE (2), something + UNDEFINED (0), or something between the three like TRANSSEXUAL (3). + +BeTalkedTo(character* Talker) + + Triggers an event tied to conversation between this character and + Talker. + +bool Talk() + + The character is asked whom he/she/it wants to talk to and + perhaps calls BeTalkedTo. Called by GetPlayerCommand. False + as return means cancel. + +bool GoUp() +bool GoDown() + + Functions tied to '<' and '>'. False is returned if level + doesn't change. + +bool Open() +bool Close() + + The character is asked what he/she/it wants to open or + close. Called by GetPlayerCommand. False as return means cancel. + +bool NOP() + + The player rests one turn, reducing nutrition consuming. + Always returns true. + +ushort CalculateArmorModifier() + + Return a percentage number between 1-100 specifying how much damage + the character receives from hits. + +ApplyExperience() + + Called by Act(). Checks whether the character has gained/lost + enough attributal experience to alter primary attributes, + and adjusts them if needed. + +bool HasHeadOfElpuri() +bool HasPerttusNut() +bool HasMaakotkaShirt() + + Self-explanatory. + +bool Save() + + Asks whether the character wants to save and quit. Called by + GetPlayerCommand. False as return means cancel. + +bool Read() + + Shows the read dialog. False as return means cancel. + +bool ReadItem(int ToBeRead, stack* ItemsStack) + + Called by bool Read(). Tries to read an item. Returns false on + unsuccess. + +uchar GetBurdenState(ulong Mass = 0) + + Returns either UNBURDENED (3), BURDENED (2), STRESSED (1), or + OVERLOADED (0) based on Mass and character's strength. If + Mass is zero, uses character's current inventory weigth + instead. + +bool Dip() + + Shows the dip dialog. Returns false on cancel. + +Save(std::ofstream* SaveFile) + + Saves the character's data to SaveFile. + +bool WizardMode() +bool RaiseStats() +bool LowerStats() +bool SeeWholeMap() +bool WalkThroughWalls() + + Functions tied to corresponding Wizard Mode keys. False + means no effect took place. + +bool IncreaseGamma() +bool DecreaseGamma() +bool IncreaseSoftGamma() +bool DecreaseSoftGamma() + + Self-explanatory. Called by GetPlayerCommand. Always return + false. + +float CWeaponStrength() +float CAttackStrength() + + Returns the weaponstrength of melee attack or wielded item. + Functions are equal which is insane. + +bool ShowKeyLayout() +bool Look() + + Functions tied to '?' and 'l'. Always return false. + +long CStrengthExperience() +long CEnduranceExperience() +long CAgilityExperience() +long CPerceptionExperience() +ushort CRegenerationCounter() +square* CSquareUnder() +levelsquare* CLevelSquareUnder() +long CAP() +bool CFainted() +long CAPsToBeEaten() + + Return their corresponding data fields. + +float CDifficulty() + + Calculates the current Difficulty of the player, used by monster + generation code. + +bool Engrave(std::string What) + + Engraves What to the square under. Crashes horribly if one doesn't + exits. Returns true without exception. + +bool WhatToEngrave() + + Function tied to 'E'. Always returns false which is terribly wrong. + +MoveRandomly() + + AI function that moves the character randomly to any available + directions that aren't blocked, or alternatively sits still. + +SWielded(item* Something) + +SMaterial(ushort Where, material* What) + +SHP(short What) +SStrengthExperience(long What) +SAgilityExperience(long What) +SEnduranceExperience(long What) +SPerceptionExperience(long What) +SAP(long What) +SFainted(bool What) +SNP(long What) +SRelations(uchar What) +SStrength(ushort What) +SEndurance(ushort What) +SAgility(ushort What) +SPerception(ushort What) +SRegenerationCounter(long What) +SConsumingCurrently(ushort What) +SAPsToBeEaten(long What) + + Set their data fields to What. + +bool TestForPickup(item* ToBeTested) + + AI function that tests whether the character would become burdened + or worse when picking ToBeTested up. + +bool CanWield() +bool CanWear() + + Self-explanatory. + +bool WearItem(item* ToBeWorn) + + Tries to wear the item ToBeWorn. Returns false if unsuccessful. + +bool OpenPos(vector APos) + + Called by bool Open(), this tries to open the overterrain in APos. + False means he/she/it cannot or wants not. + +bool Pray() + + Shows the pray dialog. False is returned on cancel. + +SpillBlood(uchar HowMuch) + + Spills blood on SquareUnder. + +HealFully(character* ToBeHealed) + + AI function that heals ToBeHealed fully, if the character can. + +bool Kick() + + Function tied to 'k'. Returns false if illegal key was pressed + when asking direction. + +bool ScreenShot() + + Saves DOUBLEBUFFER to a dib-format bmp-file "Scrshot.bmp". + Returns false. + +bool Offer() + + Shows the offer dialog. Returns false on cancellation, + sacrifice rejection and not doing this on an altar. + +ushort LOSRange() + + Returns how far the character can see, in tiles, based on + perception. + +ushort LOSRangeLevelSquare() + + Returns the square of the preseding. + +long Score() + + Calculates the score of the player character. + +long AddScoreEntry(std::string Description, float Multiplier = 1) + + Adds the player character to high score list. Description must + contain the cause of death. Score is multiplied by Multiplier, + which is used mainly on victorious games. + +bool CheckDeath(std::string Msg) + + Kills the character if HP is low enough, and calls AddScoreEntry() + if this == game::CPlayer(). Returns true on death, false otherwise. + +ulong Danger() + + Returns the danger level of the monster, used by monster generation + code. + +bool Charmable() + + Returns whether the character can be charmed and turned to NEUTRAL + by MP3s and etc. + +bool CheckBulimia() + + Returns whether the character has already surpassed his/her/its + NP limit, based on size. + +bool CheckIfConsumable(ushort Index) + + Checks whether an item in character's inventory of index Index + is consumable. + +bool DrawMessageHistory() + + Self-explanatory. Returns false. + +bool Throw() + + Shows the throw dialog. False means cancellation. + +bool ThrowItem(uchar Direction, item* ToBeThrown) + + Throws an item to Direction (0-7, index of direction vector). + Returns false if the item did not move anywhere for some reason. + +HasBeenHitByItem(item* Thingy, float Speed, bool CanBeSeen) + + Called by item::HitCharacter(). Calculates damage the Thingy caused + and adds a message of the event if its position CanBeSeen. + +bool Catches(item* Thingy, float, bool CanBeSeen) + + Called by item::HitCharacter(). The character tries to catch a + flying item, like a dog catching a bone. Returns true if + successful. + +bool DodgesFlyingItem(item*, float Speed, bool) + + Called by item::HitCharacter(). Returns whether the character + can dodge an item flying at Speed. + +ulong CBloodColor() + + Returns the color of character's blood. + +ContinueEating() + + Called by Act every tick the character is eating. Handles + stopping consume after too much time etc. + +StopEating() + + Stops eating something. Called by ContinueEating(). + +Vomit(ushort HowMuch) + + Forces the character to vomit as much as HowMuch. + +character* Clone() + + Creates a clone of the character and returns a pointer to it. + Used by character generation system. + +character* Load(std::ifstream* SaveFile, ushort MaterialQuantity = 1) + + Creates a character of this type and loads its data fields + from SaveFile via character(std::ifstream* SaveFile, + ushort MaterialQuantity). MaterialQuantity is discarded. + +ushort Possibility() + + Returns whether it is possible for the character to appear + randomly in the dungeon. Zero means not, non-zero yes. + +bool Apply() + + Shows the apply dialog. + +GetPlayerCommand() + + Called by Act, this waits for the user's proper keypress and calls + the tied function, which returns either true or false. If true, + function returns, if false, it waits for another command. + +GetAICommand() + + Basic AI function run each turn, called by Act. + +Charge(character* Target) + + Commands the character to move towards the target or hit it + if they are adjacent to each other. + +float GetMeleeStrength() + + Returns weaponstrength of hands, teeth, pepsi vomit, etc. + that is used if nothing is wielded. + +HostileAICommand() +NeutralAICommand() +FriendAICommand() + + GetAICommand() calls the one associated with the current relation + to player character. Call things like Charge or HealFully etc. + +std::string ThirdPersonWeaponHitVerb(const bool Critical) +std::string ThirdPersonMeleeHitVerb(const bool Critical) +std::string FirstPersonHitVerb(character*, const bool Critical) +std::string AICombatHitVerb(character*, const bool Critical) + + Called by AddHitMessage as appropriate. See that function body + for more information. + +std::string NormalFirstPersonHitVerb(const bool Critical) +std::string NormalThirdPersonHitVerb(const bool Critical) +std::string FirstPersonBiteVerb(const bool Critical) +std::string ThirdPersonBiteVerb(const bool Critical) +std::string FirstPersonPSIVerb(const bool Critical) +std::string ThirdPersonPSIVerb(const bool Critical) +std::string FirstPersonBrownSlimeVerb(const bool Critical) +std::string ThirdPersonBrownSlimeVerb(const bool Critical) +std::string FirstPersonPepsiVerb(const bool Critical) +std::string ThirdPersonPepsiVerb(const bool Critical) + + Called by the preseding four functions, these handle different + hit messages based on character's type. + +derived characters (character.h) +-------------------------------- + +The actual character classes instantiated in the game, and their +abstract bases such as humanoid. Functions are same as in character. + +command (command.h) +------------------- + +A class that contains information of one command, including the key that +triggers it and the character function that is tied to it. + +Functions: +---------- + +command(bool (character::*LinkedFunction)(void), std::string Description, + char Key) + + Constructs the command. Description is the one that appears in + ShowKeyLayout list. + +bool (character::*CLinkedFunction(void))() +std::string CDescription() +char CKey() + + Return the associated data components. + +game (game.h) +------------- + +The garbage can of all global functions and variables. This holds class +prototypes, message system, pointers to areas and player and many +tiny key, string, god etc. routines that can't be put into any well-defined +category. + +Functions: +---------- + +Init(std::string Name = "") + + Inits the game. Called by Main and LoadMenu. Name is the player's + name used for loading, if it's empty, it is asked from user. + +DeInit() + + Deinitializes the game and frees all memory allocated for levels + and player. + +Run() + + The Main Loop of the game, called by Main and LoadMenu. + +const int Menu(std::string sMS) + + Shows a menu like the main menu. sMS must contain all menu choices, + distinquished from each other by '\r' letter. + +int* CMoveCommandKey() + + Returns a pointer to MoveCommandKey, which is an array containing + all eight direction keys. + +const vector* CMoveVector() + + Returns a pointer to MoveVector, which is an array containing + all eight direction vectors in the same order as in MoveCommandKey. + +level* CCurrentLevel() + + Self-explanatory. + +bool FlagHandler(ushort CX, ushort CY, ushort OX, ushort OY) + + Used by LOS system. The LOS code draws lines beginning from player + and calls this function for each coordinate pair. CX and CY are + current coordinates, OX and OY the origo (player) coordnates. + This updates the pointed squares Flag (whether it can be seen) + and item memory. Returns false if the square is blocked and + linedrawing must be terminated. + +bool DoLine(int X1, int Y1, int X2, int Y2, + bool (*Proc)(ushort, ushort, ushort, ushort)) + + "Draws" a line from X1:Y1 to X2:Y2, that is, calculates the route + between them coordinate by coordinate and calls Proc() with each + coordinates as arguments. Proc is usually FlagHandler or alike. + +ushort*** CLuxTable() + + Returns a pointer to the LuxTable. See "light.txt" for more + information. + +ushort* CLuxTableSize() + + Return a pointer to LuxTableSize. See "light.txt" for more + information. + +Quit() + + Quits the game, i.e. set the Running boolean, that determines + whether Run() will continue, to false. + +character* CPlayer() + + Returns pointer to the player character. Probably the most + used function in the game, but there's no macro. + +SPlayer(character* NP) + + Sets NP to be the new Player. + +vector CCamera() + + Returns the Camera vector, i.e. vector that tells the coordinates of + the square at the top left corner of the display. + +UpDateCameraX() +UpDateCameraY() + + Updates the Camera vector based on Player's position. Called from + different places, so different functions. + +level* CLevel(ushort Index) + + Returns level identified by Index. + +character* game::CreateMonster(ushort Index) +item* game::CreateItem(ushort Index) + + Creates a monster or item with Type() Index. Used by their "Balanced" + equalents and Load functions. + +InitLuxTable() +DeInitLuxTable() + + Create and free the LuxTable and LuxTableSize. DeInitLuxTable() + is called automatically on exit. For more information, see + "light.txt". + +character* BalancedCreateMonster() +item* BalancedCreateItem() + + The main character and item generation functions. + +const char* Insult() + + Returns a pointer to a random insult string, like "navastater". + +bool BoolQuestion(std::string String, char DefaultAnswer = 0, + int OtherKeyForTrue = 0, int (*Key)(void) = game::GetKey) + + Displays a message String on the top bar, and waits for either 'y', + 'Y', 'n' or 'N' to be pressed. If DefaultAnswer is zero, any + illegal key is interpreted as a rejection, if non-zero and non-two, + as an acceptance, if two, there's no default answer and so + the question is repeated until a proper answer is given. + If OtherKeyForTrue is pressed on any of these modes, it counts + as yes. Key is the getkey method used in the question. The function + return true if the answer was yes, false otherwise. + +const char* PersonalPronoun(uchar Index) +const char* PossessivePronoun(uchar Index) + + Pass either UNDEFINED (0), MALE (1) or FEMALE (2) to these + to get a proper pronoun suited for the sex, like "she" or "its". + +DrawEverything(bool EmptyMsg = true) +DrawEverythingWithDebug(bool EmptyMsg = true) + + In game, draw everything there is draw, like messages, game screen, + information panel etc. The second function is obsolete and does + the same as the first. If EmptyMsg is true, EMPTY_MESSAGES + is called after drawing the messages. + +DrawEverythingNoBlit(bool EmptyMsg = true) + + Same as above, except graphics::BlitDBToScreen() is not called + afterwards. + +StoryScreen(const char* Text, bool GKey = true) + + Displays Text in the center of a black screen, dividing it up into + lines if '\n' characters are present, and waits for keypress if + GKey is true. + +bool Save(std::string SaveName = game::SaveName()) +bool Load(std::string SaveName = game::SaveName()) + + Save and load the game from a set of files with a body SaveName. + +material* CreateRandomSolidMaterial(ulong Volume) + + Self-explanatory. Mainly used for golems and such. + +material* CreateMaterial(ushort Index, ulong Volume) + + Creates a material of Type() Index with volume Volume. + +groundterrain* LoadGroundTerrain(std::ifstream* SaveFile) +overterrain* LoadOverTerrain(std::ifstream* SaveFile) +material* LoadMaterial(std::ifstream* SaveFile) +item* game::LoadItem(std::ifstream* SaveFile) +character* game::LoadCharacter(std::ifstream* SaveFile) + + Loading functions for various classes. Btw, three first are VERY + badly coded! + +bool CRunning() + + Is the game still running... + +EnableWizardMode() +SeeWholeMap() +GoThroughWalls() + + Enables the Wizard Mode or toggles the other two, without + questioning anything. + +bool CWizardMode() +bool CSeeWholeMapCheat() +bool CGoThroughWallsCheat() + + Returns whether these modes are on or off. + +uchar EditGamma(short Value) + + Adds Value to Gamma and corrects its boundaries. Hardware Gamma + isn't working now, so this is quite useless. + +bool EmitationHandler(ushort CX, ushort CY, ushort OX, ushort OY) +bool NoxifyHandler(ushort CX, ushort CY, ushort OX, ushort OY) + + Handlers used with DoLine, mainly by the lighting system. + See "light.txt". + +int GetKey() +int MonsGetKey() + + Call getkey and return the result. Both are obsolete and were once + used for speed measurement correction. + +UpdateCameraXWithPos(ushort Coord) +UpdateCameraYWithPos(ushort Coord) + + Update camera coordinates with Coords. Never called simultaneously, + so different functions. + +std::string StringQuestion(const char* String, ushort MaxLetters) + + Displays String on the screen and waits for the user to input + a string, which is returned once terminated by the Enter key. + The user is however not allowed to make the string longer + than MaxLetters. Calls DrawEverythingNoBlit() before doing + anything, so don't use before the game is surely initialized. + +bool KeyIsOK(char Key) + + Return false if Key is a command key, true otherwise. + +SCurrent(ushort What) + + Sets the index of current level to What. Make sure that the level + is loaded before using it though. + +ushort CCurrent() + + Returns the index of the current level. + +ushort CLevels() + + Returns the total amount of levels in game. + +int GetMoveCommandKey(vector A, vector B) + + Return the key that is tied to a MoveVector equaling (A - B). + Returns 0xFF if such is not found. + +god* CGod(uchar Index) + + Returns the god that has index of Index in the array of the + divine ones. + +std::string CAlignment(uchar Index) + + Returns a string corresponding the Alignment Index. This must + be in the range of 0-10, 0 returning "L++" and 10 "C--". + +ApplyDivineTick() + + Called every turn, this decreases the time one has to wait + until praying is possible (if its non-zero). + +ApplyDivineAlignmentBonuses(god* CompareTarget, bool Good) + + Called when a either sacrifising or praying to CompareTarget. + Adjusts the relation of other gods based on Alignment difference + and whether the deed was considered Good by CompareTarget. + +SendToHell(character* PassedAway) + + Sends character to the Hell array. Called by character::Die(). + (it cannot just delete the character, since other functions + concerning it may still be called during that tick) + +BurnHellsContents() + + Deletes all contents of the Hell array. Called at the end of + tick and at the end of game. + +vector GetDirectionVectorForKey(ushort Key) + + Returns the MoveVector associated with Key or vector(0,0) + if there is none. + +vector AskForDirectionVector() + + Waits for a key and returns GetDirectionVectorForKey(PressedKey). + +std::string StringQuestionWithClear(std::string String, ushort MaxLetters) + + Like StringQuestion, but clears the screen before asking anything + instead of calling DrawEverythingNoBlit(). + +std::string CPlayerName() +SPlayerName(std::string What) + + Self-explanatory. + +std::string SaveName() + + Returns the player's name, resized to eight letters if necessary, + with spaces replaced with '_' letters. + +bool EyeHandler(ushort CX, ushort CY, ushort OX, ushort OY) + + Used with DoLine to determine if a ray of light can cross the + line without intervention. + +long GodScore() + + Returns the highest relation value one has with any god. + +ShowLevelMessage() + + Shows the message associated with CCurrentLevel(), and removes + it afterwards. + +float Difficulty() + + Calculates the true difficulty level for monster generation + algorithm, based on Player's difficulty and the Current level. + +TriggerQuestForMaakotkaShirt() + + Self-explanatory. + +CalculateGodNumber() + + Calculates the total amount of gods in game. + +SGodNumber(uchar What) + + Sets the amount of gods to What. Called by CalculateGodNumber(). + +uchar CGodNumber() + + Returns the total amount of gods in game, as calculated before. + +long CBaseScore() + + At the beginning of the game, Player's initial score is saved + to BaseScore to be substracted from the final score. This + returns the value in question. + +Turn() + + Increases the Turn counter by one. + +float CSoftGamma() + + Returns SoftGamma, a value between 0-2, that is multiplied by + luminance before all lighted draws. + +EditSoftGamma(float E) + + E is added to SoftGamma with a boundary check. + +WhatToLoadMenu() + + Shows the load menu, and loads, runs and kills the game if a + savefile is actually chosen. + +ulong CTurns() + + Returns the number of turns that have passed since the beginning + of the game. + +std::string CAutoSaveFileName() + + Returns the default autosave filename body. + +uchar DirectionQuestion(std::string Topic, uchar DefaultAnswer = 8, + bool RequireAnswer = true) + + Displays Topic and waits for a keypress. If the pressed key + is a direction key, returns it's index in the MoveCommandKey + array. If it is not, and DefaultAnswer is a correct direction + key index, it returns DefaultAnswer. If it is not, and + RequireAnswer is false, it returns 0xFF. If it is not, + function will forcefully wait as long as a correct key + is received. + +command** CCommand() + + Returns a pointer to the command map. + +const character* const CCharacterPrototype(const ushort Index) +prototypecontainer& CCharacterPrototype() +const item* const CItemPrototype(const ushort Index) +prototypecontainer& CItemPrototype() + + Various self-explanatory functions for accessing character and item + prototypes. + +SaveLevel(std::string SaveName = SaveName(), ushort Index = CCurrent(), + bool DeleteAfterwards = true) + + Saves the level of Index to a file with name Savename + ".l" + Index, + and deletes it afterwards if wanted. Be sure the level actually + exists and is not deleted. + +LoadLevel(std::string SaveName = SaveName(), ushort Index = CCurrent()) + + Loads the level of Index from a file with name Savename + ".l" + + Index. + +RemoveSaves() + + Removes all saves with SaveName() or AutoSaveName() as a filename + body. + +item* CreateItem(std::string What) + + Tries to create an item of name What. If such doesn't exist + or cannot otherwise be created this way, returns 0. + +const unsigned int CountChars(const char cSF,std::string sSH) + + Counts how many cSFs there are in sSH. + +game::globalmessagingsystem (game.h) +------------------------------------ + +Excluding error messages and such, this handles all messages from the game +to the player, i.e. those displayed in the upper message bar of the game +screen. + +Functions: + +globalmessagingsystem() + + Initializes the system. + +AddMessage(const char* Format, ...) + + Adds a message to the messagebuffer and to the message history. + The Format is processed equally like printf() does it. This is a + very common function, so a macro ADD_MESSAGE is provided for + convenience. + +Draw() + + Draws messages to the message bar. Macro DRAW_MESSAGES is provided. + +Empty() + + Empties the messagebuffer. Macro EMPTY_MESSAGES is provided. + +const char* CBuffer() + + Returns a pointer to the buffer. + +DrawMessageHistory() + + Self-explanatory. + +Format() + + Cleans the message history. + +game::panel (game.h) +-------------------- + +A class containing routines (well, one routine ;) for handling the +information panel on the bottom of the game display screen. + +Functions: + +Draw() + + Self-explanatory. + +god (god.h) +----------- + +The most divine of all classes, this holds relation to player, +prayer timer, and praying functions, which are overloaded by derived +gods to achieve most complicated and powerful effects. + +Functions: + +god() + + Initializes the god. + +Pray() + + Based on Timer and Relation, this determines whether the + the prayer has a good or bad effect, calls the proper + function and adjusts relations. Called by the pray dialog. + +std::string Name() + + Returns the name of the god. + +std::string Description() + + Returns a general description of god's profession. + +uchar Alignment() + + Returns the alignment of the god, in range 0-10, 0 meaning L++ + and 10 C--. + +std::string CompleteDescription() + + Returns the complete description used by the pray dialog, including + alignment, name and profession. + +ApplyDivineTick() + + Called by the game function of same name, this decreases Timer + by one if its above zero. + +AdjustRelation(god* Competitor, bool Good) + + If Competitor is prayed upon or a sacrifice is made for Him/Her, + adjusts the relation of this god to player, based on whether the + deed was Good and Competitor was pleased by it, and the + alignment difference between Competitor and this. If it's + zero or very low and Good is true, relation is increased, + otherwise almost always decreased. + +AdjustRelation(short Amount) + + Adds Amount to Relation, clipping the result to the range of + -1000 to +1000 if necessary. + +AdjustTimer(long Amount) + + Adds Amount to timer, clipping the result to the range of + 0 to 1000000000 if necessary. + +Save(std::ofstream* SaveFile) +Load(std::ifstream* SaveFile) + + Save and load the Relation and Timer fields. + +SRelation(short Value) +STimer(long Value) + + Sets Relation or Timer to Value without clipping. + +uchar BasicAlignment() + + Returns either GOOD (0), NEUTRAL (1) or EVIL (2) in order to + determine material alignment bonuses/penalties applied while + sacrifying. + +short CRelation() + + Returns Relation. + +PrintRelation() + + Adds a message explaining how much the god likes you after + sacrifice. + +PrayGoodEffect() +PrayBadEffect() + + The fun ones, called by Pray(). All variants of these automatically + suspect that player is the object of their effects. + +bool ReceiveOffer(item* Sacrifice) + + Called by the offer dialog, determines if the current god wants and + can receive Sacrifice. If so, calculates the effect, displays + appropriate messages and return true. Otherwise returns false. + +independency (independ.h) +------------------------- + +The most fundamental base class for independent things, that is those +classes which are not used only for containing others, like stack +and level, and can be contained in a single square, like all objects +and worldmap terrains. It holds the basic name, drawing and saving +interface for all these. + +Functions: + +independency(void) +independency(std::ifstream*) +~independency(void) + + Currently do nothing. + +std::string Name(uchar Case) + + Returns the whole name of the independent being, according to + Case, which can be one of the following: + + UNARTICLED (0) no article is ever added + DEFINITE (2) "the" is (usually) added to the name + INDEFINITE (6) "a" or "an" is (usually) added to the name + + You can also specify the flag PLURAL (1) if you wish to. This + function is overridden by very numerous classes that don't want + to use the standard procedure. + +Save(std::ofstream* SaveFile) + + The fundamental save, that outputs the result of Type() to SaveFile. + +std::string NameSingular(void) +std::string NamePlural(void) + + Protected procedures that return the plain body of the thing's name + and its plural name. Should be overloaded by every single derived + class unless there are very good reasons to do otherwise. + +std::string CNameSingular(void) +std::string CNamePlural(void) + + Just return the preseding in public. + +std::string NameNormal(uchar Case, std::string Article) +std::string NameProperNoun(uchar Case) + + Two basic name modes. The first is used for things like "a lamp", + the second for things like "Bill Gates the ElDeR cHaOs GoD". + +DrawToTileBuffer() + + Self-explanatory. + +ushort Type() + + Returns the numeric type of the independency. For save system usage + only! (it's quite evil, y' know) + +vector CBitmapPos() + + Returns the coordinates of this thing's picture in its respective + graphics file. + +item (item.h) +------------- + +The base class for all item types, derived from object. + +Functions: + +item(ushort Size2, bool CreateMaterials) + + Constructs an item of size Size2. If CreateMaterials is false, item + doesn't initialize its own materials, that is, it must be a base + class + for some other item that does this by itself. + +item(std::ifstream* SaveFile, ushort MaterialQuantity = 1) + + Creates and loads an item from SaveFile. MaterialQuantity is + discarded. + +PositionedDrawToTileBuffer(uchar LevelSquarePosition) + + A special DrawToTileBuffer, this is called by + stack::PositionedDrawToTileBuffer. It handles not only items that are + on the floor but those that hang from the wall. LevelSquarePosition + must be either DOWN (0), LEFT (1), UP (2), RIGHT (3) or CENTER (4), + determining the position where the item should be drawn and the picture + used for the draw. + +ulong CWeight() + + Calculates the weigth of the item, based on its materials' CWeigths. + +bool CanBeRead(character* Reader) + + Returns whether Reader can read this particular item. + +bool Consume(character* Consumer, float Amount = 100) + + Forces Consumer to consume the item, at least its eatable parts. + If Amount is less than 100, only a percentage of eatable materials + specified by Amount is consumed. False means the item could not + be consumed. + +bool Read(character* Reader) + + Triggers the event that is associated with reading this particlular + item. False as return means the item was not read for some reason. + +bool Consumable(character* Eater) + + Returns whether the item is consumable by Eater. + +ushort CEmitation(void) + + Returns the total emitation of the item. See "light.txt". + +short CalculateOfferValue(char GodAlignment) + + Calculates the total offer value, based on GodAlignment, + which must be either GOOD (0), NEUTRAL (1) or EVIL (2). + +bool Destroyable(void) + + Self-explanatory. Usually true for quest items. + +bool Fly(uchar Direction, ushort Force, stack* Start, bool Hostile = true) + + Handles all aspects of getting an item up to the air from + the Start stack, flying it all the way in the specified + Direction as long as there's Force to hold it above + ground. If Hostile is true, any character that the item + passes during its flight will become hostile, even if not + hit. If the item flew to nowhere, i.e. it landed on the square + of former lift off, false is returned. + +bool HitCharacter(character* Dude, float Speed, bool CanBeSeen) + + Called by Fly. This does not actually hit the character, + but test whether the item was catched, missed the character + or hit him/her/it, and calls respective character functions. + True is returned if the item's movement was stopped. + +ushort PrepareForConsuming(character* Consumer, stack* Stack) + + Called before consuming items, this must return the index of + item-to-be-consumed in Stack, or 0xFFFF if it isn't there. + +float GetWeaponStrength() + + Calculates the weaponstrength of the item based on its form + modifier and materials. + +DrawToTileBuffer() + + Calls PositionedDrawToTileBuffer(CENTER). + +vector GetInHandsPic() + + Retrieves the human.pcx coordinates of the picture that is + maskedblitted above any humanoid wielding that particular item. + +uchar GetConsumeType() + + Returns its main material's consumetype. + +ushort TryToOpen(stack* Stack) + + Tries to open the item, places whatever was removed from inside + it to Stack, and returns the index of the new item in it. + +ushort GetArmorValue() + + Returns a number from 1-100, which is a percentage that any + damage caused to the creature wearing this item must be + multiplied by. + +bool IsHeadOfElpuri() +bool IsPerttusNut() +bool IsMaakotkaShirt() + + Self-explanatory. + +ReceiveHitEffect(character* Enemy, character*) + + Called after a succesful hit, this handles special effects + that occur when this item hits Enemy, excluding damage. + +bool CanBeDippedInto(item*) + + Returns whether this item can be dipped into anything. + +DipInto(item* DipTo) + + Dips this to DipTo. + +material* BeDippedInto() + + Returns some dipping material for purposes of DipInto. + +bool CanBeDipped() + + Can this item be dipped? Don't ask how it differs from + CanBeDippedInto, though ;) + +bool CanBeWorn() + + Self-explanatory. + +SMaterial(ushort Where, material* What) + + Sets material of index Where to What. + +item* BetterVersion() + + Returns a better version of the item for magical + transformations. + +ImpactDamage(ushort, bool IsShown, stack* ItemStack) + + Function that is applied to the item itself when it hits something + solid, like a wall. + +float OfferModifier() + + Value of all sacrifices of this type is multiplied by this number. + +long Score() + + Bonus that is added to player's score when the game ends with this + item in his/her/its inventory (before victory modifiers). + +bool DogWillCatchAndConsume() + + If thrown at it. + +uchar GetDipMaterialNumber() + + Returns the index of dipping material. Currently not used. + +item* Clone() + + Clones the item. Used by the prototype system. + +item* Load(std::ifstream* SaveFile) + + Loads an item of same type from SaveFile. Used by the prototype + system. + +ushort Possibility() + + Item's possibility to appear randomly in the dungeon. Zero for + none. The true possibility is this Possibility divided by + the total sum of Possibilities of all item types. + +bool CanBeWished() + + Self-explanatory. + +item* CreateWishedItem() + + If wishing this item will yield a different type item, + return it from here. If not, return zero. + +bool Apply(character* User) + + Self-explanatory. + +ushort CFormModifier() + + Multiplier applied when calculating weaponstrength of the item. + +level (level.h) +--------------- + +A basic dungeon level consisting of levelsquares, derived from area. + +Functions: + +level(ushort XSize, ushort YSize, ushort Index) + + Creates an empty level of specified size. All squares are initialized + to dungeon wall squares. Index must be the level's index in game's + level array. + +~level() + + Destroys the level and deletes all it's contents. + +ExpandPossibleRoute(const vector Origo, const vector Target, + const bool XMode) + + Used by the route system, this is a recursive function that sets the + ON_POSSIBLE_ROUTE flag for the Origo square, calculates the direction + where Target is from Origo's point of view, and calls itseld again + with that square as the Origo, if the ON_POSSIBLE_ROUTE and the + FORBIDDEN flag are NOT set on it. If either is, however, it tries + the closest direction, or not-so-closest if it is also blocked. If + all directions are blocked, it just returns. When calculating the + order of directions to be tried out, diagonal directions are always + ignored, directions changing the x-coordinate are preferred if XMode + is set, and vice versa. The process is terminated immediately when the + ON_POSSIBLE_ROUTE flag is set in Target square. + +ExpandStillPossibleRoute(const vector Origo, const vector Target, + const bool XMode) + + Same as above, except this toggles on the STILL_ON_POSSIBLE_ROUTE flag + for each square it touches, and ON_POSSIBLE_ROUTE must be set + and STILL_ON_POSSIBLE_ROUTE must not be set on all of these squares. + +GenerateTunnel(const vector From, const vector Target, const bool XMode) + + Generates a tunnel from From to Target, in the following way: + First it calls ExpandPossibleRoute with From as the Origo, + and then goes through all squares with ON_POSSIBLE_ROUTE flag set, + removing the flag and trying if ExpandStillPossibleRoute() + still can reach the Target. Alluseless squares on the path are + removed and those that are essential changed to walkable floor. + This is a slow, but guaranteed way to reach the target. Target and + XMode are passed to all Expands called. + +PutStairs(const vector Pos) + + Puts stairs to specified Pos and adds it to level's KeyPoint array. + +Generate() + + Generates the level using the only currently implemented generation + algorithm. It creates a random amount of rooms via MakeRoom(), + attaches all KeyPoints to the main dungeon via AttachPos() and + randomizes the level's initial items. + +AttachPos(const vector What) + + Attaches the square on coordinates What to the main dungeon + via GenerateTunnel(). + +CreateRandomTunnel() + + Creates a random tunnel, attached to the main dungeon but + ending to a completely random place (except rooms of course). + Currently not used by the generation code. + +CreateItems(const ushort Amount) + + Self-explanatory. The items are generated via + game::BalancedCreateItem(). + +CreateMonsters(const ushort Amount) + + Self-explanatory. Currently not used. + +vector CreateDownStairs() + + Creates downstairs to a random but sensible position, attaches + it, calls PutStairs on the level beneath, and returns the position + of these new stairs. + +bool MakeRoom(const vector Pos, const vector Size, + const bool AltarPossible = true, uchar DivineOwner = 0) + + Creates a room to the level. Pos marks the upper left corner of the + room. Size includes walls, so smaller than (3,3) rooms are not made. + Function checks that it won't be too close to borders and is neither + above any other rooms, stairs, vital tunnels and such. When the room + is made, a door is created for it, added to the level's Door dynarray + and attached to some other random door. If AltarPossible is true, + an altar of random alignment may be created in the room. If + DivineOwner is non-zero, no prayer can be made in the room except + to the deity of that particular index. The function returns true + if the room was made successfuly. + +HandleCharacters() + + Sends all squares a command to handle their characters. + +EmptyFlags() + + Empties the Flag boolean of every square on the level. + +PutPlayer(const bool) + + Puts player into a random, walkable position on the level. + +PutPlayerAround(vector Pos) + + Puts player on a walkable position somewhere around Pos. + +Save(std::ofstream* SaveFile) + + Self-explanatory. + +level(std::ifstream* SaveFile, ushort Index) + + Creates a level and loads its contents from SaveFile. Index must be + the level's index in game's level array. + +Luxify() + + Calls the Emitate function of all squares. + +FastAddCharacter(vector Pos, character* Guy) + + Adds Guy to Pos, without updating lights. + +Draw() + + Draws the level and it's contents. Or at least those that are not + outside the borders of the display screen. + +UpdateLOS() + + When this is called, if any given square on the level is currently seen + by the player, its Flag is set to true, otherwise false. + +GenerateNewMonsters(ushort HowMany) + + See CIdealPopulation(). Uses game::BalancedCreateMonster() as its + monster generator. + +levelsquare* CLevelSquare(vector Pos) + + Self-explanatory. + +dynarray* CKeyPoint() + + Returns a pointer to the KeyPoint dynarray. + +ushort CPopulation() + + Returns roughly the total amount of character's on the level. + +ushort CIdealPopulation() + + If CPopulation() is below this, GenerateNewMonsters() will advance + it towards this when called. + +bitmap* CFluidBuffer() + + Returns a pointer to the FluidBuffer, where blood, vomit and such + are stored. + +levelsquare (lsquare.h) +----------------------- + +A square that appears in levels only and has some special procedures +like lighting that plain squares don't have a chance to enjoy of. +Derived from square. + +Functions: + +levelsquare(level* MotherLevel, vector Pos) +~levelsquare() + + Construct and destruct the levelsquare. MotherLevel is the level + where the square is located and Pos is its position in it. + +HandleCharacters() + + Command its Character to Act(). + +SignalEmitationIncrease(ushort EmitationUpdate) +SignalEmitationDecrease(ushort EmitationUpdate) +ushort CalculateEmitation() +Emitate() +ReEmitate() +Noxify() +ForceEmitterNoxify() +ForceEmitterEmitation() +NoxifyEmitter() +uchar CalculateBitMask(vector Dir) +AlterLuminance(vector Dir, ushort AiL) +ushort CLuminance() + + See "light.txt". + +DrawToTileBuffer() + + Draws everything on the square to the TileBuffer, with the + exception of the Character. + +UpdateMemorizedAndDraw() + + Self-explanatory. + +bool Open(character* Opener) +bool Close(character* Closer) + + Pass these commands to overterrain, and return whatever the return. + +bool Save(std::ofstream* SaveFile) + + Saves the square to SaveFile. + +levelsquare(level* MotherLevel, std::ifstream* SaveFile, vector Pos) + + Creates and loads the levelsquare. See the normal constructor for + parameter info. + +SpillFluid(uchar Amount, ulong Color, ushort Lumpiness = 3, + ushort Variation = 32) + + Spills some liquid substance to the square, creating little + puddles of specified Color to its position in MotherLevel's + FluidBuffer. Amount is the amount of these puddles, Lumpiness + influences their size (bigger means, roughly, bigger, but in + no occasion larger than 3x3), and Variation affects the color + variation across them. + +AddCharacter(character* Guy) + + Adds Guy onto the square and updates lightning if necessary. + +FastAddCharacter(character* Guy) + + Adds Guy onto the square but never updates lights. + +Clean() + + Deletes the entire contents of the square's Stack and SideStacks. + +RemoveCharacter() + + Removes the Character currently standing on the square, updating + light if necessary. (Character isn't deleted, just set to zero) + +UpdateItemMemory() + + Updates RememberedItems, that is, a short text containing a + description of what is lying on the square, like "many items". + +CanBeSeen() + + Returns whether this square can be seen by the player. + +Kick(ushort Strength, uchar KickWay) + + Called when the contents of square is kicked. Forwards parameters + to Stack. + +bool CanBeSeenFrom(vector FromPos) + + Self-explanatory. + +SRememberedItems(std::string What) +std::string CRememberedItems() + + Set RememberedItems (see UpdateItemMemory()) to What and + return them. + +bool Dig(character* DiggerCharacter, item* DiggerItem) + + Called when DiggerCharacter tries to dig the square with + a pick-axe or similiar as DiggerItem. Returns whether + this was successful or not. + +char CanBeDigged(character* DiggerCharacter, item* DiggerItem) + + Quite obvious. Returns zero or two if it isn't, one if it is. + +stack* CStack() + + Returns a pointer to the square's primary stack. + +EmptyFlag() +SetFlag() +bool RetrieveFlag() + + Flag handlers. Flag determines whether the player can + currently see the square. + +stack* CSideStack(uchar Index) + + Returns a pointer to the wanted SideStack, that is a stack + attached to the side of an impassable square. Index must + be either DOWN (0), LEFT (1), UP (2) or RIGHT (3). + +ushort CEmitation() +SEmitation(ushort What) + + Returns and sets Emitation, which is used as a backup + emitation value from the last call to CalculateEmitation(). + See "light.txt". + +std::string CEngraved() + + Returns whatever is engraved here, or "" if there's nothing. + +bool Engrave(std::string What) + + Sets the Engraved string to What and returns true. + +uchar CDivineOwner() +SDivineOwner(uchar NDO) + + Return and set the DivineOwner, which is the index of the owner + deity in the divine god array, or zero for none. No other + god can be prayed upon on this particular square. + +level* CMotherLevel() + + Return a pointer to the level where the square is. + +levelsquare::emitter (lsquare.h) +-------------------------------- + +Emitter is a simple struct that contains information about an emitter, +which is shedding light upon a particular levelsquare. See "light.txt" +for more knowledge about emitters. + +Functions: + +emitter(vector Pos, ushort DilatedEmitation) + + Creates a new emitter with the given attributes. + +emitter() + + Creates an unitialized new emitter. + +bool operator==(emitter& AE) + + Operator for comparing two emitters. Doesn't care about anything + else than the Pos vector. + +emitter& operator=(emitter& AE) + + Copies all data from AE to the emitter. + +terrain (terrain.h) +------------------- + +Terrain includes every terrain type from walls to altars. The class is divided +to two larger sub-classes overterrain and groundterrain. + +Functions: + +bool CanBeDigged() + + Returns true if the square can be digged. + +bool CanBeOffered() + + Returns true if the terrain is capable of accepting offering. + +bool CanBeOpened() + + Returns true if the terrain can be opened. + +levelsquare* CLevelSquareUnder() + + Returns the levelsquareunder which currently means using CSquareUnder() + and converting the resulting pointer into a levelsquare pointer. + +bool Close(character* Closer) + + Look at Open(character* Opener) + +vector CPos() + + Returns the position of the terrain. + +square* CSquareUnder() + + Returns the square under this particular terrain. + +uchar CVisualFlags() + + Returns the Visual Flags + +terrain(material** Material2, vector) + + Constructor of terrain. Material2 is the pointer to the pointer of + the material and vector is not used. + +SSquareUnder(square* Square) + + Square is placed in SquareUnder + +bool Open(character* Opener) + + Tries to open the terrain. The function return true if the square could + be opened and false if not. Opener is the pointer to the character that + is trying to open the terrain. + +HandleVisualEffects() + + Randomizes the plausible visual flags on. + +uchar OKVisualEffects() + + Returns the Visual Flags that can be set for the current square. + +groundterrain (terrain.h) +------------------------- + +Basic ground terrain, like a floor. Drawn below overterrain. Functions don't +differ from terrain in any way. + +overterrain (terrain.h) +----------------------- + +This is positioned above groundterrain during the draw. I will list +differences to terrain. + +Functions: + +CIsWalkable() + + Returns true if the square is walkable, else false. + +uchar COwnerGod() + + Returns the index of the owning god. + +MakeNotWalkable() +MakeWalkable() + + Names of the functions tell everything. + +overterrain(material** Material, vector BitmapPos, bool IW) + + Create the overterrain. Material is a pointer to the pointer of + the staring material, BitmapPos is discarded and IW tells whether + the square is walkable or not (true = walkable). + +ShowDigMessage(character* Who, item*) + + Adds a message explaining the user if the terrain can be digged and + whether the digging is successful or not. Who is the digger and item* + is pointer to the item that is used for digging. + +square (square.h) +------------------- + +A rectangular area on the level, the smallest indivisible place where +individual terrains, characters and such can be contained. + +Functions: + +square(area* MotherArea, vector Pos) + + Constructor for square. MotherArea is the area in which the square is + created in. Position is the square's position. + +Save(std::ofstream* SaveFile) + + Saves the square to SafeFile. + +square(area* MotherArea, std::ifstream* SaveFile, vector Pos) + + Constructor for square. MotherArea is the area in which the square is + created in. SaveFile is the file from which the square should be + loaded from. Pos is the square's position. + +DrawCheat() + + Blits the square to the tilebuffer using the "see whole map" cheat. + +DrawMemorized() + + Draws the square from the players "memory". + +AddCharacter(character* Guy) + + Guy points to a character that should be added to the square. The + name of this function is slightly misleading, because a square + can currently have only 1 character. + +RemoveCharacter() + + Removes the character in the square. (however the character is not + deleted, the pointer is only set to 0) + +ChangeTerrain(groundterrain* NewGround, overterrain* NewOver) + + Changes the terrains of the square. + +SCharacter(character* What ) + + Character is replaced with What. + +character* CCharacter(void) + + Returns the Character currently standing (or whatever) on the square. + Zero means there is no one there. + +bool CKnown() + + Returns true if the square is known to the player otherwise the + function return false. If Known is false, the square will not be + drawn on the screen. + +vector CPos(void) + + Return the potion of the square as a vector. + +SKnown(bool What) + + Changes the Known-status of the square to What. If Known is false, + the square will not be drawn on the screen. + +SOverTerrain(overterrain* What) + + Changes the OverTerrain to What. + +ushort CPopulation(void) + + Returns the amount of characters in the square. (currently 1 or 0) + +SGroundTerrain(groundterrain* What) + + Changes the GroundTerrain to What. + +SOverTerrain(overterrain* What) + + Changes the OverTerrain to What. + +area* CMotherArea(void) + Returns the pointer to the area where the square is. + +ushort CPopulation(void) + Returns the amount of characters in the square. (currently 1 or 0) + +material (material.h) +--------------------- + +Material is a class for handling effects that depend on the material type +and amount of some part of an individual object. + +Functions: + +material(ulong Volume) + + Constructor of material. Volume is volume of the material to be + created. + +material(std::ifstream* SaveFile) + + Loads material's data from SaveFile. + +uchar CFleshColor(void) + + Returns the color of the material when it is used as flesh. + +uchar CItemColor(void) + + Returns the color of the material when it is used as a material for + an item. + +std::string Name(uchar Case = 0) + + Returns the whole name of the material, according to + Case, which can be either of the following: + + UNARTICLED (0) no article is ever added + INDEFINITE (6) "a" or "an" is (usually) added to the name + +ushort GetHitValue() + + Returns the hitvalue of the item, which increses the weaponstrength + of an item made of it. + +uchar CConsumeType() + + Returns the consume type of the item. + +ulong CVolume() + + Returns the volume of the material. + +ulong CWeight() + + Returns the weight of the material. + +ushort GetDensity() + + Returns the density of the material. + +uchar EffectType() + + Returns the type of effect this material has on a character. + +ushort Type() + + Return the type of the item. Note: this is evil, so don't use this + outside the save system if possible. + +ushort TakeDipVolumeAway() + + Takes some of the volume of the item away and then returns + the volume of the material that has been taken away. + +void Save(std::ofstream* SaveFile) + + Saves the material into SaveFile. + +ushort CArmorValue(void) + + Returns the armor value of the material. See item::GetArmorValue(). + +SVolume(ulong What) + + Volume of the material is set to What. + +ushort CEmitation(void) + + Returns the light emitation of the material. See "light.txt". + +ushort OfferValue(void) + + The value of offering this material a gram. + +uchar Alignment(void) + + Returns the alignment of this material. Affects value of offers. + Possible alignments are: + GOOD (1) + NEUTRAL (2) + EVIL (3) + +EatEffect(character* Eater, float Amount, float NPModifier) + Eater recives an eat effect of this material. + Amount is the percent of the whole Volume that + the character has eaten. NPModifier is a number + that is used to multiply the actual amount, which + is used by some items. + + +HitEffect(character* Enemy) + + If the material somehow affects the Enemy then this function handles + it. + +short NutritionValue() + + Returns the nutrion value of the item. The amount is + in NPs per 1000 grams. + +MinusAmount(float Amount) + decreases the volume of the material by Amount percent. + +NormalFoodEffect(character* Eater, float Amount, float NPModifier) + + Eater reseives the normal food effect. Amount is the percent of the + volume that is going to be eaten and NPModifier is floating point + number that is used to multiply this volume. NPModifier is used by + some items. + +prototypecontainer (proto.h) +--------------------------------------------- + +A template class that contains a prototype vector, an array of type +ProtoType* including a pointer to each and every concrete class derived +from ProtoType. The first and last elements must always be zero. +Used by the game's generation and save systems. + +Functions: + +prototypecontainer() + + Creates the container and sets its first element to zero. + +Add(const ushort Index, ProtoType* Proto) + + Adds Proto to the position determined by Index, resizing the + array and setting its last element to zero if obligatory. + +const ProtoType* const Access(ushort Index) const +const ProtoType* const operator [] (ushort Index) const + + Returns the prototype of specified Index, so that inspectors + like cloning functions and such may be called through it. + +worldmapsquare (wsquare.h) +-------------------------- + +A square that appears on the worldmap only and WILL have some +special routines like handling border tiles while drawing terrain +etc. that its sister, levelsquare, doesn't have. Functions +are so highly under construction that they won't be documented. + +stack (stack.h) +--------------- + +stack(square* SqureUnder) + + Stack's constructor. SquareUnder is surprisingly the square + under the stack. + +stack(std::ifstream* SaveFile) + + Saves the stack to SaveFile. + +PositionedDrawToTileBuffer(uchar LevelSquarePosition) + + Draws the stack to the right place on the TileBuffer. + LevelSquarePosition is passed to item::PositionedDrawToTileBuffer(). + Refer to it if you need to know what it does. + +ushort AddItem(item* ToBeAdded) + + Adds ToBeAdded to the stack and then returns the index of ToBeAdded. + Updates lights if needed. + +ushort FastAddItem(item* ToBeAdded) + + Same as the above, but does not update lights. + +item* RemoveItem(ushort Index) + + Removes item number Index and then returns a pointer to that item. + Updates lights if needed. + +FastRemoveItem(ushort Index) + + Just removes the number Index item and does not update lights. + +Clean() + + Deletes all items of the stack. + +ushort MoveItem(ushort Index, stack* MoveTo) + + Moves item number Index to the stack that MoveTo is pointing at. + Then returns the new index of the item in question. Updates + lights if needed. + +Optimize(ushort OptimizeBoundary) + + Optimizes the stack if there are more than OptimizeBoundary + zero pointers. Optimizing means all of these are removed. + Note: The game does not support zero pointers correctly. + Use zero as the OptimizeBoundary always! + +ushort DrawContents(const char* Topic) + + Draws the contents on the screen as a neatly organized list and + waits for keypress. The Topic is also displayed on the screen. + The list uses list::Draw and returns the same things as it does. + +ushort CEmitation() + + Returns the total emitation of all the items in the stack. + +ulong stack::SumOfMasses() + + Returns the sum of the masses in this stack. + +Save(std::ofstream* SaveFile) + + Saves the stack in SaveFile. + +stack::stack(std::ifstream* SaveFile) + + Constructor of stack that loads itself from SaveFile. + +ushort SearchItem(item* ToBeSearched) + + Searches for item ToBeSearched in the stack and then returns its + index, or 0xFFFF if it was not found. + +void stack::Move(levelsquare* To) + + Moves the whole stack to levelsquare To and updates lights if + necessary. + +vector CPos(void) + + Returns the position of the stack. + +ushort ConsumableItems(character* Eater) + + Returns the number of consumable items (for Eater). + +ushort DrawConsumableContents(const char* Topic, character* Eater) + + Makes a list out of the consumable items (for Eater) and then returns what ever + list::Draw() returns. + +DeletePointers() + + All pointers to items are set to 0 + +ushort FindItem(item* ToBeSearched) + + Returns the index of ToBeSearched, or 0xFFFF if it isn't found in the stack. + +Kick(ushort Strength, bool ShowOnScreen, uchar Direction) + + Strength is the strength of the kick. If ShowOnScreen is true kick messages are + displayed on screen. Direction is the vector in which direction the kick is going. + +long Score() + + Calculates the total score value of all the items in the stack. + +SSquareUnder(square* Square) + + SquareUnder is set to Square. + +item** CItem() + + A pointer to the item* array. + +item* CItem(ushort I) + + Returns a pointer to the item which has the index of I. + +ushort CItems(void) + + Returns the number of items in this stack. + +levelsquare* CLevelSquareUnder(void) + + Returns the pointer to the levelsquare under the stack. + +SEmitation(ushort What) + + Sets the Emitation to What. + +ushort CNonExistent() + + Returns the amount of pointers (which is stored in NonExistent) that are 0. + +SNonExistent(ushort What) + + Sets NonExistent to What. + +object (object.h) +----------------- + +A basic object is build up from different materials. Derived from independency. + +Functions: + +object() + + The default constructor which doesn't do anything. + +object(std::ifstream* SaveFile) + + Creates and loads the object from SaveFile. + +Save(std::ofstream* SaveFile) + + Saves the object to SaveFile. + +InitMaterials(ushort Materials, ...); + + Initilizes Materials-amount of materials. + + Example: InitMaterials(2, Material2, Material3) + where Material2 and Material3 are pointers to + materials. + +InitMaterials(material* FirstMaterial) + + Initilize only material FirstMaterial. + +ushort CEmitation() + + Returns the emitation of the object. + +std::string NameArtifact(uchar Case, uchar DefaultMaterial) + + Returns the name of the item like an artifact type of item. + DefaultMaterial is the Index of the normal material from which + this object is made up. If its made of something else, + the material name is shown, otherwise not. + +std::string NameWithMaterial(uchar Case) + + Returns the name of the object with the name of Material[0] + attached. (Case works like case normally works.) + +std::string NameHandleDefaultMaterial(uchar Case, std::string Article, + uchar DefaultMaterial) + + If the material is the DefaultMaterial this function returns what + ever NameNormal returns with Case and Article as parameters. If the + material is not the Default Material this function returns what + NameWithMaterial returns with Case as parameter. + +std::string object::NameContainer(uchar Case) + + Returns the name of a container object (for example: cans). Case works + like case normally works. + +std::string NameSized(uchar Case, std::string Article, ushort LillaBorder, + ushort StoraBorder) + + Returns the name of item with size categorizing adjectives. + Case works like case normally works. Article is the article + for this. LillaBorder is the maximum size for the object + to be considered small. StoraBorder is the maximum size + for an object to be considered large. + +std::string NameThingsThatAreLikeLumps(uchar Case, std::string Article) + + Case works like case normally works. Article is the possible article. + +EraseMaterials(void) + + Deletes all materials from this object. + +material* CMaterial(ushort Index) + + Returns the material that has the index of Index. + +ushort CSize() + + Returns the size of the object. + +ushort CMaterials(void) + + Returns the number of materials in this object. + +---------------------------------------------------------------------------- + +End of document. + + diff --git a/Doc/Obsolete/Material Ideas II.txt b/Doc/Obsolete/Material Ideas II.txt new file mode 100644 index 0000000..60dd58c --- /dev/null +++ b/Doc/Obsolete/Material Ideas II.txt @@ -0,0 +1,40 @@ +sapient pearwood used to create items that magically follow their owner +ommel flesh beneficial effect (spoils) +ommel skin -||- +ommel nail -||- +ommel spit -||- +ommel blood -||- +ommel bile -||- +ommel vomit -||- +ommel sperm -||- +emerald (green) SV 200-350 +opal (light blue) SV 200-350 +uranium slowly causes mutations, D 18700 +bloat poison which makes the victim increase his size and does great damage, like he was thrown into a vacuum +stainless steel doesn't rust +ambrosia food of gods; does something *really* good +nectar drink of gods; does something *really* good +cursed water of Nyanneechuan changes the gender of a male player +orichalcum copper-looking expensive material mentioned to be abundant in Atlantis by Plato +thunderbird feather lightning resistant +sleeping gas +tear gas +mustard gas +laughing gas +arrow-proof glass +electrum (yellowish-white) the alloy of gold and silver (and maybe some copper) +silicon carbide SV 140-190, D 3100 +nephrite/jade (green) SV 200-350 +tungsten carbide SV 200-350, D 15700 +aluminium SV 90, D 2650 +lead +holy spirit +elerium-115 (from xcom) huge amount of energy +wrapping paper god = Atavus +LSD +werewolf skin armors made of this cause lycanthropy +testosterone strength & beard growth +estrogen breast growth +lots of ideas http://en.wikipedia.org/wiki/Fictional_chemical_substance +cedar wood http://en.wikipedia.org/wiki/Cedar +wax diff --git a/Doc/Obsolete/Material Ideas.txt b/Doc/Obsolete/Material Ideas.txt new file mode 100644 index 0000000..e479838 --- /dev/null +++ b/Doc/Obsolete/Material Ideas.txt @@ -0,0 +1,12 @@ +//dragonhide flexible and strong, each type should confer some resistance +//meteoric steel (darker than steel) stronger than steel, weaker than spider silk +//arcanite (orange) between meteoric steel and mithril +//illithium (white) between mithril and sapphire/ruby +//adamant (adj: adamantine) (white) between diamond and valpurium +//octiron (black) very magical +//ommel bone -||- (spoils) +//ommel tooth -||- (spoils) +//ommel tears -||- +//ommel sweat -||- +//ommel cerumen -||- +//ommel snot -||- diff --git a/Doc/Obsolete/Materials.txt b/Doc/Obsolete/Materials.txt new file mode 100644 index 0000000..87e0ff8 --- /dev/null +++ b/Doc/Obsolete/Materials.txt @@ -0,0 +1,39 @@ +Material chart for RL5 by Fatal Error Productions 2001: + +material density strength offer alignm NV +iron 8000 100 10 N 0 +valpurium 3000 400 100 L 0 +stone 3000 60 5 N 0 +bananaflesh 1200 5 10 N 0.175 +gravel 2500 40 1 N 0 +moraine 2500 20 2 N 0 +schoolfood 1500 5 20 E 0.075 +air 1 0 0 N 0 +wood 500 30 5 N 0 +flesh 1200 10 15 N 0.05 +goblinoidflesh 1200 10 10 N 0.05 +pork 1200 10 20 N 0.5 +beef 1200 10 20 N 0.5 +bone 2000 30 5 N 0 +darkfrogflesh 1200 10 50 E 0.05 +elpurisflesh 2400 30 1 N 0.05 +glass 2500 30 5 N 0 +elmourine 1000 1 100 N 0.05 +bananapeal 500 10 1 N 0 +parchment 600 15 5 N 0 +cloth 100 5 5 N 0 +humanflesh 1200 10 30 E 0.05 +slime 400 2 1 N 0.01 +brownslime 400 2 1 N 0.01 +wolfflesh 1200 10 15 N 0.05 +dogflesh 1200 10 15 E 0.05 +ennerbeastflesh 1800 30 5 N 0.05 +pepsi 1500 200 50 E 0.1 +mithril 5000 200 25 N 0 +spiderflesh 1200 10 +jackalflesh 1200 10 +donkeyflesh 1200 10 +marble 3000 50 +gold 20000 30 +grass 100 2 +leather 500 10 \ No newline at end of file diff --git a/Doc/Obsolete/Message.txt b/Doc/Obsolete/Message.txt new file mode 100644 index 0000000..4fddcb8 --- /dev/null +++ b/Doc/Obsolete/Message.txt @@ -0,0 +1,22 @@ +A character hits something with his weapon, and the message is like the following: + +You slash at the goblin with your iron long sword. +The goblin slashes at something with its bronze spear. +etc. + +1. ADD_MESSAGE(RED, "%s slash%s at %s with %s %s.", GetDescription(DEFINITE).c_str(), IsPlayer() ? "" : "es", Enemy->GetDescription(DEFINITE).c_str(), GetPossessivePronoun().c_str(), Weapon->GetDescription(UNARTICLED).c_str()); +2. ADD_MESSAGE(RED, "%s slash%s at %s with %s %s.", GetDescription(DEFINITE).c_str(), VerbEnding("es").c_str(), Enemy->GetDescription(DEFINITE).c_str(), GetPossessivePronoun().c_str(), Weapon->GetDescription(UNARTICLED).c_str()); +3. ADD_MESSAGE(RED, "%s slash%s at %s with %s %s.", CHARDESCRIPTION(DEFINITE), IsPlayer() ? "" : "es", Enemy->CHARDESCRIPTION(DEFINITE), GetPossessivePronoun().c_str(), Weapon->CHARDESCRIPTION(UNARTICLED)); +4. ADD_MESSAGE(RED, "%s slash%s at %s with %s %s.", CHARDESCRIPTION(DEFINITE), VerbEnding("es").c_str(), Enemy->CHARDESCRIPTION(DEFINITE), GetPossessivePronoun().c_str(), Weapon->CHARDESCRIPTION(UNARTICLED)); +5. ADD_MESSAGE(RED, "%s slash%s at %s with %s %s.", CHARDESCRIPTION(DEFINITE), CHARVERBENDING("es"), Enemy->CHARDESCRIPTION(DEFINITE), CHARPOSSESSIVEPRONOUN(), Weapon->CHARDESCRIPTION(UNARTICLED)); +6. msgsystem << msgcolor(RED) << GetDescription(DEFINITE) << " slash" << (IsPlayer() ? "" : "es") << " at " << Enemy->GetDescription(DEFINITE) << " with " << GetPossessivePronoun() << " " << Weapon->GetDescription(UNARTICLED) << "." << endmsg; +7. msgsystem << msgcolor(RED) << GetDescription(DEFINITE) << " slash" << VerbEnding("es") << " at " << Enemy->GetDescription(DEFINITE) << " with " << GetPossessivePronoun() << " " << Weapon->GetDescription(UNARTICLED) << "." << endmsg; + +2. ADD_MESSAGE(RED, "%s slash%s at %s with %s %s.", !GetDescription(DEFINITE), VerbEnding("es").c_str(), Enemy->GetDescription(DEFINITE).c_str(), GetPossessivePronoun().c_str(), Weapon->GetDescription(UNARTICLED).c_str()); + + + +1. ADD_MESSAGE(RED, "%s slash%s at %s with %s %s.", GetDescription(DEFINITE).c_str(), VerbEnding("es").c_str(), Enemy->GetDescription(DEFINITE).c_str(), GetPossessivePronoun().c_str(), Weapon->GetDescription(UNARTICLED).c_str()); +2. ADD_MESSAGE(RED, "%s slash%s at %s with %s %s.", CHARDESCRIPTION(DEFINITE), CHARVERBENDING("es"), Enemy->CHARDESCRIPTION(DEFINITE), CHARPOSSESSIVEPRONOUN(), Weapon->CHARDESCRIPTION(UNARTICLED)); +3. ADD_MESSAGE(RED, "%s slash%s at %s with %s %s.", &GetDescription(DEFINITE), &VerbEnding("es"), &Enemy->GetDescription(DEFINITE), &GetPossessivePronoun(), &Weapon->GetDescription(UNARTICLED)); +4. msgsystem << msgcolor(RED) << GetDescription(DEFINITE) << " slash" << VerbEnding("es") << " at " << Enemy->GetDescription(DEFINITE) << " with " << GetPossessivePronoun() << " " << Weapon->GetDescription(UNARTICLED) << "." << endmsg; diff --git a/Doc/Obsolete/Monster Ideas.txt b/Doc/Obsolete/Monster Ideas.txt new file mode 100644 index 0000000..0ee49aa --- /dev/null +++ b/Doc/Obsolete/Monster Ideas.txt @@ -0,0 +1,488 @@ +// done +/ partly done or needs testing/balancing +(r) rejected + +//Genie + + The sprit that lives in a bottle. When freed grants a wish. When the Genie becomes hostile it makes a wish and wishes for a plate mail. + +/Mammoth + + Pretty normal creatures except that when they die they drop an ivory horn. + +Gremlin + + These creatures multiply when they touch water and die when see bright lights. + +Dwarf + + Dwarfs are a pretty generic character. Except that they carry pick-axes and dig through walls. They should also have more mithril and other precious metals than normal monsters. You should also be able to bribe them to your side with gold. + +Hobbit + + A Tolkien cliche, but don't mind about that. Maybe these creatures could carry more rings (especially rings of invisiblity) + +Gnome + +//Floating eye (or Beholder) + + When you hit this monster you will fall asleep (a la Nethack) + +Titan +Giant + +Scorpion + + Poisons. + +Lich + + Preferably like in BG2. + +Cyclops +Vampire + + Should be damaged by bright lights. + +Minotaur + +Kerberus + + Bites with three heads. + +Light Bugs (work in progress-name) + + These creatures normally are drawn towards light sources. + +Dark Bugs (work in progress-name) + + These creatures normally are drawn towards darkness. + +//Necromancer + + Should raise corpses as skeletons and zombies. + +Mutant tentacle + + Purple skin. Low agility, but very high intelligence. Can use humanoid weapons. + +//unnamed (maybe xorn or ghost) + + Can go through walls + +Huge firefly + + Flying. Emits light (might be a useful pet). + +Shaman + + Curses at you and so lowers the echantment level of a randomly picked item in your equipment or stack. + +Monkey + + (a la Nethack) Fights a short time and then steals one of your items and then tries to run away. + +//Eddies in space-time continuum + + These horrible "creatures" teleport the player randomly around and sometimes create weird couches. + +Mutated giant mushroom + + Most of the time stand idle and sometimes teleport from place to place. Sometimes shoot spores at player. Sometimes the spores could grow to be mushrooms. (not too often though, so the level wouldn't be filled with mushrooms.) + +//Skunk + + Smells really bad. + +//Hedgehog + + Monster appearing in level 1 which causes damage to the attacker if he doesn't use a weapon. + +? + + Guy who severs bodyparts from any corpse he encounters and laughs diabolically. + +Butterfly of storms + + Causes small tornadoes and thunderstorms around the level by flapping its wings. + +//Hattifattener + + Not dangerous in normal weather, but gathers electricity during thunderstorms and starts shooting lightnings. (always moving through the land without caring about other monsters that may or may not be on their way) + +Grid feature + + Zaps people. + +Mad cow + + Eating makes the player mad, too. + +Pikachu + + Lightning attack. Stats from pokemon.com: size 40 cm, weight 6 kg. Sometimes shouts, "The Primary Main + Objective is to destroy the Evil Power!" + +King Kong + + An ape standing six meters tall. + +Mimic + + Like in Nethack. + +Ent (or treant) + + Like in LotR. + +Hydra + + Attacks everyone around, like Elpuri. + +Wumpus + + Appears randomly for a short time on mountanous areas. Pays you something if you catch it. + +Mummy + + With the wrapping. + +Spider queen + + Named. + +//Sumo wrestler + + Very fat. + +The Monster guarding the Cave of Caerbannog + + (quote from Monthy Python & Holy Grail) + + "But follow only if you are men of valor. For the entrance to this cave + is guarded by a monster, a creature so foul and cruel that no man yet + has fought with it and lived. Bones of full fifty men lie strewn about + its lair ... therefore sweet knights if you may doubt your strength or + courage come no further, for death awaits you all with nasty pointy + teeth." + + Looks like a rabbit, but in reality, deadly. + +Philosopher's stone golem + + Turns everything it hits to gold. + +Deep sea blowfish (Singularis minutia gigantica) + + Protects itself from enemies by inflating itself to many times + its normal size. The bloat poison can be extracted from its body. + +Flying squirrel + +Mountain troll + + A mountain who is a troll. + +Exploding mushrooms + +High-level thieves + +Monster which engulfs others + +Basilisk or cockatrice + + Petrifies. + +Carpenter + + Sells wood stuff and asks the player: "How much wood would a + wood-chuck chuck if a wood-chuck could chuck wood?" + +Troll + + Regenerates at enormous rate, and its blood heals the enemy if spilled + over him -> unusually long battles, good pets. + +Mola Mola + + Gigantic hybrid of a perch and an octopus whose eyes shine with + ugly green ligth. Carnivorous and sentient. Uses almost all + of her energy in rapidly making new muscles and fins, may grow + twenty feet tall, and can shut down her life supporting systems + for days and still recover in full strength. More intelligent + than enner beasts. + +Nuas + + According to a Valpuristic legend, a sea monster which gave birth + to Mola Mola and numerous other demons. Similiar to the biblical + Leviathan. + +Flying asses + + Shoot lightnings and use chemical weapons. + +Knight of Ni + + Says "Ni". At higher levels learns to say + "Ekky-ekky-ekky-ekky-z'Bang, zoom-Boing, z'nourrrwringmm". + +Airavata + + Giant white war elephant with four tusks like mountains + (mounted by Indra, the Hindu god of weather and war) + +Varaha + + Divine warrior boar + (avatar of Vishnu which defeated the demon Hiranyakshyap, + brother of Hiranyakashipu) + +Narasimha + + Powerful man-lion with deadly nails + (avatar of Vishnu which defeated the demon Hiranyakashipu, + brother of Hiranyakshyap) + +Leviathan or Tiamat + + Giant demonic multi-headed sea monster or whale + (Leviathan is the biblical version, Tiamat Babylonian) + +Behemoth + + Unimaginably powerful amphibious monster, with a body + probably resembling that of a giant hippo + (mentioned in the Book of Job, 40:15) + +Ziz + + Gigantic bird monster + (the Jewish mythology states that its meat will be served + along with Behemoth and Leviathan after the Armageddon) + + +Sirens + + 'So far so good,' said she, when I had ended my story, 'and now pay + attention to what I am about to tell you- heaven itself, indeed, + will recall it to your recollection. First you will come to the Sirens + who enchant all who come near them. If any one unwarily draws in too + close and hears the singing of the Sirens, his wife and children + will never welcome him home again, for they sit in a green field and + warble him to death with the sweetness of their song. There is a great + heap of dead men's bones lying all around, with the flesh still + rotting off them. Therefore pass these Sirens by, and stop your + men's ears with wax that none of them may hear; but if you like you + can listen yourself, for you may get the men to bind you as you + stand upright on a cross-piece half way up the mast, and they must + lash the rope's ends to the mast itself, that you may have the + pleasure of listening. If you beg and pray the men to unloose you, + then they must bind you faster.' + (The Odyssey, by Homer) + + +Scylla + + 'Of these two rocks the one reaches heaven and its peak is lost + in a dark cloud. This never leaves it, so that the top is never + clear not even in summer and early autumn. No man though he had twenty + hands and twenty feet could get a foothold on it and climb it, for + it runs sheer up, as smooth as though it had been polished. In the + middle of it there is a large cavern, looking West and turned + towards Erebus; you must take your ship this way, but the cave is so + high up that not even the stoutest archer could send an arrow into it. + Inside it Scylla sits and yelps with a voice that you might take to be + that of a young hound, but in truth she is a dreadful monster and no + one- not even a god- could face her without being terror-struck. She + has twelve mis-shapen feet, and six necks of the most prodigious + length; and at the end of each neck she has a frightful head with + three rows of teeth in each, all set very close together, so that they + would crunch any one to death in a moment, and she sits deep within + her shady cell thrusting out her heads and peering all round the rock, + fishing for dolphins or dogfish or any larger monster that she can + catch, of the thousands with which Amphitrite teems. No ship ever + yet got past her without losing some men, for she shoots out all her + heads at once, and carries off a man in each mouth' + (The Odyssey, by Homer) + +Charybdis + + 'You will find the other rocks lie lower, but they are so close + together that there is not more than a bowshot between them. A + large fig tree in full leaf grows upon it, and under it lies the + sucking whirlpool of Charybdis. Three times in the day does she + vomit forth her waters, and three times she sucks them down again; see + that you be not there when she is sucking, for if you are, Neptune + himself could not save you; you must hug the Scylla side and drive + ship by as fast as you can, for you had better lose six men than + your whole crew.' + (The Odyssey, by Homer) + +Tennyo + + Beautiful female angelic creature with magical powers. Wears a + a feathered robe called amano hagoromo which allows her to fly and + cross the border between heaven and earth. If the robe is stolen, + the tennyo will become a servant of the thief in order to retrieve it. + (japanese mythological being) + +Briareos + + Giant with one hundred arms and fifty heads. The glare of his + hundred eyes makes everyone panic automatically. + (in Greek mythology, one of the numerous sons of Gaia and Ouranos, + earth and sky. His nephew Zeus called him to scare away Hera and her + allies who were about to rebel) + +Tooth fairy + +Miniature black hole + + Zero-intelligence "monster" which sucks items, fluids, gases, + characters and terrains inside it, never to be seen again. + Immune to all weapons, but can be destroyed by magic (thaumic + particles are not affected by gravity). + +Game abuser + + Stupid monster who has a lair on a secret level where he uses + mushrooms, banana peels, broken bottles, gods etc. to gain infinite + exp. + +Panda + +Mind worm + + 'As the writhing, teeming mass of Mind Worms swarmed over the outer + perimeter, we saw the defenders recoil in horror. "Stay calm! Use + your flame guns!" shouted the commander, but to no avail. It is well + known that the Mind Worm Boil uses psychic terror to paralyze its + prey, and then carefully implants ravenous larvae in the brains of + its still-conscious victims. Even with the best weapons, only the + most disciplined troops can resist this horrific attack. + + -- Lady Deirdre Skye, + "Our Secret War"' + + (from Sid Meier's Alpha Centauri) + +Monster which can eat metals + +Snowman + + Melts in the summer and leaves a carrot. + +The Groke (suggested by Henri Kiviluoto) + + A dark, mound-shaped creature with staring eyes; everything she + touches dies, and the ground freezes wherever she sits. Appears + in Tove Jansson's books, for instance "Who Will Comfort Toffle?" + and the Moomin series. + +Smurfs + + Devout communists. + +Brainsucker + + Jumps on one's head and sucks brains. Can be used as a helmet. + Is shot from a special launcher. + +Splitting monsters + +Monsters which release other monsters from their body when they die + +Bees + +Golden Eagle + + Hyper-powerful. + +Nightingale + +Babel fish + + "The Babel fish is small, yellow, leechlike, and probably the oddest + thing in the Universe. It feeds on brainwave energy received not from + its own carrier but from those around it. It absorbs all unconscious + mental frequencies from this brainwave energy to nourish itself with. + It then excretes into the mind of its carrier a telepathic matrix + formed by combining the conscious thought frequencies with nerve + signals picked up from the speech centers of the brain which has + supplied them. The practical upshot of all this is that if you stick + a Babel fish in your ear you can instantly understand anything said + to you in any form of language. The speech patterns you actually hear + decode the brainwave matrix which has been fed into your mind by your + Babel fish." + (The Hitchhiker's Guide to the Galaxy, by Douglas Adams) + +Mutant turtle (suggested by Henri Kiviluoto) + + Very good in martial arts. + +Poison dart frog (Dendrobates terribilis) + + A frog whose skin contains deadly neuro-toxin. Used by the natives + of South America in production of poison arrows. + +Reef stonefish (Synanceia verrucosa) + + A quite poisonous species that is camouflaged so it looks like a + rock, and its spines inject venom to anyone unfortunate to step + on one. + (I recall seeing one in the Seattle Aquarium and it looked... + stony - holybanana) + +Black mamba (Dendroaspis polylepis) + + Very dangerous snake up to 4.5m long, which can outrun a human, + crawling at speed of 20 km/h, and kill him easily with a large + dose of powerful toxin which paralyzes and kills him rapidly. + +Octopus + + Very challenging for the bodypart engine. + +Piranha + + Expert at devouring swimmers alive. + +IVAN developer + + Can alter reality in the entire Universe as he wishes and is therefore + beyond gods in strength (he could also be named "a Creator"). + +Banana Slug + + http://en.wikipedia.org/wiki/Banana_slug + + example: + + Salt is harmful to all slugs; it causes the slug to dehydrate. + Blood rushes to the surface of the skin to dilute the salt. This + process is only effective with small quantities of salt. + +Monkey, version II + + Engraves sequences of random characters in the ground. Occasionally + produces Shakespeare quotes. + +Giant anteater (Myrmecophaga tridactyla) + + Eats ants. See Wikipedia for the boring details. + +Mog + +Chocobo + + Can be catched and mounted. + +Cow + + Not the brightest creature around, but by far the wisest above Valpurus. + After all, it has integrated Zen-Buddhism deep in its enlightened life style, + answering "MU" to every question you can think of. + (Again, an idea which the gods blessed me with in a dream; There was + a female shaman who could understand animal talk and she was walking + a leashed cow - holybanana) diff --git a/Doc/Obsolete/Monsters.txt b/Doc/Obsolete/Monsters.txt new file mode 100644 index 0000000..aad1255 --- /dev/null +++ b/Doc/Obsolete/Monsters.txt @@ -0,0 +1,37 @@ + <--- Monster Chart of RLPMM ---> + + Agility: Strength: Endurance: Size cm: Weapon: Possibility: + +player 25 5 10 160 melee 0 +dark frog 20 1 2 1 teeth 100 +pure mass of Bill's will 1 2 5 150 teeth 50 +Elpuri the Dark Frog 5 2 10 500 teeth 0 +Perttu, the Highest Priest of Valpuri 50 50 50 200 Valpuri's Just. 0 +Enner Beast 50 5 5 150 scream 0 +fallen valpurist 1 10 5 170-199 pick axe 25 +Bill's SWAT commando 20 100 10 170-219 curv. 2-h sword 10 +frog-goblin hybrid 30 10 5 1 pick axe 100 +Bill Gates the ElDeR ChAoS GoD ? ? ? ? ? 0 +conical mommo slime 1 1 32 20 teeth 50 +flat mommo slime 1 1 20 10 teeth 50 + + <--- RL5 Modified Chart ---> + + Agility: Strength: Endurance: Perc. Size cm: Weapon: Poss: Dam: Melee: Speed: Danger: + +player 15-25 10-15 10-15 10-15 150-200 melee 0 0-2 2000 88 - +Perttu, the Highest Priest of Valpuri 80 80 80*5 80 200 Valpuri's Just. 0 171-286 10000 400 150000 +Oree the Pepsi Daemon King 40 40 40*10 24 225 pepsi vomit 0 60-101 40000 90 60000 +Bill's SWAT commando 30 20 15 24 200 curv. 2-h sword 5 19-33 10000 140 750 +Enner Beast 10 5 10 6 150 scream 0 37-63 200000 45 2500 +dark frog 20 1 2 12 15 teeth 100 0-2 20000 81 5 +Elpuri the Dark Frog 50 50 50 15 300 teeth 0 46-79 25000 250 5000 +pure mass of Bill's will 40 5 5 24 100 psych 50 5-10 30000 162 75 +fallen valpurist 10 5 5 12 150 pick axe 50 2-5 5000 45 25 +frog-goblin hybrid 15 10 5 18 100 spear 100 2-4 5000 70 25 +conical mommo slime 1 2 40 6 100 slime vomit 25 1-4 25000 8 250 +flat mommo slime 2 1 20 6 50 slime vomit 75 0-2 25000 10 50 +(c)golem 5 35 20 12 250 melee <20 1-3 1125 ? 75 +(v)golem 5 35 20*10 12 250 melee <20 39-66 30000 ? 20000 +wolf 20 6 6 21 100 teeth 40 1-3 7500 ? 20 +dog 15 4 4 18 75 teeth 20 0-2 5000 ? 10 \ No newline at end of file diff --git a/Doc/Obsolete/Monsters2.txt b/Doc/Obsolete/Monsters2.txt new file mode 100644 index 0000000..722c8d5 --- /dev/null +++ b/Doc/Obsolete/Monsters2.txt @@ -0,0 +1,64 @@ +Name Size Agil. Str. End. Perc. Real HP MelStr. Weapon WStr. Damage ToHit Dodge Danger +human 150-200 15-25 10-15 10-15 10-15 20-30 1000 hands 1000 0-2 36-60 10-19 61-234 +Perttu 225 75 75 75/0.2 75 750 20000 Justifier 57965 163-272 27 53 2526911 +farmer 170 10 10 10 12 20 1000 axe 13416 5-9 4 8 295 +cityguard 180 20 20 20/0.7 21 55 1000 poleaxe 17748 13-23 7 15 4259 +shopkeeper 160 10 30 30 30 60 2000 pickaxe 11401 12-22 7 12 2975 +Oree 225 50 30 30/0.1 30 600 30000 pepsi vomit 30000 33-57 120 32 545400 +swatcommando 200 30 20 30/0.5 30 120 5000 twohandedsword 28372 21-36 6 21 19406 +Enner Beast 150 10 10 25 12 50 50000 scream 100000 18-32 - 9 - +darkfrog 15 30 5 5 18 10 20000 teeth 20000 3-7 65 67 810 +Elpuri 300 10 30 50/0.1 30 1000 20000 teeth 10000 22-37 40 9 177000 +billswill 100 40 5 20 27 40 20000 psi 20000 3-7 87 34 3220 +fallenvalpurist 150 10 5 10 15 20 1000 pickaxe 11401 2-4 4 8 125 +fallenvalpurist 150 10 5 10 15 20 1000 axe 13416 2-5 4 8 147 +fallenvalpurist 200 15 18 15 15 30 1000 spikedmace 22638 15-26 4 11 1833 +froggoblin 100 15 10 15 18 30 2000 spear 6928 2-5 15 15 467 +froggoblin 100 15 10 15 18 30 2000 poleaxe 17748 6-12 5 15 931 +conicalmommo 100 2 4 50 9 100 20000 slime vomit 20000 3-6 7 3 480 +flatmommo 50 4 2 25 9 50 20000 slime vomit 20000 1-3 10 5 190 +cloth golem 250 5 20 20 12 40 750 hands 750 0-1 22 2 87 +iron golem 250 5 20 20/0.25 12 160 15000 hands 15000 11-19 22 2 6960 +mithril golem 250 5 20 20/0.1 12 400 30000 hands 30000 22-38 22 2 34800 +valpurium golem 250 5 20 20/0.1 12 400 60000 hands 60000 39-66 22 2 69600 +wolf 100 20 10 10 24 12 7500 teeth 7500 2-5 49 19 660 +dog 70 15 8 8 21 16 5000 teeth 5000 1-3 32 17 204 +spider 10 5 2 1 9 2 10000 teeth 10000 0-2 14 17 7 +jackal 80 10 5 5 18 10 5000 teeth 5000 0-2 25 11 57 +donkey 150 5 10 10 15 20 1000 teeth 1000 0-1 16 5 26 + +Danger = Strength * Endurance * WeaponStrength * (ToHit + Dodge + Agility) / Armor / 1000; + +/5! + +20-12-12-12, axe/spear/other, exp 1.0, dam 5-10, th 7.5, dg 10, av 100, rhp 25 +spider, jackal, donkey, dog, cloth golem +danger 50 (405,8) 100 + +25-15-20-12, mithril axe/spear/poleaxe, exp 1.25, chain mail, dam 10-20, th 15, dg 17.5, av 70, rhp 50 +darkfrog, froggoblin, fallenvalpurist, wolf +danger 150 (5000,30) 1000 + +30-20-25-12, two-handed sword/spiked mace/mithril poleaxe, exp 1.5, mithril chain mail, dam 20-40, th 10, dg 20, av 61, rhp 75 +billswill, flatmommo, conicalmommo +danger 500-1000 (20000,20) 5000 + +35-25-30-12, mithril two-handed sword/mithril spiked mace, exp 1.75, plate mail, dam 40-70, th 15, dg 25, av 50, rhp 125 +swatcommando, iron golem, Enner Beast +danger 10000 (60000,6) 10000 + +40-30-35-12, mithril curved two-handed sword, exp 2.0, mithril plate mail, dam 70-110, th 20, dg 30, av 37, rhp 175 +Elpuri, mithril golem +danger 30000 (150000,5) 50000 + +45-35-40-12, valpurium curved two-handed sword, exp 2.5, mithril plate mail, dam 110-190, th 30, dg 32.5, av 37, rhp 225 +valpurium golem +danger 70000 (350000,5) 100000 + +50-40-45-12, Neerc Se-Ulb, exp 2.0, mithril plate mail, dam 150-250, th 15, dg 35, av 37, rhp 250 +Oree +danger 500000 + +55-45-50-12, Neerc Se-Ulb, exp 2.5, Maakotka Shirt, dam 210-350, th 20, dg 40, av 10, rhp 1000 +Perttu +danger 2500000 \ No newline at end of file diff --git a/Doc/Obsolete/Monsters3.txt b/Doc/Obsolete/Monsters3.txt new file mode 100644 index 0000000..51ac6a1 --- /dev/null +++ b/Doc/Obsolete/Monsters3.txt @@ -0,0 +1,57 @@ +Name Size Agil. Str. End. Perc. MelStr. Volume +player 150-200 15-25 10-15 10-15 10-15 1000 +perttu 225 75 75 75 75 1000 +farmer 170 10 15 20 18 1000 +guard 180 15 20 20 24 2000 +shopkeeper 160 10 30 25 30 2000 +priest 180 10 20 15 18 1000 +oree 225 50 30 30 30 30000 +darkknight 200 30 25 30 30 5000 +ennerbeast 150 10 10 25 12 100000 +frog 25 30 5 5 18 20000 +elpuri 300 10 30 50 30 20000 +billswill 100 40 5 20 27 20000 +skeleton 150 10 5 10 15 1000 +skeleton 200 15 18 15 15 1000 +goblin 100 15 10 15 18 2000 +conicalmommo 100 2 4 50 9 20000 +flatmommo 50 4 2 25 9 20000 +golem 250 5 20 20 12 varies +wolf 100 20 10 10 24 7500 +dog 70 15 5 5 21 5000 +spider 10 5 2 1 9 10000 +jackal 80 10 3 3 18 5000 +donkey 150 5 10 10 15 1000 +ivan 230 20 50 50 18 5000 +hunter 180 20 15 15 24 2000 +polarbear 250 10 30 30 24 10000 +dolphin 300 30 10 10 30 1000 +slave 160 10 20 15 15 1000 +perttuswife 170 10 5 5 21 500 +housewife 160 15 10 15 24 500 +femaleslave 170 10 10 15 18 500 +librarian 170 5 5 5 12 500 +angel 180 35* 35* 35* 45 10000 80000 *default; depends on alignment +bat 20 40 2 1 24 20000 1000 +beholder +genie 300 30 30 30 18 5000 200000 +gibberling 90 20 5 5 15 10000 30000 +gnoll +imp 100 15 10 10 15 20000 40000 +kobold 90 10 5 5 12 2000 30000 +large cat 60 25 5 5 21 20000 15000 +large rat 30 10 3 2 12 20000 2000 +lion 300 40 30 30 24 3000 150000 +mammoth 500 20 80 80 18 5000 5000000 +raven 60 +unicorn 200 40 5 20 18 2500 200000 +werewolf (h) 170 15 15 15 15 2000 80000 +werewolf (w) 200 25 25 25 24 20000 80000 +zombie 160 5 10 5 12 1500 50000 +mistress 180 35 25 50 30 5000 60000 +orc 160 50000 +troll 230 200000 +ant 1 1 1 1 1 1 1 +kamikazedwarf 130 20 20 20 24 2000 60000 +carnivorouspl. 250 5 30 4 5 2500 20000 +buffalo 250 7 4 30 24 500 200000 diff --git a/Doc/Obsolete/Monsters4.txt b/Doc/Obsolete/Monsters4.txt new file mode 100644 index 0000000..50bd9ac --- /dev/null +++ b/Doc/Obsolete/Monsters4.txt @@ -0,0 +1,59 @@ +Name astr lstr dex agi end perc int wis cha mana mstr kstr bstr +player 10 10 15 15 10 10 10 10 10 10 5000 +petrus 42 42 42 42 42 42 42 42 42 42 5000 +farmer 20 20 20 10 20 18 10 10 10 5 5000 +guard 25 25 15 15 25 24 10 10 15 10 5000 +shopkeeper 10 30 10 10 10 30 25 15 30 10 5000 +priest 10 30 10 10 10 18 15 25 20 20 5000 +oree 40 40 90 90 30 30 30 20 3 30 15000 30000 300000 +darkknight 30 30 25 25 30 30 20 15 15 15 5000 +ennerbeast 10 10 10 10 10 12 1 1 1 0 100000 +darkfrog 5 * * 40 15 18 40 30 5 20 30000 +elpuri 90 * * 10 40 30 40 30 2 30 30000 +billswill 5 * * 50 5 27 2 2 5 10 50000* +skeleton 10*** 10*** 10*** 10*** 5**** 15 4 4 3 5 5000 +goblin 10 10 10 15 10 18 5 5 5 5 5000 +conicalmommo 4 * * 2 20 9 3 2 4 0 20000 +flatmommo 2 * * 4 20 9 2 3 3 0 20000 +golem *** *** *** *** **** 12 4 4 5 5 5000* +wolf 7 * * 25 10 24 7 5 10 0 20000 +dog 5 * * 20 7 21 7 5 15 0 15000 +spider 2 * * 4 4 9 5 5 4 0 20000 +jackal 3 * * 15 5 18 5 5 7 0 15000 +donkey 20 * * 5 15 15 5 5 5 0 2500 +communist 50 50 20 20 30 18 7 5 10 5 5000 +hunter 15 15 15 20 15 24 10 10 15 5 5000 +polarbear 50 * * 10 25 24 5 7 15 0 15000 +dolphin 10 * * 30 10 30 99 99 30 0 5000 +lightfrog 5 * * 40 15 18 30 40 15 20 30000 +slave 25 25 15 10 25 15 10 10 10 5 5000 +petrusswife 5 5 5 5 5 21 7 10 80 20 6000 +housewife 10 10 15 15 15 24 10 20 20 10 6000 +femaleslave 10 10 10 10 10 18 10 10 25 5 6000 +librarian 5 5 5 5 5 12 30 15 10 20 5000 +zombie 10 10 5 5 5 12 4 4 3 0 5000 +imp 15 15 10 10 10 15 10 10 5 10 5000 +bat 2 * * 50 10 24 7 5 5 0 15000 +mistress 25 25 35 35 25 30 15 15 40 15 5000 +werewolf (h) 15 15 15 15 15 15 15 15 15 15 5000 +werewolf (w) 25 25 35 35 35 24 10 10 10 0 20000 +kobold 5 5 10 10 5 12 4 4 4 0 5000 +gibberling 5 5 5 20 10 15 4 4 5 0 5000 +largecat 5 * * 35 10 21 10 10 20 0 20000 +largerat 3 * * 30 10 12 5 5 4 0 20000 +angel 35** 35** 35** 35** 35** 45 35 35 50 35 6000 +kamikazedwarf 20 20 20 20 15 24 10 5 10 10 5000 +mammoth 80 * * 20 25 18 5 7 10 0 20000 10000 +unicorn 15 * * 40 10 18 25 20 30 25 15000 7500 +genie 30 30*** 30 30 15 18 20 25 20 50 5000 +lion 25 * * 25 20 24 7 15 20 0 20000 +c-plant 10 * * 1 5 6 3 2 5 0 5000 +buffalo 40 * * 25 25 24 5 5 5 0 20000 10000 +snake 10 * * 15 10 9 5 5 5 0 10000 +orc 15 15 15 20 15 18 7 5 4 0 5000 +cossack 20 20 15 15 15 21 10 7 7 0 5000 + +* not valid: lstr == astr and dex == agi +** varies according to alignment +*** not used +**** has no effect diff --git a/Doc/Obsolete/Monsut.txt b/Doc/Obsolete/Monsut.txt new file mode 100644 index 0000000..d153192 --- /dev/null +++ b/Doc/Obsolete/Monsut.txt @@ -0,0 +1,130 @@ +A +Air elemental [vaaleanharmaa E], greater air elemental [valkoinen E] +Air shark [vaaleansininen A](lentävä hai featuring stormbringer) +Ankheg [lila i] +Alchemist [kirkkaan keltainen @](Heittelee erilaisia kemiallisia aineita. Kantaa aina kultaa mukanaan) + + + +B +Bat [tummanruskea B], large bat [vaaleanharmaa B], huge bat [tummanharmaa B], fruit bat [vaaleanvihreä B], mana bat [vaaleansininen B] (drains mana) +Brown bear [tummanruskea N], black bear [musta N], polar bear [valkoinen N] +Beholder [musta e] (petrifying ja paralyzing gaze) + +C +Coal slug [tummanharmaa f] (savupilvi/darkness) +Coatl [valkoinen l](lisko höyhenillä varustettuna) + +D +Small dog [tummanruskea d], big dog [tummanruskea d], dog pack leader [vaaleanharmaa d] +Disruptor [sininen X]](bypass armor) +Fire dragon [punainen D], ice dragon [valkoinen D], two-headed dragon [vaaleanharmaa D], three-headed dragon [tummanharmaa D] +Djinni/genie [vaaleansininen D](toive, ehkä?) +Doppleganger [pelaajan värinen @], greater doppleganger [pelaajan värinen @] +Dark angel [musta @] +Fire daemon [punainen &], ice daemon [valkoinen &], air daemon [vaaleanharmaa &], earth daemon [tummanruskea &] + +E +Elk [tummanruskea y](siis hirvi, eikä kirjoitusvirhe) +Dark elven wizard [musta u], grey elven bard [harmaa u], + +F +Forgotten hero [randomvärinen @] +Trained fox [punainen tai punaisenruskea y] +Falcon [valkoinen m] +Floating eye [punainen e] +Fire elemental [vaaleampi punainen E], greater fire elemental [tummanpunainen E] + +G +Goblin [vaaleansininen g], goblin hiflyer [väriään vaihtava g](confuse), goblin chieftain [tummansininen g], goblin anarchist [musta g](polttopulloja) +Goat [valkoinen y] +Genocider [punainen X](instakill/death ray) +Gibberling [sininen r] +Iron golem [musta Y], clay golem [vaaleanharmaa Y], snow golem [valkoinen Y], sand golem [oranssi Y], coal golem [tummanharmaa Y], flesh golem [vaaleankeltainen Y], adamantium golem [musta Y], eternium golem [musta Y], wax golem [valkoinen Y], wood golem [tummanruskea Y], ice golem [sininen Y], stone golem [vaaleanruskea Y] +Gnoll [vaaleanruskea g], huge gnoll tummanruskea g], gnoll chieftain [vaaleanruskea g], gnoll king [keltainen g] + +H +Hippie [väriään vaihtava @](confuse) + + +I +Ice elemental [vaaleansininen E], greater ice elemental [tummansininen E] +Imp [punainen I] + + +J +Jackal [vaaleanharmaa j] + + +K +Kobold [vaaleanvihreä k], large kobold [tummanvihreä k], troll-sized kobold [ruskea k], kobold chieftain [vaaleanvihreä k], kobold mage [sininen k] +Ki-rin [keltainen F](death ray) + +L +Lion [keltainen f] + +M +Mistress [musta @](ruoska) +Mammoth [ruskea M] + + +N +Necromacer [tummanharmaa @] +Nova [keltainen *](materia köntti) + + + +O +Orc [tummanvihreä o], orc bouncer [punainen o], orc chieftain [tummanvihreä o], orc king [keltainen o], orc archer [vaaleanvihreä o] + +P +Priest [valkoinen @] + + +Q +Quantum physician [oranssi @](fireball) + + +R +Rat [vaaleanruskea r], huge rat [tummanruskea r], mutated rat [vihreä r] +Raven [ruskea m] + +S +Large spider [vaaleansininen S], huge spider [ruskea S], tarantula [tummansininen S], black manta [musta S] +Skeleton [valkoinen z] + + +T +Troll [ruskea T], troll barbarian [ruskea T], tiny troll [ruskea t], troll emperor [tummansininen T], troll king [keltainen T], troll mage [vaaleansininen T], troll fireblower [punainen T] +Tiger [oranssi f], sabre-tooth tiger [valkoinen f] + +U +Unicorn [valkoinen U] + +V +Vampire [musta V] + + +W +Wolf [tummanharmaa j], silver wolf [valkoinen j], dire wolf [tummanharmaa j] +Werewolf [musta j] +walking tree [vihreä T] +walking plate-mail [tummanharmaa [ ] + +X +Xorn [tummanruskea x](elävä maaklöntti) + + +Y + + + +Z +Zapper [keltainen Z](sähköä) +Zombie [ruskea z] + + + +- Jotkin monstereista ovat tietysti samanvärisiä +- Yritin laittaa merkit monsuille ihan ajatuksen kanssa +- Onhan tässäkin tietysti bugeja. Herjat maililla. diff --git a/Doc/Obsolete/NP.txt b/Doc/Obsolete/NP.txt new file mode 100644 index 0000000..f31ca82 --- /dev/null +++ b/Doc/Obsolete/NP.txt @@ -0,0 +1,37 @@ +Weigth = Volume * Density / 1000 +NPBonus = NutritionValue() * Volume * Density * NPModifier / 1000000 + +Eatable items: + +Name: Material NV Volume cm^3 Density g/dm^3 NPModifier Weigth g Turns NPBonus +banana bananaflesh 200 150 1200 1.0f 180 1 36 +holybanana bananaflesh 200 1500 1200 1.0f 1800 1 360 +lump bananaflesh 200 600 1200 1.0f 720 1 144 +lump schoolfood 100 600 1500 1.0f 900 1 90 +lump pepsi 400 330 1500 1.0f 495 1 198 +potion omleurine 30 1500 1000 1.0f 1500 1 45 +bone bone 20 1500 2000 1.0f 3000 1 60 +loaf pork 400 500 1200 1.0f 600 1 240 +loaf beef 400 500 1200 1.0f 600 1 240 + +Corpses: + +player humanflesh 200 60000 1200 0.01f 72000 6 144 +farmer humanflesh 200 30000 1200 0.01f 36000 3 72 +cityguard humanflesh 200 60000 1200 0.01f 72000 6 144 +shopkeeper humanflesh 200 100000 1200 0.01f 120000 10 240 +oree pepsi 400 110000 1500 0.01f 165000 11 660 +swatcommando humanflesh 200 110000 1200 0.01f 132000 11 264 +ennerbeast ennerbeastflesh 200 30000 1800 0.01f 54000 3 108 +darkfrog darkfrogflesh 1000 2500 1200 0.01f 1200 1 30 +elpuri elpuriflesh 1000 277500 2400 0.01f 19200000 800 6660 +elpuri pork/beef 400 277500 2400 0.01f 19200000 800 2664 +froggoblin goblinoidflesh 200 25000 1200 0.01f 30000 3 60 +conicalmommo brownslime 100 250000 400 0.01f 100000 25 100 +flatmommo brownslime 100 150000 400 0.01f 60000 15 60 +golem bananaflesh 200 100000 1200 0.01f 120000 10 240 +golem pork/beef 400 100000 1200 0.01f 120000 10 480 +wolf wolfflesh 400 40000 1200 0.01f 48000 4 192 +dog dogflesh 400 30000 1200 0.01f 36000 3 144 +jackal jackalflesh 400 20000 1200 0.01f 24000 2 96 +donkey donkeyflesh 400 40000 1200 0.01f 48000 4 192 \ No newline at end of file diff --git a/Doc/Obsolete/NameSystem.txt b/Doc/Obsolete/NameSystem.txt new file mode 100644 index 0000000..9ea2e60 --- /dev/null +++ b/Doc/Obsolete/NameSystem.txt @@ -0,0 +1,70 @@ +curved flaming iron two-handed sword of destruction +valpurium can +ancient sparkling diamond mace "Neerc Se-Ulb" + +curved blessed rustproof flaming +5 iron two-handed sword of destruction named "Oblivion" +the curved flaming iron two-handed sword of destruction +a curved flaming iron two-handed sword of destruction +curved flaming iron two-handed swords of destruction +the curved flaming iron two-handed swords of destruction + +flaming iron two-handed sword of destruction +the flaming iron two-handed sword of destruction +a flaming iron two-handed sword of destruction +curved iron two-handed swords of destruction +the curved iron two-handed swords of destruction + +iron two-handed sword of destruction +the iron two-handed sword of destruction +an iron two-handed sword of destruction +iron two-handed swords of destruction +the iron two-handed swords of destruction + +two-handed sword of destruction +the iron two-handed sword of destruction +a two-handed sword of destruction +two-handed swords of destruction +the two-handed swords of destruction + +Erno the vigorous valpurium golem + +curved blessed rustproof broken flaming +5 iron two-handed sword of destruction named "Oblivion" + +curved iron two-handed sword + +Petrus the high priest of Valpurus +Kulgul the vigorous goblin king + +ancient diamond mace named "Neerc Se-Ulb" +banana / iron banana +empty iron can / iron can full of school food +lump of school food + +std::string PostFix(bool Articled) + + +article adjective prefix material namesingular/plural postfix + +std::string Article() const { return "a"; } + +GenerateName(uchar Case, std::string Article, std::string Adjective, std::string Prefix, std::string +{ + if(!(Case & PLURAL)) + if(!(Case & DEFINEBIT)) + return Material[Index]->Name() + " " + NameSingular(); + else + if(!(Case & INDEFINEBIT)) + return std::string("the ") + Material[Index]->Name() + " " + NameSingular(); + else + return Material[Index]->Name(INDEFINITE) + " " + NameSingular(); + else + if(!(Case & DEFINEBIT)) + return Material[Index]->Name() + " " + NamePlural(); + else + if(!(Case & INDEFINEBIT)) + return std::string("the ") + Material[Index]->Name() + " " + NamePlural(); + else + return Material[Index]->Name() + " " + NamePlural(); +} + +NameWithAdjective \ No newline at end of file diff --git a/Doc/Obsolete/Names.txt b/Doc/Obsolete/Names.txt new file mode 100644 index 0000000..8d58557 --- /dev/null +++ b/Doc/Obsolete/Names.txt @@ -0,0 +1,62 @@ +Täydellinen lista: + +mediosus täynnä virheitä +specus luola +sanctifico teen pyhäksi +infensus hostiili * +gladius miekka +arcus jousi +pro bono publico yleisen hyvän vuoksi +ultimatum päätös +in perpetuum loputtomiin +fatum kohtalo +iter matka +iter vehemens ad necem väkivaltainen matka kohti kuolemaa, lyhenne IVAN +occasio mahdollisuus +occidio verilöyly +omnis kaikki +oraculum ennustus, oraakkeli +origo/orsus/ortus alku +ostium sisäänkäynti +mundus maailma, ihmiskunta +via tie +vita elämä +liber vapaa +libertas vapaus +ad astra tähtiin, voittoon +bonum maximum paras +discipulus oppilas +immortalis kuolematon +pasta ex iecore anserum hanhenmaksapasteija + +...ex machina koneesta + ++ yhdistelmät näistä (esim. via infensa) + +------------------------------------------------------------------------------------------ + +Suuren Pääorganisaattorin Suositukset: + +Omnia ex Machina Omnia tai OEM Kaikki koneesta +Mundi ex Machina Mundi tai MEM Koneesta maailmoja + +Hyvin mahtipontisia (ehkä vähän liioitellunkin), monikossa sen vuoksi, että yksiköitä +omnis ja mundus ovat vaikea taivuttaa ainakin suomessa (vrt. pelataanpa omnista/omniaa, +kumpi parempi?). Kirjainlyhenteet hauskoja. + +In Omnia Libera Libera tai IOL Kaikessa vapaa +Vita Libera Libera Elämä vapaa + +Ei niin mahtipontintisia, kuvaavat erittäin hyvin ideaamme pelaajan toimien vapaudesta. Lyhenne +Libera eli vapaa sopii GNU-pelille. + +---------------------------------------------------------------------------------------- + +Kolhoosiorja suosittelee: + +Infensus - järkevä sisältö ja kuulostaa hyvältä +iter vehemens ad necem - ei ehkä nimeksi, mutta se pitää laittaa silti joksikin aliotsikoksi. +infensus pasta ex iecore anserum ex machina + +infensus mundus +...ultimatum diff --git a/Doc/Obsolete/Need.txt b/Doc/Obsolete/Need.txt new file mode 100644 index 0000000..cb48031 --- /dev/null +++ b/Doc/Obsolete/Need.txt @@ -0,0 +1,15 @@ +Valpurus - Attnam +Seges - city on a flood plain +Atavus - gift factory on tundra +Sophos - Hog +Dulcis - elven city full of bards +Silva - druid city in the middle of a deep forest +Legifer - capital of great nation +Loricatus - dwarven city deep under ground +Nefas - casino +Cleptia - thieves' guild +Mellis - headquarters of a multinational corporation +Cruentus - Sparta +Infuscor - city of the dead +Scabies - mutant base +Mortifer - chaos plane \ No newline at end of file diff --git a/Doc/Obsolete/NeededItems.txt b/Doc/Obsolete/NeededItems.txt new file mode 100644 index 0000000..2733094 --- /dev/null +++ b/Doc/Obsolete/NeededItems.txt @@ -0,0 +1,14 @@ +Shirt. +Wand of locking. +Wand of resurrection. +Wand of teleportation. +Amulet of life saving. +Amulet of ESP. +Ring of fire resistance. +Ring of polymorph. +Ring of strength. +Ring of see invisible. +Ring of teleportation. +Potion of invisibility. +Whistle. +Magic whistle. diff --git a/Doc/Obsolete/NewGoals.txt b/Doc/Obsolete/NewGoals.txt new file mode 100644 index 0000000..e903ab8 --- /dev/null +++ b/Doc/Obsolete/NewGoals.txt @@ -0,0 +1,83 @@ +New goals for version 0.30 alpha: + +-fixing all known bugs except the Great Surface Destruction Bug +D-single weapon's name not updated (Timo) +D-eating weapons made of an eatable material (Hex) +D-wrath of Seges (Timo) +D-score bug with polymorph (Timo) +D-money is temporarily lost in polymorph (Timo) +D-altar of level 10 bugs (Timo) +D-you can identify monsters in the dark by chatting with them (Timo) +D-lamps on walls break incorrectly (Timo) +D-highlightion is incorrect when the score entry is discarded (Timo) +D-the Great Name Bug which Perttu once found and which we probably forgot to fix (Timo) + -others that may be found (Hex or Timo) + +-adding new essential features +D-adding question to leaving city (Hex) +D-fixing the message of wishing forbidden items (Hex) +D-making fluid spill from breaking potions (Hex) +D-adding more blood in criticals (Hex) +D-making monsters think what they eat (Hex) +D-making rest stop when the player is hungry (Timo) +D-making weapon experience and money affect score and NP not affect it (Timo) +D-making kicking increase the unarmed weapon skill (Hex) +D-reimplementating higher victory types (Timo) +D-adding pray effects for Silva (Timo) +D-making Venius burn only hostile creatures (Hex) +D-making Mellis fill also intact empty bottles and empty cans (Hex) +D-making Loricatus fix broken plate mails (Hex) +D-adding relation effect limits to cannibalism and navastating (Timo) +D-adding Banana and Pick-axe logo to menu (Timo) +D-adding "Name too short!" message (Hex) +D-adding grass, pines and spruces to Attnam once the pics are available (Timo) +D-removing the polymorph feature that multiplies items (Timo) +D-adding autosave interval option (Timo) +D-making plate mails much less fragile when they are kicked (Hex) +D-adding infravision to some monsters (Timo) +D-prohibiting the saveload possibility caused by the nature of the corner X (Timo) +D-adding "there is something leading downwards here" message (Hex) +D-adding death in the dark messages (Timo) +D-adding information of death level to hscore (Timo) + +-fixing buggy graphics that are buggy due to code +D-can (Timo) +D-fallen valpurist (Timo) +D-Oree (Timo) +D-froggoblin (Timo) +D-earth (Hex) +D-seats (Hex) +D-stairs (Timo) +D-corpse (Timo) + -others that may be found (Timo) + +-fixing buggy graphics that are buggy due to overuse of material colors +D-many items and some terrains (Tuukka) +D-open can (Tuukka) + +-fixing buggy graphics due to other reasons +D-frog (Tuukka) + +-creating new graphics (to make Attnam a little less ugly) +D-grass (Tuukka) +D-pine and spruce (Tuukka) +D-librarian (Tuukka) +D-bookcase (Tuukka) +D-concubine (Tuukka) +D-housewife (Tuukka) +D-slave (Tuukka) +D-fountain (Tuukka) +D-bed (Tuukka) +D-tome (Tuukka) +D-broken lamp on wall (Tuukka) + -others that may be needed (Tuukka) + +-a lot of testing (everyone that can be forced to do it) + +-other, non-executable related jobs + -creating the homepage (Ikki) + -finding a good screenshot for the homepage (Hex or Timo) + -collecting a list of best features IVAN 0.30a has to offer + (lighting system, weapon skills, script etc.) for the homepage (Hex or Timo) + -writing a new readme file (Hex and Timo) + -marketing IVAN to public (Hex) \ No newline at end of file diff --git a/Doc/Obsolete/NewGoals1.5.txt b/Doc/Obsolete/NewGoals1.5.txt new file mode 100644 index 0000000..127cb2e --- /dev/null +++ b/Doc/Obsolete/NewGoals1.5.txt @@ -0,0 +1,14 @@ +Outlook tweaks: + + + +//Fountain. +//Starting pet. +//Angel possibility should be increased. +//Ivan should be moved. +//Dungeon shop. +//Kamikaze dwarf. +//Holy books. +Bonefiles. +//Fixing the door abuse. +//Broken & locked doors. \ No newline at end of file diff --git a/Doc/Obsolete/NewGoals1.6.txt b/Doc/Obsolete/NewGoals1.6.txt new file mode 100644 index 0000000..e4a43dc --- /dev/null +++ b/Doc/Obsolete/NewGoals1.6.txt @@ -0,0 +1,15 @@ +Food spoiling and cooking. +Bodyparts. +Item stacking. +Player races and classes. +Relatively complete in-code documentation. + +Traps. +Money item. +Monster names. +Panicking. + +/* + * + * + */ \ No newline at end of file diff --git a/Doc/Obsolete/NewGoals2.txt b/Doc/Obsolete/NewGoals2.txt new file mode 100644 index 0000000..504855e --- /dev/null +++ b/Doc/Obsolete/NewGoals2.txt @@ -0,0 +1,84 @@ +Goals for IVAN version 0.40 alpha +--------------------------------- + +* = a high priority job +? = a low priority job +& = an annoying job +/ = partly done +// = done + +Great Jobs: + +/*&Gcc port. +Block AI system. +*Renewed god system. +//*&Fixing the Great Surface Destruction Bug by remaking FeDX. +*&Relatively complete in-code documentation. +*Food spoiling and cooking. +Many new items and monsters with special effects for each. +Code improvements (see Code Improvements.txt). +Traps. +Cursed/uncursed/blessed statuses for items & the scroll of identify. +Player races and classes. +A second city that will act as a haven for chaotics. +*Item stacking. +*Reintegration of missile weapons. +Manual. +*Money item. +Secret Knowledge Command. +//*Manual rest/go termination. +//Graphic effects for rays. +//Dungeon shops. +Smithy to Attnam. +*Monster names. +?Some random encounters in the wilderness. +//*Various fountain effects & fountains to dungeons. +Bodyparts. + +Smaller Jobs (see SourceForge for details): + +Bones in chaos gods' altars +//Altar's owner change when polymorphed +Heavy item throwing +A tool for combining high score files +Throwers want justice +Message modes +//?Wishing artifacts in WMode +?Sitting worshippers +Confuse +//Exploding wands should be illegal +//Strength requirement for opening things +Money should weight something +Chemical warfare from afar +?Golems shouldn't talk +Attacking Attnamians should be chaotic +//*Alignment to panel +//Possibility to break armor on criticals +//*Some realism to lampfighting +Prohibition of WoS vandalism +Forbidding shop polypiles +//Parchment should burn +//*Verbal danger level to panel +ADD_MESSAGE parameter suggestion +Honey +//*Pentagram safety +Size levels for corpses +?More versatile SeeWholeMapCheat +The name of "a gold throne" is incorrect +Household weaponry +?Copulation command +More realistic hungriness +?Mutinous slaves +//Roubles in Ivan's inventory +Cats should have seven lives +*Wand recharging +Uses for the librarian +//*Dolphin pools should be bigger +Special eat effect for werewolf corpses +//*"Times used" value to wand description +//*Inventory weight should be shown +Effect for critically hitting a golem +Werewolves should gain exp +//Cats should devour rats +//Starting pet +//*Polypile balancement \ No newline at end of file diff --git a/Doc/Obsolete/Next Release 0.40.txt b/Doc/Obsolete/Next Release 0.40.txt new file mode 100644 index 0000000..f5d29cc --- /dev/null +++ b/Doc/Obsolete/Next Release 0.40.txt @@ -0,0 +1,174 @@ +* high priority +// done +/ partly done or needs testing +(r) rejected +(s) suspended + +//Datasoitujen luokkien tarkistus + erikoisyliajot. (timo) +//Nostotekoäly. +//Kick ja bite toimimaan, jos kädet irti. (timo) +//Pertun kasvot. (timo) +//Zoom lookiin. (hex) +//Värien tarkistusläpikäyminen. +//Enner beast ihmiseksi + pää kuntoon. (timo) +//Menua vasemmalle. +(r) Damage estimate kuntoon. (timo) +//Werewolfit kuntoon. (timo) +//Paneeli kuntoon: +//* Uusi siluetti. (kahvi) +//* Itemit sen ympärille. +//* Attribuutit ja status alle. +//Efektien läpikäyminen. (timo) +//Miina kuntoon. (timo) +//Lookmoden messaget kuntoon. (timo) +//Bodypartien ja actioneiden scoret. (timo) +(r) Luut kuntoon. (timo) +(r) BloodColorin animaatiostuktuuri. (timo) +//Paniikki. (hex) +//Pari kolme uutta artifaktiasetta, mutta ei miekkoja. +//Leather armorin nimi kuntoon. (hex) +//Taistelun tasapainotus. (timo) +//Animaatiot: Artifaktit, (s) lamput... +//Amulet of life saving. (hex) +//Amulet of ESP. +//Ring of polymorph. (hex) +(r) Ring of strength. +//Ring of infravision. (timo) +//Ring of teleportation. (hex) +(r) ?Potion of invisibility. (hex) +//Magic whistlen kuva. (kahvi) +//Magic whistle. (hex) +(s) Inhandspicit kuntoon. (timo) +//Ruumiinosien hoitotavat: +//* Temppelit rahaa vastaan - raaja paikoilleen (1 hp) tai uusi luonnollinen raaja (1 hp). (hex) +//* Atavus - lahjaraaja (max hp). (hex) +//* Seges - raaja paikoilleen (max hp) tai uusi luonnollinen raaja (max hp). (hex) +//* Silva - raaja paikoilleen (1 hp) tai uusi luonnollinen raaja (1 hp). (hex) +//* Loricatus - mithril raaja (max hp). (hex) +//* Scabies - mutaatioraaja eli raaja paikoilleen (1 hp) tai uusi luonnollinen raaja (1 hp), mutta ominaisuuksia on muuteltu satunnaisesti. (hex) +//* Cruentus - teräsraaja (max hp). (hex) +//* Potion of healing - uusi luonnollinen raaja (1 hp). (hex) +//* Enkelit (kerran 2500 vuorossa) - raaja paikoilleen (1 hp). (hex) +//* Perttu (kerran 16384 vuorossa) - kaikki raajat paikoilleen + kaikki hp:t maksimiin. (hex) +//Raajapulan vaikutus kickiin ja dodgaukseen. (timo) +//Avaimelle kuva. (kahvi) +//Helmet. (hex) +//BaseEmitationit toimintaan. (timo) +//Pertun nutin wishaaminen. (timo) +//Orc. +//Prices. (timo) +//OfferModifiers. (timo) +//NPModifiers. (timo) +//1-2 spesiaali helmettiä, cloakkia, gauntlettia, belttiä ja boottia. +//?Dipping to corpses, containers etc. +//Aliases!!!!!!!!!!!!!! (timo) +//Potion of poison. (timo) +//One handed penalties. (timo) +//Poisonous snake. (hex) +//Biting shouldn't make bodyparts fly. +//People shouldn't "die screaming" if their heads are not present. +//Shields. (timo) +//Lower all unarmed to hit values. (timo) +//Skeleton bodyparts. +//Amuletit, cloak +//Unicorns +//Effects and exps for wisdom and charisma. +(s) ?Shirt. +//Kamikaze dwarves. +//Naming hostile monsters. +(s) ?The flight of wielded item and amulet. +(s) ?Cats & rat corpses. +//Things thrown at doors. +//%s" +//CollectCreatures +//Drinking should be separated. +//Skeleton volume. +//"snake is standing" +//Genie's strength. +//Items inside chests carried by player at death should be shown. (hex) +//Consummo's pray effect. (hex) +//"You didn't manage to get onto the high score list" message. +//Glitter effect. +//Fire effect. +(s) Lightning effect. +//Flies over spoiling items effect. +(s) Monster mine triggering should be limited. +//Boots and gauntlets should be generated in pairs of two. +(s) Re-enable Golden Eagle Quest danger requirement. +//Prevent escape from Oree. +//?Configurations should be visually separated with colors. +//Blocking and visibility should be considered when calculating relative danger. +//Spiders must not trigger mines. +//Hit effects of thrown items. +(s) Applying wielded item should be possible. +//?Vodka. +//Splitting lines in highscores. +//Calamus and whip of Calamus should work together. +//Treasure room to the right arm of the Cathedral. +//?Artifact vault. +//Village and tunnel. +//Kick system should be checked. (timo) +//Kamikaze dwarves should be separated by colours. +(s) ?Manual. +//Item possibilities. (timo) +//Skull. (hex) +//Wand of cloning. (hex) +//Water should make gunpowder useless! +(r) ?Temporary invisibility should be cancelled if the character hits something. +//Applying a whistle should set the waypoint of all monsters in the level to point to the square under the player. (hex) +//Prevent digging with a broken pick-axe. +(s) Stethoscope should show relative danger. +//Remove the school food effects of scabies (or make them a lot more seldom). +(s) ?Raven. +Kicking chests. +//Prohibit spoiling in shops and on the cathedral floor. +/?Mellis should give information about unknown gods. +//Whips shouldn't really break when hitting armor. +//Balancing Loricatus. +//?Charges for magic whistle. +//Smithy to Attnam. +//Cut down the size of Elpuri's cave. +//Mana should be hidden. +//Solving the Elpuri Cave distance problem. +//Apply + chests. +//Stats of summoned bodyparts must be made more intelligent. +//Ergonomification of corpse consumption. +//RDAM and similiar unclear attributes should be moved to an own screen. +//?Snake flesh should be poisonous. +//?Some special effect for daemon flesh. +//Checking throw balance. +//Pick-axe available in the lowest levels. +//Boot m-color. +//?Look stretch config option. +(r) ?If a mine in the inventory is active, it should be displayed somehow. +//Potion full of antidote. +//?Teleport to level 10. +//Selecting from gear when using SoCM/SoEW/SoEA. +//Weapon volumes should be more realistic. +//?Flaming effect for holy banana. +//?Solving the iron cloak conundrum. +//?Broad tapeworm. +//Imperialist quest. +//Moving the lamp room to the tunnel. +//New background story. +//?Armor breaking. +//Tunnel boss. +(s) ?Something special to tunnel level 2. +//?Penalty for eating spoiled food. +//Pets shouldn't eat the player's bodyparts. +(s) ?Village duelist. +//?olterraincontainer +(s) Smith prices. +//?Kamikaze dwarf generation. +//Renaming "single weapon skills". +//?Scroll of repair. +(s) ?Prices of golden items. +//If a monster is polymorphed and kills the player, this should be shown in the high score entry. +//High score entry should show the dungeon where the player died. (killed by a bat in level 1 isn't informative as there are two such levels) +//Invisibility + selling. +//Bear traps should break eventually. +//More color states to silhouette. +//Digging walls should yield stones. +//Abort messages should tell the user to e-mail ivan-users@sourceforge.net. Remember to tell the user to state their version number/OS etc. +//?Zombie flesh should already be spoiled when a zombie dies +//Wait for keypress when player gets stuck in a bear trap. diff --git a/Doc/Obsolete/Next Release 0.401.txt b/Doc/Obsolete/Next Release 0.401.txt new file mode 100644 index 0000000..221777d --- /dev/null +++ b/Doc/Obsolete/Next Release 0.401.txt @@ -0,0 +1,32 @@ +* high priority +// done +/ partly done or needs testing +(r) rejected +(s) suspended + +//Fountains should never give permanent lycanthropy-state. +//Nefas shouldn't confuse plants. (maybe they should hit randomly characters around them) +//Healing should be terminated if player enters faint state. +//Atavus' bodypart fix doesn't work +(r) Odd message: "The greater carnivorous plant looks faster" +Entering a dungeon and loading an autosave saves some time. +Skeletons are probably seen by infravision. +?Messages after teleport. +"Valpurus shall not carry more continents!" +//Golden plate mails are worth less than iron ones. +//Wishing improvements. +It seems that due to certain bitshift rounding errors, resting causes more exp minus than pressing the NOP key. +//Remove the 3 letter limit for names. +//Amulet of Life saving generates high score entry. +//Explosions should be stopped by walls. +//Remove the WMode options. +//School food thingy. +//Mondedr. +//Chameleon + invisibility. +//Ommel urine + damaged metal hand. +//Golem trouble. +//Quitting in Win. +//"Angel of Atavus is crawling here." +//Horn of bravery and horn of fear. +//Poison + golems. +//When two boots are lying in the same square as player the game should say "there are 2 iron boots" here NOT "there are several items here". diff --git a/Doc/Obsolete/Optimization.txt b/Doc/Obsolete/Optimization.txt new file mode 100644 index 0000000..e8649c3 --- /dev/null +++ b/Doc/Obsolete/Optimization.txt @@ -0,0 +1,11 @@ +//datamember::GetMember() +//continent::GetRandomMember +//protosystem::BalancedCreateMonster +//map databases (speed N -> log N) +//panel is drawn maybe too often? +//Removing air. +//Limits in bitmap.cpp +//AI equipment selection calls UpdatePictures() too often. +//igraph::RemoveUser() +//Cache ability to block light +//AlterLuminance diff --git a/Doc/Obsolete/Panthenon.txt b/Doc/Obsolete/Panthenon.txt new file mode 100644 index 0000000..baf471c --- /dev/null +++ b/Doc/Obsolete/Panthenon.txt @@ -0,0 +1,180 @@ +******** + +G++ L++ King of Gods, Valpuri the Great Frog +---------------------------------------------------- + +Favours: +-gives a valpurium two-handed sword +-gives a wish +-makes you radiate with holy light for some time + +Punishments: +-smites with a small hammer +-(sends Greater Ligth Frogs to hunt the player) + +G+ L+ Seges, light goddess of health and nutrition +------------------------------------------------------------ + +Favours: +-heals when hurt +-restores lost limbs +-feeds when hungry, with pure nutrition or food items +-(cure disease) +-(cure poisoning) +-trains endurance + +Punishments: +- + +G+ B= Atavus, light god of charity and munificence +------------------------------------------------------------ + +Favours: +-gives random good items as gifts +- + +Punishments: +-removes a random item +-if none, reduces gifts (attributes) + +G B- Dulcis, light goddess of love and art +----------------------------------------------------- + +Favours: +-charms monsters around +-(increases charisma) + +Punishments: +-(reduces charisma) +-aggravates monsters + +G- L+ Venius, light god of law and order +-------------------------------------------------- + +Favours: +-rigtheous fire damages enemies around +-gives a steel plate mail + +Punishments: +- + +G- B+ Consummo, light god of knowledge and magic +---------------------------------------------------------- + +Favours: +-teleports the player elsewhere +-teleports monsters elsewhere +-gives a wand of polymorph +-gives scrolls of teleportation +-(increases intelligence) +-(uncurses items) +-gives a lamp + +Punishments: +- + +******** + +N+ C- Silva, goddess of nature +---------------------------------------- + +Favours: +-creates tame wolves +-(increases wisdom) +-causes earthquake +-(plants herbs) + +Punishments: +-creates hostile wolves +-polymorphs the player into a lousy animal monster + +N= L- Loricatus, god of fire, machines and weaponry +------------------------------------------------------------- + +Favours: +-enchants wielded item's material +-gives a scroll of change material + +Punishments: +-changes wielded item's material to bananaflesh + +N- L Mellis, god of money, trade and politics +-------------------------------------------------------- + +Favours: +-trades items +-(increases charisma) + +Punishments: +-spreads bad rumours about the player + +******** + +E+ B- Macellarius, chaos goddess of greed and forbidden pleasures +--------------------------------------------------------------------------- + +Favours: +-gives Omle urine + +Punishments: +- + +E+ C+ Infuscor, chaos god of wrong knowledge and vile magic +--------------------------------------------------------------------- + +Favours: +-gives scrolls of teleportation +-gives scrolls of create monster +-gives a wand of striking +-creates tame golems + +Punishments: +-creates hostile golems + +E C- Calamus, chaos god of crime +------------------------------------------- + +Favours: +-trains agility +-trains perception +-teleports the player elsewhere +-makes the player invisible + +Punishments: +-steals a random item that is not in the gear + +E- B+ Cruentus, chaos god of war and blood +---------------------------------------------------- + +Favours: +-gives a steel two-handed scimitar, if player's weapon isn't great +-trains strength +-trains endurance +-heals battle wounds + +Punishments: +-sends dark knights to hunt the player +-hurts the player a lot + +E- C Scabies, chaos goddess of mutations, deseases and famine +------------------------------------------------------------------------ + +Favours: +-gives slightly spoiled food for the hungry +-causes sickness to surrounding monsters +-trains endurance + +Punishments: +-sends mommo slimes to hunt the player +-causes sickness +-spoils random food items + +E-- C-- Destroyer of Worlds, Erado the Master Evil +---------------------------------------------------------- + +Favours: +-gives H'taed Foneer Cse-ulb + +Punishments: +-bolt of black energy +-(sends Greater Dark Frogs and Bill's wills to hunt the player) \ No newline at end of file diff --git "a/Doc/Obsolete/Pit+\304\204+\304\204.txt" "b/Doc/Obsolete/Pit+\304\204+\304\204.txt" new file mode 100644 index 0000000..6ee723e --- /dev/null +++ "b/Doc/Obsolete/Pit+\304\204+\304\204.txt" @@ -0,0 +1,27 @@ +//Mommoille uusi nimi (LP) +Taiat +Aseskillit +Tallennus ja lataus +Kenttiä älykkäämmiksi (LP) +--Joille jotain +Heittäminen +Tekoäly +Uudelleensyntyminen +Pick axe +--Rukoilu +??Syömismoodit +--Corpset (TP) +Curses +Itemien nostoon virittylä +--The Perttu-juttu +Kuolinmessaget (LP) +(Sairas) High score (LP) +--Scrollit +Statuscorpse +Valpuri Justifier +Inventoriot sun muut +--Monsut Pertun alle +--I& -ongelma +Rivien katkominen +Painot +Uhraaminen diff --git a/Doc/Obsolete/Planplan.txt b/Doc/Obsolete/Planplan.txt new file mode 100644 index 0000000..ca733aa --- /dev/null +++ b/Doc/Obsolete/Planplan.txt @@ -0,0 +1,124 @@ +Sight direction, size 3 +* would allow sneaking etc. + +Animation engine, size 5 +* allows flies over corpses, evil item shine etc. + +Fire effects, size 2 +* animation engine almost necessary +* with ego-item engine, allows flame weapons + +Fluid system that handles special material effects, size 1 +* with fire effects, allows lava + +*** +Ego-item engine, size 1 +* with fire effects, allows flame weapons +*** + +Ego-monster engine, size 1 +* multiplies monster amount and thus increases replayability + +*** +Races, size 2 +* new element to gameplay +* increases replayability +*** + +Guild system, size 3 +* new element to gameplay +* increases replayability + +*** +Multi-tiled creature engine, size 2 +* allows dragons and new mammoth +*** + +Government engine, size 3 +* with battle engine, allows wars + +Battle engine, size 3 +* with government engine, allows wars + +*** +Stat datafile, size 2 +* allows easier balancing +*** + +Wilderness encounters, size 1 +* fun when towns are ready + +Blessed/uncursed/cursed item statuses, size 1 +* new element to gameplay + +Identify system, size 2 +* new element to gameplay + +Magic system, size 2 +* new element to gameplay + +Skill system, size 2 +* new element to gameplay + +Throw remake using linear extrapolation, size 2 +* allows very ergonomic ranged attack system +* allows more impressive explosion effects + +New route algorithm, size 3 +* allows increased hostile AI that finds hiding players +* allows guards and shopkeepers to return to their normal place after battle +* increases level generation speed +* with mouse interface, allows "goto clicked square" command + +AI improvements like panicking and ranged attacks, size 2 +* new element to gameplay + +More commands for team members like "guard", "flee" and "unequip", size 2 +* increase playability + +Day and night, size 2 +* addition to roguelike scene +* allows IvanDev to boast with its light engine + +Random town engine, size 3 +* innovative addition to rpg scene +* increases replayability + +Random castle engine, size 3 +* innovative addition to rpg scene +* increases replayability + +Renewed god system, size 4 +* innovative addition to rpg scene + +Mouse interface, size 5 +* increases playability and enlarges game target group +* with new route algorithm, allows "goto clicked square" command + +*** +Possibility to increase window size or resolution while keeping the tile size, size 1 +* increases playability especially for new players +*** + +In-game manual, size 2 +* increase playability especially for new players + +Monster memory, size 2 +* increase playability especially for new players + +Character sagas, size 2 +* rewards the player + +Afterlife, size 2 +* rewards the player in an unseen way + +Bonefile system, size 2 +* new element to gameplay +* allows innovative ghost Überpriests + +Febot engine for the Holy Haven of Gurus, size 4 +* a must + +Colored messages, size 5 +* would make the game prettier and give more information for the player +* note: if this ought to be done, it must be done as soon as possible, as it becomes more difficult with every message added \ No newline at end of file diff --git a/Doc/Obsolete/Possib.txt b/Doc/Obsolete/Possib.txt new file mode 100644 index 0000000..c581be9 --- /dev/null +++ b/Doc/Obsolete/Possib.txt @@ -0,0 +1,41 @@ +Name Possibility +banana 200 +holybanana 10 +lamp 50 +can 200 +lump 200 +sword 0 +twohandedsword 20 +curvedtwohandedsword 10 +valpurijustifier 0 +axe 200 +pickaxe 100 +spear 100 +platemail 5 +chainmail 50 +maakotkashirt 0 +corpse 50 +potion 200 +bananapeals 100 +brokenbottle 50 +scrollofcreatemonster 100 +scrollofteleport 50 +head 0 +headofelpuri 0 +nut 0 +leftnutofperttu 0 +abone 200 +poleaxe 100 +spikedmace 50 +htaedfoneercseulb 0 +loaf 100 +scrollofwishing 1 +cheapcopyofleftnutofperttu 10 +wandofpolymorph 20 +arrow 200 +headofennerbeast 10 +brokenlamp 50 +scrollofchangematerial 3 +avatarofvalpuri 0 +wandofstriking 10 +brokenplatemail 10 \ No newline at end of file diff --git a/Doc/Obsolete/Races.txt b/Doc/Obsolete/Races.txt new file mode 100644 index 0000000..10c72c2 --- /dev/null +++ b/Doc/Obsolete/Races.txt @@ -0,0 +1,32 @@ +human + +elf ++great mana ++high intelligence ++great wisdom + +drow elf ++great mana ++great intelligence ++high wisdom + +gnome ++high mana ++great intelligence + + +hobbit + +troll ++superior arm and leg strength ++superior endurance +-low perception +-inferior agility and dexterity +-inferior charisma +-inferior intelligence and wisdom +-inferior mana +-can't read + +orc ++good arm and leg strength ++good endurance diff --git a/Doc/Obsolete/Random.jpg b/Doc/Obsolete/Random.jpg new file mode 100644 index 0000000..b9eaa7a Binary files /dev/null and b/Doc/Obsolete/Random.jpg differ diff --git a/Doc/Obsolete/Readme.txt b/Doc/Obsolete/Readme.txt new file mode 100644 index 0000000..a2c5af9 --- /dev/null +++ b/Doc/Obsolete/Readme.txt @@ -0,0 +1,246 @@ + Iter Vehemens ad Necem v. 0.231 alpha + + (eli Väkivaltainen Tie Kuolemaan) + + Copyright (C) Fatal Error Productions (FEP) 1999-2001 + +---------------------------------------------------------------------------- + + Dokumentaatio betatestaajia varten + +---------------------------------------------------------------------------- + +Aluksi +------ + +Kiitos, että vaivauduit imuroimaan IVAN:n. Jos et vielä tiedä, millainen +peli sinua odottaa, paljastamme kyseessä olevan uuden Rogue-tyylisen pelin, +eli keinotekoisen maailman, jossa pelaaja liikkuu pitkin joka kerta +uudelleen luotavaa syvää luolastoa piesten hirviöarmaadoja, joita +tulee matkalla vastaan. Jos olet pelannut Nethackia tai ADOM:ia aiemmin, +tiedät varmasti mistä on kyse. + +Olemme FEP:ssä käyttäneet paljon aikaa tämän teoksen tuottamiseen, ja +luulemme version olevan jo kohtuullisen nautittavaa pelattavaa. Siksi +toivomme, että testaisit peliä kohtuullisen tuokion ja kertoisit meille +mielipiteesi. Tarvitsemme palautetta siitä, mikä on jo hyvää ja toisaalta +siitä mitä pitäisi parantaa, ideoita uusista ominaisuuksista, joita peliin +olisi syytä lisätä, ja tietoa mahdollisista virheistä pelissä. +Jälkimmäisistä kaikkein pahimpia ovat sellaiset, jotka kaatavat pelin, +tekevät siitä läpipeluukelvottoman tai päinvastoin antavat mahdollisuuden +tarkoituksettoman helppoon pelaajan ominaisuuksien boostaamiseen. Ilmoita +HETI jos sellaisia löytyy! + +TÄRKEINTÄ KAIKESSA! +------------------- + +Kun olet pelannut tarpeeksi, lähetä palautteesi ja löytämiesi bugien lista +jompaankumpaan seuraavista osoitteista: + +sonor@phpoint.fi +Timo Kiviluoto, IVAN-ohjelmoijatiimin pääorganisaattori + +hejosa@mantta.fi +Heikki Sairanen, varaorganisaattori ja markkinointijohtaja + +---------------------------------------------------------------------------- + +Laitteistovaatimukset +--------------------- + +Teoreettinen vähimmäisvaatimus on PC ja sen sisällä i386-prosessori tai +uudempi. Todellisuudessa täysin jökkimättömään kokemukseen tarvitset ainakin +300 MHz:n härvelin ja jonkun hitusen järkevämmän tyypinkin sille. Tämä on +kenties liikaa Rogue likelle, mutta tässä vaiheessa emme ole katsoneet +optimointia pääprioriteetiksi. + +Koska pääohjelmoijallamme meni ammoin guruus päähän ja hän vaati saada +tehdä itse kaikki projektin grafiikkafunktiot konekielellä, +näytönohjaintuessa voi ilmetä ongelmia korteissa, jotka eivät ole +yhteensopivia Vesa 2.0:n kanssa tai tue resoluution 800x600 16-bittistä +tilaa. Yhdestäkään sellaisesta näytönohjaimesta emme tosin ole vielä +kuulleet. + +Käyttöjärjestelmistä Win95 ja uudemmat sekä DOS 7.0 on testattu toimiviksi. +Tulevissa versioissa myös Linuxia tullaan tukemaan. + +---------------------------------------------------------------------------- + +Pelaamisesta +------------ + +Liikkuminen +----------- + +IVAN toimii niin kuin suurin osa Rogue likeista, eli pelaajan hahmoa +liikutellaan pitkin luolastoa nuolinäppäimillä (numpad on paras vaihtoehto). +Jos vastaan tulee hirviö, sitä voi lyödä yksinkertaisesti kävelemällä +(jälleen nuolinäppäimistä) hirviötä kohti. + +Oven tullessa vastaan sitä päin voi kävellä ja ohjelma kysyy avatataanko se. + +Lista erikoiskomennoista +------------------------ + +Pelissä on muita enemmän tai vähemmän hyödyllisiä funktioita, jotka on +listattu tähän. Pelissä listan saa myös esille, painamalla ?-näppäintä. + +Huomaa, että esim. näppäimet 'd' ja 'D' tekevät täysin eri asioita, eli +shiftin tai caps lockin tila on huomioitava! + +Komennon nimi (näppäin) = Selitys. + +"consume" (e) = Syö/juo itemeitä. Ohjelma kysyy, minkä itemin haluat +käyttää tähän tarkoitukseen. Myös suoraan maasta syöminen onnistuu. + +"show inventory" (i) = Näytä hahmon kantamat itemit. + +"pick up" (,) = Nosta tavara maasta. Jos tavaroita on useampia, näytä +valikko, josta käyttäjä voi valita nostettavan. + +"quit" (q) = Lopeta peli. (Tätähän et tarvitse eikö niin?) + +"drop" (d) = Pudota itemi. + +"wield" (w) = Ota ase käteen. + +"go up" (<) = Mene ylös portaita. + +"go down" (>) = Mene alas portaita. + +"open" (o) = Avaa ovi tai tavara (esim. can). + +"close" (c) = Sulje ovi. + +"wear armor" (W) = Pue jonkin vaate tai armori päälle. + +"talk" (t) = Puhu. + +"wait" (.) = Odota vuoro, lepää. + +"save" (s) = Tallenna peli ja lopeta. + +"read" (r) = Lue pergamentti tai joku muu tavara. + +"dip" (D) = Dippaa, kasta. + +"increase gamma" (g) = Lisää gammaa. (gamma = melkein valoisuus... ei +ihan mutta melkein). Huom. tämä toimii ehkä noin joka kolmannella koneella. + +"decrease gamma" (G) = Äskeisen vastakohta. + +"show key layout" (?) = Näytä näppäimet. + +"look" (l) = Tarkastele maastoa, monstereita, itemeitä. + +"engrave" (E) = Kaiverra maahan. (Täysin turhaa, mutta hauskaa :) + +"pray" (y) = Rukoile jumalilta apua. + +"kick" (k) = Potkaise jotakin. + +"take screenshot" (S) = Kuvankaappaus, jos haluat jostain syystä laittaa +kuvan parhaasta pelistäsi kotisivullesi tai jotain. + +"offer" (O) = Uhraa. Toimii ainoastaan alttarilla. + +"increase software gamma" (f) = Vähennä gammaa softapohjaisesti. Toimii +kaikilla koneilla. + +"decrease software gamma" (F) = Äskeisen vastakohta. + +Huijaajille +----------- + +Seuraavat näppäimet on tarkoitettu lähinnä tiettyjen ominaisuuksien +nopeaan testaamiseen tuotannon aikana, ja ne ovat täysin turhia normaalille +pelaajalle. Listaamme ne kuitenkin varmuuden vuoksi. + +"wizard mode" (X) = Aktivoi huijaus-moodi. Huom! Tämä on peruuttamaton +toiminto, joka samalla evää highscore-listalle pääsyn. + +"raise stats" (R) = Nosta pelaajahahmon atribuutteja. + +"lower stats" (T) = Laske pelaajahahmon atribuutteja. + +"see whole map" (Y) = Näytä koko kartta. Tämä on varsin hidasta. + +"toggle walk through walls cheat" (U) = Toglaa päälle seinienläpikävely- +huijauksen. + +---------------------------------------------------------------------------- + +Taustaa +------- + +IVAN sijoittuu 'Terra Valpuria'-universumiin, uskonnolliseen maailmaan, +jossa jumaluudet ovat erittäin merkittävässä asemassa ja papisto mahtava. +Pelaaja on nuori valpuristinoviisi, jonka Suuri Valpurin Ylipappi Perttu +lähettää vaaralliselle tehtävälle tappamaan kauhean Elpurin, Pimeän +Sammakon, ja sen lukuisat pahuudenkyllästämät palvelijat. Tosin pelissä +on myös vaihtoehtoisia, vaikeampia ja nautinnollisempia voittamistapoja +tarpeeksi mahtaville ja Valpurin arvossa pitämille... Yritäpä löytää jokin +niistä; jos siihen pystyt, olet aika kova ivannaaja! + +OK, tiedetään, juoni on pelkkä sisäpiirivitsi tällä hetkellä, mutta se +tullaan uudistamaan. Keskitty pelin engineen äläkä välittä niin paljoa +oudosta huumoristamme. + +---------------------------------------------------------------------------- + +Selviytymisguide +---------------- + +Ensi kertaa pelaavalle IVAN saattaa tuntua vaikeahkolta, sillä taktiikat +eroavat monin tavoin muista genren peleistä. Olemme tähän koonneet +tärkeimmät saannöt alkuun pääsemiseksi. + +I +Pysy alussa Pertun lähellä, sillä vanhurskaana valpuristina hän auttaa +nuorempaansa monin tavoin. Ensimmäinen kenttä on muutenkin paljon helpompi +kuin seuraavat; jos sinulla on ongelmia siellä niin älä mene alaspäin! + +II +Syö vatsasi täyteen, mutta älä enempää. Mutta muista ruokaillessasi: +Olet valpuristi, etkä saa navastaa. + +III +Rukoile kun on tarvis. Hyvät jumalat ovat alussa hyödyllisempiä (Atavusta +ja Valpuria lukuunottamatta, he vaativat erittäin hyviä suhteita ennen kuin +antavat mitään). Veniuksen pyhän tulen, Dulciksen mesmerisoivan laulun ja +Consummon avaruudenmuuntelujen avulla selviät helposti piiritystilanteista +ja Seges auttaa jos ruokaa pääsee loppumaan. Kaaottisia jumaliakin voit +kokeilla, he antavat joskus suuriakin lahjoja, mutta suuttuessaan ovat +erittäin vaarallisia. + +IV +Käytä jokaista hirviötä vastaan sitä asetta, jota oikeassa elämässä +käyttäisit. Et varmasti mene pieksemään pientä pimeää sammakkoa kahdenkäden +miekalla, ethän osu mihinkään. Sen sijaan yrität rutistaa sen käsin. +Tämä on erittäin tärkeää huomata, sillä joihinkin monsuihin ei alussa kerta +kaikkiaan voi osua isoilla ja painavilla aseilla. + +V +Jos olet haavoittunut, älä lähde heti berserker-hyökkäykseen, vaan odota, +kunnes HP on melko lähellä maksimia. Muuten saat itsesi varmasti hengiltä. + +VI +Jos hirviö on liian vaikea pala sinulle, juokse! Olet usein nopeampi kuin +vastustajasi. Jos et, toisaalta, ainoa vaihtoehto on eksytys (monsut on +tyhmiä kuin vihannekset) tai teleportaatio taian tai Consummolle rukoilun +avulla. + +VI +Jos löydät hyvän haarniskan, pistä se päälle heti kun jaksat kantaa sen. +Pudota muita tavaroita, jos se on välttämätöntä. + +VII +Älä ikinä ja missään tapauksessa taistele burdened-tilassa tai huonommassa. +IVAN:ssa tästä koituva rangaistus on aivan liian suuri. + +VIII +Muista aina expottaa hillittömästi. + +---------------------------------------------------------------------------- + +Hyvää ja voittoisaa pelaamista, toivottaa FEP! \ No newline at end of file diff --git a/Doc/Obsolete/Remember.txt b/Doc/Obsolete/Remember.txt new file mode 100644 index 0000000..204a175 --- /dev/null +++ b/Doc/Obsolete/Remember.txt @@ -0,0 +1,19 @@ +*Inhandspics. +//Messages. +(s) Multi-tiled creatures. +(s) New team system. +/*Corpse. +//Corpse size levels. +(s) Lines & words. +//*Healing. +*Danger values & difficulty. +//Name system. +//(s) Vector based humanoid read. +(s) Throw flipping. +/*Silhouette. +//*Graphical item menu. +/*Effect types. +Priest. +/*Pick-axe. +//*New state system. +Oree author. \ No newline at end of file diff --git a/Doc/Obsolete/SecretList.txt b/Doc/Obsolete/SecretList.txt new file mode 100644 index 0000000..f608d30 --- /dev/null +++ b/Doc/Obsolete/SecretList.txt @@ -0,0 +1,78 @@ +//Gum inhandspics. +//Dark knight and guard outlook. +//Chameleon. +//Vodka. +//Broad tapeworm. +(s) More rooms to mansion. +//Patrol guard. +//BeginTemporaryState design flaw. +//*The great equipment bug. +//Kamikaze dwarf danger. +(s) ?Wolf team. +(s) ?Mellis shop +(s) Checking wands. +(s) Checking throw. (mines, artifacts, lumps etc.) +(s) Checking school food, pepsi, dark flesh. +//Return AI. +//Mutant ass emitation. +//Dipping wielded items. +//Puppys. +//*Bear trap askkeypress. +//*Poison trouble. +//Strongbox. +//*Better helmet. +(r) kiwis +//infravision +//*heal +//horns +//*wield +//poison cures +//god score +//zombie spoil +//*stethoscope info +(s) healing should be faster when satiated +//pick-axe sv +//confuse + walls +(s) wishing + broken problems +//score bug +//valpurium sword +(s) heal + ESP +//evil ommel urine +//full helmets to monster equipment +//verbal player alignment +//go should be disabled when confused +//what name do you give to snake +//*fluid update +//panic + player searching +//"you don't know any gods" in library +(r) mistresses should be better +//Rondol is WAY too easy. +//explosion of a +(r) polymorph -> rondol +//the boulders silva creates +//8 +//*enner beast reward +(s) Legifer effect +//which monster can be wished? +//which items can be wished? +//dig nps +//throw thv +//patrol guard + AI +//monster cpoly targets +//wand prices +//kiwis +//panic bonus to move spd +//pair trouble (scrolls & wish) +//enchantment limit +//lock breaking algorithm +//bronze shields are too cheap +//inventory entries +//kicking chests +(s) skeletons shouldn't heal +(s) life save effect for valpurus +//kaethos + genetrix +*Final Battle. +Petrus meal +//test on 200mhz +hexbug +shorty description diff --git a/Doc/Obsolete/Small Ideas.txt b/Doc/Obsolete/Small Ideas.txt new file mode 100644 index 0000000..7ef271e --- /dev/null +++ b/Doc/Obsolete/Small Ideas.txt @@ -0,0 +1,234 @@ +* high priority +// done +/ partly done or needs testing/balancing +(r) rejected +(s) suspended +(d) double entry + +//Image flipping (more). +//Partially converting the attribute bonuses to exp. +//Food poisoning state. +//Armor pictures of humanoids. +//Cloning. +(r) How can you change the window to the rigth size? (stickiness) +//*Banana peals should be slippery. +//Weapons should break. +//Uses for vomiting. +//CNAME should be clarified. +//Hit rate of thrown items should be lowered. +//You must be able to open door by throwing items. +//List of killed monsters and list of monsters killed by pets. +//Weapon name should be affected by dip material. +(r) Player's difficulty level should not be influenced by the current weapon/armor? +//*You should be able to fight when overloaded, because it can be abused. (Elpuri's corpse changed to mithril and wielded...). +//*Balancing offervalues. +//*Items on the level could get impactdamage when hit by an earthquake. Doors could also break. +//Donkey should be named "an ass". +//A graphical equipment screen. (from JM) +//*Specialities to daemon flesh. +//*Pressing X while in WMode should give more scrolls of wishing. +//*Random item option to script. +//Punishment of cathedral terrain destruction. +//Monsters that change colors. +//*WMap terrain height should be shown in WMode. +//*Weapon skills should affect attack AP costs as in ADOM. +//Parasites. +//*Savefile versions. +//Monster data in corpses + resurrection. +//Inventory screen could be categorised. +(r, doesn't work) The Great Line Algorithm Idea. (GLAI) +//"You didn't manage to get onto the high score list" +//*Tuukka's alternative walls should be integrated. +//Banapeals should be possible to clean from the Cat without alerting guards. (from Nihil) +//Practise dungeon? (from JM) +//*Player's ability to escape from a level shouldn't depend on his LOS but on the enemies' LOSes. +//Elpuri's lair could be even more frightening. +//*Fluid disappears too fast. +//*Mommos should not leave corpses but just slime. +//***WATER SHOULD MAKE GUNPOWDER USELESSSSS! GRAAAH! +//*Dipping an empty bottle in a fountain should fill it with water. +//*The librarian should sell scrolls of charging. +//Double wielding. (from JM) +//Invisible monsters. (from JM) +//*Explosions should be stopped by walls. +//**Message splitting. +//Angels should carry holy books of their respective OwnerGods. +//Breaking LTerrain in two parts. +//Worldmap LOS disabling. +//Symbolic inventory. +//Symbolic god menu. +//Lists, especially message history, should look better. +//*Hunters must be one-armed. +//*Copper, bronze, wooden weapons. +(r) Nuts could be bodyparts. Or perhaps not. +//Tame symbol. +//Zoom to look. +//"Need brain but you too stupid!" +//Healing potions. (from JM) +(r) Shine effect for jewel and valpurium items with outline. (from JM) +//Slow and haste. (from JM) +//Status bar with colored statuses (poisoned, flying etc). (from JM) +//*Keys to doors. (from JM) +//Pick-axes available in the lowest levels. (from JM) +//Wand of locking. +//*Satiated and bloated states. +//*Average based difficulty! +//*Strength and weapon weight should affect attack speed! +//Item list to lookmode. +//*Aliases for wishing. +//Alphablit to bottles. +//Chatting shouldn't be possible if you aren't intelligent. +//Mommo slimes and armless people shouldn't "try to use their claws to crawl forward". +//*It should be possible to use SoCM to gear. +(r) One of the possible races in Ivan should be an enner beast that could kill people by 'C'hatting with them. +//In script, Square, Random's Walkability should default to true. +//The player shouldn't be able to fly over oceans if all members of his team aren't able to do it. (currently a bat and a pet dog can cross any sea without trouble) +//*One MColor in the leg pictures should be allocated for boots (most humanoids walk barefooted now). +//Wand of striking should also open doors. +//Look stretch config option. +//If an invisible monster hits another monster, the latter should set the hitter's position as its target even if it doesn't see anything there. +//Bodyparts should be rotated when they are severed. +(r) Weight should be the fundamental character of materials, not volume? +//Kicking should do more damage if metal boots are worn. +//Zombies should be able to continue even without a head, maybe even without a torso. +//*Calamus should haste the player. +//Zombies should pick up and use heads if they lack one. +//*Pictures to Equipment Screen. +//*Poison effect for snake flesh. +//Wishing shouldn't perhaps be case sensitive. +//Werewolves should drop their equipment when they polymorph. +//*Team waypoint setting when whistling. +//If the player is polymorphed into a monster that can't heal, the rest command should be disabled. +//Random items to certain monsters' inventories. +//NP shouldn't be consumed if polymorphed into a golem. +//*Petrus's bodyparts should disappear when severed. +//If a mine in the inventory is active, it should be displayed somehow. +//*Non-living bodyparts should be immune to poison. +//"Character attributes" option to secret knowledge. +//*If the handle of a weapon is of the same material as the edge, change material should change it, too. +//Angels and Petrus should use game time to determine whether they can cure people. Their current counter is paralyzed when the player is on another level, which is stupid. +//There should be a one-way teleport at the bottom of the dungeon linked to the first level. +//*Level teleport option to wizard mode. +//*"It is too dark to read" message should be printed before selecting item, not after. +//*Bear traps should break eventually. +//*RDAM and similiar unclear attributes should be moved to an own screen. +(r) When picking up multiple gauntlets and boots, the default value of the inventory slider should maybe be 2. +//Wishing for a boot/gauntlet (or maybe the plural forms?) should yield a pair. +//Some way of adding things to character's inventory from the script. +//Kevlar. +//Lookmode should show whether a creature is panicked. +//"Entering level number 2" in Elpuri's cave should read "Entering Elpuri's cave level number 2". +//*Scroll of repair. +//Displace should consider also displaced creatures moving ability. (example: carnivorous plants can't be displaced) +//Rings appearing in fountains. (shouldn't be hard with the new system...) +//*Mistresses should scream "Oh the delightful pain" when they receive a critical hit. +//Dragon hide material. +//Buffalos shouldn't be able to throw items. +//Food in can's should spoil _much_ slower. +//Skeletons' stomachs shouldn't be able to contain liquids. +//Wand of door creation should create doors to walls. +(r... damn. I thought I was onto something) Etta Nenialoha in forest looking for mushrooms. (with usual ethanol drinks in stack) +//The smith should fix damaged metal limbs for a fee. +//When many same kind of items on ground the message should say "5 Xs here" not "...several items here". +//Polymorph question should default to the monster that the player last polymorphed to. +//When player is killed by something when he is fainting the death message should say "killed by a X while helpless". +//*Dropping multiple items should be made more convenient. +//Bottles should break more easily when kicked. +//Pets should gain wisdom from trial and error. +//Gas masks. +//Wood types. (wood is a _bit_ generic) +//Cumulative comments to script. +//*Using sickle or scythe against plants should yield a bonus. +//There should be a special effect for zapping a holy banana. +//More replies for dogs. +//Engraved messages should be shown in the lookmode. +//The starting puppy dies in every game. It should be named Kenny. +//If player uses heal command (h) when his/her HPs are at max, the game should wait for a player specified number of turns. +//Mellis should sometimes give vodka bottles. +//You should be able to can corpses. +//*HEALING SHOULD STOP IF A MAGE ATTACKS!!! +//Atavus should do something special on Christmas. +//Ivan should break doors. +//Monsters that block light. +//It should be possible to unequip party members. +//It should be possible to command party members to flee. +//Sophos should appreciate sacrificed scrolls and books more than other gods. +//There should be more ways to gain pets for non-lawfuls. +//Fireproof container for scrolls and wands. (from JM) +//Spiders should make webs. +//Scroll of gold detection that would identify every unit made of gold on the level. +//Zombies should spoil. +//Talking to oneself should abuse intelligence. +//If you are illiterate you shouldn't be able to read engraved messages. +//It should be possible to specify the material when polymorphing into a golem. +(r) "Bang your head at" command. +//Mistresses should not exchange their whips for any other weapons. +//Gold golems should be immune to acid. +//Mommos should do acid damage. +//Player with low dexterity should sometimes be caught up applying his own bear trap (or disarming a mine). +//Corrosion for materials. +(r) Acid blobs. (needs corrosion, //might also be done by changing mommos) +(r) Healing liquid should cure poison. +//Fluids could spill also over character pictures. +//Spruces should block light. +//Carrot that would increase perception a little. +//Scroll of harden material. +//Wand of resurrection shouldn't work on golem corpses. +//Msg from hit should depend more on weapon and there should be more variation. ("the monster STABS you with its wooden spear"). +//Breaking different wands should cause different effects. +(r) It should be possible to give the smith a better hammer so he could fix mithril items, too. +//Mushrooms in forest. Cause odd effects when eaten. +//Movement mode: run. +(r) Golems' corpses should be somehow useful. (eg parchment golems should give out parchment scrolls) +//Make golem scroll. +//Weapons should become covered with blood when monsters are killed with them. +//If a player vomits in a shop after eating an item bought there, the shopkeeper should say something like "No returns". +//Wielded wands should sometimes break in fights. +//Windows on walls. +//You should be able to wish for anything. (Very problematic, I know) +(r) Iron and other "good" materials should become more probable for items. (WHY?) +//Fire immunity for imps. +//One should occasionally pray to a wrong god when confused. +//Harp of charm which tames monsters. (from JM) +//Stamina. (from JM) +//Arm m-color 3 should be allocated for gauntlets. +//Message for bottles breaking inside chests. +//Bear traps made of stronger materials should do more damage. +//Being confused should decrease chance to hit. +//New fountain effect: the fountain could suck the player inside and he would come out from another one in some other level. +//New fountain effect: random level teleport. +//Library improvement - When walking towards the book cases should show the inventory (like with doors) +//Go should stop if the player teleports. +//Better mines. +//Polymorphing from nonhumanoid back to humanoid should autoequip items. +//If Vesana is slowed, plants should be created at half speed. +//Leprosy state. +//Scroll of detect material. +//Gods should give items attached to them. +//Bear trap price should depend on material. +//Maybe one should be able to polymorph into a creature only if he has seen one of its kind at least once. +//When team member doesn't see leader but sees a teammate he/she/it should try to go near the teammate. +//(hs) Levitating mushrooms should be able to move. +//(hs+) Autovomiting should choose various squares. +//Kill reasons to the "killed by other reasons" list. +//You shouldn't be able to take equipment from angels; they are sacred after all, and it is strange that you can kill an archangel by removing her stuff and commanding her to equip two plate mails which she can't use, forcing her to bite you instead. (btw, even then she's quite tough :) +//There shouldn't be doors in UT4. It is a natural cave. +(r) Chance-value correlation to change material scrolls. +(r) Scroll of create holy/fallen angel. (from JM) +(r) Scroll of light. (from JM) +(r) Scroll of life, permanent hp plus. (from JM) +(r) Book of fire/frost magic which would have random spells. (from JM) +(r) Pocket pick-axe. +//Stone types. +//Pet donkeys should be able to carry backpacks instead of the player. +//One should be able to use a scroll of change material on one's bodyparts. ("Your humanflesh head changes into a wooden head"... Well, maybe not :) +//Perception should affect chance of detecting traps. (from JM) +//Jammed bananas. +//Material-colored cursor. +//Priests should be created using CreateDivineConfigurations. +//There should be an "unusable" state for bodyparts about to be severed. +//Loricatus shouldn't change whips to steel. +//Changes in attributes should be highlighted by some other colored font. (in the sidebar) +//Vesana and other big monsters should also vomit much more than smaller creatures. +//The message which informs that an item has been broken should indicate who carries the item. +//The order of priests' limb attachment and leprosy curing services should be reversed. diff --git a/Doc/Obsolete/Something.txt b/Doc/Obsolete/Something.txt new file mode 100644 index 0000000..81e7add --- /dev/null +++ b/Doc/Obsolete/Something.txt @@ -0,0 +1,58 @@ +1 type -> +2 god +2 material +2 room +2 character +2 wterrain +2 level +2 dungeon +3 object + +1 entity -> +2 character +2 unit +2 room +2 level +2 dungeon + +2 unit -> +3 object +3 fluid + +3 object -> +4 lterrain +4 item + + entity type + + unit + + object + + lterrain gterrain oterrain wterrain + +material fluid item character glterrain olterrain gwterrain owterrain room level god + + +entity +Contains a void Be()-function. If the entity is loaded into memory and enabled, this function +is called by entitypool::Be() every tick. Character actions, food spoiling and all such +action handlers are handled through Be(). Some of entity's important information, namely +bool Exists and bool Hasbe are (for optimization reasons) stored in a class called entityinfo, +which is inserted into entitypool when the entity is allocated. If Exists == false, the +entity is deleted during next entitypool::Be(). If Hasbe == false, the entity is disabled +and its Be() in not called. For instance lterrains are all disabled, since calling thousands +of useless virtual Be()s every tick would considerably waste resources. + +type +Contains an ushort Type member, which is determined at compile time by the macro system. Each +instantatiable child class with individual properties, such as long sword, farmer, goblin +etc. are derived from type. When such classes are loaded, protocontainers uses Type member to +determine which class truly needs to be allocated. + +unit +Contains materials, which are stored in vector Material. + +object +Contains an individual, material colorized bitmap* Picture. + diff --git a/Doc/Obsolete/Spiral.txt b/Doc/Obsolete/Spiral.txt new file mode 100644 index 0000000..b4bc20e --- /dev/null +++ b/Doc/Obsolete/Spiral.txt @@ -0,0 +1,45 @@ +#define DO_RECTANGLE(CenterX, CenterY, ClipLeft, ClipTop, ClipRight, ClipBottom, Radius, DoWhat)\ +{\ + long Left = (CenterX) - (Radius),\ + Top = (CenterY) - (Radius),\ + Right = (CenterX) + (Radius),\ + Bottom = (CenterY) + (Radius);\ + \ + if(Left < (ClipLeft)) Left = (ClipLeft);\ + if(Top < (ClipTop)) Top = (ClipTop);\ + if(Right > (ClipRight)) Right = (ClipRight);\ + if(Bottom > (ClipBottom)) Bottom = (ClipBottom);\ + \ + long XPointer, YPointer;\ + \ + if((CenterY) >= (ClipTop) + (Radius))\ + for(XPointer = Left, YPointer = Top; XPointer < Right; ++XPointer)\ + {\ + DoWhat;\ + }\ + \ + if((CenterX) + (Radius) <= (ClipRight))\ + for(XPointer = Right, YPointer = Top; YPointer < Bottom; ++YPointer)\ + {\ + DoWhat;\ + }\ + \ + if((CenterY) + (Radius) <= (ClipBottom))\ + for(XPointer = Right, YPointer = Bottom; XPointer > Left; --XPointer)\ + {\ + DoWhat;\ + }\ + \ + if((CenterX) >= (ClipLeft) + (Radius))\ + for(XPointer = Left, YPointer = Bottom; YPointer > Top; --YPointer)\ + {\ + DoWhat;\ + }\ +} + + for(; Radius; --Radius) + DO_RECTANGLE(PosX, PosY, 0, 0, GetXSize() - 1, GetYSize() - 1, Radius, + { + if(Map[XPointer][YPointer]->GetLastSeen() != LOSTurns && GetHypotSquare(PosX - XPointer, PosY - YPointer) <= RadiusSquare) + femath::DoLine(PosX, PosY, XPointer, YPointer, LOSHandler); + }); \ No newline at end of file diff --git a/Doc/Obsolete/Struktuuri.txt b/Doc/Obsolete/Struktuuri.txt new file mode 100644 index 0000000..359b9b7 --- /dev/null +++ b/Doc/Obsolete/Struktuuri.txt @@ -0,0 +1,42 @@ +Struktuuri: + + World Map + ^ + | + Dungeons + ^ + | + Areas + ^ + | + Squares + ^ + /--------------/|\----------------\ + ^ ^ ^ + | | | +Terrains Stack(s) -> Characters + ^ + | + Items + +The Plan: + +Kartta generoidaan esim. Wsimmin koodilla, tärkeät paikat laitetaan joka peliin, +vähemmän essentiaalisista valitaan satunnainen osa. + +Jokaisella kartan ruudulla on pointteri joko staattiseen tai dynaamiseen luolastoon +ja sisääntulokohtaan (myös alempiin kerroksiin, vrt. Unremarkable Dungeon Adomissa). + +Luolasto käsittää kolmiulotteisen taulukon, jossa jokaiselle vokselille määritetään +kenttä janalta 0-255 (0 == penetroimaton kiviblokki, joka ei kuulu mihinkään kenttään). + +Kenttä on kaksiulotteinen taulukko ruutuja, joilla on ala- ja ylämaasto, edellinen +määrittää lattian kovuuden esim. läpihakkuuta varten, jälkimmäinen materiaalin ja +valon läpäisevyyden. + +Jokaisella ruudulla on myös joko yksi tai neljä pinoa riippuen ylämaaston läpäisevyydestä, +ja 0-3 karakteria eri tasoissa (esim. hevonen, pelaaja ja lemmikkilepakko). + +Karaktereilla on yksi pino, josta joukosta varustelu määritellään. + +Pino käsittää 0-65535 esinettä. \ No newline at end of file diff --git a/Doc/Obsolete/Tasks.txt b/Doc/Obsolete/Tasks.txt new file mode 100644 index 0000000..f9223c6 --- /dev/null +++ b/Doc/Obsolete/Tasks.txt @@ -0,0 +1,477 @@ +RL5 työlista versiosta 0.21a alkaen. + +Vastaava organisaattori Timo Kiviluoto. + +<--- Termeistä ---> + +Laajuus: arvioitu tarvittavan työn määrä varsin väljällä, +lähinnä suuntaa-antavalla asteikolla nollasta viiteen. + +<--- A: Minityöt ---> + +Niin pienet työt ettei plussia ja miinuksia ole mietitty. +Kaikilla laajuus 0, ei vaatimuksia. + +0x0000: Elmo urine lisäämään 2 hp:tä (DONE: Perttu) +0x0001: Size näkyviin paneelissa (DONE: Perttu) +0x0002: Inventorioruutua kauniimmaksi (DONE: Timo) +0x0003: Tyhjiäkin pulloja maahan, joskin vähän (DONE: Timo) +0x0004: Pullon pudotus juomisen jälkeen (DONE: Perttu) +0x0005: Cheatterin nälkäkuoleman ergonomistus (DONE: Hex) +0x0006: Maasta syömisen ergonomistus (DONE: Hex) + +<--- B: Koodilliset uudistukset ---> + +Ei näkyvää vaikutusta, sopivat erityisesti niille, +jotka pitävät ohjelmointia itsearvona ja/tai +joille projektin pitkän tähtäimen kehitys on tärkeintä. + +0x0000: Tyyppien uudelleennimeäminen, laajuus 1 + + ei vaadi aivoja + - tylsää rutiinia + - low priority + +0x0001: Funktionimien loogisuuden tarkistaminen, laajuus 1 (SUOSITUS: Timo) + + luultavasti hyvin lyhyt työ + - vaatii rakenteen tuntemusta päällisin puolin + +0x0002: Kaikenkattava esittelyjen uudelleenjärjesteleminen, laajuus 1 (SUOSITUS: Timo | Heikki) + + ei vaadi aivoja + + esteettinen ilo + +0x0003: Characterin ja gamen julma paloitteleminen, laajuus 1 + + ei vaadi aivoja + +0x0004: Helpottavat operaattorit, laajuus 1 (SUOSITUS: Timo) + + gurua + - vaatii kohtuullisen syvällistä syntaksin tuntemista + - low priority + +0x0005: Lyhennemakrot/-funktiot, laajuus 2 (SUOSITUS: Timo) + - vaatii struktuurin läpikotaista tuntemista + +0x0006: Arean muokkaus skriptin vaatimaan funktioformaattiin, laajuus 2 (OSITTAIN DONE: Timo) + + ohjelmoinnillis-looginen nautinto + - vaatimuksena perehtyminen generaatioon + +0x0007: Dungeon-luokan konstruointi, laajuus 2 (SUOSITUS: Timo) + + ohjelmoinnillis-looginen nautinto + - vaatii hyvin vahvaa strukturaalista päätöksentekokykyä + +0x0008: Levelskripti, laajuus 3, vaatimus B/0x0006 (TYÖN ALLA: Ilari) + + ohjelmoinnillis-looginen nautinto + - vaatii tiedostonhallinnan tuntemista ja kohtuullisia aivoja + +0x0009: Olemassaolofunktioiden väsääminen objekteille, laajuus 1 (SUOSITUS: Timo | Heikki) + (Hex selittää kuolevaisille: "Funktio, + joka aijetaan joka itemille joka tikki") + (Timo ihmettelee moista brutaalia ja + rahvaanomaista esitystapaa ja lisähuomattaa, + että funktio "aijetaan" myös joka karakterille + ja maastolle) + + helppoa + - vaatii strukturaalista päätöksentekokykyä + +0x000A: Karakterin atribuuttien niputus, laajuus 1 (PROHIBITIO: Timo) + + erittäin helppoa + - vaatii hitusen strukturaalista päätöksentekokykyä + +0x000B: Rukoilun strukturaalinen osuus, laajuus 2 (DONE: Timo) + + gurua + + high priority versioon 0.22a + - vaatii hyvin vahvaa strukturaalista päätöksentekokykyä + - vaatii kohtuullista syntaksiperehtyneisyyttä + +0x000C: + +0x000D: #includejen vapautus järjestysindepenteiksi, laajuus 1 (SUOSITUS: Timo ^ Heikki) + - vaatii kyvyn kylmänrauhallisesti tappaa lähes kaikki headerinlinefunktiot + cut 'n pasten viiltävällä terällä + +0x000E: Projektin kattavampi kirjastoihin pilkkominen, laajuus 2 (SUOSITUS: Timo) + + selkeä, näkyväkin etu kääntökestoissa + - vaatii struktuurin ja toiminnan kohtuullisen tarkkaa tuntemista + - vaatii kohtuullista strukturaalista päätöksentekokykyä + +0x000F: Virtuaalifunktioiden käyttöalueen laajentaminen luokkien sisälle, laajuus 2 (DONE: Heikki) + + ei vaadi aivoja + - tylsää rutiinia + +0x0010: Koodin porttaus Windozeen ja DirectX:lle, laajuus 4 (YRITETTY: Heikki) + - DirectX:n ymmärtäminen vaatii paljon aikaa ja epäloogisia aivoja + - vaatii sielun myymistä Billille + +0x0011: Vektorin komponentit signediksi + muut merkkimuunnokset, laajuus 1 (SUOSITUS: Timo | Heikki) + - vaatii välttävät aivot + - tylsää rutiinia + +0x0012: Objektin alaluokkien kattava makroittaminen, laajuus 2 (OSITTAIN DONE: Timo) + + vähentää työtä tulevaisuudessa huomattavasti + + preprosessorisyntaksin guru look + - vaatii makrojen toiminnan tuntemista + - paljon tylsää rutiinia + +<--- C: Optimointi ---> + +Jokseenkin näkyvää (koneesta riippuen), +sopii pelin struktuurin ja koneen sielunelämän +hyvin tunteville matemaatikoille. + +0x0000: Valojen optimointi, laajuus 3 (DONE: Timo) + + ohjelmoinnillis-looginen nautinto + + high priority + - vaatii hyvää ko. koodin tuntemista + - vaatii ihan oikeat aivot + +0x0001: Piirron optimointi, laajuus 2 (DONE: Timo) + ± samat kuin yllä + +0x0002: Reitityskoodin optimointi, laajuus 2 (SUOSITUS: Timo) + + vahva ohjelmoinnillis-looginen nautinto + - vaatii ihan oikeat aivot + +0x0003: RAM-tasojen määrän rajaaminen naapurikenttiin, laajuus 1 (SUOSITUS: Timo | Heikki) + + helppoa + - vaatii jonkin verran strukturaalista päätöksentekokykyä + +<--- D: Ominaisuuksien lisäykset ---> + +Koko ajan näkyvä vaikutus, +sopivat kaikentasoisille koodareille ja erityisesti niille, +joille projektin lyhyen tähtäimen kehitys on tärkeintä. + +0x0000: Uusien itemejen lisäys, laajuus 1, osittain vaatimus F/0x0000 (DONE: Heikki) + Tällä hetkellä listalla: (sword), two-handed sword, morning star, axe, + (shirt), halberd, bone + + helppoa + + pitää graafikon iloisena + +0x0001: Uusien monsujen lisäys, laajuus 1, osittain vaatimus F/0x0001 (DONE: Heikki) + Tällä hetkellä listalla: wolf, dog ja golemit + + samat kuin yllä + +0x0002: Humanoidisuuslapsuksien likvidaatio, laajuus 1 (SUSTAINED: mietinnän alla) + + helppoa + - sitaatti Pertulta: vaikea ymmärtää mitä otsikko tarkoittaa + +0x0003: Aseiden ja monsujen tason korrekti painotus levelin mukaan, laajuus 2 (OSITTAIN DONE: Heikki) + - vaatii strukturaalista päätöksentekokykyä ja jonkinnäköiset aivot + +0x0004: Monsujen dynaaminen generointi, laajuus 1 (DONE: Heikki) + - vaatii strukturaalista päätöksentekokykyä ja jonkinnäköiset aivot + +0x0005: Ultimate Überpriest Victoryn monimutkaistaminen, laajuus 1 (DONE: Timo) + + helppoa + + erittäin hauskaa + - low priority + +0x0006: Monsujen tekoälyn saattaminen remakejen 2-4 tasolle, laajuus 1 (DONE: Timo) + + verrattain pieni työ + - vaatii jonkinnäköiset aivot + +0x0007: Lemmikit, laajuus 2-3 tekoälyn tasosta riippuen, vaatimus D/0x0001 (SUOSITUS: Timo) + + hauskaa + - täysin ääliö tekoäly vaatii välttävät aivot + - siedettävä tekoäly vaatii ihan oikeat aivot + +0x0008: Ruumiinosien (ainakin pään ja muun ruumiin) erikseenkoodaus, laajuus 2, vaatimus D/0x0000 (SUOSITUS: Heikki) + - vaatii strukturaalista päätöksentekokykyä ja jonkinnäköiset aivot + +0x0009: Osumapisteiden (ainakin pään ja muun ruumiin) sisäänkoodaus, laajuus 2, vaatimus D/0x0008 (SUOSITUS: Heikki) + + tekee vastaamattoman graafikon iloiseksi + - vaatii jonkinnäköiset aivot + - vaatii kaavamuokkauksen balanssitajuntaa + +0x000A: Itemkohtaiset ergonomiset muistijäljet, laajuus 1 (SUOSITUS: Heikki) + + helppoa + - vaatii hitusen strukturaalista päätöksentekokykyä + - vaatii kaavamuokkauksen balanssitajuntaa + +0x000B: Perception atribuutiksi ja sille vaikutus LOS:iin, laajuus 1 (DONE: Timo) + + lyhyt ja helppo työ + - vaatii strukturaalista tuntemusta + +0x000C: Expanlähteitä lisää, laajuus 1 (DONE: Timo) + + lyhyt ja helppo työ + - vaatii pikku hitusen balanssitajuntaa + +0x000D: Veren koodillinen toteutus, laajuus 1 (DONE: Heikki) + + helppoa + - vaatii hitusen strukturaalista päätöksentekokykyä + +0x000E: Ruumiitten pilaantuminen, laajuus 1, vaatimus B/0x0009 (PROHIBITIO: Timo) + + lyhyt ja helppo työ + +0x000F: Lihatarjonnan monipuolistaminen, laajuus 1 (DONE: Perttu) + + helppo + - vaatii hitusen rakennetuntemusta + +0x0010: Myrkyt, laajuus 1 (DONE: Heikki) + + hauska, lyhyt ja helppo työ + +0x0011: Canin automaaginen avaus ja tiputus, laajuus 1 (DONE: Perttu) + + helppoa + +0x0012: Yleiset containerit, laajuus 1 (PROHIBITIO: Timo) + + helppoa + - vaatii hitusen strukturaalista päätöksentekokykyä + +0x0013: Ovien avauksen nopeuttaminen remaken 4 tapaan, laajuus 1 (DONE: Heikki) + + äärimmäisen helppoa + +0x0014: Pickupin nopeuttaminen stabiloimalla inventorioruutu, laajuus 1 (DONE: Heikki) + + äärimmäisen helppoa + +0x0015: Maasta nuoleminen (consumaamimen), laajuus 1 (DONE: Perttu) + + äärimmäisen helppoa + +0x0016: Näppäimet esille esim. ?:sta, laajuus 1 (DONE: Heikki) + + helppoa + +0x0017: Näppäimet vaihdettaviksi + + Vaihtoehto 1: initiedosto, laajuus 1 (TYÖN ALLA: Niko) + + helppo + - vaatii hitusen tiedostonkäsittelyosaamista + + Vaihtoehto 2: menutyökalu, laajuus 2 + - kunnollinen toteutus vaatii aivot + +0x0018: Itemien päälle saapumisen verbaalinen ilmaisu, laajuus 1 (DONE: Niko) + + äärimmäisen helppoa + +0x0019: Engrave ja tekstin päälle astumisen message, laajuus 1 (DONE: Heikki) + + helppoa + +0x001A: Fontin dynaamisen värimuunnoksen tuki FeVesaan, laajuus 1 (SUOSITUS: Timo) + - vaatii FeVesan välttävää tuntemista + +0x001B: Useampien fonttien tuki inventorioon, laajuus 1, vaatimus D/0x001A (PROHIBITIO: Timo) + + helppoa + +0x001C: Tarinaruudun tekstin keskitys, laajuus 1 (DONE: Heikki) + + helppoa + +0x001D: Lookmode, laajuus 1 (DONE: Niko & Perttu & Heikki & Timo) + + helppoa + +0x001E: Gammakorrektio, laajuus 2 (DONE: Timo) + + luultavasti gurua + + high priority + - vaatii luultavasti syvällistä Vesa-dokumentaatioiden selailua + +0x001F: Rukoiluefektien teko, laajuus 2-5 kattavuudesta riippuen, vaatimus B/0x000B (DONE: Heikki & Timo) + + erittäin hauskaa + ± töitä useammalle henkilölle + ± vaikeustaso vaihteleva + - low priority + +0x0020: Wizard-moden toiminnot, laajuus 2 (DONE: Heikki) + Tällä hetkellä listalla: ei enää mitään. + + yksittäisinä töinä hyvin helppoja + - vaatii struktuurin tuntemusta + +0x0021: Save ja load, laajuus 5 (DONE: Timo) + + high priority + - *paljon* työtä + - vaatii tiedostonhallinnan tuntemista ja kohtuullisia aivoja + - vaatii hyvin vahvaa strukturaalista päätöksentekokykyä + - vaatii struktuurin läpikotaisin tuntemista + +0x0022: ASCII-grafiikka, laajuus 4 (SUOSITUS: Ilari & Niko) + + orjien motivointi ja taas yhden työttömyystekosyyn eliminointi + + tuo versatiilisuutta käyttäjäkohderyhmään + - kenties tylsää enemmän grafiikkatilasta pitäville + - vaatii kohtalaista strukturaalista päätöksentekokykyä + - vaatii välttävää struktuurin tuntemusta + +0x0023: Saven viemän tilan pienentäminen + + Perusremontti: mustien muistikuvien karsiminen, laajuus 1 (DONE: Timo) + + helppoa + + Lisäparannus: kompressio, laajuus 2 (SUOSITUS: Timo ^ Heikki) + + oppimisen nautinto + - vaatii pakkausalgoritmien suodattamista netistä + - vaatii aivot + +0x0024: Gamman yliajokertainen muistijälki, laajuus 1 + + helppoa + - vaatii tiedostojenhallinnan tuntemista + +0x0025: Ennerin lyöntialgoritmin asetus riippuvaiseksi voimasta, laajuus 1 (DONE: Timo) + + helppoa + +0x0026: Bulimian prohibitio rankaisumenetelmiä kehittämällä, laajuus 1 (DONE: Perttu) + + äärimmäisen helppoa + +0x0027: Monsujen ovienavauksien verbaalinen ilmaisu, laajuus 1 (DONE: Heikki) + + helppoa + +0x0028: Esineporttikielto Pertun jalkojen väliin, laajuus 1 (DONE: Heikki) + + äärimmäisen helppoa + +0x0029: Monsujen nostamisvietin rationalisoiminen + + Vaihtoehto 1: painon mukaan, laajuus 1 (DONE: Heikki) + + helppoa + - melko aivoton tapa toteuttaa työ + + Vaihtoehto 2: käyttökelpoisuuden mukaan, laajuus 2 (DONE: Heikki) + + ominaisuutena varsin hieno + - kunnollinen toteutus vaatii jonkinnäköiset aivot + - vaatii jonkinnäköistä strukturaalista päätöksentekokykyä + +0x002A: Pertun egoismin lieventäminen, laajuus 1 (DONE: Heikki) + + varsin hauskaa + - vaatii jonkinnäköiset aivot + - vaatii välttävää struktuurin tuntemusta + +0x002B: Luolaston laajennus uusien tasoporausten avulla, laajuus 1 (DONE: Heikki) + + perustaltaan äärimmäisen helppo työ + + voidaan tehdä haastavaksikin lisäämällä ominaisuuksia + uusille leveleille + +0x002C: Expotuksen estokeinot ja tarpeen vähentäminen, laajuus ? + - keinot vielä osittain epäselvät + + ehdotuksia: + * monsujen tason jatkuva vaikeutuminen (D/0x0003) + * epäfataali korruptio + * Elpurin tasokorjaus ja pelin helpotus yleensä + +0x002D: Gamman softa-alternatiivin additio, laajuus 1 (DONE: Timo) + + high priority + - vaatii FeVesan rajapinnan tuntemusta + - vaatii jonkinnäköistä struktuurin tuntemusta + +0x002E: Golemien materiaalibonukset, laajuus 1 (DONE: Heikki) + + pelillisesti mitä mukavin ominaisuus + - vaatii välttävää struktuurin tuntemusta + +0x002F: Eri resoluutiot, laajuus 1 (SUOSITUS: Timo ^ Heikki) + + työ tulee hetki hetkeltä vaikeammaksi, eli syytä tehdä kohtuu pian + - osa työstä aivotonta rutiinia + - osa vaatii jonkin verran syntaksiperehtyneisyyttä (ohjelma-argumentit alkuun) + +0x0030: Hyypälle kasvamaan Parta pelin aikana, laajuus 1, vaatimus B/0x0009 (PROHIBITIO: Timo) + + helppoa ja guruudessaan mieltäylentävää työtä + - vaatii hitusen struktuurin tuntemusta + +0x0031: Pelaajan nimeäminen, laajuus 1 (DONE: Heikki) + + helppoa + +0x0032: Epäekvivalentit tallennusidentifikaattorit, laajuus 1, vaatimus D/0x0031 (DONE: Timo?) + + helppoa + +0x0033: Constant save (debug), laajuus 1 (DONE, Timo) + + helppoa + +0x0034: Crash save (debug), laajuus 1 (SUOSITUS: Timo) + ± toimintaan saattaminen vaatii mitä guruimpia kikkoja + - vaatii hyvää FEEL:in tuntemusta + +0x0035: Identify ja oheistyöt, laajuus 2 (SUOSITUS: Heikki) + - vaatii luultavasti rutiinityötä luokkien parissa + - vaatii jonkinnäköistä strukturaalista päätöksentekokykyä + +0x0036: Toivominen, laajuus 1-2 (SUOSITUS: Heikki) + + high priority niin pian kuin mahdollista + - mutta se ei ole vielä sitä + - laajuus ei aivan varma tässä vaiheessa, riippuu koodiparannuksista + +<--- E: Bugikorjaukset ---> + +Näkyvää ja helpompaa kuin ominaisuuksien lisäys, +lukuunottamatta harvinaisempia tapauksia, +jossa kaikki on kirjoitettava uusiksi. +Sopii niille, joille projektin keskipitkän +tähtäimen kehitys on tärkeintä. + +0x0000: Itsemurhakoodin korjaus (esim. hungerin alta), laajuus 2 (DONE: Timo) + - vaatii kattavia muutoksia kuolemisen toteutustapoihin + - vaatii strukturaalista päätöksentekokykyä + +0x0001: Pertun tientukkimisbugin korjaus, laajuus 1, vaatimus B/0x0006 (!EXISTED) + + helppoa + - low priority; bugi on lähinnä teoreettinen mahdollisuus + (+ mutta Murphyn laki on aina pidettävä mielessä...) + +0x0002: Gear-objektien consume-bugin fiksaus, laajuus 1 (DONE: Perttu) + + helppoa + +0x0003: Päällepukemisen rajoittamattomuusongelman korjaus, laajuus 1 (DONE: Heikki) + + helppoa + - vaatii hitusen strukturaalista päätöksentekokykyä + +0x0004: Kineettisen eliminoinnin probleemien deletointi, laajuus 1 (DONE: Timo) + + luultavasti helppoa + +0x0005: Ylikuormitetun teleportaation mahdollistaminen, laajuus 1 (DONE: Heikki) + + helppoa + +0x0006: Pertun kursoribugin poistaminen, laajuus 1 (DONE: Tuukka) + + helppoa + - vaatii kuvakäsittelyohjelman avaamisen + +0x0007: Billin mielten verenvuodon tukahduttaminen, laajuus 1 (DONE: Heikki) + + helppoa + - vaatii hitusen strukturaalista päätöksentekokykyä + +<--- F: Grafiikka ---> + +Näkyvää, tosin integraation tasosta riippuen. +Sopii taiteellisesti lahjakkaille. + +0x0000: Itemkuvia lisää, laajuus 1 (DONE: Tuukka) + Tällä hetkellä listalla: lihaa ja luuta + + Ivan loves work, riippumatta mistään plussista ja miinuksista + +0x0001: Monsukuvia lisää, laajuus 1 (DONE: Tuukka) + Tällä hetkellä listalla: golemit + + sama kuin yllä + +0x0002: Human.pcx:ään lisää armor- ja asekuvia, laajuus 1 (DONE: Tuukka) + Tällä hetkellä listalla: kaikki mikä puuttuu :) + + sama kuin yllä + +0x0003: DGRAY/LGRAY-ongelman korjaus item.pcx:ssä, laajuus 1 (DONE: Tuukka) + + sama kuin yllä + +<--- G: Suunnittelu ---> + +Ei vaadi välttämätöntä koodaustaitoa. +Usein leppoisampaa kuin raakaa logiikkaa +vaativa koodaus, mutta tietyssä pisteessä +menee tylsähköksi rutiiniksi. +Sopii luoville persoonille. + +0x0000: Kattavan monsterilistan väsääminen, laajuus 2 (DONE: Niko) + + helppoa, suurimman osanhan voi kopioida muualta + - kattavuus ja geneerisyys on syytä säilyttää; huumorille ei juuri sijaa + +<--- H: Edukaatio ---> + +Sosiaalista ja hyvin kauskatseista toimintaa. +Sopii ilmaisutaitoisille. + +0x0000: Ohjelmointitaidon iskostaminen Graafikon cortexiin, laajuus 5 (TYÖN ALLA: Heikki) + + helpottaa tulevaa työtä, ehkä + + opettaja voi hoitaa muita töitä pupillin väsätessä harjoitustehtäviä + + osa opiskelusta voidaan toteuttaa itse- tai etäopiskeluna + - vaatii paljon aikaa ja määrätöntä kärsivällisyyttä + +<--- I: Dokumentaatio ---> + +Vaatii dokumentoitavan kohteen syvällistä tuntemusta, +ja sitoutumista työn päivittämiseen tarpeen niin vaatiessa. +Sopii erityisesti äidinkilen taitoisile. + +0x0000: Koodin kaikenkattava dokumentointi, laajuus 4 (VARATTU: Timo) + + oiva tilaisuus kirjoittaa mahdollisimman gurua ja epäselvää tekstiä + + high priority, mikäli halutaan kunnollisia lisäohjelmoijia tiimiin + - paljon työtä, osa aivotonta sellaista + - vaatii struktuurin ja toiminnan läpikotaisin tuntemista + +0x0001: Työlistan suomentaminen, laajuus 1 (PROHIBITIO: Heikki) + + helpottaa työskentelyä tulevaisuudessa + - mitä ikävin työ; suorastaan huvinpilausta \ No newline at end of file diff --git a/Doc/Obsolete/Tavoitteet.txt b/Doc/Obsolete/Tavoitteet.txt new file mode 100644 index 0000000..121f2e9 --- /dev/null +++ b/Doc/Obsolete/Tavoitteet.txt @@ -0,0 +1,44 @@ +B/0x0010 ? Koodin porttaus Windozeen ja DirectX:lle, laajuus 4 Heikki +D/0x0003 Aseiden ja monsujen tason korrekti painotus levelin mukaan, laajuus 2 Heikki +D/0x0000 Uusien itemejen lisäys, laajuus 1 Heikki ym. + +D/0x001F Rukoiluefektien teko, laajuus 2-5 usealle + +D/0x0026 Bulimian prohibitio rankaisumenetelmiä kehittämällä, laajuus 1 Perttu? + +B/0x000C Koodin kaikenkattava dokumentointi, laajuus 4 (huom! englanniksi) Timo +B/0x0005 Lyhennemakrot/-funktiot, laajuus 2 Timo +B/0x000F Virtuaalifunktioiden käyttöalueen laajentaminen luokkien sisälle, laajuus 2 Timo? +D/0x000B Perception atribuutiksi ja sille vaikutus LOS:iin, laajuus 1 Timo +B/0x0002 Kaikenkattava esittelyjen uudelleenjärjesteleminen, laajuus 1 Timo +B/0x0003 Characterin ja gamen julma paloitteleminen, laajuus 1 Timo +B/0x0004 Helpottavat operaattorit, laajuus 1 Timo +B/0x000D #includejen vapautus järjestysindepenteiksi, laajuus 1 Timo +B/0x0009 Olemassaolofunktioiden väsääminen objekteille, laajuus 1 Timo +D/0x0006 Monsujen tekoälyn saattaminen remakejen 2-4 tasolle, laajuus 1 Timo + +*B/0x0012: Objektin alaluokkien kattava makroittaminen, laajuus 2 (TYÖN ALLA: Timo) +*B/0x000B: Rukoilun strukturaalinen osuus, laajuus 2 (VARATTU: Timo) +-B/0x0006: Arean muokkaus skriptin vaatimaan funktioformaattiin, laajuus 2 (SUOSITUS: Timo) +*E/0xXXXX: Kaikki bugit pois, useita ++D/0x001F: Rukoiluefektien teko, laajuus 2-5 kattavuudesta riippuen, vaatimus B/0x000B (SUOSITUS: Kaikki) +*B/0x000F: Virtuaalifunktioiden käyttöalueen laajentaminen luokkien sisälle, laajuus 2 (SUOSITUS: Timo ^ Heikki) +*D/0x0000: Uusien itemejen lisäys, laajuus 1, osittain vaatimus F/0x0000 (PROHIBITIO: Timo) +*D/0x0006: Monsujen tekoälyn saattaminen remakejen 2-4 tasolle, laajuus 1 +*F/0x0000: Itemkuvia lisää, laajuus 1 (OSITTAIN DONE: Tuukka) +*D/0x002B: Luolaston laajennus uusien tasoporausten avulla, laajuus 1 +*A/0x0006: Maasta syömisen ergonomistus +-D/0x000A: Itemkohtaiset ergonomiset muistijäljet, laajuus 1 (SUOSITUS: Heikki) +-D/0x002F: Eri resoluutiot, laajuus 1 (SUOSITUS: Timo ^ Heikki) +*D/0x002E: Golemien materiaalibonukset, laajuus 1 (OSITTAIN DONE: Heikki) +*D/0x0029: Monsujen nostamisvietin rationalisoiminen +*D/0x0028: Esineporttikielto Pertun jalkojen väliin, laajuus 1 (PROHIBITIO: Timo) +oD/0x002A: Pertun egoismin lieventäminen, laajuus 1 (PROHIBITIO: Timo) +*D/0x0027: Monsujen ovienavauksien verbaalinen ilmaisu, laajuus 1 (PROHIBITIO: Timo) +*D/0x001D: Lookmode, laajuus 1 (OSITTAIN DONE: Niko & Perttu) +*D/0x0014: Pickupin nopeuttaminen stabiloimalla inventorioruutu, laajuus 1 (PROHIBITIO: Timo) +*D/0x0013: Ovien avauksen nopeuttaminen remaken 4 tapaan, laajuus 1 (PROHIBITIO: Timo) +*D/0x000F: Lihatarjonnan monipuolistaminen, laajuus 1 (OSITTAIN DONE: Perttu) ++D/0x000B: Perception atribuutiksi ja sille vaikutus LOS:iin, laajuus 1 +-B/0x000D: #includejen vapautus järjestysindepenteiksi, laajuus 1 (SUOSITUS: Timo ^ Heikki) +* InHandsPiccejä lisää \ No newline at end of file diff --git a/Doc/Obsolete/Teams.txt b/Doc/Obsolete/Teams.txt new file mode 100644 index 0000000..186e45c --- /dev/null +++ b/Doc/Obsolete/Teams.txt @@ -0,0 +1,26 @@ +All characters are divided into teams. Teams may have a leader, +in which case all team members will follow and obey him/her/it. +Teams are related to each other as follows: + +HOSTILE + + Team 1 will attack team 2 members in sigth. + + A member of team 2 is attacked by a member of team 3. + If team 3 is NEUTRAL towards team 1, teams 1 & 3 will + become FRIENDs. Otherwise team 1 will not care. + +NEUTRAL + + Team 1 will not attack team 2. + + A member of team 2 is attacked by a member of team 3. + If team 3 is HOSTILE towards team 1, teams 1 & 2 will + become FRIENDs. Otherwise team 1 will not care. + +FRIEND + + Team 1 will not attack team 2. + + A member of team 2 is attacked by a member of team 3. + Team 1 will attack team 3. \ No newline at end of file diff --git a/Doc/Obsolete/Tuukka.txt b/Doc/Obsolete/Tuukka.txt new file mode 100644 index 0000000..a78fc00 --- /dev/null +++ b/Doc/Obsolete/Tuukka.txt @@ -0,0 +1,13 @@ +?LTerrain should be divided. +//Battle symbol. +Hit symbol. +/Small, medium, large, very large and mega-large corpses. +/Broken items. +Broken inhandspics. +//Shield items. +//48x48 mammoth. +?Bodypart items +//Tame symbol. +Silhouettes. +Angel and genie splitting. +Key item. diff --git a/Doc/Obsolete/Type.txt b/Doc/Obsolete/Type.txt new file mode 100644 index 0000000..08efc69 --- /dev/null +++ b/Doc/Obsolete/Type.txt @@ -0,0 +1,13 @@ +Type is an unsigned short int. + +Material types: 0x1000 + Index game::LoadMaterial(ifstream*) + +Item types: 0x2000 + Index game::LoadItem(ifstream*) + +Character types: 0x3000 + Index game::LoadCharacter(ifstream*) + +Terrain types: +L-G-Terrain: 0x4000 + Index game::LoadLevelGroundTerrain(ifstream*) +L-O-Terrain: 0x4400 + Index game::LoadLevelOverTerrain(ifstream*) +W-G-Terrain: 0x4800 + Index game::LoadWorldMapGroundTerrain(ifstream*) +W-O-Terrain: 0x4C00 + Index game::LoadWorldMapOverTerrain(ifstream*) \ No newline at end of file diff --git a/Doc/Obsolete/Valot.txt b/Doc/Obsolete/Valot.txt new file mode 100644 index 0000000..5961705 --- /dev/null +++ b/Doc/Obsolete/Valot.txt @@ -0,0 +1,23 @@ +Jokainen objekti on emittoija. +Jos emittaatio on alle 64, se jätetään huomiotta. +Ruudun emittaatio on sen suurin emittaatio. (dev/nulliin luonnonlait...) + +Emittoijan siirtyessä ruutuun tai poistuessa ruudusta tarkistetaan, +muuttuuko ruudun emittaatio, jos se niin tekee, ajetaan emittaatiofunktio. + +Jokainen ruutu on luminaatioalue. +Luminaatioalueella on emittoijalista, +jossa luetellaan kaikki emittoijat, jotka vaikuttavat ruutuun. +Läpikuljettavan ruudun luminaatio on sen suurin luminaatio. +Läpikulkemattoman ruudun luminaatio on sen suurin luminaatio, +jonka lähteeseen voidaan vetää suora viiva pelaajasta. + +Emittaatiofunktio levittää valoisuuden ruudusta ulospäin +ja päivittää emittoijaruudun tiedot luminaatioalueen emittoijalistassa, +lisää itsensä sinne tai poistaa itsensä sieltä. + +Mikäli luminaatioalueen läpikuljettavuus muuttuu, +sen kaikkien emittoijien emittaatiofunktiot ajetaan. +Läpikuljettavuuden vapautuessa emittaatiofunktio ajetaan suoraan. +Läpikuljettavuuden estyessä emittaatiofunktio ajetaan ensin nolla-arvolla +ja estymisen aktivoitua suoraan. \ No newline at end of file diff --git a/Doc/Obsolete/Value Plan.txt b/Doc/Obsolete/Value Plan.txt new file mode 100644 index 0000000..a1c4cd5 --- /dev/null +++ b/Doc/Obsolete/Value Plan.txt @@ -0,0 +1,129 @@ +Description of values +--------------------- + +name characteristics connecting creatures valuing it + +others altruists +self egoists + +life people who prefer to solve conflicts in peaceful and life-preserving ways +death will resort to violence when facing opposite opinions + +order beings that believe in the power of ordered societies and laws +chaos individualists, anarchists and generally people who don't care about rules + +nature those who see nature as value in itself +man those who see nature only as a resource to be exploided by man + +Gods of belonging to each value +------------------------------- + +self others +Nefas Atavus +Mellis Sophos +Infuscor Legifer + +life death +Terra Mortifer +Seges Scabies +Dulcis Cruentus + +order chaos +Legifer Cleptia +Valpurus Scabies +Mellis Infuscor + +nature man +Silva Loricatus +Sophos Mellis +Dulcis Seges + +List of all gods +---------------- + +Valpurus the king of gods order +Legifer god of truth, law and loyalty others, order +Atavus god of charity and munificence others +Sophos god of knowledge and magic nature, others +Terra goddess of the earth nature +Silva goddess of forests and wildlife life, nature +Dulcis goddess of love and art life +Seges goddess of health and agriculture man, life +Loricatus god of fire, machines and smithing man +Mellis god of money, trade and politics self, man +Nefas goddess of greed and forbidden pleasures self +Infuscor goddess of wrong knowledge and vile magic chaos, self +Cleptia goddess of crime and rebellion chaos +Scabies goddess of mutations, disease and famine death, chaos +Mortifer god of death and destruction death +Cruentus god of war and blood order, death + +List of all possible alignment combinations +------------------------------------------- + +Alignment: life order man others +Friends: Valpurus Seges Atavus Dulcis Legifer Loricatus +Enemies: Terra Sophos Silva Mellis Nefas Cruentus Cleptia Infuscor Scabies Mortifer + +Alignment: life order man self +Friends: Valpurus Seges Dulcis Loricatus Mellis Nefas +Enemies: Atavus Terra Sophos Silva Legifer Cruentus Cleptia Infuscor Scabies Mortifer + +Alignment: life order nature others +Friends: Valpurus Atavus Terra Dulcis Sophos Silva Legifer +Enemies: Seges Loricatus Mellis Nefas Cruentus Cleptia Infuscor Scabies Mortifer + +Alignment: death order nature others +Friends: Valpurus Atavus Terra Sophos Legifer Cruentus Mortifer +Enemies: Seges Dulcis Silva Loricatus Mellis Nefas Cleptia Infuscor Scabies + +Alignment: death order man others +Friends: Valpurus Atavus Legifer Loricatus Cruentus Mortifer +Enemies: Seges Terra Dulcis Sophos Silva Mellis Nefas Cleptia Infuscor Scabies + +Alignment: life order nature self +Friends: Valpurus Terra Dulcis Silva Nefas +Enemies: Seges Atavus Sophos Legifer Loricatus Mellis Cruentus Cleptia Infuscor Scabies Mortifer + +Alignment: death order nature self +Friends: Valpurus Terra Nefas Cruentus Mortifer +Enemies: Seges Atavus Dulcis Sophos Silva Legifer Loricatus Mellis Cleptia Infuscor Scabies + +Alignment: death order man self +Friends: Valpurus Loricatus Mellis Nefas Cruentus Mortifer +Enemies: Seges Atavus Terra Dulcis Sophos Silva Legifer Cleptia Infuscor Scabies + +Alignment: life chaos man others +Friends: Seges Atavus Dulcis Loricatus Cleptia +Enemies: Valpurus Terra Sophos Silva Legifer Mellis Nefas Cruentus Infuscor Scabies Mortifer + +Alignment: life chaos man self +Friends: Seges Dulcis Loricatus Mellis Nefas Cleptia Infuscor +Enemies: Valpurus Atavus Terra Sophos Silva Legifer Cruentus Scabies Mortifer + +Alignment: life chaos nature others +Friends: Atavus Terra Dulcis Sophos Silva Cleptia +Enemies: Valpurus Seges Legifer Loricatus Mellis Nefas Cruentus Infuscor Scabies Mortifer + +Alignment: death chaos nature others +Friends: Atavus Terra Sophos Cleptia Scabies Mortifer +Enemies: Valpurus Seges Dulcis Silva Legifer Loricatus Mellis Nefas Cruentus Infuscor + +Alignment: death chaos man others +Friends: Atavus Loricatus Cleptia Scabies Mortifer +Enemies: Valpurus Seges Terra Dulcis Sophos Silva Legifer Mellis Nefas Cruentus Infuscor + +Alignment: life chaos nature self +Friends: Terra Dulcis Silva Nefas Cleptia Infuscor +Enemies: Valpurus Seges Atavus Sophos Legifer Loricatus Mellis Cruentus Scabies Mortifer + +Alignment: death chaos nature self +Friends: Terra Nefas Cleptia Infuscor Scabies Mortifer +Enemies: Valpurus Seges Atavus Dulcis Sophos Silva Legifer Loricatus Mellis Cruentus + +Alignment: death chaos man self +Friends: Loricatus Mellis Nefas Cleptia Infuscor Scabies Mortifer +Enemies: Valpurus Seges Atavus Terra Dulcis Sophos Silva Legifer Cruentus + +End of document +--------------- diff --git a/Doc/Obsolete/Virhe.txt b/Doc/Obsolete/Virhe.txt new file mode 100644 index 0000000..ae64d23 --- /dev/null +++ b/Doc/Obsolete/Virhe.txt @@ -0,0 +1,8 @@ +Konvertoitujen kuvien kartan avaimessa on datajäsen, joka osoittaa dynaamisesti +varattuun muistipaikkaan, joka sisältää kuvan materiaalivärit. Minä tyhmyyksissäni +alustin sen kuvaa ekaa kertaa luotaessa luontipyynnön lähettäjän toimittamalla +värijonon osoittimella. Kun kyseinen objekti sitten kerran tuhottiin, avaimen +värijäsen alkoi osoittaa deletoituun sontaan. Se ei kuitenkaan kaatunut niitä +haettaessa vaan joskus myöhemmin, kun ensin oli luotu yksi uusi sen tyypin kuva +ja sitten tuhottu vanhempi samanlainen, jolloin uusi kuva alkoi osoittaa saastaan +ja draw kaatui. \ No newline at end of file diff --git a/Doc/Obsolete/Weapons.txt b/Doc/Obsolete/Weapons.txt new file mode 100644 index 0000000..e18bc8a --- /dev/null +++ b/Doc/Obsolete/Weapons.txt @@ -0,0 +1,21 @@ +Kirves - kova lämä, huono osuma, huono blokki +Puunuija tms. - surkea lämä, melko huono osuma, melko huono blokki +Mace - melko kova lämä, melko huono osuma, huono blokki +Isot sotavasarat - kova lämä, melko hyvä osuma, normaali blokki +Puukko - huono lämä, loistava osuma, itkettävän surkea blokki, rikkominen vaikeaa +Miekka - normaali lämä, melko hyvä osuma, hyvä blokki, rikkominen vaikeaa, kokemus tärkeää +Kahdenkäden miekka - melko kova lämä, normaali osuma, hyvä blokki, rikkominen vaikeaa +Poleaxe - melko kova lämä, hyvä osuma, melko hyvä blokki, kokemus todella tärkeää +Keihäs - vaihteleva lämä, melko hyvä osuma, melko huono blokki, helposti rikkoutuva + +Hillitön morning star - erittäin kova lämä, erittäin huono osuma, surkea blokki. +Pitää kiihdyttää ennen lyöntiä special-näppäimestä, kiihdytys vie vuoron. Pallon pitäminen +kiihdytettynä kuluttaa np:tä. Pallolla voi lyödä peräkkäisinä vuoroina, mutta osuessa on +mahdollisuus että pallo pysähtyy ja se pitää kiihdytettää uudelleen. Vaatii hahmon joka +puolelle ruudun tyhjää tilaa (siis ei seinää). Mikäli hahmon ympärillä useampia monsuja, +osumamahdollisuus lasketaan jokaiseen monsuun, ja useampiin monsuihin voi tehdä lämää samalla +vuorolla. Mikäli rikkoontuu kiihdytettynä, pallo lentää satunnaiseen suuntaan ja aiheuttaa +lämää tielle osuville. + +Pienempi morning star - hyvin kova lämä, hyvin huono osuma, surkea blokki. Muuten sama kuin +isompi veljensä, paitsi: kiihdytettynä pitäminen vie vähemmän np:tä, pysähtyy helpommin. \ No newline at end of file diff --git a/Doc/Obsolete/WhatToWishFor.txt b/Doc/Obsolete/WhatToWishFor.txt new file mode 100644 index 0000000..7cc6428 --- /dev/null +++ b/Doc/Obsolete/WhatToWishFor.txt @@ -0,0 +1,65 @@ +(The following document is valid with version 0.430 of IVAN.) + +This guide deals with the following issue + +What item to wish for? +---------------------- + +IVAN has a lot of items, but the following are extremely useful and so +are a good idea to wish for. + +* scroll of change material: This is great way of improving your gear. + Phoenix feather and meteoric steel are the best wishable materials, + the former being more flexible (it causes less dexterity/agility + penalties) and the latter being harder. + +* scroll of taming: There is nothing like a good pet, but do not try to + use this on a quest monster. + +* wand of cloning: These by themselves are not useful, but if you do have + some other useful item to clone, this is worth its weight in gold. With + one charge you can clone a pile of five items. Note that certain items + like wands of cloning, scrolls of wishing or scrolls of charging cannot + be cloned because this could be abused. It is therefore sometimes wise + to drop more than five items in pile in case the game doesn't allow some + to be cloned. If you have a powerful pet like Ivan or a mammoth, you may + lure it above the items so it will be cloned, too. You can also stand + on the item pile and zap yourself, in which case you will be copied + along with the stack. Note that when cloning a pet or yourself, the + clone will be born without any equipment and only four items under + it will be copied. + +* wand of resurrection: If you have managed to get a unique monster as + a pet, this may be extremely useful. It may also be used to quickly + replace lost limbs. + +* cloak or ring of invisibility: Invisibility is a great tactical + advantage, but it also causes much more dangerous monsters to be + generated. + +* ring of polymorph control: Very useful if happen to have a way of + polymorphing. Note that being very powerful may cause tough monsters + like dark mages to spawn, which is not nice if you encounter them + after you have returned to your original form. + +* amulet of life saving: Basically gives you a second life. That's + a *lot*. + +* magical whistle: Handling more than two pets is almost impossible + without this. + +* Mjolak, Turox, Saal'thul, thunder hammer: Way more powerful than + normal weapons. Saal'thul also grants invisibility. It is sometimes + wiser to wish for a broken thunder hammer instead of an intact one, + since the game often generates better materials for broken items + to conpensate the need of repairing. + +* holy banana of Oily Orpiv: Has terrible explosive power which can + aid you in boss battles. Also trains all attributes when eaten. + Be sure to store this in a hard container like strong-box, + because if it explodes in your inventory, you're in BIG trouble. + +* armor of great health: If you have scrolls of enchant armor, + this gives a huge boost to your HPs. Again, a broken one may + have a better material, but usually you want to use a scroll + of change material for this one, anyway. diff --git a/Doc/Obsolete/attnam.jpg b/Doc/Obsolete/attnam.jpg new file mode 100644 index 0000000..b2eb3bb Binary files /dev/null and b/Doc/Obsolete/attnam.jpg differ diff --git a/Doc/Obsolete/balance3.txt b/Doc/Obsolete/balance3.txt new file mode 100644 index 0000000..335151f --- /dev/null +++ b/Doc/Obsolete/balance3.txt @@ -0,0 +1,25 @@ +Petrus the high priest of the Great Frog amulet of life saving, Justifier, diamond plate mail, angel hair cloak, phoenix feather gauntlets of dexterity, ring of fire/poison res, diamond belt, nymph hair boots of agility +Sherarax the mistress queen (r) amulet of life saving, spider silk whip of thievery x 2 (p), angel hair cloak, spider silk armor, phoenix feather gauntlets of dexterity, phoenix feather boots of agility, ring of fire/poison res, angel hair belt +Oree the blood daemon king mithril helmet, angel hair gauntlets, angel hair boots, phoenix feather cloak, mithril belt, Shirt of the Golden Eagle +Golgor Dhan the grand master dark knight (r) diamond helmet, ruby gauntlets, ruby boots, mithril plate mail, phoenix feather cloak, sapphire belt, diamond two-handed scimitar (p) +Haedlac Galladon VII the master guard mithril helmet of perception, amulet of ESP, long valpurium sword, valpurium shield, mithril plate mail, mithril belt, mithril gauntlets of strength, mithril boots of strength +Wendrel the duelist champion (s) +Elpuri the master dark frog - +Ur-Khan the orc marshal (r) sapphire helmet, angel hair cloak, steel gauntlets of strength, steel boots of strength, diamond belt, diamond halberd (p) +Xinroch the skeleton warlord (r) ruby helmet, ruby flaming sword, ruby chain mail, phoenix feather gauntlets, phoenix feather boots, phoenix feather belt +good angel phoenix feather helmet, phoenix feather armor, phoenix feather cloak, spider silk boots, mithril long sword, mithril shield, phoenix feather gauntlets +neutral angel mithril helmet, steel plate mail, spider silk cloak, mithril mace, mithril mace, mithril gauntlets, mithril boots +evil angel steel helmet, steel chain mail, nymph hair cloak, spider silk gauntlets, mithril halberd (p), steel boots +Ivan the communist steel helmet, steel plate mail, mithril sickle, mithril hammer, hardened leather cloak, troll hide belt, steel gauntlets, steel boots +Merka the shopkeeper (Dungeon) mithril helmet, mithril chain mail, phoenix feather cloak, nymph hair belt of carrying, mithril pick-axe +Guugzamesh the goblin king (r) steel helmet, mithril chain mail, nymph hair cloak, nymph hair belt, steel gauntlets, steel boots, ring of teleportation +Olcuri the greater dark frog - +genie phoenix feather armor, two-handed iron scimitar, spider silk belt +Ikiros the smith steel helmet, steel plate mail, hardened leather cloak, steel belt, mithril hammer, steel gauntlets, steel boots +Igor the cossack steel helmet, steel chain mail, leather cloak, hardened leather belt, mithril long sword, steel gauntlets, steel boots +Sergei the cossack steel helmet, steel chain mail, leather cloak, hardened leather belt, mithril long sword, steel gauntlets, steel boots +Richel Decos the imperialist mithril chain mail +genetrix vesana - +Hulbo the shopkeeper (Attnam) steel helmet, steel chain mail, nymph hair cloak, nymph hair belt, leather belt of carrying, steel pick-axe, nymph hair gauntlet/boot +Rondol the kobold patriarch (r) iron helmet, iron chain mail, hardened leather cloak, troll hide belt, mithril short sword (p), mithril short sword (p), leather gauntlet, troll hide boot +Haathbar the librarian - diff --git a/Doc/Obsolete/battles.txt b/Doc/Obsolete/battles.txt new file mode 100644 index 0000000..f1a11ec --- /dev/null +++ b/Doc/Obsolete/battles.txt @@ -0,0 +1,9 @@ +Hard-coded battles: + +genetrix vesana - sprouting plants, protection ring +wolf level +enner beast +imperialist - guards, angel of Mellis +elpuri - dark frogs and greater dark frogs +oree - ambush - elite dark knights, blood golems, angel of Mortifer +petrus - elite guards, master guard, light frogs, greater light frogs, angels of Valpurus, priests of Valpurus \ No newline at end of file diff --git a/Doc/Obsolete/char.txt b/Doc/Obsolete/char.txt new file mode 100644 index 0000000..62ec2e6 --- /dev/null +++ b/Doc/Obsolete/char.txt @@ -0,0 +1,278 @@ +---------------------------------------------------------------------------- + + How to make a new character type, step-by-step tutorial + +---------------------------------------------------------------------------- + +1. Choose whether you want to make an abstract character type, i.e. one that + is a base class for other character types but itself can't be + instantiated, like a "humanoid", or a concrete character like a "flat + mommo slime". +2. Choose whether you want to place the code of the character's constructor + in a header file or a source file. You generally choose the latter only + when you want to add items into the creatures backpack during it's + creation process, like a six pack of pepsi for Oree the Pepsi Daemon King. +3. If you chose concrete character with header constructor, goto 4. + If you chose abstract character with header constructor, goto 5. + If you chose concrete character with source constructor, goto 6. + If you chose abstract character with source constructor, goto 7. + +---------------------------------------------------------------------------- + +4. Use a syntax like the following: + + HEADER_CONSTRUCTED_CHARACTER( + name, + base, + cparameters, + bparameters, + dparameters, + constructor, + load, + type, + possibility, + data + ); + + 4.01. Add this at the end of char.h. + 4.02. Replace "name" with the code name of the new character. This is + the character's true name written in lowercase without any spaces + or special letters. You may also shorten it somewhat as long as + it's clear whom you mean. I.e. "pure mass of Bill's will" could be + in this syntax just "billswill". + 4.03. Replace "base" with the code name of the base class, like + "character", "humanoid" or "frog". The new class inherits all + specialities of the base class. + 4.04. Replace "cparameters" with "NORMAL_CHILD_PARAMETERS" if your + class is not derived from "humanoid", "HUMANOID_CHILD_PARAMETERS" + otherwise. + 4.05. Replace "bparameters" with "NORMAL_BASE_PARAMETERS" or + "HUMANOID_BASE_PARAMETERS" as above. + 4.06. Replace "dparameters" with the following: + + ( + NewMaterial(1, material), + size, + agility, + strength, + endurance, + perception, + relation + ) + + 4.6.1. Replace "material" with the correct material, like + "new ennerbeastflesh(60000)". + 4.6.2. Specify size, agility, strength, endurance and perception. + 4.6.3. Specify the initial relation (0==hostile, 1==neutral, + 2==friend) to the player character. (usually this is 0) + 4.6.4. If your character is derived from "humanoid", add the + following parameters between perception and relation: + index-of-arm-picture, index-of-head-picture, + index-of-legs-picture, index-of-torso-picture. + All indices refer to the order of pictures in human.pcx, + of course. + + 4.07. Replace "constructor" with empty brackets, or optionally write + some brief constructor code between them. + 4.08. Replace "load" with empty brackets, or optionally write some brief + loading code between them. + 4.09. Look above your class. Pick the last concrete class declared there, + and take its "type". Add one to this, and place the result to + "type". + 4.10. Replace "possibility" with 0 if you don't want these monster + to appear randomly in the dungeon. Otherwise use any other + number. + 4.11. Place all the functions you want to overwrite to the "data" + field. The essential functions are: + + NAME_SINGULAR RET() + NAME_PLURAL RET() + DANGER RET() + + Which specify the name of the character and its danger level, + which affects how strong the player must be for this character + to start appearing in the dungeon. + + If your character is not a humanoid, you must also specify this + function: + + C_BITMAP_POS RETV(x, y) + + Replace x and y with the coordinates of the character's + picture in char.pcx. + + Add any other functions here you want, like CHARMABLE RET(false) + or CAN_WIELD RET(true). If the function code is long, place + semicolon (;) in the place of "RET()" and write + your code to char.cpp instead. + + 4.12. Your character is ready! + +---------------------------------------------------------------------------- + +5. Use a syntax like the following: + + HEADER_CONSTRUCTED_BASE( + name, + base, + cparameters, + bparameters, + constructor, + load, + data + ); + + 5.01. Add this at the end of char.h. + 5.02. Replace "name" with the code name of this new base class. + 5.03. Replace "base" with the code name of the base class of this base + class, like "character" or "humanoid". The new base class and all + its derived classes will inherit all specialities of the base. + 5.04. Replace "cparameters" with "NORMAL_CHILD_PARAMETERS" if your class + is not derived from "humanoid", "HUMANOID_CHILD_PARAMETERS" otherwise. + 5.05. Replace "bparameters" with "NORMAL_BASE_PARAMETERS" or + "HUMANOID_BASE_PARAMETERS" as above. + 5.06. Replace "constructor" with empty brackets, or optionally write + some brief constructor code between them. + 5.07. Replace "load" with empty brackets, or optionally write some brief + loading code between them. + 5.08. Place all the functions you want to overwrite to the "data" + field. Add any functions here you want, like CHARMABLE RET(false) + or CAN_WIELD RET(true). If the function code is long, place + semicolon (;) in the place of "RET()" and write + your code to char.cpp instead. All character classes derived + from your base will inherit these functions. + + 5.09. Your new base class is ready! + +---------------------------------------------------------------------------- + +6. Use a syntax like the following: + + SOURCE_CONSTRUCTED_CHARACTER( + name, + base, + cparameters, + type, + possibility, + data + ); + + 6.01. Add this at the end of char.h. + 6.02. Replace "name" with the code name of the new character. This is + the character's true name written in lowercase without any spaces + or special letters. You may also shorten it somewhat as long as + it's clear whom you mean. I.e. "pure mass of Bill's will" could be + in this syntax just "billswill". + 6.03. Replace "base" with the code name of the base class, like + "character", "humanoid" or "frog". The new class inherits all + specialities of the base class. + 6.04. Replace "cparameters" with "NORMAL_CHILD_PARAMETERS" if your + class is not derived from "humanoid", "HUMANOID_CHILD_PARAMETERS" + otherwise. + 6.05. Make your new non-default constructor to char.cpp. Use the + following syntax if your character is not a humanoid: + + name::name(material** Material, vector BitmapPos, ushort Size, + ushort Agility, ushort Strength, ushort Endurance, + ushort Perception, uchar Relations) : base(Material, BitmapPos, + Size, Agility, Strength, Endurance, Perception, Relations) {} + + If it is, use this: + + name::name(material** Material, vector BitmapPos, ushort Size, + ushort Agility, ushort Strength, ushort Endurance, + ushort Perception, uchar PArmType, uchar PHeadType, uchar PLegType, + uchar PTorsoType, uchar Relations) : base(Material, BitmapPos, Size, + Agility, Strength, Endurance, Perception, PArmType, PHeadType, + PLegType, PTorsoType, Relations) {} + + Write your construction code between the empty brackets. Then make + the new default constructor as follows: + + name::name(void) : base(dparameters) {} + + Refer to 4.06. for information about dparameters. Then copy your + non-default constructor's code inside the brackets. + + 6.06. Look above your class. Pick the last concrete class declared there, + and take its "type". Add one to this, and place the result to + "type". + 6.07. Replace "possibility" with 0 if you don't want these monster + to appear randomly in the dungeon. Otherwise use any other + number. + 6.08. Place all the functions you want to overwrite to the "data" + field. The essential functions are: + + NAME_SINGULAR RET() + NAME_PLURAL RET() + DANGER RET() + + Which specify the name of the character, and its danger level, + which affects how strong the player must be for this character + to start appearing in the dungeon. + + If your character is not a humanoid, you must also specify this + function: + + C_BITMAP_POS RETV(x, y) + + Replace x and y with the coordinates of the character's + picture in char.pcx. + + Add any other functions here you want, like CHARMABLE RET(false) + or CAN_WIELD RET(true). If the function code is long, place + semicolon (;) in the place of "RET()" and write + your code to char.cpp instead. + + 6.09. Your character is ready! + +---------------------------------------------------------------------------- + +7. Use a syntax like the following: + + SOURCE_CONSTRUCTED_BASE( + name, + base, + cparameters, + data + ); + + 7.01. Add this at the end of char.h. + 7.02. Replace "name" with the code name of this new base class. + 7.03. Replace "base" with the code name of the base class of this base + class, like "character" or "humanoid". The new base class and all + its derived classes will inherit all specialities of the base. + 7.04. Replace "cparameters" with "NORMAL_CHILD_PARAMETERS" if your + class is not derived from "humanoid", "HUMANOID_CHILD_PARAMETERS" + otherwise. + 7.05. Make your new non-default constructor to char.cpp. Use the + following syntax if your character is not a humanoid: + + name::name(material** Material, vector BitmapPos, ushort Size, + ushort Agility, ushort Strength, ushort Endurance, + ushort Perception, uchar Relations) : base(Material, BitmapPos, + Size, Agility, Strength, Endurance, Perception, Relations) {} + + If it is, use this: + + name::name(material** Material, vector BitmapPos, ushort Size, + ushort Agility, ushort Strength, ushort Endurance, + ushort Perception, uchar PArmType, uchar PHeadType, uchar PLegType, + uchar PTorsoType, uchar Relations) : base(Material, BitmapPos, Size, + Agility, Strength, Endurance, Perception, PArmType, PHeadType, + PLegType, PTorsoType, Relations) {} + + Write your construction code between the empty brackets. + + 7.06. Place all the functions you want to overwrite to the "data" + field. Add any functions here you want, like CHARMABLE RET(false) + or CAN_WIELD RET(true). If the function code is long, place + semicolon (;) in the place of "RET()" and write + your code to char.cpp instead. All character classes derived + from your base will inherit these functions. + + 7.07. Your new base class is ready! + +---------------------------------------------------------------------------- + +End of document. \ No newline at end of file diff --git a/Doc/Obsolete/files.txt b/Doc/Obsolete/files.txt new file mode 100644 index 0000000..e192c92 --- /dev/null +++ b/Doc/Obsolete/files.txt @@ -0,0 +1,31 @@ +---------------------------------------------------------------------------- + + Iter Vehemens ad Necem source code documentation + + for Windows version 0.240 alpha + + Copyright (C) Fatal Error Productions 2001 + +---------------------------------------------------------------------------- + +This directory includes or at least it should include: + +char.txt - How to make a new character type tutorial +FeDX.txt - Fatal Error DirectX Graphics Library (FeDX) documentation +FEEL.txt - Fatal Error Exception Library (FEEL) documentation +FELL.txt - Fatal Error Listing Library (FELL) documentation +FeWin.txt - Fatal Error Window Operator Library (FeWin) documentation +files.txt - this file +general.txt - General Information about IVAN source +Include.txt - Documentation of miscellaneous shared include files +item.txt - How to make a new item type tutorial +LibTest.txt - Fatal Error Library Test (LibTest) documentation +light.txt - IVAN lighting system documentation +Main.txt - Iter Vehemens ad Necem main executable source documentation +project.txt - How to create a new project in IVAN workspace tutorial + +---------------------------------------------------------------------------- + +End of document. + + diff --git a/Doc/Obsolete/form.txt b/Doc/Obsolete/form.txt new file mode 100644 index 0000000..a7d7424 --- /dev/null +++ b/Doc/Obsolete/form.txt @@ -0,0 +1,16 @@ +Valpurus - frog +Seges - cow +Atavus - +Sophos - owl +Dulcis - swan +Verax - hawk +Silva - tree +Legifer - +Loricatus - machine +Nefas - pig +Cleptia - magpie +Mellis - snake +Cruentus - black dragon +Infuscor - vampire +Scabies - mutant, changes form all the time +Mortifer - reaper dude diff --git a/Doc/Obsolete/general.txt b/Doc/Obsolete/general.txt new file mode 100644 index 0000000..1ad76b5 --- /dev/null +++ b/Doc/Obsolete/general.txt @@ -0,0 +1,23 @@ +---------------------------------------------------------------------------- + + General Information about IVAN source + +---------------------------------------------------------------------------- + +The current version should be compiled with Microsoft Visual C++ compiler, +version 5.0 or newer. The project is however designed to be very easily +portable, and so most of these files' information would apply to any +potential ports as well. + +The current projects included in the workspace (IVAN.dsw): + +Fatal Error Exception Library (FEEL.dsp) +Fatal Error DirectX Graphics Library (FeDX.dsp) +Fatal Error Window Operator Library (FeWin.dsp) +Fatal Error Listing Library (FELL.dsp) +Fatal Error Library Test (LibTest.dsp) +Iter Vehemens ad Necem main project (Main.dsp) + +---------------------------------------------------------------------------- + +End of document. \ No newline at end of file diff --git a/Doc/Obsolete/holydoc2.jpg b/Doc/Obsolete/holydoc2.jpg new file mode 100644 index 0000000..7fdf5f3 Binary files /dev/null and b/Doc/Obsolete/holydoc2.jpg differ diff --git a/Doc/Obsolete/item.txt b/Doc/Obsolete/item.txt new file mode 100644 index 0000000..8b29fb7 --- /dev/null +++ b/Doc/Obsolete/item.txt @@ -0,0 +1,136 @@ +---------------------------------------------------------------------------- + + How to make a new item type, step-by-step tutorial + +---------------------------------------------------------------------------- + +1. Choose whether you want to make an abstract item type, i.e. one that + is a base class for other item types but itself can't be instantiated, + like a "meleeweapon", or a concrete item like a "banana". +2. If you chose your item to be concrete, goto 3, else goto 4. + +---------------------------------------------------------------------------- + +3. Use the following syntax: + + ITEM( + name, + base, + imaterials, + dparameters, + constructor, + load, + type, + possibility, + data + ); + + 3.01. Add this at the end of item.h. + 3.02. Replace "name" with the code name of the new item. This is + the item's true name written in lowercase without any spaces + or special letters. You may also shorten it somewhat as long as + it's clear what you mean. I.e. "holy broadsword named + Valpuri's Justifier" could be in this syntax just + "valpurijustifier". + 3.03. Replace "base" with the code name of the base class, like + "item", "meleeweapon" or "scroll". The new class inherits all + specialities of the base class. + 3.04. Replace "imaterials" with the number of material types used in + the item. A "banana" would have 2 materials: "bananaflesh" and + "bananapeal". + 3.05. Use the following syntax for "dparameters": + + ( + size, + creatematerials + ) + + Place here the size of the item. Creatematerials determines + whether the base class creates its own materials. Normally + this is "false". + + 3.06. If your item will have only one material, replace "constructor" + with the following syntax: + + { if(CreateMaterials) InitMaterials(material); } + + Where "material" is the chosen material, like "new glass(150)". + + If you want many materials, type something like this: + + { if(CreateMaterials) + InitMaterials(n, material1, material2, ..., materialn); } + + Where n is the same number as imaterials, and material1-n are + the materials created by default for your item. + + 3.07. Replace "load" with empty brackets, or optionally write some brief + loading code between them. + 3.08. Look above your class. Pick the last concrete class declared there, + and take its "type". Add one to this, and place the result to + "type". + 3.09. Replace "possibility" with the possibility of appearing in the + dungeon you want this item to have. It is compared with the + possibilities of other items, so you should look at them to + see how much this should be. + 3.10. Place all the functions you want to overwrite to the "data" + field. The essential functions are: + + NAME_SINGULAR RET() + NAME_PLURAL RET() + C_BITMAP_POS RETV(, ) + C_FORM_MODIFIER RET(
) + OFFER_MODIFIER RET() + + Which specify the name of the character, the coordinates + of the item's picture in item.pcx, the modifier that is + applied when calculating hit damage the item does when + wielded, and the modifier which is used to determine + you much the item is worth when sacrifying it to deities. + + Add any other functions here you want, like + CAN_BE_WISHED RET(true) or IS_PERTTUS_NUT RET(true). If the + function code is long, place semicolon (;) in the place of + "RET()" and write your code to item.cpp instead. + + 3.11. Your item is ready! + +---------------------------------------------------------------------------- + +4. Use the following syntax: + + ABSTRACT_ITEM( + name, + base, + imaterials, + constructor, + load, + data + ); + + 4.01. Add this at the end of item.h. + 4.02. Replace "name" with the code name of the new item base class. + 4.03. Replace "base" with the code name of the base class of the base + class, like "meleeweapon" for "sword" or "item" for "scroll". + The children of your base class will inherit all specialities + of both classes. + 4.04. Replace "imaterials" with the number of material types used in + the item. A "sword" would have 3 materials: the material of the + handle, the material of the edge and the material of the + optional dipped poison. + 4.05. Replace "constructor" with empty brackets, or optionally write + some brief constructor code between them. + 4.06. Replace "load" with empty brackets, or optionally write some brief + loading code between them. + 4.07. Place all the functions you want to overwrite to the "data" + field. Add any other functions here you want, like + CAN_BE_WISHED RET(true) or READ;. If the function code is long, + place semicolon (;) in the place of "RET()" and write + your code to item.cpp instead. All classes derived from this class + will inherit these functions unless they overload them themselves. + + 4.08. Your new item base class is ready! + +---------------------------------------------------------------------------- + +End of document. \ No newline at end of file diff --git a/Doc/Obsolete/item4.txt b/Doc/Obsolete/item4.txt new file mode 100644 index 0000000..dc9ea59 --- /dev/null +++ b/Doc/Obsolete/item4.txt @@ -0,0 +1,97 @@ + weight size dam volume mw fm req + +fists +dagger 400 40 2,25 25/25 200 70 0 300 +short sword 1000 70 3,25 90/35 720 80 0 200 +long sword 1400 120 4 130/45 1040 100 3 100 +scimitar 1600 100 4,25 150/50 1200 105 3 +katana 1200 100 4,75 110/40 880 150 2 +!broadsword 2200 100 4,5 225/50 1800 90 6 +axe (t) 1800 60 3 200/400w 1600 45 1 750 +battle axe (t) 2400 110 4,5 275/400w 2200 85 9 +pick-axe (t) 2000 75 2,5 225/400w 1800 35 3 100 +morning star (t) 3750 55 4,5 450/300w 3600 65 5 +mace (t) 2650 70 4 300/500w 2400 65 4 500 +whip 420 210 1 800l/40w 400 7*f 1 50 +war hammer (t) 2250 60 3,5 250/500w 2000 55 2 500 +sickle 825 40 2,5 100/50w 800 45 0 50 +club (t) 1500 90 1,5 3000w 1500 35 2 + +scythe (t) 2100 200 3 150/1800w 1200 50 24 +spear (t) 1800 190 3,5 100/2000w 800 85 16 1000 +bastard sword (t) 2600 130 5 225/100 1800 115 15 +two-handed sword (t) 4000 150 6 350/150 2800 135 50 50 +two-handed scimitar (t) 4400 140 6,25 400/150 3200 140 53 0 +halberd (t) 3900 170 5,75 375/1800w 3000 115 61 200 +war flail (t) - 160 ? 200*h/2000w 1600*h +colossal m-star (t) 7500 140 6,5 750/3000w 6000 115 154 +quarterstaff (t) 2000 220 1,75 4000w 2000 40 27 +great axe (t) 4200 140 5,5 450/1000w 3600 100 48 + +Justifier 900 125 12,25 200v/100v 600 300 0 +Neerc Se-ulb (t) 6500 100 12,75 800m/500m 4000 250 56 +Mjolak (t) 4000 180 9 400s/2000w 3000 225 69 +Vermis (t) 1250 220 7,75 100d/1800w 350 250 10 +Turox (t) 6300 90 10 800s/600w 6000 200 42 +whip of thievery 525 250 2 1000l/50w 500 225 2 + +*chain mail 16000 75 15 +*leather armor 2000 75 15 +*plate mail 32000 75 15 +shirt 100 60 15 +robe 1800 + +buckler 900 50? +small shield 1350 +*iron shield 4000 15 +iron tower shield 6800 +*leather cloak 1500 +*leather boots 400 +*leather gauntlets 300 +*leather belt 150 +*iron helmet 1600 + +PALMLEAF + +lantern 2500 30 1000 2500 20 +can 400 10 50 400 15 +lump 600 10 500 600 10 +banana 200 20 40/150 20 25 +banana peels 20 20 40 20 20 +water bottle 1650 30 60/1500 150 25 +broken bottle 150 10 1,1 60 150 50 +scroll 150 30 250 150 20 +nut 30 3 25 30 10 +bone 1000 50 1,1 500 1000 25 +loaf of beef 600 40 500 600 20 +iron wand 1000 30 125 1000 30 +kiwi 60 10 50 60 10 +pineapple 1200 20 1000 1200 10 +palmbranch 2000 80 4000 2000 15 +backpack (empty) 500 80 1000 500 10 +holy book 1200 25 0,75 2000 1200 20 +50 million roubles 500 20 1000 500 15 +oil lamp 3000 30 1,25 150 3000 20 +diamond stone 350 30 100 350 10 +mine (empty) 800 50 100 800 10 +key 120 10 15 120 30 +diamond ring 10 2 3 10 10 +diamond amulet 105 20 30 105 10 +whistle 290 50 40 290 25 +small chest (empty) 3750 50 1,25 7500 3750 15 +chest (empty) 30000 100 60000 30000 15 +large chest (empty) 240000 200 480000 240000 15 +bear trap 4000 50 3 500 4000 30 +stethoscope 400 40 200 400 10 +skull 1200 20 0,9 600 1200 15 + +head 10 +torso 10 +arm 15 +groin 10 +leg 15 + +holy banana 2000 30 1 400/1500 600 100 0 +left nut of Petrus 600 10 0,5 500 600 15 0 +Avatar of Valpurus 750 10 2,75 250 750 15 0 +head of Elpuri 60000 60 2,25 25000 60000 15 1728 diff --git a/Doc/Obsolete/itemohje.txt b/Doc/Obsolete/itemohje.txt new file mode 100644 index 0000000..81e55f7 --- /dev/null +++ b/Doc/Obsolete/itemohje.txt @@ -0,0 +1,16 @@ +item.h + +NUMBER_OF_ITEM_TYPES++ + +#define T_ITEM ++ + +item.c + +item_tile_posx = x kuvatiedostossa +item_tile_posy = y kuvatiedostossa + +name + +generate_items -> eka for ++ + +mahdollisesti randomi \ No newline at end of file diff --git a/Doc/Obsolete/jobs.txt b/Doc/Obsolete/jobs.txt new file mode 100644 index 0000000..a1d1074 --- /dev/null +++ b/Doc/Obsolete/jobs.txt @@ -0,0 +1,70 @@ +//++++++kamikaze dwarf death ++++++low-gc balancing +//+++++ommel bone/temporary sell +//+++++selectfrompossessions bugs in smith +//++++harden material + limbs +//++++gods + harden material +//++++mine field +//++++fix odd hostilities +//++++aslightly rusted +//++++check hex's cathedral explosive code +//++++don't consume valuable +++++charisma/int req for taming +//++++walls of GC10-11 +//++++slowing spoiling +//++++pleasant/ etc. +//++++petrus etc. should be immune to faint +//++++priest should cure lycanthropy +++++test oree +//++++stab, slash, smash +//++++sumo wrestler goes insane bug +//++++acid name to death msg +//++++moving sumo +//++++shirt of the golden eagle av +//++++charisma sparkle + blood bug +//++++autovomit to Valpurus's altar +//++++"swimming" +//++++"killed accidentally by Legifer" +//+++shops should be more difficult +*+++slipping when running +*+++blue poison face +//+++"there's vomit on the wall" ++++making staff of wondrous smells better +//*+++Breaking wand of mirroring +//*+++character spill ++++lyre + charisma +//+++vermis teleport state ++++rectangular amnesia +//+++ommel bone values +//+++day difficulty +//+++ring/cloak of acid resistance +//+++"slight tingling in your hands" even if none +*+++weak kick has no chance +*+++gc13 locks +//++water border tiles +//*++big spider +++dipping to corpses +//++science talk messages +//++poisonous spider blood +++vesana + giant plant animation +//++mistress whips +++readme/librarian spoiler +++leprosy head drop +//+archangel to petrus ++applying potions -> liquid to ground ++taming vesana ++getting Ivan +*+gods give pets ++displacement priority ++optional: singing dwarves +victory screen etc. +//search \:b$ + + +check polymorph +check vaults + +:screenshots - menu, map, new attnam, attnam, vesana, ut4, lights, explosion, bodypart lose, polymorph, high levels +:thumbnails +:readme +:game description diff --git a/Doc/Obsolete/jumalehdotukset.txt b/Doc/Obsolete/jumalehdotukset.txt new file mode 100644 index 0000000..c5f6f72 --- /dev/null +++ b/Doc/Obsolete/jumalehdotukset.txt @@ -0,0 +1,45 @@ +Jumalaehdotukset + +Formaatti: +selite - vanha nimi, vanhan nimen suku m/f/n (suomennos), nimiehdotus 1, suku (suomennos), nimehdotus 2, suku (suomennos) ... + +King of Gods - Valpurus, m, Valpuri, n +goddess of health and nutrition - !*Seges, f (viljapelto), Salus, f (terveys), *Salubris, f (terve), Viva, f (elävä), Culina, f (ruoka/keittiö) +god of charity and munificence - !Atavus, m (isoisoisoisä), Donum, n (lahja), *Beneficus, m (hyväntekeväinen/antelias) +goddess of love and art - !*Dulcis, n (ihana), Decora, f (kauneus/kunnia/arvokuus), Artifex, n (taiteilija), *Amatrix, f (rakastajatar) +god of law and order - Venius, m (ei tarkoita mitään?), Iustitia, f (oikeus), Iudex, m (tuomari/valamies), !*Legifer, n (lakia lukeva) +god of knowledge and magic - Consummo, n (saan valmiiksi), Magus, m (taikuri/viisas mies/astrologi), !*Sophos, m (viisas mies) + +goddess of nature - !*Silva, f (metsä), Nux, f (pähkinä), *Florea, f (kukkainen), Ver, n (kevät) +god of fire, machines and weaponry - Loricatus, m (rintahaarniskaa pukeva), Incus, f (alasin), Ignipotens, n (tulen herra), *Ignigena, m (tulesta syntynyt), *Faber, m (työntekijä, seppä) +god of money, trade and politics - !Mellis, n (hunaja, maukkuus), Mercator, m (kauppias), Auraria, f (kultakaivos), Leguleius, m (kieroileva lakimies), Pactio, f (sopimus/kauppa) + +goddess of greed and forbidden pleasures - Macellarius, m (elintarvikekauppias?), !*Nefas, n (synti/jumalaisen lain rikkomus) +chaos god of wrong knowledge and vile magic - !Infuscor, n (pimentää/lahjoa/turmella), Sinistra, f (vasen käsi/väärä/pahaenteinen), Veneficus, m (myrkyttäjä/taikuri), *Lamia, f (noita, vampyyri), Praecantrix, f (noita) +god of crime - Calamus, m (oksa/sivuhaara/keppi/vapa/me kutsumme/me irrotamme/yms.), Noxia, f (rikos/virhe), *Fraus, f (huijaus/temppu/rikos), Sicarius, m (salamurhaaja), Furtum, n (varkaus), *Latro, m (maantierosvo) +god of war and blood - !*Cruentus, m (verinen) +goddess of mutations, deseases and famine - !Scabies, f (syyhy/kutina), *Vomica, f (haava/rutto/kirous), Fames, f (nälkä), *Pestilentia, f (rutto) +Destroyer of Worlds - Erado, n (minä raavin pois/ajan partaa?), Perditor, m (tuhoaja), Sepelio, n (minä hautaan/hajotan), Trucido, n (minä teurastan/joukkomurhaan), Nex, f (kuolema), !*Mortifer, n (tappava, tuhoava) + +Valpurus m +Seges f +Atavus m +Dulcis f +Legifer m +Sophos m +Silva f +Loricatus m +Mellis m +Nefas m +Infuscor f +Calamus? +Scabies f +Cruentus m +Mortifer m + +Pugio (tikari) +Vira (myrkyt) +Proditor (petturi) +Furax (varasteleva) +Cleptor/Cleptes (varas) +Cleptia (varkaus?) diff --git "a/Doc/Obsolete/k+\304\204+\304\204nt+\303\202.txt" "b/Doc/Obsolete/k+\304\204+\304\204nt+\303\202.txt" new file mode 100644 index 0000000..613c294 --- /dev/null +++ "b/Doc/Obsolete/k+\304\204+\304\204nt+\303\202.txt" @@ -0,0 +1,19 @@ +vanhat: + +djgpp debug 10:45 +djgpp release 17:20 + +vc debug 4:50 +vc release 5:20 + +hex 16:00 + +uudet: + +djgpp debug 4:20 -6:25 -60% +djgpp release 11:05 -6:15 -36% + +vc debug 1:45 -3:05 -64% +vc release 2:10 -3:10 -60% + +hex 6:40 -9:20 -58% diff --git a/Doc/Obsolete/levelf.txt b/Doc/Obsolete/levelf.txt new file mode 100644 index 0000000..eb3ffe9 --- /dev/null +++ b/Doc/Obsolete/levelf.txt @@ -0,0 +1,5 @@ +levelf.txt -- Level Format + +first two lines : x and y, player default start position. +(I'm gonna remote this, when stairs and suchs is ready) +then about tree thousand and 68? lines are level information diff --git a/Doc/Obsolete/light.txt b/Doc/Obsolete/light.txt new file mode 100644 index 0000000..74183db --- /dev/null +++ b/Doc/Obsolete/light.txt @@ -0,0 +1,165 @@ +---------------------------------------------------------------------------- + + IVAN lighting system documentation + +---------------------------------------------------------------------------- + +The lighting system in IVAN is a quite complex one and extends to numerous +different files and so it is rather difficult to understand it just by +looking at the source. This file explains it's mechanisms throughoutly. + +All light is divided into two categories: emitation and luminance. +The former is light shed by a light-producing object like a lamp, +the latter is how much actual light there is on an object's surface. +Emitation is a natural attribute of an object, luminance a calculated +value shared by all objects on the same square, determined by present +emitating objects and their distance from the luminanced object. Both +emitation and luminance are of type ushort and are measured in a scale +from zero to five hundred and eleven, minimum meaning no light and maximum +meaning the ultimate edge of brigthness. + +Class levelsquare is the basic light emitter of IVAN. The amount of light +it sheds is directly calculated in levelsquare::CalculateEmitation() +by going through all instances of class object's derived classes +(characters, items, terrains) currently on the levelsquare, calling their +CEmitation() functions and picking up the largest returned value of all of +them. Most of these classes use material::CEmitation() to consult their +materials in order to retrieve the emitation value, other classes like +the lamp just override their own CEmitation() with a constant value. +Choosing always the highest value instead of a sum of values is not very +realistic, but it works fine. + +When a level is generated and initialized to be playable, a function +named levelsquare::Emitate() is called for every tile. If emitation +is greater than or equal to 160, it creates a virtual square of tiles with +a diameter of 4 * sqrt(Emitation / 5 - 32) + 1 (*) centered on the current +square. Then via game::DoLine() it "draws" lines beginning from the center +to each square on the edges, calls game::EmitationHandler on each square on +the line, which adds the source square to the "emitation list" of that +particular square. If a tile with an overterrain impassable for light is +detected, the line stops and no further emitation lists are updated until +next line is begun. + +Of course applying the former formula with the square root for every +square would be slow, so the wanted number is precalculated when the +game begins in game::InitLuxTable() and stored in class game, where it can +be retrieved with game::CLuxTableSize()[Emitation], where Emitation +is the emitation of the source levelsquare. + +EmitationHandler adds the emitator to the emitation list of the +current square via levelsquare::AlterLuminance(vector(OX, OY), Emit), +where OX and OY are the coordinates of the emitator and Emit is +the emitation. Of course, at this point it is not the same emitation +as in the origo of the emitation square. As the laws of physics show, +luminance is inverse to the square of distance. However this would +produce a division by zero on the origo square, so IVAN uses a bit +more complicated formula: Emitation / (Distance^2 / 128 + 1). +Of course this calculation cannot be made for every square, so it is +precalculated in the formely mentioned game::InitLuxTable() and +can be retrieved by the following call: + +game::CLuxTable()[Emitation] +[long(CX) - long(OX) + (game::CLuxTableSize()[Emitation] >> 1)] +[long(CY) - long(OY) + (game::CLuxTableSize()[Emitation] >> 1)] + +Where CX and CY are current coordinates, OX and OY emitator coordinates +and Emitation the emitation of the emitator. + +The emitation list of levelsquare is stored in a dynarray of type emitter +named Emitter, which contains both coordinate vectors and emitation values. +It is used by ushort levelsquare::CLuminance() to determine the actual +luminance of the square, which is the highest emitation value of the list. +Of course, AlterLuminance discards all emitation values less than 160, +the minimum luminance possible. + +Emitate is not called only at the beginning of the level. When light +level of some square changes, it or its sister functions may need to +be recalled. + +Function levelsquare::CalculateEmitation() is may be rather slow if +for example a hundred items are lying on the square, so it cannot be +used to detect lighting level changes. For this there are two other +functions: If an emitating object is removed from the square, +levelsquare::SignalEmitationDecrease(ushort EmitationUpdate) +is called, and levelsquare::SignalEmitationIncrease(ushort EmitationUpdate) +is called if the opposite event happens. The value of the last +CalculateEmitation() is stored in an ushort named levelsquare::Emitation, +so SignalEmitationIncrease() and SignalEmitationDecrease() can very easily +detect the need to Emitate or ReEmitate, respectively. + +To call Emitate again is safe, since AlterLuminance can detect whether +the emitator is already in the emitator list, and only update its +emitation value. So there's no fear it'll bloat over time. + +ReEmitate differs from emitate only by the fact that the old emitation +is used to determine the emitation range instead of the new one. +So AlterLuminance is run for also those squares that are not in the +new range, and a number less than 160 is passed to it as a parameter. +This causes AlterLuminance to actually remove the origo square from the +emitator list. + +But what happens if a square becomes impassable and an emitator is shedding +light on it? Most obviously the emitator's Emitate must be run again. +But that doesn't affect squares beyound the new impenetrable square. +So before the square is made impassable, it is imperative that +levelsquare::ForceEmitterNoxify() is called for that square. It in turn +calls levelsquare::Noxify() for all squares on its emitter list, +which is a kind of ReEmitate of value zero. So the emitter's emitation value +is set to zero in all emitator lists it is currently, including those beyond +the square about to be blocked, but it is NOT removed from them. This is +achieved by using a whole different line routine, game::NoxifyHandler(), +which calls a different variant of AlterLuminance named NoxifyEmitter(). +Then the square must change its OverTerrain's Walkability value swiftly and +call levelsquare::ForceEmitterEmitation(), which forces all emitters (with +a temporary emitter value of zero) to Emitate themselves across the dungeon. +Zero emitations are replaced by correct ones, except beyond the impassable +square, where they are left for the time being but have no effect on the +luminance. + +If a square loses its impassability, only ForceEmitterEmitation() needs +to be called after the square has opened for light. + +That's all about emitation. Lumination is far simpler, since in walkable +squares it is calculated in simply by picking the highest emitation in the +emitter list. In non-walkable squares, each emitator must pass one test +before its emitation can be taken into account: Player must be able to +see at least one side of this impassable block that can be seen from +the emitator also. Function named levelsquare::CalculateBitMask(vector Dir) +returns a bitmask that describes those sides that can be seen from Dir, +although no-one remembers accurately how this works. These two bitmask +are just logically anded, and if any bit is set in the result, the +process continues. + +Result of all this is the max light shed on the square, and it is used +for drawing the square. Both bitmap::Blit and bitmap::MaskedBlit have +variants that support the 0-511 leveled luminance in the following +way: Luminance - 256 is added to each color component and the result +is bound to the range 0-255. This means that an item on a square with +256 luminance is shown on the screen as it appears in item.pcx. +Zero would mean the item is night black, and 511 that it is tooth +white. + +These blits are slower than their normal counterparts, however, so +they are only used twice per square: once when blitting the +igraph::TileBuffer to Memorized and once blitting it to the DOUBLEBUFFER. +Software gamma corrections and such are applied before the blitting, +and in that calculation the luminance value takes its final form +after a long journey. + +---------------------------------------------------------------------------- + +(*) Since 160 is the minimum luminance border for square to be drawn, + minimum needed distance from the center can be calculated with + the following formula (e is emitation, r is radius): + + e / (r^2 / 128 + 1) >= 160 + e >= 1.25 * r^2 + 160 + e - 160 >= 1.25 * r^2 + r <= 2 * sqrt(e / 5 - 32) + + This is the minimum radius. So the diameter of the square needs not + to be more than 4 * sqrt(e / 5 - 32) + 1. + +---------------------------------------------------------------------------- + +End of document. \ No newline at end of file diff --git a/Doc/Obsolete/priest.txt b/Doc/Obsolete/priest.txt new file mode 100644 index 0000000..e41da2f --- /dev/null +++ b/Doc/Obsolete/priest.txt @@ -0,0 +1,19 @@ +std::string legifer::GetPriestMessage() const +{ + return "\"" + GetName() + " is the Great Protector of all Law and Order. Prayed upon, He may burn thy enemies with the Fire of Justice, if thou art worthy.\""; +} + +std::string dulcis::GetPriestMessage() const +{ + return "\"" + GetName() + " is the Creator of everything we call Art and Beauty. When thou prayest for Her help, She may calm thy worst enemies with Her love. But beware! There are villains that may resist even Her call!\""; +} + +std::string seges::GetPriestMessage() const +{ + return "\"" + GetName() + " brings Life, Health and Nutrition to all who follow Her. When thou callest upon Her with an empty stomach, a miracle may indeed fill it."; +} + +std::string sophos::GetPriestMessage() const +{ + return "\"The Wise bow before " + GetName() + ", for He maketh the Universe as rational as it is. Those who follow Him are not bound to space and time, since knowledge controls them. This is why those chosen by Him may escape any danger with their wisdom. Alas, beware! Soon thou mayst find thyself in an even worse situation!\""; +} diff --git a/Doc/Obsolete/project.txt b/Doc/Obsolete/project.txt new file mode 100644 index 0000000..76d0225 --- /dev/null +++ b/Doc/Obsolete/project.txt @@ -0,0 +1,39 @@ +---------------------------------------------------------------------------- + + How to create a new project in IVAN workspace, step by step tutorial + +---------------------------------------------------------------------------- + + == name of the new project + +1. Create a subfolder named "" into the IVAN main directory. +2. Create empty subfolders named "Source" and "Include" to the "" + folder. +3. Create the new project to IVAN workspace with VC++. Choose your IVAN + folder as the folder of the project, *not* IVAN/ as Visual + Studio suggests. If your project is a library, make it dependent of + "Main". +4. Go to project settings. + 4.1. In the "General" sheet, place "/" in the beginning of + the text in "Intermediate files:" and "Output files:" textboxes. + Do this to both Debug and Release modes. + 4.2. In the "C/C++" sheet, choose category "Preprocessor" and place + the following text into the "Additional include directories" text + box: "Include,Main/Include, /Include", and after that all + Include directories of all projects currently in the workspace. + 4.3. Add the "/Include" into all other projects' Include + settings. + 4.4. If your project is a library, switch to project settings of Main + Debug and add "/Debug/.lib" to "Link"->"General"-> + "Object/library modules", and "/Release/.lib" to + the same place in Main Release. Do this also to all other + exe-producing projects, like LibTest, if they need your project. +5. Add a folder named "Source" to the project, and choose file extensions + "c,cc,cpp". Do the same for "Include", with file extensions "h,hh,hpp". +6. Add or create your needed files to the project. They will be + automatically placed into appropriate directories. +7. Begin programming. + +---------------------------------------------------------------------------- + +End of document. \ No newline at end of file diff --git a/Doc/Obsolete/tehty.txt b/Doc/Obsolete/tehty.txt new file mode 100644 index 0000000..954cc5a --- /dev/null +++ b/Doc/Obsolete/tehty.txt @@ -0,0 +1,25 @@ +A: ikkunan siirtoon, pienentämiseen, piilottamiseen ja sulkemiseen liittyvät ongelmat korjattu +B: fullscreenin db-ongelmat korjattu +C: saven viemää aikaa pienennetty rajusti, joskaan ei ihan tarpeeksi :( +D: koodinparantelua viety eteenpäin: characterien ja itemien lisäys tarvitsee nyt tehdä vain *yhdessä* paikassa +E: pcx-laturi tehty ja kuvat konvertoitu ko. formaattiin +F: värisysteemi uusittu (vanha oli turhan monimutkainen) +G: Kouluruokajumala- ja Inasmus-abuset tukittu +H: Moninkertaisiin kuolemiin liittyvät bugit korjattu + +I: lisätty in-game näyttömoodin vaihto (näppäimestä F4) +J: soft-gammaan liittyvät bugit korjattu +K: lisätty paljon pisteitä messageihin, joiden lopusta moinen uupui +L: korjattu oksennuksessa ollut messagebugi +M: poistettu toinen receivehiteffect-systeemi (niitä oli jostain syystä kaksi (?)) + +N: RememberedItemssiin liittyvät bugit korjattu +O: LevelMsg:ihin liittyvät bugit korjattu +P: poistettu uudempi prototyyppijärjestelmätesti, joka olisi selvästi vaikeuttanut koodaamistasi +Q: korjattu bugi funktiossa game::TriggerQuestForMaakotkaShirt() +R: korjattu bugi, joka salli lamppujen kiinnittymisen aukinaisiin oviin + +S: ikkunan pyyhkimisbugi korjattu + +T: SoW integroitu +U: saven viemää tilaa pienennetty tarpeeksi \ No newline at end of file diff --git a/Doc/Obsolete/towns.txt b/Doc/Obsolete/towns.txt new file mode 100644 index 0000000..8122359 --- /dev/null +++ b/Doc/Obsolete/towns.txt @@ -0,0 +1,15 @@ +Attnam + +Hog + +Mondedr + +Dwarf city + +Water palace + +Genies' capital + +Ivan's home village + +Barbarians' capital \ No newline at end of file diff --git a/Doc/Obsolete/wish balance.txt b/Doc/Obsolete/wish balance.txt new file mode 100644 index 0000000..3666fe0 --- /dev/null +++ b/Doc/Obsolete/wish balance.txt @@ -0,0 +1,18 @@ +orc general 27 27 18 35 32 36 435 polearms 1000 +mistress whip champion 26 26 51 52 26 39 165 whips 1000 +elite dark knight 36 36 13 18 26 30 301 lswords 1000 +polar bear 50 - - 10 20 24 600 - 9-16 +communist 40 40 12 12 25 18 373 several +mithril golem 200200 4 4 1 12 1000 - 14-24 +diamond golem 300300 2 2 1 12 2250 - 17-30 +phoenix fearher golem 80 80 60 60 1 12 157 - 9-16 + + +!ring of invisibility +magic whistle +scroll of change material +amulet of life saving +ring of infravision +amulet of ESP +ring of poison resistance +armor of great health diff --git a/Doc/Source/CVS/Entries b/Doc/Source/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/Doc/Source/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/Doc/Source/CVS/Repository b/Doc/Source/CVS/Repository new file mode 100644 index 0000000..2c11a45 --- /dev/null +++ b/Doc/Source/CVS/Repository @@ -0,0 +1 @@ +ivan/Doc/Source diff --git a/Doc/Source/CVS/Root b/Doc/Source/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/Doc/Source/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git "a/Doc/Ty+\303\202t/CVS/Entries" "b/Doc/Ty+\303\202t/CVS/Entries" new file mode 100644 index 0000000..1784810 --- /dev/null +++ "b/Doc/Ty+\303\202t/CVS/Entries" @@ -0,0 +1 @@ +D diff --git "a/Doc/Ty+\303\202t/CVS/Repository" "b/Doc/Ty+\303\202t/CVS/Repository" new file mode 100644 index 0000000..5adfbe6 --- /dev/null +++ "b/Doc/Ty+\303\202t/CVS/Repository" @@ -0,0 +1 @@ +ivan/Doc/Työt diff --git "a/Doc/Ty+\303\202t/CVS/Root" "b/Doc/Ty+\303\202t/CVS/Root" new file mode 100644 index 0000000..8d60cee --- /dev/null +++ "b/Doc/Ty+\303\202t/CVS/Root" @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/Doc/Visual/CVS/Entries b/Doc/Visual/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/Doc/Visual/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/Doc/Visual/CVS/Repository b/Doc/Visual/CVS/Repository new file mode 100644 index 0000000..de3856e --- /dev/null +++ b/Doc/Visual/CVS/Repository @@ -0,0 +1 @@ +ivan/Doc/Visual diff --git a/Doc/Visual/CVS/Root b/Doc/Visual/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/Doc/Visual/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/Doc/Work/Axis Ideas.txt b/Doc/Work/Axis Ideas.txt new file mode 100644 index 0000000..80358dc --- /dev/null +++ b/Doc/Work/Axis Ideas.txt @@ -0,0 +1,10 @@ +Possible alignment axes: + +good - evil +egoism - altruism +individualism - collectivism +anarchism - totalitarianism +conservatism - radicalism +capitalism - communism +ecologism - anthropocentrism +magicism - anti-magicism diff --git a/Doc/Work/Balance.txt b/Doc/Work/Balance.txt new file mode 100644 index 0000000..e740510 --- /dev/null +++ b/Doc/Work/Balance.txt @@ -0,0 +1,22 @@ +* high priority +// done +/ partly done or needs testing +(r) rejected +(s) suspended + +Turox's price is too high. +Dagger is "somewhat difficult to use" if one has 9 strength. +Golems yield too much score. +Phoenix feather shields may be too good. +The patrol guard's helmet is too good considering how easily he can be tamed or killed by tricks. +World map locations may be too far from each other. +The amount of mithril etc. beartraps should be decreased. +Staff of wondrous smells is useless. +Limb weights may be incorrect. +School food abuse!!!! +Monster danger is adjusted too fast. +It should be checked whether read times of scrolls used in emergency situations are short enough. +Repairing special limbs in Attnam is too cheap. +The mage room is too difficult. +'.' should take less time. +Legs made of ommel cerumen. diff --git a/Doc/Work/Bugs.txt b/Doc/Work/Bugs.txt new file mode 100644 index 0000000..09d5ff2 --- /dev/null +++ b/Doc/Work/Bugs.txt @@ -0,0 +1,108 @@ +* high priority +// fixed +/ partly fixed or needs still testing +(r) rejected +(s) suspended +(?) not understandable +(initials +/-) reserved for the following coder, plusses or minuses indicate priority + +(s) Alt+Enter twice. +(s) Alt+Enter in config screen. +(s) The White Screen Bug. +(s) Wishing for corpse or any bodypart in WMode crashes. +(s) One cannot eat a can MADE of bananaflesh. +Entering a dungeon and loading an autosave saves some time. +(s) Bear trap in shop. +(s) "Valpurus shall not carry more continents!" +(s) (example of the problem) If you generate a sign in the script future random palms may overwrite the sign. (comment: can usually be avoided by reordering script) +(s) Trying to polymorph into a valpurium golem (which is prohibited) gives a wrong message. +/Putting mines in Attnam is considered OK by guards. +(s) Wielded items can't be eaten. +(s) Throwing a potion of water at a wall in a shop makes the guards hostile. +(s) If the player can attack with arms but is a more powerful kicker, the danger system still only checks the arm attack. +(s) The head of Ivan is left in bone levels. +(s) Kamikaze dwarves die smiling even if headless. +(s) Odd pentagram missing bug. +(s) A puppy can polymorph into a puppy. +(s) Do new bodyparts of player clones work properly? (no) +Healing liquid penetrates chests. +"You manage to hurt your left leg even more" (even though leg is not hurt) +(s) While in read-mode player can find mines. +Trying to resurrect a headless player of a bonefile when his ghost is the only light source doesn't + give the right message since the soul is temporarily sucked into the corpse and everything is dark. + Should this be so? +AOGH can be abused. +Border tiles aren't shown in the dark. +Vladimir doesn't destroy walls on the turn when it is created. +Mirage puppies. +(s) Still some problems with bad edge tiles. +If the whole level is full of monsters, the game won't work correctly. +/ Better gentoo packages. No root should be needed. +Petrus's AI goes rather insane (he just runs around his throne) if he is severely wounded. +Valpurus, Dulcis and Nefas can't harm headless zombies nor skeletons. +(hs) Golems can be poisoned at least by poison covered weapons. +It seems to be possible to occasionally find traps by saveloading next to them. +If a pet polymorphs into an invisible stalker which the player can't see, the polymorph msg is incorrect. +If you hit someone with one hand and either one of you teleports, you can still hit the opponent with the other hand. +(s) Border tile bug. +Mirror + WorldMap. +You can destroy terrains in Attnam with a wand of acid rain without causing an alarm. +(d) "The kobold glows and transforms into an invisible stalker." +Kamikaze dwarfs' death message is wrong when they kamikaze themselves to death. +"There is many items on the wall" +Vladimir can now be teleported (example: magical whistle) to places where he destroys the cathedral and buildings like that before moving away from them. +Funny colors with dexterity etc when going to change equipment and then pressing esc. +MISSING: Etat the Maniac. When given anything confusing to eat or drink, will go mad and will try to kill everything in sight including the player. +Light sources in glass chests don't work correctly. +Digging walls with lanterns doesn't update memorized etc. correctly. +//(hs) Shopkeepers/librarians can buy items even if unconsciouss. +If an NPC is poisoned and vomits at the player, the game thinks it's an attack and makes teams hostile to each other. +//(hs) You can't rest while panicked, yet you can continue resting if you panic while doing so, for instance because of poison or acid damaging you. +One has no way to use stairs covered by sticky slime. +//Typo in GPL notice +(hs) Tailor doesn't fix straight from equipment +//Spider corpses are not poisonous. +Charmed guards often block hallways when guarding them and the player who can't displace them has to dig a path around them. +Small chest X fits in small chest Y, even though Y fits in X as well. +The patrol guard's AI works oddly if the player rests on one of his waypoints for a long time. +(hs) Scroll of golem creation destroys chest contents. +Danger sensed when a bunny panics. +I think these might be a problem in bonefile names which could cause the eleventh+ bone of GC11/12 to appear in place of GC2. +Do you get strength bonus for digging? +(this problem verified only with windows version) Alt+tab then return to program and alt-enter screws things up. +(hs) Should picking up items from chests require one useable hand? +I think there *might* be a screen update problem when a non-animated monster, for instance a kobold, chokes itself in a spider web (the 'Z' symbol may not appear immediately). +Changing resolutions, visiting programs that take control of the whole screen, etc. can sometimes cause IVAN's graphics system not to be able to open the window again, or fail to show anything on it. +Earthquake always drops morraine, even though it may be incorrect in the respective level. +No real need to correct this, but I'll mention because it's a bit funny: if a 1x1 water tile is surrounded by a massive continent in the world map, lookmode still shows "You see here ocean." as its description. +"You feel very ill. Your right arm drops to the ground." while flying over an ocean. +Blood on the floor, door closed -> blood on the door. +Plants can vomit. +Odd font graphics bug in gentoo. +Several archangels with the same name can be generated. +Several artifact weapons with the same name can be generated. +Check: does Ivan break doors correctly? +"Weak kick has no chance to affect door" even if the door is sometimes affected by a second kick. +Earthquakes drop moraine in the level even if it's inappropriate. +Check that detecting gunpowder works. +ESC should undo material detection etc. +Lumps of glass are considered food. +"the corpse of dark frog is too important for you to sacrifice" +Cloak and armor shouldn't protect from broken bottles. (from Ighalli) +You can bite with an unusable head. +Petrus says that they have witnessed you leaving the tunnel even if you got here by means of levitation. (from Z) +Crash : During the reading of a scroll of repair. See forum. +(hs) Leprosy + cathedral bug. +(hs) Mines do not disappear in the cathedral. +10000/100 HP bug. +If you try to open a (closed) chest that stands on the place of open door, the game calls you idiot for no reason. +Floating eyes can vomit. +Selling activated holy grenades should be handled. +The script seems not to accept '§' character. +//Patheon book of Mellis etc. +The Static Whore Insects of the Unholy Solar Eclipse. +The Ultra-Difficult Hell-Freezing Death Exception Bug. +Glittering slaves. +Since "fainting" is a proportional alertion state of stamina, great increases in stamina may trigger it. Ie. wearing an AoGH may cause you to become fainting, although probably not to actually faint. +"The levitating ostrich begins to float." +There may be some bug in the full screen mode activation. diff --git a/Doc/Work/CVS/Entries b/Doc/Work/CVS/Entries new file mode 100644 index 0000000..8a165f3 --- /dev/null +++ b/Doc/Work/CVS/Entries @@ -0,0 +1,22 @@ +/Axis Ideas.txt/1.2/Fri Nov 29 22:37:45 2002// +/Balance.txt/1.33/Sat Apr 2 17:14:58 2005// +/Bugs.txt/1.285/Mon Aug 21 18:58:55 2006// +/City ideas.txt/1.2/Fri Jul 4 18:11:09 2003// +/Code Improvements.txt/1.57/Tue Mar 1 18:01:00 2005// +/Cookies.txt/1.4/Fri Feb 18 22:00:47 2005// +/Descriptions.txt/1.1/Mon Jul 17 07:01:37 2006// +/Encounter Ideas.txt/1.2/Thu Apr 3 15:23:49 2003// +/Game End Ideas.txt/1.1/Thu Jul 8 21:56:21 2004// +/Graphics.txt/1.58/Sun Jul 24 23:18:21 2005// +/Great Ideas.txt/1.75/Tue May 10 20:32:40 2005// +/Great Jobs.txt/1.18/Fri Feb 11 12:44:29 2005// +/HexWork.txt/1.3/Thu Jul 8 18:00:45 2004// +/Holy Water.txt/1.2/Thu Apr 3 15:23:53 2003// +/Humanoids.txt/1.3/Thu Apr 3 15:23:54 2003// +/Optimization.txt/1.9/Fri Feb 11 12:44:29 2005// +/Place Ideas.txt/1.2/Wed Apr 30 21:15:00 2003// +/Pray Ideas.txt/1.2/Thu Apr 3 15:23:57 2003// +/Small Ideas.txt/1.384/Mon Aug 21 18:58:55 2006// +/Value Plan.txt/1.9/Fri Mar 4 13:08:06 2005// +/Version Requirements.txt/1.3/Thu Sep 23 21:13:32 2004// +D diff --git a/Doc/Work/CVS/Repository b/Doc/Work/CVS/Repository new file mode 100644 index 0000000..da998eb --- /dev/null +++ b/Doc/Work/CVS/Repository @@ -0,0 +1 @@ +ivan/Doc/Work diff --git a/Doc/Work/CVS/Root b/Doc/Work/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/Doc/Work/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/Doc/Work/City ideas.txt b/Doc/Work/City ideas.txt new file mode 100644 index 0000000..b45efb8 --- /dev/null +++ b/Doc/Work/City ideas.txt @@ -0,0 +1,39 @@ +Attnam - city of order (totalitarianist city) +terrain: evergreen forest +alignment: order, death, self, man + +Hog - city of gurus (greece philosopher town) +terrain: leafy forest +alignment: order, life, others, nature + +Lunethia - elven city (somewhat like Lothl¢rien) +terrain: leafy forest +alignment: order, others, nature + +Mondedr - city of thieves and assassins (like Ankh-Morpork) +terrain: swamp +alignment: chaos, death, self, man + +Rihajab - desert city (like medieval Bagdad) +terrain: desert +alignment: order, man + +Kharaz-Arad - dwarven city (inside rock) +terrain: mountains +alignment: order, self, man + +Urok - city of barbarians +terrain: steppe +alignment: chaos, death, self, nature + +Athvalia - amazon city +terrain: jungle +alignment: order, death, nature + +Bazaria - trader city +terrain: coast, river delta +alignment: order, life, self, man + +Irinox - city of witches and shamans (like Kalevala's Pohjola) +terrain: tundra +alignment: order, death, self, nature diff --git a/Doc/Work/Code Improvements.txt b/Doc/Work/Code Improvements.txt new file mode 100644 index 0000000..73c214a --- /dev/null +++ b/Doc/Work/Code Improvements.txt @@ -0,0 +1,70 @@ +This file is written in Timo-dialect (a highly modified version +of the Finnish language), since these tasks are not meant to be +done nor understood by anyone else than the writer. + +* high priority +// done +/ partly done +(r) rejected +(s) suspended + +//*Funktioiden verkottaminen. +(r) Assistenttioperaattorien integrointi? (<- osoitinluokat) +//*Kenttäkutsujen modifikaatio riippumattomaksi pelaajan lokaatiosta. +//Tilojen luokittaminen. +//*Tuonpuoleisen prototyypitys. +//*Tasovaihdosten funktionaalisuuden kohotus. +//Syöntisallimusten liputtaminen. +(r) Karakterin atribuuttien kollektivointi? +(r) Karakterin atribuuttien luokitus. +(r) Tarkkailijoiden tuhoaminen? +//Pinon listaimplementaatio. (ja gearin iteraattoritoteutus?) +//Dynarrayn eliminaatio. +(r) Osoitinluokat? +//Skriptin perusdatatyyppien luokittaminen. +Kenttänluonnin uudelleentoteuttaminen tavukarttojen ja A*:n avulla. +(r) Aliasten klarifikaatio. (Perhana. Mitähän tällä tarkoitin?) +//Merkkijonoviittausten regularisoiminen. +//Merkkitaulukoiden hävittäminen. +//*Funktioparametrisoitujen aliohjelmien suosiminen makrojen sijaan. +//Protopilkonta. +//MS-koodin poisajo grafiikkakirjastosta (SDL myös Windozeen?). +//Kirjastoniputus. +(s) Esittelyjen uudelleenjärjesteleminen? (tosi tylsää ja täysin hyödytöntä) +//*Toimintokohtainen pinon käyttökelpoisuusseulonta ennen visualisointia. (tämä on väärässä tiedostossa) +//*Uusintaparseria vaatimaton järjestelmä skriptatuille satunnaismuuttujille. +//Kirjastorajapinnan organisointi systeemiriippumattomaksi. +//*Projektin kattava kompleksisten datajäsenten cachaus. +//*Pinon rajapinnan modifikaatio vähemmän kuolevaisia pelottavaksi. +Ruumiinosien sisäisen lokerokäsittelyn iteratiivisuuden alentaminen. +//Värimakrojen sisärivittäminen. +//Tilainformaation strukturalisaatio visuaalisen vastenmielisyyden vähentämiseksi. +//AP-hintojen negaatio. +*Selitepohjainen ja itsetaipuva viestijärjestelmä. (<-nimivälimuisti) +//Listaimplementaation piirtokutsujen yliparametrisaation lieventäminen. +*Nimivälimuisti. +Tietovarastojen ja prototyyppien yleisyliluokat ja/tai perustemplaatti. +//Charba.cpp:n resplittaus (kontrollit erilleen?). +Kirjallisen yliluonnollisuuden epäfunktioittaminen. +//Säiliöluokat. +//Piirron deasmaus. +//Repliikkien datafilesaatio. +Raajarajapinta. +//Jumalten arrayttäminen. +//Tiedostojen tekijätiedottaminen. +Hyökkäyslajien olioiminen. +Markkinatalouden yhdenmukaistaminen. +Potentiaali konfiguraatioiden eksplisiittiseen periyttämiseen. +//Eliöiden totuusjäsenten yhteensulauttaminen. +(r) Vektoripakkaus? +Leikisti aktiivin 1. partisiipissa olevien data-arvojen muokkaus kykylauseiksi. +Hidastinten kirjastointi. +//Litkujen materialisoiminen. +(r) Massattomat maastot. +//Metsästysvälineiden lokeroittaminen. +//Artikkelien bitittäminen. +//Inttien synninpäästö. +Tehokkuustietojen välitys ruutuilmiöihin. +Enumeraatiot barrikadeille. +Keinosielujen uudelleenvaistoittaminen. +Rotuspesialiteettien standardointi. diff --git a/Doc/Work/Cookies.txt b/Doc/Work/Cookies.txt new file mode 100644 index 0000000..2383d4c --- /dev/null +++ b/Doc/Work/Cookies.txt @@ -0,0 +1,6 @@ +Never eat anything that glows in the dark. +When shopkeeper dies, bananas spoil. +They say they are lying. +You cannot consistently prove this sentence true. I can; therefore I am better than you. QED. +They say Zulko offers the cheapest bananas in the whole Multiverse. +They say all fortunate cookie writers should be shot. diff --git a/Doc/Work/Descriptions.txt b/Doc/Work/Descriptions.txt new file mode 100644 index 0000000..aa6773b --- /dev/null +++ b/Doc/Work/Descriptions.txt @@ -0,0 +1,47 @@ +Ent + +They found that they were looking at a most extraordinary face. It belonged to a large Man-like, almost Troll-like, figure, at least fourteen foot high, very sturdy, with a tall head, and hardly any neck. Whether it was clad in stuff like green and grey bark, or whether that was its hide, was difficult to say. At any rate the arms, at a short distance from the trunk, were not wrinkled, but covered with a brown smooth skin. The large feet had seven toes each. The lower part of the long face was covered with a sweeping grey beard, bushy, almost twiggy at the roots, thin and mossy at the ends. But at the moment the hobbits noted little but the eyes. These deep eyes were now surveying them, slow and solemn, but very penetrating. They were brown, shot with a green light. Often afterwards Pippin tried to describe his first impression of them. +'One felt as if there was an enormous well behind them, filled up with ages of memory and long, slow, steady thinking; but their surface was sparkling with the present: like sun shimmering on the outer leaves of a vast tree, or on the ripples of a very deep lake. I don't know but it felt as if something that grew in the ground-asleep, you might say, or just feeling itself as something between roof-tip and leaf-tip, between deep earth and sky had suddenly waked up, and was considering you with the same slow care that it had given to its own inside affairs for endless years.' + +(The Lord of the Rings, by J.R.R. Tolkien) + +------------------------------------------------------------------------- + +Lembas material + +The lembas had a virtue without which they would long ago have lain down to die. It did not satisfy desire, and at times Sam's mind was filled with the memories of food, and the longing for simple bread and meats. And yet this waybread of the Elves had a potency that increased as travellers relied on it alone and did not mingle it with other foods. It fed the will, and it gave strength to endure, and to master sinew and limb beyond the measure of mortal kind. +(The Lord of the Rings, by J.R.R. Tolkien) + +------------------------------------------------------------------------- + +Cram material + +After going a short way they struck the old road, and before long came to a deep dell sheltered among the rocks; there they rested for a while and had such a breakfast as they could, chiefly cram and water. (If you want to know what cram is, I can only say that I don't know the recipe; but it is biscuitish, keeps good indefinitely, is supposed to be sustaining, and is certainly not entertaining, being in fact very uninteresting except as a chewing exercise. It was made by the Lake-men for long journeys). +(The Lord of the Rings, by J.R.R. Tolkien) + +------------------------------------------------------------------------- + +Athelas or kingsfoil, a material with high curative powers + +Thereupon the herb-master entered. 'Your lordship asked for kingsfoil, as the rustics name it, he said; or athelas in the noble tongue, or to those who know somewhat of the Valinorean...' +'I do so,' said Aragorn, 'and I care not whether you say now asëa aranion or kingsfoil, so long as you have some.' +'Your pardon lord!' said the man. 'I see you are a lore-master, not merely a captain of war. But alas! sir, we do not keep this thing in the Houses of Healing, where only the gravely hurt or sick are tended. For it has no virtue that we know of, save perhaps to sweeten a fouled air, or to drive away some passing heaviness. Unless, of course, you give heed to rhymes of old days which women such as our good Ioreth still repeat without understanding. +When the black breath blows +and death's shadow grows +and all lights pass, +come athelas! come athelas! + Life to the dying +In the king's hand lying! +It is but a doggrel, I fear, garbled in the memory of old wives. Its meaning I leave to your judgement, if indeed it has any. But old folk still use an infusion of the herb for headaches.' +(The Lord of the Rings, by J.R.R. Tolkien) + +------------------------------------------------------------------------- + +A serpent of brass named Nehustan which cures poisons + +And the LORD said unto Moses, Make thee a fiery serpent, and set it upon a pole: and it shall come to pass, that every one that is bitten, when he looketh upon it, shall live. +And Moses made a serpent of brass, and put it upon a pole, and it came to pass, that if a serpent had bitten any man, when he beheld the serpent of brass, he lived. +(Num 21:8-9) + +He removed the high places, and brake the images, and cut down the groves, and brake in pieces the brasen serpent that Moses had made: for unto those days the children of Israel did burn incense to it: and he called it Nehustan. +(2 Kings 18:4) diff --git a/Doc/Work/Encounter Ideas.txt b/Doc/Work/Encounter Ideas.txt new file mode 100644 index 0000000..cec72e4 --- /dev/null +++ b/Doc/Work/Encounter Ideas.txt @@ -0,0 +1,8 @@ +Glacier - polar bear, mammoth +Tundra - polar bear, mammoth +Evergreen forest - wolf, goblin, spider, gibberling, bat, kobold +Leafy forest - wolf, goblin, spider, gibberling, bat, kobold, unicorn +Steppe - Cossack, bison, horse +Swamp - dark knight, dark frog +Desert - sand worm, nomad, Osama bin Laden +Jungle - carnivorous plant, lion diff --git a/Doc/Work/Game End Ideas.txt b/Doc/Work/Game End Ideas.txt new file mode 100644 index 0000000..4b5bbdb --- /dev/null +++ b/Doc/Work/Game End Ideas.txt @@ -0,0 +1,5 @@ +game time used (days, hours, minutes) +turns used +game started, real time day & time +game ended, real time day & time +average time used for each turn diff --git a/Doc/Work/Graphics.txt b/Doc/Work/Graphics.txt new file mode 100644 index 0000000..d3c609c --- /dev/null +++ b/Doc/Work/Graphics.txt @@ -0,0 +1,43 @@ +hp - high priority +lp - low priority +// - done +(r) - rejected + +Hit symbol. +Scimitar + broken picture. +Katana + broken picture. +Broadsword + broken picture. +Club + broken picture. +Colossal morning star + broken picture. +Great axe + broken picture. +Forge. +Several types of key pictures. (lp) +Harp. +Nuts shouldn't be peeled. +Jungle bays. +Robe. +Mouse. +//Torturing devices. +Rolling pin. +Frying pan. +Clothes peg. +Showel. +//Sausage. +Fungi. +Nomad. +Cactus. +Oterrain border tiles. +Skis. +Female character. +//Tailor. +M-colored explosions. +//Iron maiden. +Victory screen background, etc. +Tentacle limbs. +Piano. + +Bronze hammer on gravel floor is not very clear. +Goblin berserker on grassy ground is really NOT visible. +Visible invisible stalker is too invisible. +Machete +Bunnies are difficult to see. diff --git a/Doc/Work/Great Ideas.txt b/Doc/Work/Great Ideas.txt new file mode 100644 index 0000000..ddac937 --- /dev/null +++ b/Doc/Work/Great Ideas.txt @@ -0,0 +1,150 @@ +* high priority +// done +/ partly done or needs testing +(r) rejected +(s) suspended + +Long range weapons. +Tutorial. +(r) Sid Meier. +Santa Claus. (from Renne) +Mouse control. +//Day and night. +Seasons. +//Multi-tiled creatures. +Smithing. +Ghost-Überpriests. +Thougth Control. +(r) Microwave oven. +Different types of item enchantments. (like what?) +(r) Random gods. +Ego monsters. +Monster, item and god memories. +The Saveloader. +Random fellow adventurers. +Indiana Jones. +Fake gods. +Capitalists. +//Material color editor. +Graphical script editor. +The Holy Haven of Gurus. +Febot the Oracle. +Undead states, for example zombie-state and vampirism. +Snow White and the Seven Kamikaze Dwarves. +Castle generator. +Sight direction. +Spreading fire. +Burial. +Wars. +Homes. +Family. And lost siblings that can be found by digging on the World Map. +(r) Diapers which can be applied to babies to prevent them from spilling + yellow and brown fluids to the floor. +The Reaper. +Script event system. +Intergame pets. +Player god. +Different holy water effects for each god. +Camera. +Monolith. +Elevator. +Plaque epidemic. +AK-47. +Swedish educator. +Summon Pelzar spell. +Flying asses (donkeys). +//Levitating ostrichs which export bananas from the jungle colony. +Space suit. +//Colored lights. +Timo should increase his commit frequency. +Antilight. +Sleeping. +Dreams. +Prophet. +Theologists. +Olympic games. +Unique angels for each god, for instance giant frogs for Valpurus. +Fallout-like perks. +Flaming bushes. +Global warming. +Specialities for highest SWSkills like unwield prohibition and Stormbringer + effect. +Game saga. +Pregnancy state for female players. +Storks. (alternative for the last one) +One day, the dolphins in the cathedral should vanish, leaving behind only a + glass bowl. +Inquisition in Attnam. +Random ruins in the wilderness. +Random artifacts. +Random demigods. +On the world map, there should be a sign in the middle of nowhere with text + "All your base are belong to us." +Graphical statistics of kills per 100 turns etc. to endgame. +Player-creatable magic. +Nobility ranks. +Pyramids. +Temperatures. +"Saveload" spell. +Master Mind Lemming. +Out-of-body experience spell. +Proletarian revolution. +Tengwar font. +Quest to save the prince frog. +"We apologize for the inconvenience." somewhere in the worldmap. +Rainbows. +Tabernae vagantes. +Shackles. +Telephats in each town which would handle long distance communication. +Gradual stoning/freezing system. +Steam-powered vodka flame thrower. +//Stamina. +Winds. +Undead disko where multicolored lights flicker and skeletons sing "Hip bone + is connected to the leg bone..." +Sampo. +Avalanches & landslides. +Strange drool jokes. +Languages. +Moksa state. +Smells that could be used for tracking monsters. +Leprosy asylums. +Breaking TryMove() to parts. +Richel Decos should have a mistress in his closet. +Halfling village where a number of hobbits carry tomatoes (eg) from their + vegetable fields to be transported away by levitating ostriches. + Officially a colony of Attnam, but led ruthlessly by Mrs Fortuna + Decos, the wife of Richel, who has an athlete lover in her closet. +There's a two-headed moose in the game; therefore it should be possible to + eat radioactive materials and get another head for the player, too. + This would behave like any other bodypart, but would have a very + annoying personality, make stupid comments all the time, panic + easily, disagree with about every decision you make and occasionally + give irrational commands to your body, for instance he could eat + mutant bunnies so that both of you gain polymorphing state. +A female missionary in New Attnam, who teaches children that learning + Attnamese and reading the holy book of Valpurus equals food. +Organs. It'd be fun to dissect a tourist child and eat his liver. +Mr Decos' Amazing Funeral Home where you would be resurrected as a zombie + if you have prepaid a _HUGE_ sum before your death and you die in a + way that leaves a corpse. +Siamese twins who can have up to fourteen bodyparts. +Replay to enner beast kill. +Brain surgeon who offers to cure your gullibility. Operations have random + effects to mental stats, may cause insanity as a side effect, and + naturally cost a hell. +Every day of Attnamese military and clergy classes should begin with a + Two Minutes Hate, where they loathe Elpuri collectively. + (funny thing, I noticed later that the latter was *exactly the 1984th + line* written in ivan/Doc/Work! This Has To Be A Sign! ;) +McDonalds in New Attnam. (from Henri Kiviluoto) +It should be possible for a tourist in a swimsuit to appear from a fountain. +Friction. +Team formations. +After the 16x16 -> 32x32 conversion, the old graphics could be used for + midgets. +Petrus should be able to divide the sea in two when he travels abroad. +Some sorta obscure reference to the Founders' legendary Lego realm back + in '92-93. +Plastic surgeon. +Adult filter config option. diff --git a/Doc/Work/Great Jobs.txt b/Doc/Work/Great Jobs.txt new file mode 100644 index 0000000..e6c46fd --- /dev/null +++ b/Doc/Work/Great Jobs.txt @@ -0,0 +1,120 @@ +//Porting IGOR to linux and adding it to CVS. + +Porting IvanMap to linux and adding it to CVS. + +//Creating a tool to combine high score files. + +Script precompiler. + +//Febot engine for Hog's oracle. + +//Documenting graphics formats. + +Documenting script. + +Documenting FeLib. + +Documenting IVAN. (haha) + +In-game manual. + +Mouse interface. + +//Removing asm from FeLib. + +Converting Ivan's graphics to 32x32 tiles. + +Support for multiple resolutions. + +Ego-item engine. + +Ego-monster engine. + +Races. + +//Multi-tiled creature engine. + +Blessed/uncursed/cursed item statuses. + +Identify system. + +Magic system. + +Spreading fire. + +//Fluid system that handles special material effects, eg. molten metals damaging legs. + +//Corrosion/rust for materials. + +Dividing physical damage to slashing, blunt, piercing etc. damage. + +//Balancing enchantment formulas. + +Unusable state for bodyparts. + +Long range weapons including bow. + +(r) More flexible throw using linear extrapolation. + +Support for multiple items flying at the same time. + +//Breaking different wands should cause different effects. + +Faster tunnel generation algorithm. + +//Better AI route algorithm. + +Sight direction. + +Dynamic team system. + +Monsters generating in groups. + +Making AI capable of using wands and scrolls and throwing items. + +More commands for team members like "guard", "flee" and "unequip". + +Renewed value-based god system. + +Different holy water effects for each god. + +Different angels for each god. + +//Bonefile system. + +Support for colored messages. + +//Armor pictures for player. + +//Weapon pictures for player. + +Blindness state. + +Adding Mondedr the chaos town. + +Adding Hog the neutral town. + +Money item. + +Some kind of rank/danger level system. + +Removing worldmap. + +Zombie state. + +//Terrain border tiles. + +System for representing sounds in the game. + +Permanent wounds. + +More vague weight descriptions. + +Rivers. + +Mounting. + +The tick based event engine copied from Adom should be replaced by more realistic and efficient + a method based on dynamic event handling. + +Ivan's and Leader's vision of Attnam's glorious future diff --git a/Doc/Work/HexWork.txt b/Doc/Work/HexWork.txt new file mode 100644 index 0000000..59e2aeb --- /dev/null +++ b/Doc/Work/HexWork.txt @@ -0,0 +1,35 @@ +Pakkausalgoritmi +Savefilet nousee nykyään kymmeniin megoihin, maailmankartan levelittämisen +jälkeen satoihin. Onneksi hexaeditointi osoittaa joidenkin savejen tavujen +(esim. 0 ja 255) ja eräiden niiden ryhmien (esim. transparent color = +0xF81F) olevan hyvin toistuvia. Tee pakkausalgoritmi, joka hyödyntää tätä. +Kovin monimutkainen sen ei tarvitse olla, koska senhän pitää yhtä aikaa +olla nopea (ehdotan, että autosaveja ei kuitenkaan pakata, vaan vain +levelistä ja gamesta poistettaessa tehdyt). Esimerkiksi voit tehdä +taulukon käytetyistä tavuista ja yhdistelmistä ja korvata ne lyhyemmillä +bittijonoilla ja/tai käyttää niihin RLE-pakkausta. Luulen, että std::map/ +std::set voi olla hyödyllistä muistuttaa mieleen. FeLibin tiedostoluokkien +käyttö on mahdollista, muttei pakollista, koska pakkaus on erillinen +toimitus. Voit tehdä omaan ohjelmaan testin tai rakentaa suoraan +savesysteemiin. + +Nimigeneraatioalgoritmi (copypaste viime vuoden assignmentista) +Tätä varten luot erillisen ohjelman nimeltä Intelligent Random Imitation +Name Assembler (IRINA), joka analysoi tiedoston, jossa on nimiä ja luo +niiden perusteella databasen, jonka avulla voi nopeasti luoda lisää +samankaltaisia. IVAN sitten lataa tuon databasen. IRINAn kannattanee pystyä +myös tulostamaan noita uusia nimiä suoraan kehotteeseen, jotta voit testata +tuota ennen IVANiin liittämistä. Algoritmit saat päättää itse, mutta jos et +parempaa keksi niin ehdotan Markovin ketjuja, eli tietty kirjain seuraa +tiettyä N kirjaimen jonoa aina tietyllä todennäköisyydellä, ja N on +käyttäjän antama tai ehkä satunnainen. Databasen luominen saa olla miten +hidasta vain, mutta sen lataaminen ja nimien tuottaminen siitä on syytä olla +kohtuullisen tehokasta. Pääalgoritmit pitäisi tulla FeLibiin jotta molemmat +ohjelmat pystyy käyttämään niitä. + +16x16 -> 32x32 tilekonvertoija +Erillinen commandlineohjelma, joka muuntaa 256-värisen pcx-kuvan neljä +kertaa isommaksi ja smoothaa syntynyttä tiedostoa. Voit käyttää felibin +rawbitmap-luokkaa ja sen tallennus- ja latausrutiineja jos et parempia +tiedä. Minkäänlaista eleganttiutta ei tarvita, koska rutiini on ilmeisen +kertakäyttöinen. diff --git a/Doc/Work/Holy Water.txt b/Doc/Work/Holy Water.txt new file mode 100644 index 0000000..ab0dd85 --- /dev/null +++ b/Doc/Work/Holy Water.txt @@ -0,0 +1 @@ +Scabies - polymorph diff --git a/Doc/Work/Humanoids.txt b/Doc/Work/Humanoids.txt new file mode 100644 index 0000000..35c2c50 --- /dev/null +++ b/Doc/Work/Humanoids.txt @@ -0,0 +1,27 @@ +Thief + + TorsoBitmapPos = 48, 272; + HeadBitmapPos = 112, 272; + ArmBitmapPos = 80, 272; + LegBitmapPos = 0, 272; + +Grave digger + + TorsoBitmapPos = 32, 80; + HeadBitmapPos = 112, 272; + ArmBitmapPos = 64, 0; + LegBitmapPos = 0, 16; + +Imperialist + + TorsoBitmapPos = 48, 240; + HeadBitmapPos = 96, 208; + ArmBitmapPos = 80, 112; + LegBitmapPos = 0, 0; + +Smith + + TorsoBitmapPos = 48, 192; /* color tan */ + HeadBitmapPos = 112, 160; + ArmBitmapPos = 64, 64; + LegBitmapPos = 0, 16; /* color dark brown */ diff --git a/Doc/Work/Optimization.txt b/Doc/Work/Optimization.txt new file mode 100644 index 0000000..14d5db3 --- /dev/null +++ b/Doc/Work/Optimization.txt @@ -0,0 +1,5 @@ +level::RandomSquare() +spoiling & applyexperience +reduce the number of CalculateNeighbourBitmapPoses calls +DrawMemorized may be called too often +ChangeAlpha diff --git a/Doc/Work/Place Ideas.txt b/Doc/Work/Place Ideas.txt new file mode 100644 index 0000000..760f5da --- /dev/null +++ b/Doc/Work/Place Ideas.txt @@ -0,0 +1,11 @@ +Woods of Whispers +Battle Grounds +Giant Cliffs/Valley +Swamp of Illusions +Graveyard +Forgotten temple +Forest of Silence +Magical forest +Dragon mountain +Orc fortress +Ant hill. diff --git a/Doc/Work/Pray Ideas.txt b/Doc/Work/Pray Ideas.txt new file mode 100644 index 0000000..42493cf --- /dev/null +++ b/Doc/Work/Pray Ideas.txt @@ -0,0 +1,71 @@ +*heal the hurt +*feed the hungry +uncurse items +give an item +*-weapon +*-armor +-offensive potion/wand/spellbook/scroll +-defensive potion/wand/spellbook/scroll +-utility potion/wand/spellbook/scroll +-food +-utility artifact +-money +*cure disease +*cure poisoning +*change item material +give hints +*damage/kill enemies +*charm enemies (temporarily?) +*create (temporary?) pets +teleport the fateful +*-elsewhere in the level +-to another level +-out of the dungeon +teleport enemies +*-elsewhere in the level +-to another level +*trade items +*give attribute +*-strength +*-agility +*-endurance +*-perception +*-intelligence +*-wisdom +*-charisma +temporarily increase attribute +polymorph the faithful into a powerful monster +*earthquake (open new passages & damage enemies) +enchant an item +identify items +*give a wish, under very special conditions +recommend the faithful to a friend deity +increase chances of success for the next command (*) +*plant herbs onto the ground +give a special quest (perhaps teleport the hero to a special dungeon to complete it?) +cast a special spell like Locate Monsters +give a beneficial intrinsic +remove a dangerous intrinsic +make the level collapse completely and teleport the player out, under very special conditions +bless a new altar for use? (you must build an unaligned altar first) +convert the altar under (**) +bless items on the altar under +restore mana points +*make you radiate holy light for a certain period +sterilize fast breeders and self-cloning monsters on the level +crown the faithful as his/her champion + +(*) Some people use to pray for their deity in real life when it is left for fortune to decide + the success of important events. This would be a similiar command, and it would be separated + from the normal praying. It is done silently and so the deity often doesn't notice it, + but chances for angering gods by overusing it would also be very low. +(**) We can't use the old sacrifice & convert system unmodified, since the player has many friend + gods and only one can receive the altar. I propose this: normaly you cannot pray in an altar + room to anyone else than the owner. However, when the command is selected directly over the + altar, you are asked whether you want to try to convert the altar. If you don't, you will pray + to the owner with some special bonuses (you are on the deity's altar, anyway). If you do, you + must make a sacrifice to another god you select. If he/she thinks it's worthy enough, he/she + will figth the owner of the altar room for it's control. + This could also be an independent command, since receiving altar bonuses when praying may + become a habit for some players, and so the "Do you want to convert the altar?" prompt would + become quite annoying. diff --git a/Doc/Work/Small Ideas.txt b/Doc/Work/Small Ideas.txt new file mode 100644 index 0000000..299ac19 --- /dev/null +++ b/Doc/Work/Small Ideas.txt @@ -0,0 +1,553 @@ +* high priority +// done +/ partly done or needs testing/balancing +(r) rejected +(s) suspended +(d) double entry +(initials +/-) reserved for the following coder, plusses or minuses indicate priority + +Lowscore. +Altering the attributes of monsters according to the player's level. +Write msgbuffer to a file command. +(hs) Date to highscores. +It should be possible to sort highscores according to date. +Blood could flow around. +/Gods should have more than two effects. +We should give more thought to the role of the gods. +Maybe some keys other than Alt+Enter and Print Screen should also work during getkey? +Double-sized draw of the tiles as a switch. +Size of player should increase from eating. +Pickup all. +Some way of writing a word on a different colour with ADD_MESSAGE(). +Blindness. +One should be able to summon tame Bill's Wills from Windoze keys in WMode. +Teleport disabling. +Teleport should bounce back from valpurium. +It should be possible to determine line height as a felist draw function argument. (comment: why?) +Intelligent monsters should read scrolls and use wands. +Several types of critical hits (e.g. one that throws the opponent one square backwards). +Highscores should be crypted somehow. +Script should be crypted or precompiled. +Golems should be able to eat things made of their material to increase size and stats. +Prepare bug-report function that would make bug-reporting easier. +Bill's wills should bloat with rising temperature according to Gay-Lussac's law. +A program which would translate a pcx file into a script content map. +Some variety to monster death messages, e.g. some messages influenced by the cause of death. +Script should have an initial blood level of square option. +Full backpacks should decrease AV, but they would also increase the chance of item damage. +Explosion animation. +Cube-like screenshot system. +Gurus. +Meditation command. +Opening a banana should remove its peel. +Fire should do extra damage to parchment golems. +As Heikki suggested, when the player jumps in the WMap and is strong enough, he should reach the orbit. (If Valpurus carries the Earth, what orbit is he talking about?) +Attnam's shopkeeper should send an experienced player on a quest to kill his competitor in the main dungeon. +Drugs. +Karateka. +Monsters should pray and occasionaly be punished by the gods for praying too often. +("You hit the farmer. The farmer prays for Valpurus's help. Valpurus is annoyed. Valpurus smites the farmer with a small hammer. The farmer's head is severed off. The farmer dies without a sound.") +Special "enemy sighted" reactions. (like what?) +Vampires should be able to polymorph into a bat. +(s) Kicking a throne should yield precious stones. (suspended, Überpriest victory code suspects that thrones are indestructible) +Throw remake using linear extrapolation. +Throw flipping. +Throw speed to conf. +Pets that resurrect the player. +Compressed savefiles. +Crash save. +Cats and dogs should attack each other even if team comrades. +Lanterns should dim over time. (from Antti) +Oil lamps should use oil as their fuel. +Oil could have other uses as well. +Fungi that grow around the player and paralyze him. +Stat point based character generation? (from Antti) +Guilds. (from Tapani Tiilikainen) +Option to change the size of font. (from sturmkoenig2k) +Elemental damage. (from JM) +Zombies should lose weight when they drop lumps of flesh. +Some death messages should be displayed even if the monster is not on LOS. This would make L6 more interesting, as Ivan's actions would produce constant "you hear a goblin screaming" messages. +Fix wear-time AV inconsistency. (very difficult) +Dark frogs (except Elpuri) should be purified and turned into tame light frogs when holy water is thrown at them. +Fluid should handle melded metals. +In-game manual. +In-game credits. +Valpurus's altar rooms should be frog-shaped. +When we add magic, low level characters should be able to accidentally miscast a spell so that it would produce really weird effects. ("Uh oh! Something went wrong while casting the force bolt. You transform into an ass.") +Something mysterious should happen when one lays a pick-axe on a holy banana of Liukas Vipro. +Intelligent polymorphed monsters should have unique replies? +Hirelings. (from JM) +Books of . (from JM) +Fighting tactics. (from JM) +Attack magic scrolls. (from JM) +Socketed items. (from JM) +Gambling. (from JM) +Inns. (from JM) +Thief monsters. (from JM) +If both hands of a kamikaze dwarf are cut off, he shouldn't be able to carry his backpack anymore. +One should be able to buy better bodyparts somewhere in the game. +Gems should increase score. +Non-fatal battles like robberies. +Angels as enemies. +Amputation. +Beard item. +Monsters that eat other monsters alive, like Nethack's purple worm. +Explosion level. +Mirages. +Mirrors. +Something special should happen if the player is polymorphed into a chicken and loses his head. +Allergies. +Special birth gifts like "big head" that gives more intelligence but increases enemies' chance to hit the head. +Potion effect from fountain. +Bananas should have black dots when spoiled. +High level priests should be able to summon angels. +Kamikaze dwarves should not kamikaze the player if he serves the same god. Instead, they should give him some of their explosives. +Pet priest. (from JM) +Sword of Fire/Frost/Lightning. (from JM) +Frozen state to monsters. (from JM) +Dialogs. (from JM) +A way to steal from the weapon vault of the Cathedral. (key to secret door?) (from JM) +Hunters should ask the player to aid them in a quest battle against a goblin king/mammoth. (from JM) +Pits with sticks. (from JM) +Acid pits. (from JM) +Flying monsters should be more difficult to hit. (from JM) +Flying monsters should occasionally be displaced. +Additional heads for flails. (from JM) +Desperado attacks like in FFs. (from JM) +Secret doors that can be opened by whistling. (from JM) +Heads on walls that can be talked to. (from JM) +Scribe scroll skill. (from JM) +Deathblow that penetrates armor. (from JM) +Very hard-walled treasure vaults with locked doors in dungeons. (from JM) +Money to humanoid monsters. (from JM) +Stores specialized on potions or scrolls. (from JM) +In Nethack one can leash pets. In Ivan it should be possible that powerful pets leash the player. +Bananas should act as boomerangs when thrown. +Kicking bodyparts and corpses on the ground should spill blood. +One must be able to use a stethoscope on doors. +Kamikaze dwarfs should sometimes have gunpowder golems as pets. +Mutant monster that would have totally random bodyparts from any humanoid in game. +Continue menu could show some additional info, eg. dungeon level. +Dipping to oceans. +If the player has enormous amounts of items in his backpack, some of them could randomly drop, at least after sight direction is implemented. +Moving diagonally should use sqrt(2) times more AP. +Msghistory max length to conf. +Opening a can should be more difficult with less hands. +Fly function for character. +Item category "rubbish" which would contain bananapeels and roubles. +Wand levels. +Possibility to override keys that felist accepts. +Angels should become hostile if the player's relation to their DivineMaster drops radically. +Explosions could be m-colored: this way magical explosions could look different. +WoDC should clone its own material to created doors. +Wishing for "corpse" should give something. +Poison should spread from bodypart to bodypart. (from JM) +Titles and bonuses. (from JM) +Informative pictures to message panel. (from JM) +If one the player mounts an ass and has a carrot in the inventory, it should be held automatically in front of the "vehicle" to increase speed. +An amulet of life saving worn by a skeleton or zombie should either kill it or transform it into a living human. +Healing liquid should severely damage undead. +In my (Timo's) opinion, we should add some powerful undead monsters and maybe a boss soon after the latter is done, as dipping a weapon into healing liquid then becomes rather a useful tactic. +Wand of resurrection should behave correctly when zapped at undead. +Werewolves (wolf form) should maybe be nonhumanoids. +One should be also able to wish for terrains, like an altar of Valpurus. +It should be possible to specify the material when wishing for items. +Allow animate and allow alpha blend options to configuration. +Draw during automated action as an option. +We could show a scroll bar during reading, digging etc. that would show the remaining time needed for the action's completion. +Some balancing related game values like GLOBAL_WEAK_BODYPART_HIT_MODIFIER should come from the datafiles. +Handling flying bear traps. +Punishing people kicking bear traps. +Whips should sometimes remove the helmet of the target. +Page up should work correctly in felists. +If a melee hit is blocked with a weapon, the attacking arm should take damage. +Some monsters should be generated in groups. +Monsters with multiple configs should advance in levels. +Wings should be bodyparts. +It shouldn't be possible to mutate nor resurrect non-living bodyparts. +(hs--) A special message should be printed when zapping a WoR at a bone or a skull. +"There is no door to close, silly." +Bodypart state system. +Poisoned bodypart state. +Bleeding bodypart state. +It should be possible for a critical hit to sever a lump flesh. +It should be possible to dig the ground to create a hole through which you could fall to the next level. +Physical damage should be divided to slashing, blunt, piercing etc. damage. (from Atte Aholainen) +More info about players' old games in highscore list (maybe a sub menu). +Stressed state should slowly decrease player's size. +Being satiated or bloated should decrease move ease. +If HP == MaxHP, healing liquid should increase MaxHP a bit. +Metal should look different from other materials: the lighter parts should be even lighter and darker parts slightly darker to increase the contrast. +Frost weapons should gradually develop ice which would increase damage and disappear after successful hit. +Lick command. +It should be possible to slip on a bloody floor. +Reading holy books should aggravate certain monsters. +(hs---) Prohibit vomiting over the Avatar. +Monster that only sees in red, green or blue light. +Page up, page down, home and end should work with message panel. +If Petrus is attacked via a ranged weapon, his guards should jump in the missile's path to protect him and scream "NOOOOOOOOOH!" +Monsters that have died of poisoning should be poisoned when eaten. +Eat, drink and offer selection should accept multiple items. +If something inside a chest explodes, the container should be completely destroyed. +Fly system should be remade (with the ability of having two or more items in the air at the same time). +Picking up VERY heavy items should be prohibited. +If an invisible monster hits the player, a Nethack-style question mark should be displayed in its square. +In Attnam there should be thought guards that use amulets of ESP to spy on citizens and have rings of teleportation and teleport control which they use to enter to suspicious people's houses. +When picking up many items the overall weight of the items should be displayed. +The smith of Attnam should have a golden statue of a woman in his closet. (A Kalevala reference) +Wishing for a plate mail shouldn't yield a leather armor. +Money unit and its plural and symbol should be possible to define from config. +Broken bananas should spoil faster than intact ones. +Coconuts falling from palms. +Changing keyboard layout (some people don't seem to like numpad). +Reflecting items (mirror and shield of reflection). +Mellis could act as a small shop when prayed to. +Some monster types should taunt the player. +Some way of level teleportation. +Once implemented, the game saga should show how many times the player has polymorphed. +Getting knowledge of all gods should be rewarded somehow. +If a fireball beam doesn't hit anything, it should still explode at the end of its range. +SWeaponskills for gauntlets and boots. +The Cathedral should employ a gardener whose duty is take care of the firs around the building. +An environmentalist character who would protest outside the Cathedral against the imprisonment of the dolphins. +Monsters shouldn't drink antidote liquid if they don't need it. +Monsters should drink antidote liquid if they need it. +Ask for auto wear for picked up items. +The spirit appearing in fountains should have a picture. +Town portal scroll or something similiar. +Small shadows for items. (one pixel right and down from the items graphic) +"Select the direction you wish to enter Attnam from". +Petrus should offer a small meal for player when he first visits him. +Unlike their colleagues, Nefas's angels should have a groin. +Priests, Petrus and angels should be able to resurrect dead pets. +Movement mode: stealth. +"No load" mode (if weight of stack < 1 kg) +Flaming sword should burn the player if he doesn't have gauntlets. +Items inside shelves should take damage from explosions. +Lanterns inside glass chests should still emit light. +Axes should also be used for going through doors. (a la Shining) +Finding Attnam in the map can be difficult if the distance is long. +Zapping food with wand of slow/haste should slow/fasten the spoiling time. +Enchantments should increase thunder hammer's lightning possibility or lightning damage. +One should be able to fix non-living bodyparts with a scroll of repair. +Vodka should increase or decrease wisdom. (probably determined randomly) +(hs) Monsters that can eat already spoiled food without damage. (eg zombie) +Lightning and metal items should interact somehow. +Footprints. +Town hit by "zombie-plague". +Panicking should be more probable when friends around the character have panicked. +If the player has enough wisdom, the game should warn about obviously damaging food. +Invisible creatures carrying a light source should be revealed. +IVAN's linux version should be distributed in rpm form, too. +There should be more ways to put back or summon missing bodyparts of pets. +It should be possible to change materials of golems and other monsters. +One should be able to wish for a new limb. +Ring of conflict should cause two-headed mooses to kill themselves. +Confused two-headed mooses should occasionally bite themselves. +Cigarettes. +You should be able to kick an eating dog to teach it not to eat that thing again. +Guillotine terrain. +Eating only unspoiled food should require a bit of wisdom. +Monsters with ESP should be seen from a longer distance with ESP. +Pets should also be seen from a longer distance with ESP. +Rings of teleportation on the ground should occasionally teleport around the level. +Characters wearing bright colored cloaks should be more visible in the dark. +(hs) When showing inventory at the endgame, equipment should also be displayed. +If the player has a floating eye in his team and has ESP, the eye's field of vision should be added to the player's LOS. +Options for sorting massacre history. +Shops should replenish their inventory frequently. (from JM) +Exploding potions (other than vodka). (from JM) +/Disease. (from JM) +Different kinds of jewels. (from JM) +Monsters that are afraid of fire. (from JM) +Pipes. +Magical herbs. +More shapes for rooms. +Slight variation to weapon properties. +Slipping on ice. (from JM) +Bridges. (from JM) +Outposts. (from JM) +Berries in forests. (from JM) +Game in forest. (from JM) +Bandits. (from JM) +Camp fires. (from JM) +Digging potatoes. (from JM) +Whirlpools. (from JM) +Marketplaces. (from JM) +Fish. (from JM) +Floods. (from JM) +Wand's explosive effect strength should depend on its charges. +Alpha halos to some other items, too. +Shirt of the Golden Eagle shouldn't emit light unless it's worn. +Using the lookmode while polymorphed into a floating eye should have a special effect. +Raining frogs. +Zombies transporting vodka. +(hs) Gods shouldn't like spoiled corpses. +Teleport trap. +Poisoned food. +A wand which could penetrate walls. +Mjolak should do more energy damage if the player has good relation with Cruentus. +Very high damage should sometimes leave permanent scars, which would decrease charisma. +Number of turns in high score list. +Ghost and Bill's wills should be able to change level anywhere. +Nymph hair should burn. +If one tries to eat the holy banana, there should be a boolquestion to prevent accidents. +Mistresses should sometimes kick male players in the groin. +(hs---) Banana peels should become bananas if zapped with a wand of resurrection. +Skeletons should throw bones at dogs. +Uses for engraving. (random magic word?) +One should get some score for pets. +If the player has a wand of resurrection, a ghost should steal it and float quickly to raise his corpse. +Unarmed accustomization. +(example) Hex is dead. Player starts game with the name Hex. He should be called Hex II (should be togglable). +The game should ask player whether to unlock a door or chest with a key or not. +Thunder hammer's power should be proportional to relation with Loricatus. +The smith should repair pet golems. +Golems should pick up and read scrolls of repair. +Rabbits should be brown in summer and white in winter, as in real life. +Houses floating in water. +Fountain of youth. +Sometimes when taking (really bad) damage the player might receive permanent wounds (or in other words loose a couple of points from the MaxHP of the bodypart taking damage.) +If one monster can see an invisible player, then it should reveal his location his comrades, too. +Cave paintings. +List of dead friends. +Carnivorous plants should be carnivorous, not anorectic. +(hs) There should be an option to display weapon skills at the end of the game. +Kiss command. +Cloak of fire resistance should protect items in the inventory from fire. +Eyeglasses. +[IGOR] Reload option. +[IGOR] Copy picture. +Shift + direction should act as go command. +Flying monsters can fly over jungle... +Jussi The tenant farmer wielding a hoe on some swamp. +Snow pines should change to regular pines when kicked a few times. +Wand of change material (change everything in wand path to material of the wand). +Ability to absorb specific elemental damage. +Corpse which makes you sober. +Ivan should buy vodka from player. +//(hs) Big t and p should work when controlling containers. +There could be possibility to revive a pet by praying (from shvagier (?)) +(hs-) One armed characters should get discounts in Attnamian stores. +Confuse should affect throwing also. +There should be a special effect for selling empty potions. +(hs-) Polymorph beam should change engraved messages to random gibberish. +(hs) Breaking a staff of wondrous smells should result in a lot of erupting gas. +Leather should have several possible colors. For instance mistress should wear black leather. +Drinking and surviving a potion of poison should give some permanent poison resistance. +'.' in battle should act as a defend command, which would double the chance of blocking. +Fortune cookies. +Doing evil/good deeds should have greater effect on the god on whose altar room the deed is done. +Mutant limbs should be of different color (maybe even have a different picture?), they should emitate light and drip odd liquid occasionally. +Randomly generated bonefiles. +(hs) Faster scrolling for look mode. (maybe when shift is pressed) +(hs--) Petrus' birthday (petrus gives player 1 banana). +When an earthquake is shaking the level, the game screen should randomly shift position by a pixel or two every second or so. +The player shouldn't be able to pick up infinitely many infinitely big items. Its rather odd that a spider can have a large chest in its inventory (it can't move it, but IMHO it shouldn't even be able to pick it up). +(hs) Graphical effect for Mjolak's energy attack. +How much a monster can eat should depend on its volume (pet rats eat exactly as much as mammoths before they become bloated and stop). +When seasons are integrated, bears should hibernate through winter. +(hs) Loricatus should change weak golems to steel. +(hs+) Statistics about the real time spent on a game and the average spend on a turn. +Living mud. +New snake types. +Blink dog drool should blink and teleport around. +Shops and some other places should close for the night. +Creation of magpies depends on dangervalues. (maybe a problem) +(hs) Maybe players with 0 points shouldn't be recorded in highscores. +(hs-) Drinking vodka (maybe just getting confused) should improve relations with Nefas. +Items in bonefiles should spoil a bit before player enters again. +Equipment weight to inventory. +(d) Offering many items at the same time. +Running and levitating. +Wands show "times used" even if they come from a bone file. This shouldn't be so. +Hunter (hunts with bear traps). +Taxidermy. +Hunters should carry stuffed heads of random animals. +Pets should be able to drink from fountains. +(hs) There should be a possibility to examine an item (show materials etc.) in the inventory screen. +Goblins should be generated tame if goblin king belongs to the player team. +If the player is poisoned, he should be able to slow the poison's effect by standing still. (from Henri Kiviluoto) +He should be also able to call 112/911. (from Henri Kiviluoto) +The pictures of mirrored creatures should also be mirrored and their limbs swapped. +Things less dense than a liquid should float in a square full of that liquid. +Simple protection for changing highscore files. Needed if list of best scores is done. +If a creature has lost all its limbs, leprosy should drop its head. +Good NPCs should try not to use ommel bone/tooth weapons. +Death message for team member killing each other while confused should be "X killed by confused Y". +Lair of the Loser Kamikaze Dwarf somewhere below Dark Level (he destroys half of the level with him, if he somehow manages to light his megaexplosives). +Geysirs. +Damaged bodyparts should work less well. (ie. when arms are damaged armstrength should go down) +Wand of Polymorph to X (where X is a monster) +Magic hassel which causes invisibility. (from Henri Kiviluoto) +Stickiness value for materials. +Detecting spider silk should identify webs in the level. +Eddies should teleport smoke, fluids and webs around. +Spilling blood on Seges' altar should anger Her. +Altars should have special features like healing on Seges' altar. +Golems' wisdom shouldn't rise. +System should be more intuitive so that people wouldn't be confuse wielding and wearing. +If the player has a giant beard, Richel Decos shouldn't pay him for wearing the ad shirt, since the banana logo can't be seen by anyone. +There should be a possibility that Vermis teleports away bodyparts it hits. It is not very useful as it is now. +Boulders should cast small shadows in sunlight. +Explosions should break pineapples and kiwis, leaving only some juice to the ground. +A frog should be able to use its long, sticky tongue as a ranged weapon and catch small creatures up to three squares away so it may digest them afterwards. Elpuri should also be able to catch man-sized creatures who would find themselves in its acidous stomach, like they would have been engulfed by a purple worm in Nethack. +Every 10000th new game or so IVAN should make a Nethack level 1 represented by ASCII characters and show it instead of the real one, print the standard Amulet of Yendor story, ask for key press, then print "Oh gosh. Wrong game, sorry!" and continue the original game. +Go back -function to menus like ?-key. +When there is vomit stuck to character, its charisma should be lowered. +Mommos should follow the player more agressively; as of now they are useless as pets. +"Sulphuric acid is raining here." to look mode when appropriate. +If the player is confused or has bad relations to Dulcis, using a magical whistle should sometimes fail, teleporting all monsters in the level around the player. +When confused, stethoscope should produce very inaccurate results. +There should be a way of getting a verbal hint whether a god's pray timer allows praying without relation penalties; maybe the relation description printed upon offering could be modified, or a special msg could be given when reading a holy book. +There could be a more interesting effect for trying to teleport over a monster; perhaps the player's and the monster's souls could be swapped (ie. they would polymorph into each other for some time) or their bodyparts could get randomly mixed. +Drinkin _lots_ of water should maybe help poisonings. +Shopkeepers should dislike Cleptia. (for example they shouldn't buy or sell holy books of cleptia) +Petrus's wives should wear chastity belts. +SmileyWorm should be a hidden minigame somewhere in IVAN. +Popup ads of Decos Bananas Co. in some areas of the game. +Wait for yourself to calm done command instead of "You are too scared to rest" +When confused, the player should occasionally zap the wrong wand or drink the wrong potion. +If the player is very intelligent, he should be able to decrypt the encrypted scroll. +Spilling blood on chaotic altars should better relationships with those gods. +Quicksand traps. (from Henri Kiviluoto) +Toys for Attnam's peasant children. (from Henri Kiviluoto) +You shouldn't be able to go upstairs when overloaded. +Priest should perhaps be able to remove parasites. +It should be possible to name individual items. +You should be able to change level anywhere if WalkThroughWalls cheat is on. +Getting hit in the head should sometimes cause the player to forget a god's praying rituals. +Using teleport control / polymorph control should train either intelligence or willpower. +Bears shouldn't attack you if you play dead. +Dog drool should freeze in Attnam. +woodpecker. Does double damage against wooden golems. +Reading should be faster in Sophos's altar rooms. +Applying potions should empty them. +Watering carnivorous plants or feeding them flesh should tame them. +Watering Vesana or feeding her flesh should make her peaceful, so she'll let the player pass. +Untrapping oneself should yield DEX & AGI exp. +Displacement priority. +How much you need to eat should depend on volume. +Lobh-Se the spider queen. +Enchantment should affect electricity resistance. +Weapons that attack every creature around the player, or several lined in front of him. +Wind magic that throws enemies away. +Carnivorous plants should attack everyone in UT3. +Wands of invisibility should make walls invisible. +Mages should generate eddies. +It should be possible to beg money from tourists. +//"You hear coins clinking inside." when stethoscope is applied to the imperialist. +Eating hedgehogs should cause damage to the throat and stomach. +Monsters should avoid stepping on banana peels when they are moving randomly. +Zulko should buy pineapples and kiwis; Decos doesn't have a monopoly in their production. +Petrus should mirror himself during the final battle. +If you have high intelligence, ESP and telecontrol, you should sometimes be able to prevent monsters from telecontrolling near you. +It should be possible to use bladed weapons for amputation purposes. +Light frog blood should do something beneficial, since it's the opposite of acicid dark frog blood. +There should be a special symbol for mirrored items. +There should be a gold golem in Petrus's treasure room. +The priestess of New Attnam should change her replies after the revolution. +There should be a one day warranty for the slave: if he dies you can resurrect him in the shop, or perhaps the shopkeeper gives you a resurrection coupon which you can give to the priest so he resurrects the slave for free. +Healing liquid should train endurance; currently there is little sense in drinking it since you lose valuable end exp. +Water splashing on the player should remove some of the acid over him. +It should be possible to dip oneself, for instance by applying a potion in the square where one stands. +Some of the liquid that is over the player should occasionally drop on the ground, not just disappear. +Zombies raised from a spesific corpse should retain their old heads. +/Kamikaze dwarves should loudly sing hymns which would warn about their presense in the level. +The player's avatar should reflect the broken/intact state of his armours. +There should be a hint of direction in the enner beast yell msg. +Different confuse effects for scrolls. +Taming special named monsters should require some INT etc. because they are so great compared to anything else you can get as a pet. +If you are poisoned, your face should turn blue. +Lyre of charm's effect should depend on charisma and the relation to Dulcis. +Amnesia should use different shapes to blur memory, for instance remove some rectangular areas from one's map. +Maybe vesana could be animated somehow? +There should be more savescumming detection, at least hidden data in highscores we could use. +HP-based monster generation of special monsters shouldn't consider HPs of polymorph forms. +When wishing for "a mithril chain mail +5", the game should ask "Do you mean 'chain mail'? [y/n]". +Damage source IDs. +Ultraviolet light which causes the disease skin cancer. +MIHAIL should something sensible if one tries to merge a file which doesn't exist. +MIHAIL --version could say its version. +INT requirements for certain other replies. +If the player has very low INT, he shouldn't be able to say anything else than "Ugh." to NPCs. +Commented insults to savescummers above RemoveSaves() in the code. +Installer to the Win32 version. +A way to define default attack mode (melee/bite/kick). +Water trap. +New fountain effect: lots of water is splashed over the player. +Altar rooms should somehow react to player's alignment, for instance you shouldn't be able to take a god's stuff from his/her altar room. +Vomiting should decrease poisoning. +Autopickup kiwis option. (from DA) +Wield menu should only show sensible choices as default. (from DA) +Open/close commands should be much more intelligent. (from DA) +The screen allocation is maybe a bit wasteful considering much of it isn't used for anything except in special occasions. (from DA) +Limbs should be shown in an extended inventory screen. Special materials are often important to know. +You should get a bonus to your carrying capacity equal to your arms' weight (or some similiar gum), so that detached arms would be roughly as easy to carry as attached, which is imho logical. +More funny and strange features, less swift and deadly ways to die. (from Timofei Shatrov) +Extra blood mode. +Code improvement: char.cpp could be split in two. +Aligned monsters could occasionally be generated on an altar. +Spam scrolls in dungeons. +Carpenter, stoneworker & jeweller in Attnam. +Maybe the item's volume could be accounted when determining SoCM intelligence requirement? +Trapped chests. +"Are you sure you want to step on the mine?"/"Allow stepping on discovered mines?" +Ways to cure pet leprosy. +Previous games should affect the world more, for instance top 10 statues could be hidden somewhere. +Mellis could transport his worshippers to a divine bazaar. +//"What is this 'ommel'?" to extremely frequently asked questions. +Healing should decrease panic counter somewhat. It is annoying that you must wait years for panic to wear off after drinking a potion of healing. +//Vodka should cure panic. +You should be able to cut down balsa walls with an axe. +Anyone named 'Captain Action' should encounter completely impossible monsters starting from the first level. +A tin foil hat should make you immune to ESP. +HP-based monster generation should use HP averages over long periods of time instead of current max HP. +Butcher in Attnam - converts pets to cans of flesh. +Tailor and smith should be able to make items from raw materials. +Two rings of searching should have more effect than one. +Canning food from lumps and corpses. +Wearing equipment of a god's material should make Him/Her slightly more favourable. +Playing musical instruments should increase relation to Dulcis somewhat. +Holy books could have several parts. +Random stories could be generated to describe what the player reads from a holy book. +Newspaper stand in Attnam where you can buy newspapers with randomized headlines and lots of religious propaganda are sold. +Tentacle limbs. +An infinite dungeon. +An ancient battlefield where skeleton warriors constantly rise up and blattle each other and the player. +The strength value of half spoiled ommel bone/tooth should be lower than normally. +Dead sea lake. +There should be a toggle whether wish, SoCM etc. limitations are disabled in WMode or not. +More logical error checking in the script. +The effect of the danger system could be made more obvious by eg. a message "Valpurus sees that you are very strong. Prepare for a test." +Some way to disarm mines. +You should only be able to wish for materials which you have seen. +Bleeding could detract from your health even further. +On April 1st, the game should occasionally generate a valpurium plate mail +5, which would disappear with a msg "April Fool!" when picked up. +Eating a kamikaze dwarf's head should reveal his god's rituals. +The eyes of certain characters should shine in the dark. +Walking on blood etc. should spill the liquid to boots/legs. +Cloth category that can be colored dynamically. +Peeping through keyholes. (maybe using Ivan3D) +Doublewield information to silhouette. +Mistresses with lots of experience should have lots of scars. +Certain weapons (maybe mithril ones?) should shine blue in the presence of orcs. +A mine planter dwarf sitting on a throne in the middle of the mine field and repeating "It's mine! All mine!". +Fairy tooth should sometimes offer to exchange ommel tooth items to gold. +Eating lots of solid food when starving should cause considerable damage. +Destroying walls should be a difficult action even for Vladimir; it should use stamina and he should be forced to rest between periods of crunching. The A* algorithm should be modified to handle this. +Being bloated or starving should be displayed in the player graphics. +If the player is in the bloated state, the child tourist should say "Wow! You're fatter than daddy, too! What a village!" +Sword of mana absorption - regenerates mana when blocks hits. +Silva's branch - replaces the palm branches in the Cathedral, regenerates HP and stamina of friendly characters. +Attnam should be surrounded by barbed wire to make it more difficult for citizens to escape. +The most dextrous/strongest arm could be shown with a different color in the equipment screen. +A crutch - decreases the penalty for having just one leg. +A fountain could give temporary or "permanent" (lasting until the next death) life saving - very easy to implement. +Again something revealed to me in a dream: There nine thaums, the basic particles of magic (kinda like the six quarks); four thaums have an antithaum except the E-thaum, short for Existence, whose antithaum is Non-Existence, which doesn't exist. +Teleporting version of magpie. Really annoying. +Polymorph grenade. +It should be easier to define secondary colors of items. +Doors of shops shouldn't be locked. +You should be able to break chests with a pick-axe of sufficient strength value. +Vegetarian hunters. diff --git a/Doc/Work/Value Plan.txt b/Doc/Work/Value Plan.txt new file mode 100644 index 0000000..f84a28f --- /dev/null +++ b/Doc/Work/Value Plan.txt @@ -0,0 +1,152 @@ +Description of values +--------------------- + +name characteristics connecting creatures valuing it + +others altruists +self egoists + +life people who prefer to solve conflicts in peaceful and life-preserving ways +death will resort to violence when facing opposite opinions + +order beings that believe in the power of ordered societies and laws +chaos individualists, anarchists and generally people who don't care about rules + +nature those who see nature as value in itself +man those who see nature only as a resource to be exploided by man + +Gods of belonging to each value +------------------------------- + +order chaos +Legifer Hetairia +Valpurus Cleptia +Sophos Infuscor + +life death +Sophos Infuscor +Dulcis Cruentus +Seges Mortifer + +nature man +Mortifer Seges +Terra Loricatus +Silva Mellis + +self others +Mellis Silva +Nefas Atavus +Hetairia Legifer + +List of all gods +---------------- + +Valpurus god of divine order order +Sophos god of wisdom, stability and handicraft order, life +Dulcis goddess of love and art life +Seges goddess of health and agriculture life, man +Loricatus god of fire, machines and smithing man +Mellis god of money, trade and politics man, self +Nefas goddess of greed and forbidden pleasures self +Hetairia goddess of lies, betrayal and illusions self, chaos +Cleptia goddess of crime and rebellion chaos +Infuscor goddess of mutations and dark magic chaos, death +Cruentus god of war and violence death +Mortifer god of death, disease and famine death, nature +Terra goddess of the earth nature +Silva goddess of forests and wildlife nature, others +Atavus god of charity and munificence others +Legifer god of truth, law and loyalty others, order + +Graphical representation +------------------------ + +Each god(dess) hates the three gods on the opposite side of the octagon and likes +the two adjacent to Him/Her. The exact opposite god(dess) is His/Her archenemy, +who always belongs to the opposite sex. + + + Legifer Valpurus Sophos + + Atavus Dulcis + + Silva Seges + + Terra Loricatus + + Mortifer Mellis + + Cruentus Nefas + + Infuscor Cleptia Hetairia + + +List of all possible alignment combinations +------------------------------------------- + +Alignment: order life man others +Friends: Valpurus Seges Atavus Dulcis Sophos Legifer Loricatus +Enemies: Terra Silva Mellis Nefas Cruentus Cleptia Infuscor Hetairia Mortifer + +Alignment: order life man self +Friends: Valpurus Seges Dulcis Sophos Loricatus Mellis Nefas +Enemies: Atavus Terra Silva Legifer Cruentus Cleptia Infuscor Hetairia Mortifer + +Alignment: order life nature others +Friends: Valpurus Atavus Terra Dulcis Sophos Silva Legifer +Enemies: Seges Loricatus Mellis Nefas Cruentus Cleptia Infuscor Hetairia Mortifer + +Alignment: order death nature others +Friends: Valpurus Atavus Terra Silva Legifer Cruentus Mortifer +Enemies: Seges Dulcis Sophos Loricatus Mellis Nefas Cleptia Infuscor Hetairia + +Alignment: order death man others +Friends: Valpurus Atavus Legifer Loricatus Cruentus +Enemies: Seges Terra Dulcis Sophos Silva Mellis Nefas Cleptia Infuscor Hetairia Mortifer + +Alignment: order life nature self +Friends: Valpurus Terra Dulcis Sophos Nefas +Enemies: Seges Atavus Silva Legifer Loricatus Mellis Cruentus Cleptia Infuscor Hetairia Mortifer + +Alignment: order death nature self +Friends: Valpurus Terra Nefas Cruentus Mortifer +Enemies: Seges Atavus Dulcis Sophos Silva Legifer Loricatus Mellis Cleptia Infuscor Hetairia + +Alignment: order death man self +Friends: Valpurus Loricatus Mellis Nefas Cruentus +Enemies: Seges Atavus Terra Dulcis Sophos Silva Legifer Cleptia Infuscor Hetairia Mortifer + +Alignment: chaos life man others +Friends: Seges Atavus Dulcis Loricatus Cleptia +Enemies: Valpurus Terra Sophos Silva Legifer Mellis Nefas Cruentus Infuscor Hetairia Mortifer + +Alignment: chaos life man self +Friends: Seges Dulcis Loricatus Mellis Nefas Cleptia Hetairia +Enemies: Valpurus Atavus Terra Sophos Silva Legifer Cruentus Infuscor Mortifer + +Alignment: chaos life nature others +Friends: Atavus Terra Dulcis Silva Cleptia +Enemies: Valpurus Seges Sophos Legifer Loricatus Mellis Nefas Cruentus Infuscor Hetairia Mortifer + +Alignment: chaos death nature others +Friends: Atavus Terra Silva Cruentus Cleptia Infuscor Mortifer +Enemies: Valpurus Seges Dulcis Sophos Legifer Loricatus Mellis Nefas Hetairia + +Alignment: chaos death man others +Friends: Atavus Loricatus Cruentus Cleptia Infuscor +Enemies: Valpurus Seges Terra Dulcis Sophos Silva Legifer Mellis Nefas Hetairia Mortifer + +Alignment: chaos life nature self +Friends: Terra Dulcis Nefas Cleptia Hetairia +Enemies: Valpurus Seges Atavus Sophos Silva Legifer Loricatus Mellis Cruentus Infuscor Mortifer + +Alignment: chaos death nature self +Friends: Terra Nefas Cruentus Cleptia Infuscor Hetairia Mortifer +Enemies: Valpurus Seges Atavus Dulcis Sophos Silva Legifer Loricatus Mellis + +Alignment: chaos death man self +Friends: Loricatus Mellis Nefas Cruentus Cleptia Infuscor Hetairia +Enemies: Valpurus Seges Atavus Terra Dulcis Sophos Silva Legifer Mortifer + +End of document +--------------- diff --git a/Doc/Work/Version Requirements.txt b/Doc/Work/Version Requirements.txt new file mode 100644 index 0000000..d8416ff --- /dev/null +++ b/Doc/Work/Version Requirements.txt @@ -0,0 +1,15 @@ +Every version must henceforth include one new feature +from every one of these categories: + +fountain effects +banana jokes +special monsters +special items +special materials +Monty Python jokes +insults +diseases + +every other version is ok for these: +traps +states other than diseases diff --git a/FEEL/CVS/Entries b/FEEL/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FEEL/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FEEL/CVS/Entries.Log b/FEEL/CVS/Entries.Log new file mode 100644 index 0000000..b7481e5 --- /dev/null +++ b/FEEL/CVS/Entries.Log @@ -0,0 +1,2 @@ +A D/Include//// +A D/Source//// diff --git a/FEEL/CVS/Repository b/FEEL/CVS/Repository new file mode 100644 index 0000000..7340fc6 --- /dev/null +++ b/FEEL/CVS/Repository @@ -0,0 +1 @@ +ivan/FEEL diff --git a/FEEL/CVS/Root b/FEEL/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FEEL/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FEEL/Include/CVS/Entries b/FEEL/Include/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FEEL/Include/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FEEL/Include/CVS/Repository b/FEEL/Include/CVS/Repository new file mode 100644 index 0000000..d1162b2 --- /dev/null +++ b/FEEL/Include/CVS/Repository @@ -0,0 +1 @@ +ivan/FEEL/Include diff --git a/FEEL/Include/CVS/Root b/FEEL/Include/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FEEL/Include/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FEEL/Source/CVS/Entries b/FEEL/Source/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FEEL/Source/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FEEL/Source/CVS/Repository b/FEEL/Source/CVS/Repository new file mode 100644 index 0000000..0cd6dd0 --- /dev/null +++ b/FEEL/Source/CVS/Repository @@ -0,0 +1 @@ +ivan/FEEL/Source diff --git a/FEEL/Source/CVS/Root b/FEEL/Source/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FEEL/Source/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FELL/CVS/Entries b/FELL/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FELL/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FELL/CVS/Entries.Log b/FELL/CVS/Entries.Log new file mode 100644 index 0000000..b7481e5 --- /dev/null +++ b/FELL/CVS/Entries.Log @@ -0,0 +1,2 @@ +A D/Include//// +A D/Source//// diff --git a/FELL/CVS/Repository b/FELL/CVS/Repository new file mode 100644 index 0000000..6627afc --- /dev/null +++ b/FELL/CVS/Repository @@ -0,0 +1 @@ +ivan/FELL diff --git a/FELL/CVS/Root b/FELL/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FELL/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FELL/Include/CVS/Entries b/FELL/Include/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FELL/Include/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FELL/Include/CVS/Repository b/FELL/Include/CVS/Repository new file mode 100644 index 0000000..a362e33 --- /dev/null +++ b/FELL/Include/CVS/Repository @@ -0,0 +1 @@ +ivan/FELL/Include diff --git a/FELL/Include/CVS/Root b/FELL/Include/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FELL/Include/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FELL/Source/CVS/Entries b/FELL/Source/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FELL/Source/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FELL/Source/CVS/Repository b/FELL/Source/CVS/Repository new file mode 100644 index 0000000..e77a220 --- /dev/null +++ b/FELL/Source/CVS/Repository @@ -0,0 +1 @@ +ivan/FELL/Source diff --git a/FELL/Source/CVS/Root b/FELL/Source/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FELL/Source/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeDX/Asm/CVS/Entries b/FeDX/Asm/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FeDX/Asm/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FeDX/Asm/CVS/Repository b/FeDX/Asm/CVS/Repository new file mode 100644 index 0000000..9a9e093 --- /dev/null +++ b/FeDX/Asm/CVS/Repository @@ -0,0 +1 @@ +ivan/FeDX/Asm diff --git a/FeDX/Asm/CVS/Root b/FeDX/Asm/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeDX/Asm/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeDX/CVS/Entries b/FeDX/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FeDX/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FeDX/CVS/Entries.Log b/FeDX/CVS/Entries.Log new file mode 100644 index 0000000..ae8653a --- /dev/null +++ b/FeDX/CVS/Entries.Log @@ -0,0 +1,3 @@ +A D/Asm//// +A D/Include//// +A D/Source//// diff --git a/FeDX/CVS/Repository b/FeDX/CVS/Repository new file mode 100644 index 0000000..45ac5f8 --- /dev/null +++ b/FeDX/CVS/Repository @@ -0,0 +1 @@ +ivan/FeDX diff --git a/FeDX/CVS/Root b/FeDX/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeDX/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeDX/Include/CVS/Entries b/FeDX/Include/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FeDX/Include/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FeDX/Include/CVS/Repository b/FeDX/Include/CVS/Repository new file mode 100644 index 0000000..0e8a854 --- /dev/null +++ b/FeDX/Include/CVS/Repository @@ -0,0 +1 @@ +ivan/FeDX/Include diff --git a/FeDX/Include/CVS/Root b/FeDX/Include/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeDX/Include/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeDX/Source/CVS/Entries b/FeDX/Source/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FeDX/Source/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FeDX/Source/CVS/Repository b/FeDX/Source/CVS/Repository new file mode 100644 index 0000000..06fa57f --- /dev/null +++ b/FeDX/Source/CVS/Repository @@ -0,0 +1 @@ +ivan/FeDX/Source diff --git a/FeDX/Source/CVS/Root b/FeDX/Source/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeDX/Source/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeFile/CVS/Entries b/FeFile/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FeFile/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FeFile/CVS/Entries.Log b/FeFile/CVS/Entries.Log new file mode 100644 index 0000000..b7481e5 --- /dev/null +++ b/FeFile/CVS/Entries.Log @@ -0,0 +1,2 @@ +A D/Include//// +A D/Source//// diff --git a/FeFile/CVS/Repository b/FeFile/CVS/Repository new file mode 100644 index 0000000..ad4bcb0 --- /dev/null +++ b/FeFile/CVS/Repository @@ -0,0 +1 @@ +ivan/FeFile diff --git a/FeFile/CVS/Root b/FeFile/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeFile/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeFile/Include/CVS/Entries b/FeFile/Include/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FeFile/Include/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FeFile/Include/CVS/Repository b/FeFile/Include/CVS/Repository new file mode 100644 index 0000000..a621a10 --- /dev/null +++ b/FeFile/Include/CVS/Repository @@ -0,0 +1 @@ +ivan/FeFile/Include diff --git a/FeFile/Include/CVS/Root b/FeFile/Include/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeFile/Include/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeFile/Source/CVS/Entries b/FeFile/Source/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FeFile/Source/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FeFile/Source/CVS/Repository b/FeFile/Source/CVS/Repository new file mode 100644 index 0000000..8175d77 --- /dev/null +++ b/FeFile/Source/CVS/Repository @@ -0,0 +1 @@ +ivan/FeFile/Source diff --git a/FeFile/Source/CVS/Root b/FeFile/Source/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeFile/Source/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeIO/CVS/Entries b/FeIO/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FeIO/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FeIO/CVS/Entries.Log b/FeIO/CVS/Entries.Log new file mode 100644 index 0000000..b7481e5 --- /dev/null +++ b/FeIO/CVS/Entries.Log @@ -0,0 +1,2 @@ +A D/Include//// +A D/Source//// diff --git a/FeIO/CVS/Repository b/FeIO/CVS/Repository new file mode 100644 index 0000000..e48319d --- /dev/null +++ b/FeIO/CVS/Repository @@ -0,0 +1 @@ +ivan/FeIO diff --git a/FeIO/CVS/Root b/FeIO/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeIO/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeIO/Include/CVS/Entries b/FeIO/Include/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FeIO/Include/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FeIO/Include/CVS/Repository b/FeIO/Include/CVS/Repository new file mode 100644 index 0000000..a394f48 --- /dev/null +++ b/FeIO/Include/CVS/Repository @@ -0,0 +1 @@ +ivan/FeIO/Include diff --git a/FeIO/Include/CVS/Root b/FeIO/Include/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeIO/Include/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeIO/Source/CVS/Entries b/FeIO/Source/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FeIO/Source/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FeIO/Source/CVS/Repository b/FeIO/Source/CVS/Repository new file mode 100644 index 0000000..1103b5e --- /dev/null +++ b/FeIO/Source/CVS/Repository @@ -0,0 +1 @@ +ivan/FeIO/Source diff --git a/FeIO/Source/CVS/Root b/FeIO/Source/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeIO/Source/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeLib/CVS/Entries b/FeLib/CVS/Entries new file mode 100644 index 0000000..bd1a95c --- /dev/null +++ b/FeLib/CVS/Entries @@ -0,0 +1,2 @@ +/Makefile.am/1.1/Sun Apr 21 18:33:44 2002// +D diff --git a/FeLib/CVS/Entries.Log b/FeLib/CVS/Entries.Log new file mode 100644 index 0000000..b7481e5 --- /dev/null +++ b/FeLib/CVS/Entries.Log @@ -0,0 +1,2 @@ +A D/Include//// +A D/Source//// diff --git a/FeLib/CVS/Repository b/FeLib/CVS/Repository new file mode 100644 index 0000000..1717e37 --- /dev/null +++ b/FeLib/CVS/Repository @@ -0,0 +1 @@ +ivan/FeLib diff --git a/FeLib/CVS/Root b/FeLib/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeLib/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeLib/Include/CVS/Entries b/FeLib/Include/CVS/Entries new file mode 100644 index 0000000..bca770f --- /dev/null +++ b/FeLib/Include/CVS/Entries @@ -0,0 +1,24 @@ +/Makefile.am/1.15/Fri Dec 10 23:03:24 2004// +/allocate.h/1.13/Tue May 10 20:32:41 2005// +/bitmap.h/1.58/Sun Sep 3 18:02:55 2006// +/config.h/1.7/Tue May 10 20:32:41 2005// +/error.h/1.11/Tue May 10 20:32:41 2005// +/fearray.h/1.9/Wed Aug 16 10:15:58 2006// +/febot.h/1.7/Tue May 10 20:32:41 2005// +/feio.h/1.29/Sun Sep 3 18:02:55 2006// +/felibdef.h/1.39/Tue May 10 20:32:41 2005// +/felist.h/1.38/Tue May 10 20:32:41 2005// +/feloops.h/1.6/Mon Oct 18 18:57:02 2004// +/femath.h/1.41/Tue Aug 15 20:38:15 2006// +/festring.h/1.30/Tue May 10 20:32:41 2005// +/fetime.h/1.3/Wed Sep 29 21:54:32 2004// +/graphics.h/1.27/Fri Jul 22 20:25:17 2005// +/hscore.h/1.28/Tue May 10 20:32:41 2005// +/pragmas.h/1.5/Fri Dec 10 09:09:32 2004// +/rawbit.h/1.8/Tue May 10 20:32:41 2005// +/rect.h/1.7/Wed Sep 29 21:54:32 2004// +/save.h/1.40/Tue May 10 23:50:00 2005// +/typedef.h/1.13/Tue May 10 20:32:41 2005// +/v2.h/1.4/Tue May 10 20:32:41 2005// +/whandler.h/1.29/Sun Jul 16 14:30:12 2006// +D diff --git a/FeLib/Include/CVS/Repository b/FeLib/Include/CVS/Repository new file mode 100644 index 0000000..61edbcc --- /dev/null +++ b/FeLib/Include/CVS/Repository @@ -0,0 +1 @@ +ivan/FeLib/Include diff --git a/FeLib/Include/CVS/Root b/FeLib/Include/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeLib/Include/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeLib/Include/Makefile.am b/FeLib/Include/Makefile.am new file mode 100644 index 0000000..ae58718 --- /dev/null +++ b/FeLib/Include/Makefile.am @@ -0,0 +1 @@ +noinst_HEADERS = allocate.h bitmap.h config.h error.h fearray.h feio.h felibdef.h felist.h feloops.h femath.h festring.h fetime.h graphics.h hscore.h pragmas.h rawbit.h rect.h save.h typedef.h v2.h whandler.h diff --git a/FeLib/Include/allocate.h b/FeLib/Include/allocate.h new file mode 100644 index 0000000..ee485a0 --- /dev/null +++ b/FeLib/Include/allocate.h @@ -0,0 +1,90 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __ALLOCATE_H__ +#define __ALLOCATE_H__ + +#include "typedef.h" + +template +inline void Alloc2D(type**& Map, int XSize, int YSize) +{ + cint Size = XSize * (sizeof(type*) + YSize * sizeof(type)); + Map = reinterpret_cast(new char[Size]); + type* XPointer = reinterpret_cast(Map + XSize); + + for(int x = 0; x < XSize; ++x, XPointer += YSize) + Map[x] = XPointer; +} + +template +inline void Alloc2D(type**& Map, int XSize, int YSize, + const type& Initializer) +{ + cint Size = XSize * (sizeof(type*) + YSize * sizeof(type)); + Map = reinterpret_cast(new char[Size]); + type* XPointer = reinterpret_cast(Map + XSize); + + for(int x = 0; x < XSize; ++x, XPointer += YSize) + { + Map[x] = XPointer; + + for(int y = 0; y < YSize; ++y) + Map[x][y] = Initializer; + } +} + +template +inline void Alloc3D(type***& Map, int XSize, int YSize, int ZSize) +{ + cint Size = XSize * (sizeof(type**) + + YSize * (sizeof(type*) + + ZSize * sizeof(type))); + Map = reinterpret_cast(new char[Size]); + type** XPointer = reinterpret_cast(Map + XSize); + type* YPointer = reinterpret_cast(XPointer + XSize * YSize); + + for(int x = 0; x < XSize; ++x, XPointer += YSize) + { + Map[x] = XPointer; + + for(int y = 0; y < YSize; ++y, YPointer += ZSize) + Map[x][y] = YPointer; + } +} + +template +inline void Alloc3D(type***& Map, int XSize, int YSize, + int ZSize, const type& Initializer) +{ + cint Size = XSize * (sizeof(type**) + + YSize * (sizeof(type*) + + ZSize * sizeof(type))); + Map = reinterpret_cast(new char[Size]); + type** XPointer = reinterpret_cast(Map + XSize); + type* YPointer = reinterpret_cast(XPointer + XSize * YSize); + + for(int x = 0; x < XSize; ++x, XPointer += YSize) + { + Map[x] = XPointer; + + for(int y = 0; y < YSize; ++y, YPointer += ZSize) + { + Map[x][y] = YPointer; + + for(int z = 0; z < ZSize; ++z) + Map[x][y][z] = Initializer; + } + } +} + +#endif diff --git a/FeLib/Include/bitmap.h b/FeLib/Include/bitmap.h new file mode 100644 index 0000000..4e2f05a --- /dev/null +++ b/FeLib/Include/bitmap.h @@ -0,0 +1,200 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __BITMAP_H__ +#define __BITMAP_H__ + +#include "v2.h" + +class bitmap; +class rawbitmap; +class outputfile; +class inputfile; +class festring; + +typedef void (*bitmapeditor)(bitmap*, truth); + +struct blitdata +{ + bitmap* Bitmap; + podv2 Src; + podv2 Dest; + podv2 Border; + union + { + int Flags, Stretch; + col24 Luminance; + }; + col16 MaskColor; + ulong CustomData; +}; + +class bitmap +{ + public: + friend class cachedfont; + bitmap(cfestring&); + bitmap(cbitmap*, int = 0, truth = true); + bitmap(v2); + bitmap(v2, col16); + ~bitmap(); + void Save(outputfile&) const; + void Load(inputfile&); + void Save(cfestring&) const; + void PutPixel(int X, int Y, col16 Color) { Image[Y][X] = Color; } + void PutPixel(v2 Pos, col16 Color) { Image[Pos.Y][Pos.X] = Color; } + void PowerPutPixel(int, int, col16, alpha, priority); + col16 GetPixel(int X, int Y) const { return Image[Y][X]; } + col16 GetPixel(v2 Pos) const { return Image[Pos.Y][Pos.X]; } + + void Fill(int, int, int, int, col16); + void Fill(v2, int, int, col16); + void Fill(int, int, v2, col16); + void Fill(v2, v2, col16); + + void ClearToColor(col16); + void NormalBlit(cblitdata&) const; + void NormalBlit(bitmap*, int = 0) const; + void FastBlit(bitmap*) const; + void FastBlit(bitmap*, v2) const; + + void LuminanceBlit(cblitdata&) const; + void NormalMaskedBlit(cblitdata&) const; + void LuminanceMaskedBlit(cblitdata&) const; + void SimpleAlphaBlit(bitmap*, alpha, col16 = TRANSPARENT_COLOR) const; + void AlphaMaskedBlit(cblitdata&) const; + void AlphaLuminanceBlit(cblitdata&) const; + + void DrawLine(int, int, int, int, col16, truth = false); + void DrawLine(v2, int, int, col16, truth = false); + void DrawLine(int, int, v2, col16, truth = false); + void DrawLine(v2, v2, col16, truth = false); + + void DrawVerticalLine(int, int, int, col16, truth = false); + void DrawHorizontalLine(int, int, int, col16, truth = false); + + void StretchBlit(cblitdata&) const; + + void DrawRectangle(int, int, int, int, col16, truth = false); + void DrawRectangle(v2, int, int, col16, truth = false); + void DrawRectangle(int, int, v2, col16, truth = false); + void DrawRectangle(v2, v2, col16, truth = false); + + void BlitAndCopyAlpha(bitmap*, int = 0) const; + void MaskedPriorityBlit(cblitdata&) const; + void AlphaPriorityBlit(cblitdata&) const; + void FastBlitAndCopyAlpha(bitmap*) const; + v2 GetSize() const { return Size; } + void DrawPolygon(int, int, int, int, col16, truth = true, truth = false, double = 0); + void CreateAlphaMap(alpha); + truth Fade(long&, packalpha&, int); + void SetAlpha(int X, int Y, alpha Alpha) { AlphaMap[Y][X] = Alpha; } + void SetAlpha(v2 Pos, alpha Alpha) { AlphaMap[Pos.Y][Pos.X] = Alpha; } + alpha GetAlpha(int X, int Y) const { return AlphaMap[Y][X]; } + alpha GetAlpha(v2 Pos) const { return AlphaMap[Pos.Y][Pos.X]; } + void Outline(col16, alpha, priority); + void FadeToScreen(bitmapeditor = 0); + void CreateFlames(rawbitmap*, v2, ulong, int); + truth IsValidPos(v2 What) const { return What.X >= 0 && What.Y >= 0 && What.X < Size.X && What.Y < Size.Y; } + truth IsValidPos(int X, int Y) const { return X >= 0 && Y >= 0 && X < Size.X && Y < Size.Y; } + void CreateSparkle(v2, int); + void CreateFlies(ulong, int, int); + void CreateLightning(ulong, col16); + truth CreateLightning(v2, v2, int, col16); + packcol16** GetImage() const { return Image; } + packalpha** GetAlphaMap() const { return AlphaMap; } + static truth PixelVectorHandler(long, long); + void FillAlpha(alpha); + void InitPriorityMap(priority); + void FillPriority(priority); + void SafeSetPriority(int, int, priority); + void SafeSetPriority(v2 Pos, priority What) { SafeSetPriority(Pos.X, Pos.Y, What); } + void SafeUpdateRandMap(v2, truth); + void UpdateRandMap(long, truth); + void InitRandMap(); + v2 RandomizePixel() const; + void AlphaPutPixel(int, int, col16, col24, alpha); + void AlphaPutPixel(v2 Pos, col16 Color, col24 Luminance, alpha Alpha) { AlphaPutPixel(Pos.X, Pos.Y, Color, Luminance, Alpha); } + void CalculateRandMap(); + alpha CalculateAlphaAverage() const; + void ActivateFastFlag() { FastFlag = 1; } + void DeactivateFastFlag() { FastFlag = 0; } + void Wobble(int, int, truth); + void MoveLineVertically(int, int); + void MoveLineHorizontally(int, int); + void InterLace(); + protected: + v2 Size; + ulong XSizeTimesYSize : 31; + ulong FastFlag : 1; + packcol16** Image; + packalpha** AlphaMap; + packpriority** PriorityMap; + truth* RandMap; +}; + +inline void bitmap::SafeUpdateRandMap(v2 Pos, truth What) +{ + if(RandMap) + UpdateRandMap(Pos.Y * Size.X + Pos.X, What); +} + +inline void bitmap::SafeSetPriority(int x, int y, priority What) +{ + if(PriorityMap) + PriorityMap[y][x] = What; +} + +inline void bitmap::FastBlit(bitmap* Bitmap) const +{ + memcpy(Bitmap->Image[0], Image[0], XSizeTimesYSize * sizeof(packcol16)); +} + +inline void bitmap::FastBlit(bitmap* Bitmap, v2 Pos) const +{ + packcol16** SrcImage = Image; + packcol16** DestImage = Bitmap->Image; + cint Bytes = Size.X * sizeof(packcol16); + cint Height = Size.Y; + + for(int y = 0; y < Height; ++y) + memcpy(&DestImage[Pos.Y + y][Pos.X], SrcImage[y], Bytes); +} + +inline void bitmap::NormalBlit(bitmap* Bitmap, int Flags) const +{ + blitdata B = { Bitmap, + { 0, 0 }, + { 0, 0 }, + { Size.X, Size.Y }, + { Flags }, + 0, + 0 }; + NormalBlit(B); +} + +outputfile& operator<<(outputfile&, cbitmap*); +inputfile& operator>>(inputfile&, bitmap*&); + +class cachedfont : public bitmap +{ + public: + cachedfont(v2); + cachedfont(v2, col16); + ~cachedfont() { delete [] MaskMap; } + void PrintCharacter(cblitdata) const; + void CreateMaskMap(); + private: + packcol16** MaskMap; +}; + +#endif diff --git a/FeLib/Include/config.h b/FeLib/Include/config.h new file mode 100644 index 0000000..3e431cd --- /dev/null +++ b/FeLib/Include/config.h @@ -0,0 +1,140 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +#include + +#include "festring.h" + +class inputfile; +class felist; +struct configoption; +struct stringoption; +struct numberoption; +struct truthoption; + +class configsystem +{ + public: + static truth Save(); + static truth Load(); + static void Show(void (*)() = 0, void (*)(felist&) = 0, truth = false); + static void AddOption(configoption*); + static void NormalStringDisplayer(const stringoption*, festring&); + static void NormalNumberDisplayer(const numberoption*, festring&); + static void NormalTruthDisplayer(const truthoption*, festring&); + static truth NormalStringChangeInterface(stringoption*); + static truth NormalNumberChangeInterface(numberoption*); + static truth NormalTruthChangeInterface(truthoption*); + static void NormalStringChanger(stringoption*, cfestring&); + static void NormalNumberChanger(numberoption*, long); + static void NormalTruthChanger(truthoption*, truth); + static void SetConfigFileName(cfestring& What) + { ConfigFileName = What; } + private: + static configoption* Option[MAX_CONFIG_OPTIONS]; + static festring ConfigFileName; + static int Options; +}; + +/* Currently there's no human-readable output option in outputfile, + so we're forced to use std::ofstream */ + +struct configoption +{ + configoption(cchar*, cchar*); + virtual ~configoption() { } + virtual void SaveValue(std::ofstream&) const = 0; + virtual void LoadValue(inputfile&) = 0; + virtual truth ActivateChangeInterface() = 0; + virtual void DisplayeValue(festring&) const = 0; + cchar* Name; + cchar* Description; +}; + +struct stringoption : public configoption +{ + stringoption(cchar*, cchar*, cfestring&, + void (*)(const stringoption*, festring&) + = &configsystem::NormalStringDisplayer, + truth (*)(stringoption*) + = &configsystem::NormalStringChangeInterface, + void (*)(stringoption*, cfestring&) + = &configsystem::NormalStringChanger); + virtual void SaveValue(std::ofstream&) const; + virtual void LoadValue(inputfile&); + virtual void DisplayeValue(festring& Entry) const + { ValueDisplayer(this, Entry); } + virtual truth ActivateChangeInterface() { return ChangeInterface(this); } + void ChangeValue(cfestring& What) { ValueChanger(this, What); } + festring Value; + void (*ValueDisplayer)(const stringoption*, festring&); + truth (*ChangeInterface)(stringoption*); + void (*ValueChanger)(stringoption*, cfestring&); +}; + +struct numberoption : public configoption +{ + numberoption(cchar*, cchar*, long, + void (*)(const numberoption*, festring&) + = &configsystem::NormalNumberDisplayer, + truth (*)(numberoption*) + = &configsystem::NormalNumberChangeInterface, + void (*)(numberoption*, long) + = &configsystem::NormalNumberChanger); + virtual void SaveValue(std::ofstream&) const; + virtual void LoadValue(inputfile&); + virtual void DisplayeValue(festring& Entry) const + { ValueDisplayer(this, Entry); } + virtual truth ActivateChangeInterface() { return ChangeInterface(this); } + void ChangeValue(long What) { ValueChanger(this, What); } + long Value; + void (*ValueDisplayer)(const numberoption*, festring&); + truth (*ChangeInterface)(numberoption*); + void (*ValueChanger)(numberoption*, long); +}; + +struct scrollbaroption : public numberoption +{ + scrollbaroption(cchar*, cchar*, long, + void (*)(const numberoption*, festring&), + truth (*)(numberoption*), + void (*)(numberoption*, long) + = &configsystem::NormalNumberChanger, + void (*)(long) = 0); + void (*BarHandler)(long); +}; + +struct truthoption : public configoption +{ + truthoption(cchar*, cchar*, truth, + void (*)(const truthoption*, festring&) + = &configsystem::NormalTruthDisplayer, + truth (*)(truthoption*) + = &configsystem::NormalTruthChangeInterface, + void (*)(truthoption*, truth) + = &configsystem::NormalTruthChanger); + virtual void SaveValue(std::ofstream&) const; + virtual void LoadValue(inputfile&); + virtual void DisplayeValue(festring& Entry) const + { ValueDisplayer(this, Entry); } + virtual truth ActivateChangeInterface() { return ChangeInterface(this); } + void ChangeValue(truth What) { ValueChanger(this, What); } + truth Value; + void (*ValueDisplayer)(const truthoption*, festring&); + truth (*ChangeInterface)(truthoption*); + void (*ValueChanger)(truthoption*, truth); +}; + +#endif diff --git a/FeLib/Include/error.h b/FeLib/Include/error.h new file mode 100644 index 0000000..09fe220 --- /dev/null +++ b/FeLib/Include/error.h @@ -0,0 +1,47 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __ERROR_H__ +#define __ERROR_H__ + +#include "felibdef.h" + +#define ABORT globalerrorhandler::Abort + +#ifdef __DJGPP__ +#define SIGNALS 8 +#endif + +class globalerrorhandler +{ + public: + static void Install(); + static void DeInstall(); + static void NO_RETURN LIKE_PRINTF(1, 2) Abort(cchar*, ...); + static cchar* GetBugMsg() { return BugMsg; } + private: + static cchar* BugMsg; +#ifdef VC + static int NewHandler(size_t); + static int (*OldNewHandler)(size_t); +#else + static void NewHandler(); + static void (*OldNewHandler)(); +#endif +#ifdef __DJGPP__ + static void SignalHandler(int); + static void (*OldSignal[SIGNALS])(int); + static int Signal[SIGNALS]; +#endif +}; + +#endif diff --git a/FeLib/Include/fearray.h b/FeLib/Include/fearray.h new file mode 100644 index 0000000..ed72377 --- /dev/null +++ b/FeLib/Include/fearray.h @@ -0,0 +1,161 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __FEARRAY_H__ +#define __FEARRAY_H__ + +#include "femath.h" + +template struct fearray +{ + typedef uint sizetype; + fearray() : Data(0), Size(0) { } + fearray(const fearray&); + fearray(const type*, sizetype); + ~fearray(); + fearray& operator=(const fearray&); + type& operator[](sizetype I) { return Data[I]; } + const type& operator[](sizetype I) const { return Data[I]; } + void Allocate(sizetype); + void Add(const type&); + void Clear(); + const type& GetRandomElement() const { return Data[RAND_N(Size)]; } + type* Data; + sizetype Size; +}; + +template +inline fearray::fearray(const fearray& A) +: Data(A.Data), Size(A.Size) +{ + if(Data) + ++REFS(Data); +} + +template +inline fearray::fearray(const type* Array, sizetype Size) +: Size(Size) +{ + char* Ptr = new char[Size * sizeof(type) + sizeof(ulong)]; + *reinterpret_cast(Ptr) = 0; + Data = reinterpret_cast(Ptr + sizeof(ulong)); + + for(sizetype c = 0; c < Size; ++c) + new(&Data[c]) type(Array[c]); +} + +template inline fearray::~fearray() +{ + type* Ptr = Data; + + if(Ptr && !REFS(Ptr)--) + { + type* TempPtr = Ptr, * EndPtr = Ptr + Size; + + for(; TempPtr != EndPtr; ++TempPtr) + TempPtr->~type(); + + delete [] &REFS(Ptr); + } +} + +template +inline fearray& fearray::operator=(const fearray& A) +{ + if(A.Data) + ++REFS(Data = A.Data); + + Size = A.Size; + return *this; +} + +template inline void fearray::Clear() +{ + type* Ptr = Data; + + if(Ptr) + { + if(!REFS(Ptr)--) + { + for(sizetype c = 0; c < Size; ++c) + Ptr[c].~type(); + + delete [] &REFS(Ptr); + } + + Data = 0; + Size = 0; + } +} + +template inline void fearray::Allocate(sizetype What) +{ + char* Ptr = new char[What * sizeof(type) + sizeof(ulong)]; + *reinterpret_cast(Ptr) = 0; + Data = reinterpret_cast(Ptr + sizeof(ulong)); + Size = What; + + for(sizetype c = 0; c < What; ++c) + new(&Data[c]) type; +} + +/* Don't use unless necessary */ + +template inline void fearray::Add(const type& Type) +{ + type* Ptr = Data; + + if(Ptr) + { + sizetype Size = this->Size++; + char* NewPtr = new char[(Size + 1) * sizeof(type) + sizeof(ulong)]; + *reinterpret_cast(NewPtr) = 0; + type* NewData = reinterpret_cast(NewPtr + sizeof(ulong)); + + if(!REFS(Ptr)--) + { + for(sizetype c = 0; c < Size; ++c) + { + new(&NewData[c]) type(Ptr[c]); + Ptr[c].~type(); + } + + delete [] &REFS(Ptr); + } + else + for(sizetype c = 0; c < Size; ++c) + new(&NewData[c]) type(Ptr[c]); + + Data = NewData; + new(&NewData[Size]) type(Type); + } + else + { + char* NewPtr = new char[sizeof(type) + sizeof(ulong)]; + *reinterpret_cast(NewPtr) = 0; + Data = reinterpret_cast(NewPtr + sizeof(ulong)); + Size = 1; + new(Data) type(Type); + } +} + +template +inline void ArrayToVector(const fearray& Array, + std::vector& Vect) +{ + Vect.resize(Array.Size, type2()); + + for(typename fearray::sizetype c = 0; c < Array.Size; ++c) + Vect[c] = Array.Data[c]; +} + +#endif diff --git a/FeLib/Include/febot.h b/FeLib/Include/febot.h new file mode 100644 index 0000000..fc329e5 --- /dev/null +++ b/FeLib/Include/febot.h @@ -0,0 +1,63 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __FEBOT_H__ +#define __FEBOT_H__ + +#include +#include + +#include "festring.h" + +class outputfile; +class inputfile; + +class febot +{ + public: + void Initialize(ushort); + void Save(outputfile&) const; + void Load(inputfile&); + void BeTalkedTo(festring); + void Reply(festring&) const; + void Amnesify() { Initialize(ChainLength); } + truth IsIgnorant() const { return WordChainSet.size() <= 1; } + private: + struct wordchain + { + wordchain(ushort ChainLength) : String(ChainLength, festring()) { } + wordchain(const wordchain*, cfestring&); + bool operator<(const wordchain&) const; + wordchain* GetRandomLink() const; + std::list String; + std::vector Link; + }; + wordchain* CreateWordChain(const wordchain*, cfestring&); + wordchain* GetControlWordChain() const; + typedef std::set wordchainset; + wordchainset WordChainSet; + ushort ChainLength; +}; + +inline outputfile& operator<<(outputfile& SaveFile, const febot& Febot) +{ + Febot.Save(SaveFile); + return SaveFile; +} + +inline inputfile& operator>>(inputfile& SaveFile, febot& Febot) +{ + Febot.Load(SaveFile); + return SaveFile; +} + +#endif diff --git a/FeLib/Include/feio.h b/FeLib/Include/feio.h new file mode 100644 index 0000000..65d8ddb --- /dev/null +++ b/FeLib/Include/feio.h @@ -0,0 +1,46 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __FEIO_H__ +#define __FEIO_H__ + +#include "v2.h" +#include "festring.h" + +class bitmap; + +typedef truth (*stringkeyhandler)(int, festring&); +typedef void (*bitmapeditor)(bitmap*, truth); + +class iosystem +{ + public: + static festring ContinueMenu(col16, col16, cfestring&); + static int StringQuestion(festring&, cfestring&, v2, col16, + festring::sizetype, festring::sizetype, + truth, truth, stringkeyhandler = 0); + static long NumberQuestion(cfestring&, v2, col16, + truth, truth = false); + static long ScrollBarQuestion(cfestring&, v2, long, long, long, + long, long, col16, col16, col16, int, + int, truth, void (*)(long) = 0); + static int Menu(cbitmap*, v2, cfestring&, + cfestring&, col16, + cfestring& = CONST_S(""), + cfestring& = CONST_S("")); + static void TextScreen(cfestring&, v2 Disp = ZERO_V2, + col16 = 0xFFFF, truth = true, + truth = true, bitmapeditor = 0); + static truth IsAcceptableForStringQuestion(char); +}; + +#endif diff --git a/FeLib/Include/felibdef.h b/FeLib/Include/felibdef.h new file mode 100644 index 0000000..87da286 --- /dev/null +++ b/FeLib/Include/felibdef.h @@ -0,0 +1,212 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __FELIBDEF_H__ +#define __FELIBDEF_H__ + +/* + * Global defines for the project FeLib. + * This file is created to decrease the need of including headers in + * other headers just for the sake of some silly macros, because it + * decreases compilation efficiency and may cause cross-including + * + * List of macros that should be gathered here: + * 1. all numeric defines used in multiple .cpp or .h files + * 2. all inline functions used in multiple .cpp or .h files + * and independent enough (do not require other headers) + * 3. class construction macros used in multiple .h files + */ + +#include "typedef.h" + +cint MapMoveX[9] = { -1, 0, 1, -1, 1, -1, 0, 1, 0 }; +cint MapMoveY[9] = { -1, -1, -1, 0, 0, 1, 1, 1, 0 }; + +culong SquarePartTickMask[4] = { 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }; + +#define FPI 3.1415926535897932384626433832795 + +/* Btw, both __attribute__ ((regparm(3))) and __fastcall SUCK! */ + +#ifdef GCC +#define NO_ALIGNMENT __attribute__ ((packed)) +#define NO_RETURN __attribute__ ((noreturn)) +#define LIKE_PRINTF(p1, p2) __attribute__ ((format(printf, p1, p2))) +#else +#define NO_ALIGNMENT +#define NO_RETURN +#define LIKE_PRINTF(p1, p2) +#endif + +template +inline type Max(type X, type Y) { return X >= Y ? X : Y; } + +template +inline type Max(type X, type Y, type Z) +{ return X >= Y ? (X >= Z ? X : Z) : (Y >= Z ? Y : Z); } + +template +inline type Min(type X, type Y) { return X <= Y ? X : Y; } + +template +inline type Min(type X, type Y, type Z) +{ return X <= Y ? (X <= Z ? X : Z) : (Y <= Z ? Y : Z); } + +template +inline type HypotSquare(type X, type Y) { return X * X + Y * Y; } + +template +inline type Limit(type Value, type Minimum, type Maximum) +{ return Value >= Minimum ? Value <= Maximum ? Value : Maximum : Minimum; } + +template +inline void LimitRef(type& Value, type Minimum, type Maximum) +{ + if(Value <= Minimum) + Value = Minimum; + else if(Value >= Maximum) + Value = Maximum; +} + +template +inline void Swap(type& X, type& Y) +{ + const type T = X; + X = Y; + Y = T; +} + +inline col16 GetRed16(col16 Color) { return Color >> 8 & 0xF8; } +inline col16 GetGreen16(col16 Color) { return Color >> 3 & 0xFC; } +inline col16 GetBlue16(col16 Color) { return Color << 3 & 0xF8; } + +inline col16 MakeRGB16(int Red, int Green, int Blue) +{ + return (Red << 8 & 0xF800) | (Green << 3 & 0x7E0) | (Blue >> 3 & 0x1F); +} + +inline col16 MakeShadeColor(col16 Color) +{ + return MakeRGB16(GetRed16(Color) / 3, + GetGreen16(Color) / 3, + GetBlue16(Color) / 3); +} + +inline col24 GetRed24(col24 Color) { return Color >> 16 & 0xFF; } +inline col24 GetGreen24(col24 Color) { return Color >> 8 & 0xFF; } +inline col24 GetBlue24(col24 Color) { return Color & 0xFF; } + +inline col24 MakeRGB24(int Red, int Green, int Blue) +{ + return (Red << 16 & 0xFF0000) | (Green << 8 & 0xFF00) | (Blue & 0xFF); +} + +inline int GetMaxColor24(col24 Color) +{ + return Max(GetRed24(Color), GetGreen24(Color), GetBlue24(Color)); +} + +inline int GetMinColor24(col24 Color) +{ + return Min(GetRed24(Color), GetGreen24(Color), GetBlue24(Color)); +} + +#define NONE 0 +#define MIRROR 1 +#define FLIP 2 +#define ROTATE 4 + +#define TRANSPARENT_COLOR 0xF81F + +#define RED 0xF800 +#define GREEN 0x07E0 +#define BLUE 0x001F + +#define YELLOW 0xFFE0 +#define PINK 0xF01E + +#define WHITE 0xFFFF +#define LIGHT_GRAY 0x94B2 +#define DARK_GRAY 0x528A +#define BLACK 0x0000 + +#define NORMAL_LUMINANCE 0x808080 + +#define KEY_BACK_SPACE 0x08 +#define KEY_ESC 0x1B +#define KEY_ENTER 0x0D +#define KEY_UP 0x148 +#define KEY_DOWN 0x150 +#define KEY_RIGHT 0x14D +#define KEY_LEFT 0x14B +#define KEY_HOME 0x147 +#define KEY_END 0x14F +#define KEY_PAGE_DOWN 0x151 +#define KEY_PAGE_UP 0x149 +#define KEY_SPACE ' ' +#define KEY_NUMPAD_5 2 + +#define NO_FLAME 0xFFFF + +#define SELECTABLE 1 +#define INVERSE_MODE 2 +#define BLIT_AFTERWARDS 4 +#define DRAW_BACKGROUND_AFTERWARDS 8 +#define FADE 16 + +/* felist errors */ + +#define FELIST_ERROR_BIT 0x8000 +#define LIST_WAS_EMPTY 0xFFFF +#define ESCAPED 0xFFFE +#define NOTHING_SELECTED 0xFFFD + +#define NO_LIMIT 0xFFFF + +#define MAX_CONTROLS 0x10 + +#define HIGHEST 0xFF + +#define NORMAL_EXIT 0 +#define ABORTED 1 + +#define MAX_CONFIG_OPTIONS 0x100 + +#define FLY_PRIORITY ((10 << 4) + 10) +#define SPARKLE_PRIORITY ((12 << 4) + 12) +#define LIGHTNING_PRIORITY ((14 << 4) + 14) +#define AVERAGE_PRIORITY ((8 << 4) + 8) + +#define NO_IMAGE 0xFFFF + +#define ZERO_POOLS 1 +#define RAND_ALLOC 2 + +#define REFS(ptr) reinterpret_cast(ptr)[-1] + +#define SKIP_FIRST 1 +#define ALLOW_END_FAILURE 2 + +#define MAX_RAND 0x7FFFFFFF + +#define TRANSPARENT_PALETTE_INDEX 191 + +#define MAX_HIGHSCORES 100 + +/* sparkling flags */ + +#define SPARKLING_A 1 +#define SPARKLING_B 2 +#define SPARKLING_C 4 +#define SPARKLING_D 8 + +#endif diff --git a/FeLib/Include/felist.h b/FeLib/Include/felist.h new file mode 100644 index 0000000..c04c3e9 --- /dev/null +++ b/FeLib/Include/felist.h @@ -0,0 +1,84 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __LIST_H__ +#define __LIST_H__ + +#include + +#include "v2.h" + +class outputfile; +class inputfile; +class rawbitmap; +class bitmap; +class festring; +struct felistentry; +struct felistdescription; + +typedef void (*entrydrawer)(bitmap*, v2, uint); + +class felist +{ + public: + felist(cfestring&, col16 = WHITE, uint = 0); + ~felist(); + void AddEntry(cfestring&, col16, uint = 0, + uint = NO_IMAGE, truth = true); + void AddDescription(cfestring&, col16 = WHITE); + uint Draw(); + void QuickDraw(bitmap*, uint) const; + void Empty(); + void EmptyDescription(); + festring GetEntry(uint) const; + col16 GetColor(uint) const; + void SetColor(uint, col16); + uint GetLength() const; + uint GetLastEntryIndex() const; + void Load(inputfile&); + void Save(outputfile&) const; + truth IsEmpty() const; + uint GetSelected() const { return Selected; } + void SetSelected(uint What) { Selected = What; } + void EditSelected(int What) { Selected += What; } + truth DrawPage(bitmap*) const; + void Pop(); + static void CreateQuickDrawFontCaches(rawbitmap*, col16, uint); + void PrintToFile(cfestring&); + void SetPos(v2 What) { Pos = What; } + void SetWidth(uint What) { Width = What; } + void SetPageLength(uint What) { PageLength = What; } + void SetBackColor(col16 What) { BackColor = What; } + void SetFlags(uint What) { Flags = What; } + void AddFlags(uint What) { Flags |= What; } + void RemoveFlags(uint What) { Flags &= ~What; } + void SetUpKey(uint What) { UpKey = What; } + void SetDownKey(uint What) { DownKey = What; } + void SetEntryDrawer(entrydrawer What) { EntryDrawer = What; } + private: + void DrawDescription(bitmap*) const; + std::vector Entry; + std::vector Description; + uint PageBegin; + uint Maximum; + uint Selected; + v2 Pos; + uint Width; + uint PageLength; + col16 BackColor; + uint Flags; + uint UpKey; + uint DownKey; + entrydrawer EntryDrawer; +}; + +#endif diff --git a/FeLib/Include/feloops.h b/FeLib/Include/feloops.h new file mode 100644 index 0000000..4f8c3e9 --- /dev/null +++ b/FeLib/Include/feloops.h @@ -0,0 +1,132 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __FELOOPS_H__ +#define __FELOOPS_H__ + +#include "typedef.h" + +template +struct combinepredicates +{ + typedef elementtype* (objecttype::*extractor)(int) const; + typedef truth (elementtype::*routine)() const; + truth operator()(objecttype* O, extractor X, routine F, int A, truth OrBit) const + { + for(int c = 0; c < A; ++c) + { + elementtype* E = (O->*X)(c); + + if(E && !(E->*F)() == !OrBit) + return OrBit; + } + + return !OrBit; + } +}; + +template +struct combinepredicateswithparam +{ + typedef elementtype* (objecttype::*extractor)(int) const; + typedef truth (elementtype::*routine)(param) const; + truth operator()(objecttype* O, extractor X, routine F, param P, int A, truth OrBit) const + { + for(int c = 0; c < A; ++c) + { + elementtype* E = (O->*X)(c); + + if(E && !(E->*F)(P) == !OrBit) + return OrBit; + } + + return !OrBit; + } +}; + +template +struct doforelements +{ + typedef elementtype* (objecttype::*extractor)(int) const; + typedef void (elementtype::*routine)(); + void operator()(objecttype* O, extractor X, routine F, int A) const + { + for(int c = 0; c < A; ++c) + { + elementtype* E = (O->*X)(c); + + if(E) + (E->*F)(); + } + } +}; + +template +struct doforelementswithparam +{ + typedef elementtype* (objecttype::*extractor)(int) const; + typedef void (elementtype::*routine)(param); + void operator()(objecttype* O, extractor X, + routine F, param P, int A) const + { + for(int c = 0; c < A; ++c) + { + elementtype* E = (O->*X)(c); + + if(E) + (E->*F)(P); + } + } +}; + +template +struct sumproperties +{ + typedef elementtype* (objecttype::*extractor)(int) const; + typedef int (elementtype::*routine)() const; + int operator()(objecttype* O, extractor X, routine F, int A) const + { + int Sum = 0; + + for(int c = 0; c < A; ++c) + { + elementtype* E = (O->*X)(c); + + if(E) + Sum += (E->*F)(); + } + + return Sum; + } +}; + +template +struct findelement +{ + typedef elementtype* (objecttype::*extractor)(int) const; + typedef truth (elementtype::*routine)(param) const; + elementtype* operator()(objecttype* O, extractor X, + routine F, param P, int A) const + { + for(int c = 0; c < A; ++c) + { + elementtype* E = (O->*X)(c); + + if(E && (E->*F)(P)) + return E; + } + + return 0; + } +}; + +#endif diff --git a/FeLib/Include/femath.h b/FeLib/Include/femath.h new file mode 100644 index 0000000..a342d57 --- /dev/null +++ b/FeLib/Include/femath.h @@ -0,0 +1,316 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __FEMATH_H__ +#define __FEMATH_H__ + +#include +#include + +#include "v2.h" +#include "rect.h" + +#define RAND femath::Rand +#define RAND_N femath::RandN +#define RAND_2 (femath::Rand() & 1) +#define RAND_4 (femath::Rand() & 3) +#define RAND_8 (femath::Rand() & 7) +#define RAND_16 (femath::Rand() & 15) +#define RAND_32 (femath::Rand() & 31) +#define RAND_64 (femath::Rand() & 63) +#define RAND_128 (femath::Rand() & 127) +#define RAND_256 (femath::Rand() & 255) +#define RAND_GOOD femath::RandGood + +class outputfile; +class inputfile; +template struct fearray; + +class femath +{ + public: + static long Rand(); + static void SetSeed(ulong); + static long RandN(long N) { return long(double(N) * Rand() / 0x80000000); } + static long RandGood(long N) + { return long(double(N) * Rand() / 0x80000000); } + static int WeightedRand(long*, long); + static int WeightedRand(const std::vector&, long); + static double CalculateAngle(v2); + static void CalculateEnvironmentRectangle(rect&, const rect&, v2, int); + static truth Clip(int&, int&, int&, int&, int&, int&, int, int, int, int); + static void SaveSeed(); + static void LoadSeed(); + static long SumArray(const fearray&); + static int LoopRoll(int, int); + static void GenerateFractalMap(int**, int, int, int); + protected: + static ulong mt[]; + static long mti; + static ulong mtb[]; + static long mtib; +}; + +struct interval +{ + long Randomize() const + { return Min < Max ? Min + RAND() % (Max - Min + 1) : Min; } + long Min; + long Max; +}; + +struct region +{ + v2 Randomize() const { return v2(X.Randomize(), Y.Randomize()); } + interval X; + interval Y; +}; + +void ReadData(interval&, inputfile&); +void ReadData(region&, inputfile&); + +outputfile& operator<<(outputfile&, const interval&); +inputfile& operator>>(inputfile&, interval&); +outputfile& operator<<(outputfile&, const region&); +inputfile& operator>>(inputfile&, region&); + +template class mapmath +{ + public: + static truth DoLine(int, int, int, int, int = 0); + static void DoArea(); + static void DoQuadriArea(int, int, int, int, int); +}; + +template +inline truth mapmath::DoLine(int X1, int Y1, + int X2, int Y2, int Flags) +{ + if(!(Flags & SKIP_FIRST)) + controller::Handler(X1, Y1); + + cint DeltaX = abs(X2 - X1); + cint DeltaY = abs(Y2 - Y1); + cint DoubleDeltaX = DeltaX << 1; + cint DoubleDeltaY = DeltaY << 1; + cint XChange = X1 < X2 ? 1 : -1; + cint YChange = Y1 < Y2 ? 1 : -1; + int x = X1, y = Y1; + + if(DeltaX >= DeltaY) + { + int c = DeltaX; + cint End = X2; + + while(x != End) + { + x += XChange; + c += DoubleDeltaY; + + if(c >= DoubleDeltaX) + { + c -= DoubleDeltaX; + y += YChange; + } + + if(!controller::Handler(x, y)) + return x == End && !(Flags & ALLOW_END_FAILURE); + } + } + else + { + int c = DeltaY; + cint End = Y2; + + while(y != End) + { + y += YChange; + c += DoubleDeltaX; + + if(c >= DoubleDeltaY) + { + c -= DoubleDeltaY; + x += XChange; + } + + if(!controller::Handler(x, y)) + return y == End && !(Flags & ALLOW_END_FAILURE); + } + } + + return true; +} + +struct basequadricontroller +{ + static cint OrigoDeltaX[4]; + static cint OrigoDeltaY[4]; + static int OrigoX, OrigoY; + static int StartX, StartY; + static int XSize, YSize; + static int RadiusSquare; + static truth SectorCompletelyClear; +}; + +template +struct quadricontroller : public basequadricontroller +{ + static truth Handler(int, int); + static int GetStartX(int I) + { + SectorCompletelyClear = true; + return StartX = (OrigoX << 1) + OrigoDeltaX[I]; + } + static int GetStartY(int I) + { + return StartY = (OrigoY << 1) + OrigoDeltaY[I]; + } +}; + +template +truth quadricontroller::Handler(int x, int y) +{ + cint HalfX = x >> 1, HalfY = y >> 1; + + if(HalfX >= 0 && HalfY >= 0 && HalfX < XSize && HalfY < YSize) + { + ulong& SquareTick = controller::GetTickReference(HalfX, HalfY); + cint SquarePartIndex = (x & 1) + ((y & 1) << 1); + culong Mask = SquarePartTickMask[SquarePartIndex]; + + if((SquareTick & Mask) < controller::ShiftedTick[SquarePartIndex]) + { + SquareTick = SquareTick & ~Mask + | controller::ShiftedQuadriTick[SquarePartIndex]; + int DeltaX = OrigoX - HalfX, DeltaY = OrigoY - HalfY; + + if(DeltaX * DeltaX + DeltaY * DeltaY <= RadiusSquare) + { + if(SectorCompletelyClear) + { + if(controller::Handler(x, y)) + return true; + else + SectorCompletelyClear = false; + } + else + return mapmath::DoLine(StartX, StartY, + x, y, + SKIP_FIRST + |ALLOW_END_FAILURE); + } + } + } + + return false; +} + +cint ChangeXArray[4][3] = { { -1, 0, -1 }, + { 0, 1, 1 }, + { -1, -1, 0 }, + { 1, 0, 1 } }; +cint ChangeYArray[4][3] = { { -1, -1, 0 }, + { -1, -1, 0 }, + { 0, 1, 1 }, + { 0, 1, 1 } }; + +template +inline void mapmath::DoArea() +{ + int Buffer[2][2048]; + int* OldStack = Buffer[0]; + int* NewStack = Buffer[1]; + + for(int c1 = 0; c1 < 4; ++c1) + { + cint* ChangeX = ChangeXArray[c1], * ChangeY = ChangeYArray[c1]; + int OldStackPos = 0, NewStackPos = 0; + int StartX = controller::GetStartX(c1); + int StartY = controller::GetStartY(c1); + + for(int c2 = 0; c2 < 3; ++c2) + { + OldStack[OldStackPos] = StartX + ChangeX[c2]; + OldStack[OldStackPos + 1] = StartY + ChangeY[c2]; + OldStackPos += 2; + } + + while(OldStackPos) + { + while(OldStackPos) + { + OldStackPos -= 2; + cint X = OldStack[OldStackPos], Y = OldStack[OldStackPos + 1]; + + if(controller::Handler(X, Y)) + for(int c2 = 0; c2 < 3; ++c2) + { + NewStack[NewStackPos] = X + ChangeX[c2]; + NewStack[NewStackPos + 1] = Y + ChangeY[c2]; + NewStackPos += 2; + } + } + + OldStackPos = NewStackPos; + NewStackPos = 0; + int* T = OldStack; + OldStack = NewStack; + NewStack = T; + } + } +} + +template +inline void mapmath::DoQuadriArea(int OrigoX, int OrigoY, + int RadiusSquare, + int XSize, int YSize) +{ + basequadricontroller::OrigoX = OrigoX; + basequadricontroller::OrigoY = OrigoY; + basequadricontroller::RadiusSquare = RadiusSquare; + basequadricontroller::XSize = XSize; + basequadricontroller::YSize = YSize; + + for(int c = 0; c < 4; ++c) + controller::Handler((OrigoX << 1) + basequadricontroller::OrigoDeltaX[c], + (OrigoY << 1) + basequadricontroller::OrigoDeltaY[c]); + + mapmath >::DoArea(); +} + +/* Chance for n < Max to be returned is (1-CC)*CC^n, + for n == Max chance is CC^n. */ + +inline int femath::LoopRoll(int ContinueChance, int Max) +{ + int R; + for(R = 0; RAND_N(100) < ContinueChance && R < Max; ++R); + return R; +} + +template +type*& ListFind(type*& Start, predicate Predicate) +{ + type** E; + for(E = &Start; *E && !Predicate(*E); E = &(*E)->Next); + return *E; +} + +template +struct pointercomparer +{ + pointercomparer(const type* Element) : Element(Element) { } + truth operator()(const type* E) const { return E == Element; } + const type* Element; +}; + +#endif diff --git a/FeLib/Include/festring.h b/FeLib/Include/festring.h new file mode 100644 index 0000000..7c497a2 --- /dev/null +++ b/FeLib/Include/festring.h @@ -0,0 +1,344 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __FESTRING_H__ +#define __FESTRING_H__ + +#include + +#include "felibdef.h" + +#define FESTRING_PAGE 0x7F + +class festring +{ + public: + typedef ulong sizetype; + typedef const ulong csizetype; + /* It can be proven that the code works even if OwnsData is left + uninitialized. However, Valgrind reports this as a possible error + which is annoying */ + festring() : Data(0), Size(0), OwnsData(false) { } + explicit festring(sizetype); + festring(sizetype, char); + festring(cchar* CStr) + : Data(const_cast(CStr)), Size(strlen(CStr)), OwnsData(false) { } + festring(cchar* CStr, sizetype N) + : Data(const_cast(CStr)), Size(N), OwnsData(false) { } + festring(cfestring&); + ~festring(); + festring& Capitalize(); + festring CapitalizeCopy() const { return festring(*this).Capitalize(); } + festring& operator=(cchar*); + festring& operator=(cfestring&); + festring& operator<<(char); + festring& operator<<(cchar*); + festring& operator<<(cfestring&); + festring& operator<<(short Int) { return Append(Int); } + festring& operator<<(ushort Int) { return Append(Int); } + festring& operator<<(int Int) { return Append(Int); } + festring& operator<<(uint Int) { return Append(Int); } + festring& operator<<(long Int) { return Append(Int); } + festring& operator<<(ulong Int) { return Append(Int); } + bool operator<(cfestring&) const; + truth operator==(cfestring&) const; + truth operator!=(cfestring&) const; + truth operator==(cchar*) const; + truth operator!=(cchar*) const; + int Compare(cfestring&) const; + cchar* CStr() const; + sizetype GetSize() const { return Size; } + void Empty(); + void Assign(sizetype, char); + void Resize(sizetype, char = ' '); + sizetype Find(char, sizetype = 0) const; + sizetype Find(cchar* CStr, sizetype Pos = 0) const + { return Find(CStr, Pos, strlen(CStr)); } + sizetype Find(cchar*, sizetype, sizetype) const; + sizetype Find(cfestring& S, sizetype Pos = 0) const + { return Find(S.Data, Pos, S.Size); } + sizetype FindLast(char, sizetype = NPos) const; + sizetype FindLast(const char* CStr, sizetype Pos = NPos) const + { return FindLast(CStr, Pos, strlen(CStr)); } + sizetype FindLast(const char*, sizetype, sizetype) const; + sizetype FindLast(const festring& S, sizetype Pos = NPos) const + { return FindLast(S.Data, Pos, S.Size); } + void Erase(sizetype, sizetype); + void Insert(sizetype Pos, cchar* CStr) + { Insert(Pos, CStr, strlen(CStr)); } + void Insert(sizetype, cchar*, sizetype); + void Insert(sizetype Pos, cfestring& S) + { Insert(Pos, S.Data, S.Size); } + festring& Append(cfestring& Str, sizetype N) + { return Append(Str.Data, N); } + static csizetype NPos; + static void SplitString(festring&, festring&, sizetype); + static int SplitString(cfestring&, std::vector&, + sizetype, sizetype = 0); + static sizetype IgnoreCaseFind(cfestring&, + cfestring&, sizetype = 0); + static void SearchAndReplace(festring&, cfestring&, + cfestring&, sizetype = 0); + static bool IgnoreCaseCompare(cfestring&, cfestring&); + truth IsEmpty() const { return !Size; } + /* HORRIBLE ERROR!!!! */ + char& operator[](sizetype Index) const { return Data[Index]; } + void PreProcessForFebot(); + void PostProcessForFebot(); + void SwapData(festring&); + void ExtractWord(festring&); + long GetCheckSum() const; + void EnsureOwnsData(); + private: + static void InstallIntegerMap(); + static void DeInstallIntegerMap(); + void CreateOwnData(cchar*, sizetype); + festring& Append(long); + festring& Append(cchar*, sizetype); + void SlowAppend(char); + void SlowAppend(cchar*, sizetype); + static char** IntegerMap; + static char* EmptyString; + char* Data; + sizetype Size; + sizetype OwnsData : 1; + sizetype Reserved : 31; +}; + +class festringpile +{ + public: + festringpile(cfestring& String) : String(String) { } + template + festringpile& operator+(type What) { String << What; return *this; } + festringpile& operator+(cfestring& What) + { String << What; return *this; } + festringpile& operator+(const festringpile& What) + { String << What.String; return *this; } + operator festring() const { return String; } + private: + festring String; +}; + +template +inline festringpile operator+(cfestring& S, type What) +{ return festringpile(S) + What; } +inline festringpile operator+(cfestring& S, cfestring& What) +{ return festringpile(S) + What; } +inline festringpile operator+(cfestring& S, const festringpile& What) +{ return festringpile(S) + What; } + +inline festring::festring(cfestring& Str) +: Data(Str.Data), Size(Str.Size), + OwnsData(Str.OwnsData), Reserved(Str.Reserved) +{ + if(Data && OwnsData) + ++REFS(Data); +} + +inline festring::festring(sizetype N) +: Size(N), OwnsData(true), Reserved(N|FESTRING_PAGE) +{ + char* Ptr = 4 + new char[Reserved + 5]; + REFS(Ptr) = 0; + Data = Ptr; +} + +inline festring::festring(sizetype N, char C) +: Size(N), OwnsData(true), Reserved(N|FESTRING_PAGE) +{ + char* Ptr = 4 + new char[Reserved + 5]; + REFS(Ptr) = 0; + Data = Ptr; + memset(Ptr, C, N); +} + +inline festring::~festring() +{ + if(OwnsData) + { + char* Ptr = Data; + + if(Ptr && !REFS(Ptr)--) + delete [] &REFS(Ptr); + } +} + +inline bool festring::operator<(cfestring& Str) const +{ + sizetype ThisSize = Size; + sizetype StrSize = Str.Size; + + if(ThisSize && StrSize) + { + int Comp = memcmp(Data, Str.Data, + StrSize > ThisSize ? ThisSize : StrSize); + return Comp < 0 || (!Comp && StrSize > ThisSize); + } + else + return !ThisSize && StrSize; +} + +inline truth festring::operator==(cfestring& Str) const +{ + sizetype StrSize = Str.Size; + return Size == StrSize && (!StrSize || !memcmp(Data, Str.Data, StrSize)); +} + +inline truth festring::operator!=(cfestring& Str) const +{ + sizetype StrSize = Str.Size; + return Size != StrSize || (StrSize && memcmp(Data, Str.Data, StrSize)); +} + +inline truth festring::operator==(cchar* CStr) const +{ + sizetype StrSize = strlen(CStr); + return Size == StrSize && (!StrSize || !memcmp(Data, CStr, StrSize)); +} + +inline truth festring::operator!=(cchar* CStr) const +{ + sizetype StrSize = strlen(CStr); + return Size != StrSize || (StrSize && memcmp(Data, CStr, StrSize)); +} + +/* Returns -1 if this is before Str in alphabetical order, zero + if strings are identical, else 1 */ + +inline int festring::Compare(cfestring& Str) const +{ + sizetype ThisSize = Size; + sizetype StrSize = Str.Size; + + if(ThisSize && StrSize) + { + int Comp = memcmp(Data, Str.Data, + StrSize > ThisSize ? ThisSize : StrSize); + + if(Comp) + return Comp; + } + + return ThisSize < StrSize ? -1 : ThisSize != StrSize; +} + +inline cchar* festring::CStr() const +{ + char* Ptr = Data; + + if(Ptr) + { + if(OwnsData) + Ptr[Size] = 0; + + return Ptr; + } + else + return EmptyString; +} + +inline void festring::Empty() +{ + Size = 0; + + if(OwnsData) + { + char* Ptr = Data; + + if(Ptr && REFS(Ptr)) + { + --REFS(Ptr); + Data = 0; + } + } + else + Data = 0; +} + +inline festring& festring::operator<<(char Char) +{ + char* OldPtr = Data; + sizetype OldSize = Size; + + if(OwnsData && OldPtr && !REFS(OldPtr) && OldSize < Reserved) + { + OldPtr[OldSize] = Char; + ++Size; + } + else + SlowAppend(Char); + + return *this; +} + +inline festring& festring::operator<<(cchar* CStr) +{ + sizetype N = strlen(CStr); + sizetype OldSize = Size; + sizetype NewSize = OldSize + N; + char* OldPtr = Data; + + if(OwnsData && OldPtr && !REFS(OldPtr) && NewSize <= Reserved) + { + memcpy(OldPtr + OldSize, CStr, N); + Size = NewSize; + } + else + SlowAppend(CStr, N); + + return *this; +} + +inline festring& festring::operator<<(cfestring& Str) +{ + sizetype N = Str.Size; + sizetype OldSize = Size; + sizetype NewSize = OldSize + N; + char* OldPtr = Data; + char* OtherPtr = Str.Data; + + if(OwnsData && OldPtr && !REFS(OldPtr) && NewSize <= Reserved) + { + memcpy(OldPtr + OldSize, OtherPtr, N); + Size = NewSize; + } + else + SlowAppend(OtherPtr, N); + + return *this; +} + +struct charcomparer +{ + bool operator()(cchar* const& S1, cchar* const& S2) const + { return strcmp(S1, S2) < 0; } +}; + +struct ignorecaseorderer +{ + bool operator()(cfestring& S1, cfestring& S2) const + { return festring::IgnoreCaseCompare(S1, S2); } +}; + +#define CONST_S(str) festring(str, sizeof(str) - 1) + +/* + * This macro doesn't evaluate with if what + * is not found so it's often faster + */ + +#define SEARCH_N_REPLACE(where, what, with)\ +if(where.Find(what) != festring::NPos)\ + festring::SearchAndReplace(where, what, with); + +#endif diff --git a/FeLib/Include/fetime.h b/FeLib/Include/fetime.h new file mode 100644 index 0000000..767de04 --- /dev/null +++ b/FeLib/Include/fetime.h @@ -0,0 +1,30 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __FETIME_H__ +#define __FETIME_H__ + +#include + +#include "festring.h" + +class time +{ + public: + static time_t GetZeroTime(); + static time_t TimeAdd(time_t,time_t); + static time_t TimeDifference(time_t,time_t); + static festring VerbalizeAsTimeSpent(time_t); + static festring VerbalizeAsCalenderTime(time_t); +}; + +#endif diff --git a/FeLib/Include/graphics.h b/FeLib/Include/graphics.h new file mode 100644 index 0000000..4f9f199 --- /dev/null +++ b/FeLib/Include/graphics.h @@ -0,0 +1,116 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __GRAPHICS_H__ +#define __GRAPHICS_H__ + +#ifdef USE_SDL +#include "SDL.h" +#endif + +#include "v2.h" + +#define DOUBLE_BUFFER graphics::GetDoubleBuffer() +#define RES graphics::GetRes() +#define FONT graphics::GetDefaultFont() + +class bitmap; +class rawbitmap; +class festring; + +class graphics +{ + public: + friend class bitmap; + static void Init(); + static void DeInit(); +#ifdef USE_SDL + static void SwitchMode(); +#endif +#ifdef __DJGPP__ + static void SwitchMode() { } +#endif + static void SetMode(cchar*, cchar*, v2, truth); + static void BlitDBToScreen(); + static v2 GetRes() { return Res; } + static bitmap* GetDoubleBuffer() { return DoubleBuffer; } + static void LoadDefaultFont(cfestring&); + static rawbitmap* GetDefaultFont() { return DefaultFont; } + static void SetSwitchModeHandler(void (*What)()) + { SwitchModeHandler = What; } + private: + static void (*SwitchModeHandler)(); +#ifdef USE_SDL + static SDL_Surface* Screen; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + static SDL_Surface* TempSurface; +#endif +#endif +#ifdef __DJGPP__ + static ulong BufferSize; + static ushort ScreenSelector; + static struct vesainfo + { + void Retrieve(); + ulong Signature NO_ALIGNMENT; + ushort Version NO_ALIGNMENT; + ulong OEMString NO_ALIGNMENT; + ulong Capabilities NO_ALIGNMENT; + ulong ModeList NO_ALIGNMENT; + ushort Memory NO_ALIGNMENT; + uchar Shit[493] NO_ALIGNMENT; + } VesaInfo; + static struct modeinfo + { + void Retrieve(ushort); + ushort Attribs1 NO_ALIGNMENT; + uchar AWindowAttribs NO_ALIGNMENT; + uchar BWindowAttribs NO_ALIGNMENT; + ushort Granularity NO_ALIGNMENT; + ushort WindowSize NO_ALIGNMENT; + ushort WindowASegment NO_ALIGNMENT; + ushort WindowBSegment NO_ALIGNMENT; + ulong WindowMoveFunction NO_ALIGNMENT; + ushort BytesPerLine NO_ALIGNMENT; + ushort Width NO_ALIGNMENT; + ushort Height NO_ALIGNMENT; + uchar CharWidth NO_ALIGNMENT; + uchar CharHeight NO_ALIGNMENT; + uchar Planes NO_ALIGNMENT; + uchar BitsPerPixel NO_ALIGNMENT; + uchar Banks NO_ALIGNMENT; + uchar MemoryModel NO_ALIGNMENT; + uchar BankSize NO_ALIGNMENT; + uchar ImagePages NO_ALIGNMENT; + uchar Reserved1 NO_ALIGNMENT; + uchar RedBits NO_ALIGNMENT; + uchar RedShift NO_ALIGNMENT; + uchar GreenBits NO_ALIGNMENT; + uchar GreenShift NO_ALIGNMENT; + uchar BlueBits NO_ALIGNMENT; + uchar BlueShift NO_ALIGNMENT; + uchar ResBits NO_ALIGNMENT; + uchar ResShift NO_ALIGNMENT; + uchar Attribs2 NO_ALIGNMENT; + ulong PhysicalLFBAddress NO_ALIGNMENT; + ulong OffScreenMem NO_ALIGNMENT; + ushort OffScreenMemSize NO_ALIGNMENT; + uchar Reserved2[206] NO_ALIGNMENT; + } ModeInfo; +#endif + static bitmap* DoubleBuffer; + static v2 Res; + static int ColorDepth; + static rawbitmap* DefaultFont; +}; + +#endif diff --git a/FeLib/Include/hscore.h b/FeLib/Include/hscore.h new file mode 100644 index 0000000..1d48a18 --- /dev/null +++ b/FeLib/Include/hscore.h @@ -0,0 +1,59 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __HSCORE_H__ +#define __HSCORE_H__ + +#include +#include + +#include "festring.h" + +#ifdef LINUX +#define HIGH_SCORE_FILENAME LOCAL_STATE_DIR "/ivan-highscore.scores" +#endif + +#if defined(WIN32) || defined(__DJGPP__) +#define HIGH_SCORE_FILENAME CONST_S("HScore.dat") +#endif + +class festring; + +class highscore +{ + public: + highscore(cfestring& = HIGH_SCORE_FILENAME); + truth Add(long, cfestring&); + void Draw() const; + void Save(cfestring& = HIGH_SCORE_FILENAME) const; + void Load(cfestring& = HIGH_SCORE_FILENAME); + truth LastAddFailed() const; + void AddToFile(highscore*) const; + truth MergeToFile(highscore*) const; + int Find(long, cfestring&, time_t, long); + cfestring& GetEntry(int) const; + long GetScore(int) const; + long GetSize() const; + ushort GetVersion() const { return Version; } + void Clear(); + truth CheckVersion() const; + private: + truth Add(long, cfestring&, time_t, long); + std::vector Entry; + std::vector Score; + std::vector Time; + std::vector RandomID; + int LastAdd; + ushort Version; +}; + +#endif diff --git a/FeLib/Include/pragmas.h b/FeLib/Include/pragmas.h new file mode 100644 index 0000000..6c604ce --- /dev/null +++ b/FeLib/Include/pragmas.h @@ -0,0 +1,27 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __PRAGMAS_H__ +#define __PRAGMAS_H__ + +#include +#include + +#ifdef VC +#pragma warning(disable : 4786 4146) +//#pragma optimize("g", on) +#pragma intrinsic(abs, fabs, labs, memcmp, memcpy, memset) +#pragma intrinsic(strcat, strcmp, strcpy, strlen, pow) +#pragma intrinsic(exp, log, sin, cos, tan, atan, sqrt) +#endif + +#endif diff --git a/FeLib/Include/rawbit.h b/FeLib/Include/rawbit.h new file mode 100644 index 0000000..fe637d7 --- /dev/null +++ b/FeLib/Include/rawbit.h @@ -0,0 +1,78 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __COLORBIT_H__ +#define __COLORBIT_H__ + +#include +#include + +#include "v2.h" + +class outputfile; +class inputfile; +class bitmap; +class cachedfont; +class festring; + +typedef std::map > fontcache; + +class rawbitmap +{ + public: + rawbitmap(cfestring&); + rawbitmap(v2); + ~rawbitmap(); + void Save(cfestring&); + void MaskedBlit(bitmap*, v2, v2, + v2, packcol16*) const; + void MaskedBlit(bitmap*, packcol16*) const; + + void LIKE_PRINTF(5, 6) Printf(bitmap*, v2, packcol16, + cchar*, ...) const; + void LIKE_PRINTF(5, 6) PrintfUnshaded(bitmap*, v2, packcol16, + cchar*, ...) const; + cachedfont* Colorize(cpackcol16*, alpha = 255, + cpackalpha* = 0) const; + bitmap* Colorize(v2, v2, v2, + cpackcol16*, alpha = 255, + cpackalpha* = 0, + cuchar* = 0, truth = true) const; + v2 GetSize() const { return Size; } + + void AlterGradient(v2, v2, int, int, truth); + void SwapColors(v2, v2, int, int); + void Roll(v2, v2, v2, paletteindex*); + + void CreateFontCache(packcol16); + static truth IsMaterialColor(int Color) { return Color >= 192; } + static int GetMaterialColorIndex(int Color) { return Color - 192 >> 4; } + int GetMaterialColorIndex(int X, int Y) const + { return PaletteBuffer[Y][X] - 192 >> 4; } + truth IsTransparent(v2) const; + truth IsMaterialColor1(v2) const; + v2 RandomizeSparklePos(cv2*, v2*, v2, v2, int, int) const; + void CopyPaletteFrom(rawbitmap*); + void PutPixel(v2 Pos, paletteindex Color) + { PaletteBuffer[Pos.Y][Pos.X] = Color; } + paletteindex GetPixel(v2 Pos) const + { return PaletteBuffer[Pos.Y][Pos.X]; } + void Clear(); + void NormalBlit(rawbitmap*, v2, v2, v2, int = 0) const; + protected: + v2 Size; + uchar* Palette; + paletteindex** PaletteBuffer; + fontcache FontCache; +}; + +#endif diff --git a/FeLib/Include/rect.h b/FeLib/Include/rect.h new file mode 100644 index 0000000..78adbb5 --- /dev/null +++ b/FeLib/Include/rect.h @@ -0,0 +1,28 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __RECT_H__ +#define __RECT_H__ + +#include "v2.h" + +struct rect +{ + rect() { } + rect(int X1, int Y1, int X2, int Y2) : X1(X1), Y1(Y1), X2(X2), Y2(Y2) { } + rect(v2 V1, v2 V2) : X1(V1.X), Y1(V1.Y), X2(V2.X), Y2(V2.Y) { } + rect operator+(v2 V) const + { return rect(X1 + V.X, Y1 + V.Y, X2 + V.X, Y2 + V.Y); } + int X1, Y1, X2, Y2; +}; + +#endif diff --git a/FeLib/Include/save.h b/FeLib/Include/save.h new file mode 100644 index 0000000..98873ae --- /dev/null +++ b/FeLib/Include/save.h @@ -0,0 +1,462 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __SAVE_H__ +#define __SAVE_H__ + +#include +#include +#include +#include +#include +#include +#include + +#include "error.h" +#include "festring.h" +#include "fearray.h" + +#define RAW_SAVE_LOAD(type)\ +inline outputfile& operator<<(outputfile& SaveFile, type Value)\ +{\ + SaveFile.Write(reinterpret_cast(&Value), sizeof(Value));\ + return SaveFile;\ +}\ +\ +inline inputfile& operator>>(inputfile& SaveFile, type& Value)\ +{\ + SaveFile.Read(reinterpret_cast(&Value), sizeof(Value));\ + return SaveFile;\ +} + +typedef std::map valuemap; + +/* fstream seems to bug with DJGPP, so we use FILE* here */ + +class outputfile +{ + public: + outputfile(cfestring&, truth = true); + ~outputfile(); + void Put(char What) { fputc(What, Buffer); } + void Write(cchar* Offset, long Size) + { fwrite(Offset, 1, Size, Buffer); } + truth IsOpen() { return truth(Buffer); } + void Close() { fclose(Buffer); } + void Flush() { fflush(Buffer); } + void ReOpen(); + private: + FILE* Buffer; + festring FileName; +}; + +class inputfile +{ + public: + inputfile(cfestring&, const valuemap* = 0, truth = true); + ~inputfile(); + festring ReadWord(truth = true); + void ReadWord(festring&, truth = true); + char ReadLetter(truth = true); + long ReadNumber(int = 0xFF, truth = false); + v2 ReadVector2d(); + rect ReadRect(); + int Get() { return fgetc(Buffer); } + void Read(char* Offset, long Size) { fread(Offset, 1, Size, Buffer); } + truth IsOpen() { return truth(Buffer); } + truth Eof() { return feof(Buffer); } + void ClearFlags() { clearerr(Buffer); } + void SeekPosBegin(long Offset) { fseek(Buffer, Offset, SEEK_SET); } + void SeekPosCurrent(long Offset) { fseek(Buffer, Offset, SEEK_CUR); } + void SeekPosEnd(long Offset) { fseek(Buffer, Offset, SEEK_END); } + long TellPos() { return ftell(Buffer); } + ulong TellLine() { return TellLineOfPos(TellPos()); } + ulong TellLineOfPos(long); + cfestring& GetFileName() const { return FileName; } + void Close() { fclose(Buffer); } + private: + int HandlePunct(festring&, int, int); + FILE* Buffer; + festring FileName; + const valuemap* ValueMap; +}; + +/* Reads a binary form variable of type type and returns it. + * An inputfile template member function would be far more elegant, + * but VC doesn't seem to understand it. */ + +template inline type ReadType(inputfile& SaveFile) +{ + type Variable; + SaveFile >> Variable; + return Variable; +} + +inline void ReadData(char& Type, inputfile& SaveFile) +{ Type = SaveFile.ReadNumber(); } +inline void ReadData(uchar& Type, inputfile& SaveFile) +{ Type = SaveFile.ReadNumber(); } +inline void ReadData(short& Type, inputfile& SaveFile) +{ Type = SaveFile.ReadNumber(); } +inline void ReadData(ushort& Type, inputfile& SaveFile) +{ Type = SaveFile.ReadNumber(); } +inline void ReadData(long& Type, inputfile& SaveFile) +{ Type = SaveFile.ReadNumber(); } +inline void ReadData(ulong& Type, inputfile& SaveFile) +{ Type = SaveFile.ReadNumber(); } +inline void ReadData(int& Type, inputfile& SaveFile) +{ Type = SaveFile.ReadNumber(); } +inline void ReadData(packv2& Type, inputfile& SaveFile) +{ Type = SaveFile.ReadVector2d(); } +inline void ReadData(v2& Type, inputfile& SaveFile) +{ Type = SaveFile.ReadVector2d(); } +inline void ReadData(rect& Type, inputfile& SaveFile) +{ Type = SaveFile.ReadRect(); } +void ReadData(festring&, inputfile&); +void ReadData(fearray&, inputfile&); +void ReadData(fearray&, inputfile&); + +template +inline void ReadData(fearray& Array, inputfile& SaveFile) +{ + Array.Clear(); + festring Word; + SaveFile.ReadWord(Word); + + if(Word == "=") + SaveFile.ReadWord(Word); + + if(Word == "=") + { + Array.Allocate(1); + ReadData(Array.Data[0], SaveFile); + return; + } + + if(Word != "{") + ABORT("Array syntax error \"%s\" found in file %s, line %ld!", + Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TellLine()); + + typedef typename fearray::sizetype sizetype; + sizetype Size = SaveFile.ReadNumber(); + Array.Allocate(Size); + + for(sizetype c = 0; c < Size; ++c) + ReadData(Array.Data[c], SaveFile); + + if(SaveFile.ReadWord() != "}") + ABORT("Illegal array terminator \"%s\" encountered in file %s, line %ld!", + Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TellLine()); +} + +inline outputfile& operator<<(outputfile& SaveFile, char Value) +{ + SaveFile.Put(Value); + return SaveFile; +} + +inline inputfile& operator>>(inputfile& SaveFile, char& Value) +{ + Value = SaveFile.Get(); + return SaveFile; +} + +inline outputfile& operator<<(outputfile& SaveFile, uchar Value) +{ + SaveFile.Put(Value); + return SaveFile; +} + +inline inputfile& operator>>(inputfile& SaveFile, uchar& Value) +{ + Value = SaveFile.Get(); + return SaveFile; +} + +inline outputfile& operator<<(outputfile& SaveFile, short Value) +{ + SaveFile.Put(Value); + SaveFile.Put(Value >> 8); + return SaveFile; +} + +inline inputfile& operator>>(inputfile& SaveFile, short& Value) +{ + int LowWord = SaveFile.Get(); + Value = SaveFile.Get() << 8 | LowWord; + return SaveFile; +} + +inline outputfile& operator<<(outputfile& SaveFile, ushort Value) +{ + SaveFile.Put(Value); + SaveFile.Put(Value >> 8); + return SaveFile; +} + +inline inputfile& operator>>(inputfile& SaveFile, ushort& Value) +{ + int LowWord = SaveFile.Get(); + Value = SaveFile.Get() << 8 | LowWord; + return SaveFile; +} + +RAW_SAVE_LOAD(long) +RAW_SAVE_LOAD(ulong) +RAW_SAVE_LOAD(int) +RAW_SAVE_LOAD(uint) +RAW_SAVE_LOAD(double) +RAW_SAVE_LOAD(packv2) +RAW_SAVE_LOAD(v2) +RAW_SAVE_LOAD(rect) + +outputfile& operator<<(outputfile&, cfestring&); +inputfile& operator>>(inputfile&, festring&); +outputfile& operator<<(outputfile&, cchar*); +inputfile& operator>>(inputfile&, char*&); + +template +inline outputfile& operator<<(outputfile& SaveFile, + const std::pair& Pair) +{ + SaveFile << Pair.first << Pair.second; + return SaveFile; +} + +template +inline inputfile& operator>>(inputfile& SaveFile, + std::pair& Pair) +{ + SaveFile >> Pair.first >> Pair.second; + return SaveFile; +} + +template +inline outputfile& operator<<(outputfile& SaveFile, + const std::vector& Vector) +{ + SaveFile << ulong(Vector.size()); + + for(ulong c = 0; c < Vector.size(); ++c) + SaveFile << Vector[c]; + + return SaveFile; +} + +template +inline inputfile& operator>>(inputfile& SaveFile, + std::vector& Vector) +{ + Vector.resize(ReadType(SaveFile), type()); + + for(ulong c = 0; c < Vector.size(); ++c) + SaveFile >> Vector[c]; + + return SaveFile; +} + +template +inline outputfile& operator<<(outputfile& SaveFile, + const std::deque& Deque) +{ + SaveFile << ulong(Deque.size()); + + for(ulong c = 0; c < Deque.size(); ++c) + SaveFile << Deque[c]; + + return SaveFile; +} + +template +inline inputfile& operator>>(inputfile& SaveFile, + std::deque& Deque) +{ + Deque.resize(ReadType(SaveFile), type()); + + for(ulong c = 0; c < Deque.size(); ++c) + SaveFile >> Deque[c]; + + return SaveFile; +} + +template +inline outputfile& operator<<(outputfile& SaveFile, + const std::list& List) +{ + SaveFile << ulong(List.size()); + + for(typename std::list::const_iterator i = List.begin(); + i != List.end(); ++i) + SaveFile << *i; + + return SaveFile; +} + +template +inline inputfile& operator>>(inputfile& SaveFile, + std::list& List) +{ + List.resize(ReadType(SaveFile), type()); + + for(typename std::list::iterator i = List.begin(); + i != List.end(); ++i) + SaveFile >> *i; + + return SaveFile; +} + +template +inline outputfile& operator<<(outputfile& SaveFile, + const std::map& Map) +{ + SaveFile << ulong(Map.size()); + + for(typename std::map::const_iterator i = Map.begin(); + i != Map.end(); ++i) + SaveFile << i->first << i->second; + + return SaveFile; +} + +template +inline inputfile& operator>>(inputfile& SaveFile, + std::map& Map) +{ + Map.clear(); + type1 First; + ulong Size; + SaveFile >> Size; + typename std::map::iterator i; + + for(ulong c = 0; c < Size; ++c) + { + SaveFile >> First; + i = Map.insert(Map.end(), std::make_pair(First, type2())); + SaveFile >> i->second; + } + + return SaveFile; +} + +template +inline outputfile& operator<<(outputfile& SaveFile, + const std::set& Set) +{ + SaveFile << ulong(Set.size()); + + for(typename std::set::const_iterator i = Set.begin(); + i != Set.end(); ++i) + SaveFile << *i; + + return SaveFile; +} + +template +inline inputfile& operator>>(inputfile& SaveFile, + std::set& Set) +{ + Set.clear(); + ulong Size; + SaveFile >> Size; + + for(ulong c = 0; c < Size; ++c) + { + type Value; + SaveFile >> Value; + Set.insert(Value); + } + + return SaveFile; +} + +template +inline outputfile& operator<<(outputfile& SaveFile, + const fearray& Array) +{ + typename fearray::sizetype c, Size = Array.Size; + SaveFile << Size; + + for(c = 0; c < Size; ++c) + SaveFile << Array[c]; + + return SaveFile; +} + +template +inline inputfile& operator>>(inputfile& SaveFile, + fearray& Array) +{ + typename fearray::sizetype c, Size; + SaveFile >> Size; + Array.Allocate(Size); + + for(c = 0; c < Size; ++c) + SaveFile >> Array[c]; + + return SaveFile; +} + +template +inline outputfile& SaveLinkedList(outputfile& SaveFile, + const type* Element) +{ + for(const type* E = Element; E; E = E->Next) + { + SaveFile.Put(true); + SaveFile << E; + } + + SaveFile.Put(false); + return SaveFile; +} + +template +inline inputfile& LoadLinkedList(inputfile& SaveFile, + type*& Element) +{ + if(SaveFile.Get()) + { + SaveFile >> Element; + type* E; + + for(E = Element; SaveFile.Get(); E = E->Next) + SaveFile >> E->Next; + + E->Next = 0; + } + else + Element = 0; + + return SaveFile; +} + +template +inline outputfile& SaveArray(outputfile& SaveFile, + const type* Array, int Count) +{ + for(int c = 0; c < Count; ++c) + SaveFile << Array[c]; + + return SaveFile; +} + +template +inline inputfile& LoadArray(inputfile& SaveFile, + type* Array, int Count) +{ + for(int c = 0; c < Count; ++c) + SaveFile >> Array[c]; + + return SaveFile; +} + +#endif diff --git a/FeLib/Include/sdl.h b/FeLib/Include/sdl.h new file mode 100644 index 0000000..ea99dc4 --- /dev/null +++ b/FeLib/Include/sdl.h @@ -0,0 +1 @@ +#include diff --git a/FeLib/Include/typedef.h b/FeLib/Include/typedef.h new file mode 100644 index 0000000..ac67206 --- /dev/null +++ b/FeLib/Include/typedef.h @@ -0,0 +1,58 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __TYPEDEF_H__ +#define __TYPEDEF_H__ + +#include "pragmas.h" + +class bitmap; +class festring; +struct blitdata; +struct v2; + +typedef int truth; +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef int col16; +typedef ushort packcol16; +typedef ulong col24; +typedef int alpha; +typedef uchar packalpha; +typedef int priority; +typedef uchar packpriority; +typedef uchar paletteindex; + +typedef const char cchar; +typedef const int cint; +typedef const int ctruth; +typedef const unsigned char cuchar; +typedef const unsigned short cushort; +typedef const unsigned int cuint; +typedef const unsigned long culong; +typedef const int ccol16; +typedef const ushort cpackcol16; +typedef const ulong ccol24; +typedef const int calpha; +typedef const uchar cpackalpha; +typedef const int cpriority; +typedef const uchar cpackpriority; +typedef const uchar cpaletteindex; + +typedef const bitmap cbitmap; +typedef const blitdata cblitdata; +typedef const festring cfestring; +typedef const v2 cv2; + +#endif diff --git a/FeLib/Include/v2.h b/FeLib/Include/v2.h new file mode 100644 index 0000000..ba4acd0 --- /dev/null +++ b/FeLib/Include/v2.h @@ -0,0 +1,116 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __V2_H__ +#define __V2_H__ + +#include + +#include "felibdef.h" + +struct v2; + +struct packv2 +{ + operator v2() const; + short X, Y; +}; + +struct podv2 +{ + operator v2() const; + int X, Y; +}; + +/* Standard structure for representing positions */ + +struct v2 +{ + v2() { } + v2(int X, int Y) : X(X), Y(Y) { } + v2 operator+(v2 V) const { return v2(X + V.X, Y + V.Y); } + v2& operator+=(v2 V) { X += V.X; Y += V.Y; return *this; } + v2 operator-(v2 V) const { return v2(X - V.X, Y - V.Y); } + v2& operator-=(v2 V) { X -= V.X; Y -= V.Y; return *this; } + v2 operator-()const { return v2(-X, -Y); } + v2 operator*(int I) const { return v2(X * I, Y * I); } + v2& operator*=(int I) { X *= I; Y *= I; return *this; } + v2 operator/(int I) const { return v2(X / I, Y / I); } + v2& operator/=(int I) { X /= I; Y /= I; return *this; } + v2 operator*(double D) const + { return v2(int(X * D), int(Y * D)); } + v2& operator*=(double D) + { X = int(X * D); Y = int(Y * D); return *this; } + v2 operator/(double D) const + { return v2(int(X / D), int(Y / D)); } + v2& operator/=(double D) + { X = int(X / D); Y = int(Y / D); return *this; } + truth operator==(v2 V) const { return X == V.X && Y == V.Y; } + truth operator!=(v2 V) const { return X != V.X || Y != V.Y; } + v2 operator<<(int S) const { return v2(X << S, Y << S); } + v2& operator<<=(int S) { X <<= S; Y <<= S; return *this; } + v2 operator>>(int S) const { return v2(X >> S, Y >> S); } + v2& operator>>=(int S) { X >>= S; Y >>= S; return *this; } + bool operator<(v2 V) const + { return X < V.X || (X == V.X && Y < V.Y); } + int GetLengthSquare() const { return X * X + Y * Y; } + /* Also returns true if V == *this */ + truth IsAdjacent(v2 V) const + { return V.X >= X - 1 && V.X <= X + 1 && V.Y <= Y + 1 && V.Y >= Y - 1; } + int GetManhattanLength() const { return Max(abs(X), abs(Y)); } + truth Is0() const { return X == 0 && Y == 0; } + operator packv2() const + { + packv2 V = { X, Y }; + return V; + } + operator podv2() const + { + podv2 V = { X, Y }; + return V; + } + + // v2 Randomize() const; Would be a good idea. + int X, Y; +}; + +inline packv2::operator v2() const { return v2(X, Y); } +inline podv2::operator v2() const { return v2(X, Y); } + +/* + * Rotates a position Vect of a square map of size + * Size x Size according to Flags (see felibdef.h) + */ + +inline void Rotate(v2& Vect, int Size, int Flags) +{ + cint Limit = Size - 1; + + if(Flags & ROTATE) + { + cint T = Vect.X; + Vect.X = Limit - Vect.Y; + Vect.Y = T; + } + + if(Flags & MIRROR) + Vect.X = Limit - Vect.X; + + if(Flags & FLIP) + Vect.Y = Limit - Vect.Y; +} + +cv2 ZERO_V2(0, 0); +cv2 ERROR_V2(-0x8000, -0x8000); +cv2 ABORT_V2(-0x7FFF, -0x7FFF); + +#endif diff --git a/FeLib/Include/whandler.h b/FeLib/Include/whandler.h new file mode 100644 index 0000000..8567dee --- /dev/null +++ b/FeLib/Include/whandler.h @@ -0,0 +1,66 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __WHANDLER_H__ +#define __WHANDLER_H__ + +#ifdef USE_SDL +#include +#include "SDL.h" +#endif + +#ifdef __DJGPP__ +#include +#endif + +#include "felibdef.h" + +#define GET_KEY globalwindowhandler::GetKey +#define READ_KEY globalwindowhandler::ReadKey +#define GET_TICK globalwindowhandler::GetTick + +class globalwindowhandler +{ + public: + static int GetKey(truth = true); + static int ReadKey(); + static void InstallControlLoop(truth (*)()); + static void DeInstallControlLoop(truth (*)()); + static ulong GetTick() { return Tick; } + static truth ControlLoopsInstalled() { return Controls; } + static void EnableControlLoops() { ControlLoopsEnabled = true; } + static void DisableControlLoops() { ControlLoopsEnabled = false; } + static truth ShiftIsDown(); +#ifdef USE_SDL + static void Init(); + static void SetQuitMessageHandler(truth (*What)()) + { QuitMessageHandler = What; } + static ulong UpdateTick() { return Tick = SDL_GetTicks() / 40; } +#endif +#ifdef __DJGPP__ + static void Init() { } + static void SetQuitMessageHandler(truth (*)()) { } + static ulong UpdateTick() { return Tick = uclock() * 25 / UCLOCKS_PER_SEC; } +#endif + private: +#ifdef USE_SDL + static void ProcessMessage(SDL_Event*); + static std::vector KeyBuffer; + static truth (*QuitMessageHandler)(); +#endif + static truth (*ControlLoop[MAX_CONTROLS])(); + static int Controls; + static ulong Tick; + static truth ControlLoopsEnabled; +}; + +#endif diff --git a/FeLib/Makefile.am b/FeLib/Makefile.am new file mode 100644 index 0000000..30b7568 --- /dev/null +++ b/FeLib/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = Source Include diff --git a/FeLib/Source/CVS/Entries b/FeLib/Source/CVS/Entries new file mode 100644 index 0000000..1415765 --- /dev/null +++ b/FeLib/Source/CVS/Entries @@ -0,0 +1,17 @@ +/Makefile.am/1.7/Sun Aug 22 01:04:56 2004// +/bitmap.cpp/1.71/Sun Sep 3 18:02:55 2006// +/config.cpp/1.8/Tue May 10 20:32:41 2005// +/error.cpp/1.15/Tue May 10 20:32:41 2005// +/febot.cpp/1.8/Tue May 10 20:32:41 2005// +/feio.cpp/1.61/Sun Sep 3 18:02:55 2006// +/felist.cpp/1.54/Tue May 10 20:32:42 2005// +/femain.cpp/1.20/Sat Jul 23 13:48:56 2005// +/femath.cpp/1.29/Tue May 10 20:32:42 2005// +/festring.cpp/1.26/Tue May 10 20:32:42 2005// +/fetime.cpp/1.5/Wed Sep 29 21:54:32 2004// +/graphics.cpp/1.24/Fri Jul 22 20:25:17 2005// +/hscore.cpp/1.42/Tue May 10 20:32:42 2005// +/rawbit.cpp/1.11/Tue May 10 20:32:42 2005// +/save.cpp/1.40/Tue May 10 20:32:42 2005// +/whandler.cpp/1.45/Mon Feb 28 17:43:34 2005// +D diff --git a/FeLib/Source/CVS/Repository b/FeLib/Source/CVS/Repository new file mode 100644 index 0000000..90204aa --- /dev/null +++ b/FeLib/Source/CVS/Repository @@ -0,0 +1 @@ +ivan/FeLib/Source diff --git a/FeLib/Source/CVS/Root b/FeLib/Source/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeLib/Source/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeLib/Source/Makefile.am b/FeLib/Source/Makefile.am new file mode 100644 index 0000000..734579d --- /dev/null +++ b/FeLib/Source/Makefile.am @@ -0,0 +1,3 @@ +noinst_LIBRARIES = libFeLib.a +libFeLib_a_SOURCES = bitmap.cpp config.cpp error.cpp feio.cpp felist.cpp femain.cpp femath.cpp festring.cpp fetime.cpp graphics.cpp hscore.cpp save.cpp rawbit.cpp whandler.cpp +INCLUDES=-I@top_srcdir@/FeLib/Include diff --git a/FeLib/Source/bitmap.cpp b/FeLib/Source/bitmap.cpp new file mode 100644 index 0000000..d63e373 --- /dev/null +++ b/FeLib/Source/bitmap.cpp @@ -0,0 +1,2149 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include +#include + +#include "bitmap.h" +#include "graphics.h" +#include "save.h" +#include "allocate.h" +#include "femath.h" +#include "rawbit.h" + +/* + * Blitting must be as fast as possible, even if no optimizations are used; + * therefore we can't use inline functions inside loops, since they may be + * left unexpanded. These macros will do the job efficiently, even if they + * are rather ugly + */ + +#define LOAD_SRC() int SrcCol = *SrcPtr; +#define LOAD_DEST() int DestCol = *DestPtr; +#define LOAD_ALPHA() int Alpha = *AlphaPtr; + +#define STORE_COLOR() *DestPtr = Red | Green | Blue; + +#define NEW_LUMINATE_RED()\ +int Red = (SrcCol & 0xF800) + NewRedLuminance;\ +\ +if(Red >= 0)\ +{\ + if(Red > 0xF800)\ + Red = 0xF800;\ +}\ +else\ + Red = 0; + +#define NEW_LUMINATE_GREEN()\ +int Green = (SrcCol & 0x7E0) + NewGreenLuminance;\ +\ +if(Green >= 0)\ +{\ + if(Green > 0x7E0)\ + Green = 0x7E0;\ +}\ +else\ + Green = 0; + +#define NEW_LUMINATE_BLUE()\ +int Blue = (SrcCol & 0x1F) + NewBlueLuminance;\ +\ +if(Blue >= 0)\ +{\ + if(Blue > 0x1F)\ + Blue = 0x1F;\ +}\ +else\ + Blue = 0; + +#define NEW_APPLY_ALPHA_RED()\ +{\ + int DestRed = (DestCol & 0xF800);\ + Red = (((Red - DestRed) * Alpha >> 8) + DestRed) & 0xF800;\ +} + +#define NEW_APPLY_ALPHA_GREEN()\ +{\ + int DestGreen = (DestCol & 0x7E0);\ + Green = (((Green - DestGreen) * Alpha >> 8) + DestGreen) & 0x7E0;\ +} + +#define NEW_APPLY_ALPHA_BLUE()\ +{\ + int DestBlue = (DestCol & 0x1F);\ + Blue = ((Blue - DestBlue) * Alpha >> 8) + DestBlue;\ +} + +#define NEW_LOAD_AND_APPLY_ALPHA_RED()\ +int Red;\ +{\ + int DestRed = DestCol & 0xF800;\ + Red = ((((SrcCol & 0xF800) - DestRed) * Alpha >> 8) + DestRed) & 0xF800;\ +} + +#define NEW_LOAD_AND_APPLY_ALPHA_GREEN()\ +int Green;\ +{\ + int DestGreen = DestCol & 0x7E0;\ + Green = ((((SrcCol & 0x7E0) - DestGreen) * Alpha >> 8) + DestGreen)\ + & 0x7E0;\ +} + +#define NEW_LOAD_AND_APPLY_ALPHA_BLUE()\ +int Blue;\ +{\ + int DestBlue = DestCol & 0x1F;\ + Blue = (((SrcCol & 0x1F) - DestBlue) * Alpha >> 8) + DestBlue;\ +} + +bitmap::bitmap(cfestring& FileName) +: FastFlag(0), AlphaMap(0), PriorityMap(0), RandMap(0) +{ + inputfile File(FileName.CStr(), 0, false); + + if(!File.IsOpen()) + ABORT("Bitmap %s not found!", FileName.CStr()); + + uchar Palette[768]; + File.SeekPosEnd(-768); + File.Read(reinterpret_cast(Palette), 768); + File.SeekPosBegin(8); + Size.X = File.Get(); + Size.X += (File.Get() << 8) + 1; + Size.Y = File.Get(); + Size.Y += (File.Get() << 8) + 1; + XSizeTimesYSize = Size.X * Size.Y; + File.SeekPosBegin(128); + Alloc2D(Image, Size.Y, Size.X); + packcol16* Buffer = Image[0]; + + for(int y = 0; y < Size.Y; ++y) + for(int x = 0; x < Size.X; ++x) + { + int Char1 = File.Get(); + + if(Char1 > 192) + { + --x; + int Char2 = File.Get(); + int Char3 = Char2 + (Char2 << 1); + int Color = int(Palette[Char3] >> 3) << 11 + | int(Palette[Char3 + 1] >> 2) << 5 + | int(Palette[Char3 + 2] >> 3); + + for(; Char1 > 192; --Char1) + { + *Buffer++ = Color; + + if(++x == Size.X) + { + x = 0; + ++y; + } + } + } + else + { + int Char3 = Char1 + (Char1 << 1); + *Buffer++ = int(Palette[Char3] >> 3) << 11 + | int(Palette[Char3 + 1] >> 2) << 5 + | int(Palette[Char3 + 2] >> 3); + } + } +} + +bitmap::bitmap(cbitmap* Bitmap, int Flags, truth CopyAlpha) +: Size(Bitmap->Size), XSizeTimesYSize(Bitmap->XSizeTimesYSize), + FastFlag(0), PriorityMap(0), RandMap(0) +{ + Alloc2D(Image, Size.Y, Size.X); + + if(CopyAlpha && Bitmap->AlphaMap) + { + Alloc2D(AlphaMap, Size.Y, Size.X); + Bitmap->BlitAndCopyAlpha(this, Flags); + } + else + { + AlphaMap = 0; + + if(!Flags) + Bitmap->FastBlit(this); + else + Bitmap->NormalBlit(this, Flags); + } +} + +bitmap::bitmap(v2 Size) +: Size(Size), XSizeTimesYSize(Size.X * Size.Y), + FastFlag(0), AlphaMap(0), PriorityMap(0), RandMap(0) +{ + Alloc2D(Image, Size.Y, Size.X); +} + +bitmap::bitmap(v2 Size, col16 Color) +: Size(Size), XSizeTimesYSize(Size.X * Size.Y), + FastFlag(0), AlphaMap(0), PriorityMap(0), RandMap(0) +{ + Alloc2D(Image, Size.Y, Size.X); + ClearToColor(Color); +} + +bitmap::~bitmap() +{ + delete [] Image; + delete [] AlphaMap; + delete [] PriorityMap; + delete [] RandMap; +} + +void bitmap::Save(outputfile& SaveFile) const +{ + SaveFile.Write(reinterpret_cast(Image[0]), + XSizeTimesYSize * sizeof(packcol16)); + + if(AlphaMap) + { + SaveFile.Put(true); + SaveFile.Write(reinterpret_cast(AlphaMap[0]), + XSizeTimesYSize * sizeof(packalpha)); + } + else + SaveFile.Put(false); + + if(PriorityMap) + { + SaveFile.Put(true); + SaveFile.Write(reinterpret_cast(PriorityMap[0]), + XSizeTimesYSize * sizeof(packpriority)); + } + else + SaveFile.Put(false); + + SaveFile << uchar(FastFlag); +} + +void bitmap::Load(inputfile& SaveFile) +{ + SaveFile.Read(reinterpret_cast(Image[0]), + XSizeTimesYSize * sizeof(packcol16)); + + if(SaveFile.Get()) + { + Alloc2D(AlphaMap, Size.Y, Size.X); + SaveFile.Read(reinterpret_cast(AlphaMap[0]), + XSizeTimesYSize * sizeof(packalpha)); + } + + if(SaveFile.Get()) + { + Alloc2D(PriorityMap, Size.Y, Size.X); + SaveFile.Read(reinterpret_cast(PriorityMap[0]), + XSizeTimesYSize * sizeof(packpriority)); + } + + FastFlag = ReadType(SaveFile); +} + +void bitmap::Save(cfestring& FileName) const +{ + static char BMPHeader[] = + { + char(0x42), char(0x4D), char(0xB6), char(0x4F), char(0x12), char(0x00), + char(0x00), char(0x00), char(0x00), char(0x00), char(0x36), char(0x00), + char(0x00), char(0x00), char(0x28), char(0x00), char(0x00), char(0x00), + char(0x20), char(0x03), char(0x00), char(0x00), char(0xF4), char(0x01), + char(0x00), char(0x00), char(0x01), char(0x00), char(0x18), char(0x00), + char(0x00), char(0x00), char(0x00), char(0x00), char(0x80), char(0x4F), + char(0x12), char(0x00), char(0x33), char(0x0B), char(0x00), char(0x00), + char(0x33), char(0x0B), char(0x00), char(0x00), char(0x00), char(0x00), + char(0x00), char(0x00), char(0x00), char(0x00), char(0x00), char(0x00) + }; + + outputfile SaveFile(FileName); + BMPHeader[0x12] = Size.X & 0xFF; + BMPHeader[0x13] = (Size.X >> 8) & 0xFF; + BMPHeader[0x16] = Size.Y & 0xFF; + BMPHeader[0x17] = (Size.Y >> 8) & 0xFF; + SaveFile.Write(BMPHeader, 0x36); + + for(int y = Size.Y - 1; y >= 0; --y) + for(int x = 0; x < Size.X; ++x) + { + col16 Pixel = GetPixel(x, y); + SaveFile << char(Pixel << 3) + << char((Pixel >> 5) << 2) + << char((Pixel >> 11) << 3); + } +} + +void bitmap::Fill(v2 TopLeft, int Width, int Height, col16 Color) +{ Fill(TopLeft.X, TopLeft.Y, Width, Height, Color); } +void bitmap::Fill(int X, int Y, v2 FillSize, col16 Color) +{ Fill(X, Y, FillSize.X, FillSize.Y, Color); } +void bitmap::Fill(v2 TopLeft, v2 FillSize, col16 Color) +{ Fill(TopLeft.X, TopLeft.Y, FillSize.X, FillSize.Y, Color); } + +void bitmap::Fill(int X, int Y, int Width, int Height, col16 Color) +{ + if(X >= Size.X || Y >= Size.Y) + return; + + if(X + Width > Size.X) + Width = Size.X - X; + + if(Y + Height > Size.Y) + Height = Size.Y - Y; + + if(Color >> 8 == (Color & 0xFF)) + { + Width <<= 1; + + for(int y = 0; y < Height; ++y) + memset(&Image[Y + y][X], Color, Width); + } + else + for(int y = 0; y < Height; ++y) + { + packcol16* Ptr = &Image[Y + y][X]; + cpackcol16*const EndPtr = Ptr + Width; + + while(Ptr != EndPtr) + *Ptr++ = Color; + } +} + +void bitmap::ClearToColor(col16 Color) +{ + packcol16* Ptr = Image[0]; + + if(Color >> 8 == (Color & 0xFF)) + memset(Ptr, Color, XSizeTimesYSize * sizeof(packcol16)); + else + { + cpackcol16*const EndPtr = Ptr + XSizeTimesYSize; + + while(Ptr != EndPtr) + *Ptr++ = Color; + } +} + +void bitmap::NormalBlit(cblitdata& BlitData) const +{ + blitdata B = BlitData; + + if(!FastFlag) + { + if(!B.Border.X || !B.Border.Y) + ABORT("Zero-sized bitmap blit attempt detected!"); + + if(B.Flags & ROTATE && B.Border.X != B.Border.Y) + ABORT("Blit error: FeLib supports only square rotating!"); + + if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y)) + return; + } + + packcol16** SrcImage = Image; + packcol16** DestImage = B.Bitmap->Image; + + switch(B.Flags & 7) + { + case NONE: + { + if(!B.Src.X && !B.Src.Y && !B.Dest.X && !B.Dest.Y + && B.Border.X == Size.X && B.Border.Y == Size.Y + && B.Border.X == B.Bitmap->Size.X && B.Border.Y == B.Bitmap->Size.Y) + memcpy(DestImage[0], SrcImage[0], XSizeTimesYSize * sizeof(packcol16)); + else + { + cint Bytes = B.Border.X * sizeof(packcol16); + + for(int y = 0; y < B.Border.Y; ++y) + memcpy(&DestImage[B.Dest.Y + y][B.Dest.X], &SrcImage[B.Src.Y + y][B.Src.X], Bytes); + } + + break; + } + + case MIRROR: + { + B.Dest.X += B.Border.X - 1; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X]; + + for(; SrcPtr != EndPtr; ++SrcPtr, --DestPtr) + *DestPtr = *SrcPtr; + } + + break; + } + + case FLIP: + { + B.Dest.Y += B.Border.Y - 1; + cint Bytes = B.Border.X * sizeof(packcol16); + + for(int y = 0; y < B.Border.Y; ++y) + memcpy(&DestImage[B.Dest.Y - y][B.Dest.X], &SrcImage[B.Src.Y + y][B.Src.X], Bytes); + + break; + } + + case (MIRROR | FLIP): + { + B.Dest.X += B.Border.X - 1; + B.Dest.Y += B.Border.Y - 1; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = &DestImage[B.Dest.Y - y][B.Dest.X]; + + for(; SrcPtr != EndPtr; ++SrcPtr, --DestPtr) + *DestPtr = *SrcPtr; + } + + break; + } + + case ROTATE: + { + B.Dest.X += B.Border.X - 1; + int TrueDestXMove = B.Bitmap->Size.X; + packcol16* DestBase = &DestImage[B.Dest.Y][B.Dest.X]; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = DestBase - y; + + for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr += TrueDestXMove) + *DestPtr = *SrcPtr; + } + + break; + } + + case (MIRROR | ROTATE): + { + int TrueDestXMove = B.Bitmap->Size.X; + packcol16* DestBase = &DestImage[B.Dest.Y][B.Dest.X]; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = DestBase + y; + + for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr += TrueDestXMove) + *DestPtr = *SrcPtr; + } + + break; + } + + case (FLIP | ROTATE): + { + B.Dest.X += B.Border.X - 1; + B.Dest.Y += B.Border.Y - 1; + int TrueDestXMove = B.Bitmap->Size.X; + packcol16* DestBase = &DestImage[B.Dest.Y][B.Dest.X]; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = DestBase - y; + + for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= TrueDestXMove) + *DestPtr = *SrcPtr; + } + + break; + } + + case (MIRROR | FLIP | ROTATE): + { + B.Dest.Y += B.Border.Y - 1; + int TrueDestXMove = B.Bitmap->Size.X; + packcol16* DestBase = &DestImage[B.Dest.Y][B.Dest.X]; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = DestBase + y; + + for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= TrueDestXMove) + *DestPtr = *SrcPtr; + } + + break; + } + } +} + +void bitmap::LuminanceBlit(cblitdata& BlitData) const +{ + blitdata B = BlitData; + + if(B.Luminance == NORMAL_LUMINANCE) + { + B.Flags = 0; + NormalBlit(B); + return; + } + + if(!FastFlag) + { + if(!B.Border.X || !B.Border.Y) + ABORT("Zero-sized bitmap blit attempt detected!"); + + if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y)) + return; + } + + packcol16** SrcImage = Image; + packcol16** DestImage = B.Bitmap->Image; + int NewRedLuminance = (B.Luminance >> 7 & 0x1F800) - 0x10000; + int NewGreenLuminance = (B.Luminance >> 4 & 0xFE0) - 0x800; + int NewBlueLuminance = (B.Luminance >> 2 & 0x3F) - 0x20; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X]; + + for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr) + { + LOAD_SRC(); + NEW_LUMINATE_RED(); + NEW_LUMINATE_GREEN(); + NEW_LUMINATE_BLUE(); + STORE_COLOR(); + } + } +} + +void bitmap::NormalMaskedBlit(cblitdata& BlitData) const +{ + blitdata B = BlitData; + + if(!FastFlag) + { + if(!B.Border.X || !B.Border.Y) + ABORT("Zero-sized bitmap masked blit attempt detected!"); + + if(B.Flags & ROTATE && B.Border.X != B.Border.Y) + ABORT("MaskedBlit error: FeLib supports only square rotating!"); + + if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y)) + return; + } + + packcol16** SrcImage = Image; + packcol16** DestImage = B.Bitmap->Image; + packcol16 PackedMaskColor = B.MaskColor; + + switch(B.Flags & 7) + { + case NONE: + { + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X]; + + for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr) + if(*SrcPtr != PackedMaskColor) + *DestPtr = *SrcPtr; + } + + break; + } + + case MIRROR: + { + B.Dest.X += B.Border.X - 1; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X]; + + for(; SrcPtr != EndPtr; ++SrcPtr, --DestPtr) + if(*SrcPtr != PackedMaskColor) + *DestPtr = *SrcPtr; + } + + break; + } + + case FLIP: + { + B.Dest.Y += B.Border.Y - 1; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = &DestImage[B.Dest.Y - y][B.Dest.X]; + + for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr) + if(*SrcPtr != PackedMaskColor) + *DestPtr = *SrcPtr; + } + + break; + } + + case (MIRROR | FLIP): + { + B.Dest.X += B.Border.X - 1; + B.Dest.Y += B.Border.Y - 1; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = &DestImage[B.Dest.Y - y][B.Dest.X]; + + for(; SrcPtr != EndPtr; ++SrcPtr, --DestPtr) + if(*SrcPtr != PackedMaskColor) + *DestPtr = *SrcPtr; + } + + break; + } + + case ROTATE: + { + B.Dest.X += B.Border.X - 1; + int TrueDestXMove = B.Bitmap->Size.X; + packcol16* DestBase = &DestImage[B.Dest.Y][B.Dest.X]; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = DestBase - y; + + for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr += TrueDestXMove) + if(*SrcPtr != PackedMaskColor) + *DestPtr = *SrcPtr; + } + + break; + } + + case (MIRROR | ROTATE): + { + int TrueDestXMove = B.Bitmap->Size.X; + packcol16* DestBase = &DestImage[B.Dest.Y][B.Dest.X]; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = DestBase + y; + + for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr += TrueDestXMove) + if(*SrcPtr != PackedMaskColor) + *DestPtr = *SrcPtr; + } + + break; + } + + case (FLIP | ROTATE): + { + B.Dest.X += B.Border.X - 1; + B.Dest.Y += B.Border.Y - 1; + int TrueDestXMove = B.Bitmap->Size.X; + packcol16* DestBase = &DestImage[B.Dest.Y][B.Dest.X]; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = DestBase - y; + + for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= TrueDestXMove) + if(*SrcPtr != PackedMaskColor) + *DestPtr = *SrcPtr; + } + + break; + } + + case (MIRROR | FLIP | ROTATE): + { + B.Dest.Y += B.Border.Y - 1; + int TrueDestXMove = B.Bitmap->Size.X; + packcol16* DestBase = &DestImage[B.Dest.Y][B.Dest.X]; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = DestBase + y; + + for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= TrueDestXMove) + if(*SrcPtr != PackedMaskColor) + *DestPtr = *SrcPtr; + } + + break; + } + } +} + +void bitmap::LuminanceMaskedBlit(cblitdata& BlitData) const +{ + blitdata B = BlitData; + + if(B.Luminance == NORMAL_LUMINANCE) + { + B.Flags = 0; + NormalMaskedBlit(B); + return; + } + + if(!FastFlag) + { + if(!B.Border.X || !B.Border.Y) + ABORT("Zero-sized bitmap masked blit attempt detected!"); + + if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y)) + return; + } + + packcol16** SrcImage = Image; + packcol16** DestImage = B.Bitmap->Image; + int NewRedLuminance = (B.Luminance >> 7 & 0x1F800) - 0x10000; + int NewGreenLuminance = (B.Luminance >> 4 & 0xFE0) - 0x800; + int NewBlueLuminance = (B.Luminance >> 2 & 0x3F) - 0x20; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X]; + + for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr) + { + LOAD_SRC(); + + if(SrcCol != B.MaskColor) + { + NEW_LUMINATE_RED(); + NEW_LUMINATE_GREEN(); + NEW_LUMINATE_BLUE(); + STORE_COLOR(); + } + } + } +} + +void bitmap::SimpleAlphaBlit(bitmap* Bitmap, alpha Alpha, col16 MaskColor) const +{ + if(Alpha == 255) + { + blitdata B = { Bitmap, + { 0, 0 }, + { 0, 0 }, + { Size.X, Size.Y }, + { 0 }, + MaskColor, + 0 }; + + NormalMaskedBlit(B); + return; + } + + if(!FastFlag && (Size.X != Bitmap->Size.X || Size.Y != Bitmap->Size.Y)) + ABORT("Fast simple alpha blit attempt of noncongruent bitmaps detected!"); + + cpackcol16* SrcPtr = Image[0]; + cpackcol16* EndPtr = SrcPtr + XSizeTimesYSize; + packcol16* DestPtr = Bitmap->Image[0]; + + for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr) + { + LOAD_SRC(); + + if(SrcCol != MaskColor) + { + LOAD_DEST(); + NEW_LOAD_AND_APPLY_ALPHA_RED(); + NEW_LOAD_AND_APPLY_ALPHA_GREEN(); + NEW_LOAD_AND_APPLY_ALPHA_BLUE(); + STORE_COLOR(); + } + } +} + +void bitmap::AlphaMaskedBlit(cblitdata& BlitData) const +{ + blitdata B = BlitData; + + if(!AlphaMap) + { + B.Flags = 0; + NormalMaskedBlit(B); + return; + } + + if(!FastFlag) + { + if(!B.Border.X || !B.Border.Y) + ABORT("Zero-sized bitmap alpha blit attempt detected!"); + + if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y)) + return; + } + + packcol16** SrcImage = Image; + packalpha** SrcAlphaMap = AlphaMap; + packcol16** DestImage = B.Bitmap->Image; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackalpha* AlphaPtr = &SrcAlphaMap[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X]; + + for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr, ++AlphaPtr) + { + LOAD_SRC(); + + if(SrcCol != B.MaskColor) + { + LOAD_DEST(); + LOAD_ALPHA(); + NEW_LOAD_AND_APPLY_ALPHA_RED(); + NEW_LOAD_AND_APPLY_ALPHA_GREEN(); + NEW_LOAD_AND_APPLY_ALPHA_BLUE(); + STORE_COLOR(); + } + } + } +} + +void bitmap::DrawLine(v2 From, int ToX, int ToY, col16 Color, truth Wide) { DrawLine(From.X, From.Y, ToX, ToY, Color, Wide); } +void bitmap::DrawLine(int FromX, int FromY, v2 To, col16 Color, truth Wide) { DrawLine(FromX, FromY, To.X, To.Y, Color, Wide); } +void bitmap::DrawLine(v2 From, v2 To, col16 Color, truth Wide) { DrawLine(From.X, From.Y, To.X, To.Y, Color, Wide); } + +void bitmap::DrawLine(int OrigFromX, int OrigFromY, int OrigToX, int OrigToY, col16 Color, truth Wide) +{ + if(OrigFromY == OrigToY) + { + DrawHorizontalLine(OrigFromX, OrigToX, OrigFromY, Color, Wide); + return; + } + + if(OrigFromX == OrigToX) + { + DrawVerticalLine(OrigFromX, OrigFromY, OrigToY, Color, Wide); + return; + } + + static cint PointX[] = { 0, 0, -1, 1, 0 }; + static cint PointY[] = { 0, -1, 0, 0, 1 }; + cint Times = Wide ? 5 : 1; + + for(int c1 = 0; c1 < Times; ++c1) + { + cint X1 = OrigFromX + PointX[c1]; + cint Y1 = OrigFromY + PointY[c1]; + cint X2 = OrigToX + PointX[c1]; + cint Y2 = OrigToY + PointY[c1]; + cint DeltaX = abs(X2 - X1); + cint DeltaY = abs(Y2 - Y1); + int x, c2; + int XChange, PtrXChange, PtrYChange; + int DoubleDeltaX, DoubleDeltaY, End; + + if(DeltaX >= DeltaY) + { + x = X1; + c2 = DeltaX; + PtrXChange = XChange = X1 < X2 ? 1 : -1; + PtrYChange = Y1 < Y2 ? Size.X : -Size.X; + DoubleDeltaX = DeltaX << 1; + DoubleDeltaY = DeltaY << 1; + End = X2; + } + else + { + x = Y1; + c2 = DeltaY; + XChange = Y1 < Y2 ? 1 : -1; + PtrXChange = Y1 < Y2 ? Size.X : -Size.X; + PtrYChange = X1 < X2 ? 1 : -1; + DoubleDeltaX = DeltaY << 1; + DoubleDeltaY = DeltaX << 1; + End = Y2; + } + + packcol16* Ptr = &Image[Y1][X1]; + *Ptr = Color; + + while(x != End) + { + x += XChange; + Ptr += PtrXChange; + c2 += DoubleDeltaY; + + if(c2 >= DoubleDeltaX) + { + c2 -= DoubleDeltaX; + Ptr += PtrYChange; + } + + *Ptr = Color; + } + } +} + +void bitmap::DrawVerticalLine(int OrigX, int OrigFromY, int OrigToY, col16 Color, truth Wide) +{ + static cint PointX[] = { 0, -1, 1 }; + cint Times = Wide ? 3 : 1; + + for(int c = 0; c < Times; ++c) + { + int X = OrigX + PointX[c]; + int FromY = OrigFromY; + int ToY = OrigToY; + + if(FromY > ToY) + Swap(FromY, ToY); + + if(Wide && !c) + { + --FromY; + ++ToY; + } + + if(X < 0 || X >= Size.X || ToY < 0 || FromY >= Size.Y) + continue; + + FromY = Max(FromY, 0); + ToY = Min(ToY, Size.Y-1); + packcol16* Ptr = &Image[FromY][X]; + + for(int y = FromY; y <= ToY; ++y, Ptr += Size.X) + *Ptr = Color; + } +} + +void bitmap::DrawHorizontalLine(int OrigFromX, int OrigToX, int OrigY, col16 Color, truth Wide) +{ + static cint PointY[] = { 0, -1, 1 }; + cint Times = Wide ? 3 : 1; + + for(int c = 0; c < Times; ++c) + { + int Y = OrigY + PointY[c]; + int FromX = OrigFromX; + int ToX = OrigToX; + + if(FromX > ToX) + Swap(FromX, ToX); + + if(Wide && !c) + { + --FromX; + ++ToX; + } + + if(Y < 0 || Y >= Size.Y || ToX < 0 || FromX >= Size.X) + continue; + + FromX = Max(FromX, 0); + ToX = Min(ToX, Size.X-1); + packcol16* Ptr = &Image[Y][FromX]; + + for(int x = FromX; x <= ToX; ++x, ++Ptr) + *Ptr = Color; + } +} + +void bitmap::DrawPolygon(int CenterX, int CenterY, int Radius, int NumberOfSides, col16 Color, truth DrawSides, truth DrawDiameters, double Rotation) +{ + if(!DrawSides && !DrawDiameters) + return; + + v2* Point = new v2[NumberOfSides]; + double AngleDelta = 2 * FPI / NumberOfSides; + int c; + + for(c = 0; c < NumberOfSides; ++c) + { + Point[c].X = CenterX + int(sin(AngleDelta * c + Rotation) * Radius); + Point[c].Y = CenterY + int(cos(AngleDelta * c + Rotation) * Radius); + } + + if(DrawDiameters) + { + if(DrawSides) + { + for(c = 0; c < NumberOfSides; ++c) + for(int a = 0; a < NumberOfSides; ++a) + if(c != a) + DrawLine(Point[c].X, Point[c].Y, Point[a].X, Point[a].Y, Color, true); + } + else + { + for(c = 0; c < NumberOfSides; ++c) + for(int a = 0; a < NumberOfSides; ++a) + if((int(c - a) > 1 || int(a - c) > 1) && (a || c != NumberOfSides - 1) && (c || a != NumberOfSides - 1)) + DrawLine(Point[c].X, Point[c].Y, Point[a].X, Point[a].Y, Color, true); + } + } + else + { + for(c = 0; c < NumberOfSides - 1; ++c) + DrawLine(Point[c].X, Point[c].Y, Point[c + 1].X, Point[c + 1].Y, Color, true); + + DrawLine(Point[NumberOfSides - 1].X, Point[NumberOfSides - 1].Y, Point[0].X, Point[0].Y, Color, true); + } + + delete [] Point; +} + +void bitmap::CreateAlphaMap(alpha InitialValue) +{ + if(AlphaMap) + ABORT("Alpha leak detected!"); + + Alloc2D(AlphaMap, Size.Y, Size.X); + memset(AlphaMap[0], InitialValue, XSizeTimesYSize); +} + +truth bitmap::Fade(long& AlphaSum, packalpha& AlphaAverage, int Amount) +{ + if(!AlphaMap) + ABORT("No alpha map to fade."); + + truth Changes = false; + long Alphas = 0; + long NewAlphaSum = 0; + long Size = XSizeTimesYSize; + + for(long c = 0; c < Size; ++c) + { + packalpha* AlphaPtr = &AlphaMap[0][c]; + + if(*AlphaPtr) + if(*AlphaPtr > Amount) + { + *AlphaPtr -= Amount; + NewAlphaSum += *AlphaPtr; + ++Alphas; + Changes = true; + } + else + { + *AlphaPtr = 0; + Changes = true; + + if(RandMap) + UpdateRandMap(c, false); + } + } + + AlphaSum = NewAlphaSum; + AlphaAverage = Alphas ? NewAlphaSum / Alphas : 0; + return Changes; +} + +void bitmap::Outline(col16 Color, alpha Alpha, priority Priority) +{ + if(!AlphaMap) + CreateAlphaMap(255); + + col16 LastColor, NextColor; + int XMax = Size.X; + int YMax = Size.Y - 1; + + for(int x = 0; x < XMax; ++x) + { + packcol16* Buffer = &Image[0][x]; + LastColor = *Buffer; + + for(int y = 0; y < YMax; ++y) + { + NextColor = *(Buffer + XMax); + + if((LastColor == TRANSPARENT_COLOR || !y) && NextColor != TRANSPARENT_COLOR) + { + *Buffer = Color; + SetAlpha(x, y, Alpha); + SafeSetPriority(x, y, Priority); + } + + Buffer += XMax; + + if(LastColor != TRANSPARENT_COLOR && (NextColor == TRANSPARENT_COLOR || y == YMax - 1)) + { + *Buffer = Color; + SetAlpha(x, y + 1, Alpha); + SafeSetPriority(x, y + 1, Priority); + } + + LastColor = NextColor; + } + } + + --XMax; + ++YMax; + + for(int y = 0; y < YMax; ++y) + { + packcol16* Buffer = Image[y]; + LastColor = *Buffer; + + for(int x = 0; x < XMax; ++x) + { + NextColor = *(Buffer + 1); + + if((LastColor == TRANSPARENT_COLOR || !x) && NextColor != TRANSPARENT_COLOR) + { + *Buffer = Color; + SetAlpha(x, y, Alpha); + SafeSetPriority(x, y, Priority); + } + + ++Buffer; + + if(LastColor != TRANSPARENT_COLOR && (NextColor == TRANSPARENT_COLOR || x == XMax - 1)) + { + *Buffer = Color; + SetAlpha(x + 1, y, Alpha); + SafeSetPriority(x + 1, y, Priority); + } + + LastColor = NextColor; + } + } +} + +void bitmap::FadeToScreen(bitmapeditor BitmapEditor) +{ + bitmap Backup(DOUBLE_BUFFER); + Backup.ActivateFastFlag(); + blitdata B = { DOUBLE_BUFFER, + { 0, 0 }, + { 0, 0 }, + { RES.X, RES.Y }, + { 0 }, + 0, + 0 }; + + for(int c = 0; c <= 5; ++c) + { + clock_t StartTime = clock(); + int Element = 127 - c * 25; + B.Luminance = MakeRGB24(Element, Element, Element); + Backup.LuminanceMaskedBlit(B); + + if(BitmapEditor) + BitmapEditor(this, true); + + SimpleAlphaBlit(DOUBLE_BUFFER, c * 50, 0); + graphics::BlitDBToScreen(); + while(clock() - StartTime < 0.05 * CLOCKS_PER_SEC); + } + + DOUBLE_BUFFER->ClearToColor(0); + + if(BitmapEditor) + BitmapEditor(this, true); + + B.Flags = 0; + NormalMaskedBlit(B); + graphics::BlitDBToScreen(); +} + +void bitmap::StretchBlit(cblitdata& BlitData) const +{ + blitdata B = BlitData; + + if(!FastFlag) + { + if(!B.Border.X || !B.Border.Y) + ABORT("Zero-sized bitmap stretch blit attempt detected!"); + + if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y)) + return; + } + + if(B.Stretch > 1) + { + int tx = B.Dest.X; + + for(int x1 = B.Src.X; x1 < B.Src.X + B.Border.X; ++x1, tx += B.Stretch) + { + int ty = B.Dest.Y; + + for(int y1 = B.Src.Y; y1 < B.Src.Y + B.Border.Y; ++y1, ty += B.Stretch) + { + packcol16 Pixel = Image[y1][x1]; + + if(Pixel != TRANSPARENT_COLOR) + for(int x2 = tx; x2 < tx + B.Stretch; ++x2) + for(int y2 = ty; y2 < ty + B.Stretch; ++y2) + B.Bitmap->Image[y2][x2] = Pixel; + } + } + + return; + } + else if(B.Stretch < -1) + { + int tx = B.Dest.X; + + for(int x1 = B.Src.X; x1 < B.Src.X + B.Border.X; x1 -= B.Stretch, ++tx) + { + int ty = B.Dest.Y; + + for(int y1 = B.Src.Y; y1 < B.Src.Y + B.Border.Y; y1 -= B.Stretch, ++ty) + { + packcol16 Pixel = Image[y1][x1]; + + if(Pixel != TRANSPARENT_COLOR) + B.Bitmap->Image[ty][tx] = Pixel; + } + } + + return; + } + else + { + B.Flags = 0; + NormalMaskedBlit(B); + return; + } +} + +outputfile& operator<<(outputfile& SaveFile, cbitmap* Bitmap) +{ + if(Bitmap) + { + SaveFile.Put(1); + SaveFile << Bitmap->GetSize(); + Bitmap->Save(SaveFile); + } + else + SaveFile.Put(0); + + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, bitmap*& Bitmap) +{ + if(SaveFile.Get()) + { + Bitmap = new bitmap(ReadType(SaveFile)); + Bitmap->Load(SaveFile); + } + else + Bitmap = 0; + + return SaveFile; +} + +void bitmap::DrawRectangle(v2 TopLeft, int Right, int Bottom, col16 Color, truth Wide) { DrawRectangle(TopLeft.X, TopLeft.Y, Right, Bottom, Color, Wide); } +void bitmap::DrawRectangle(int Left, int Top, v2 BottomRight, col16 Color, truth Wide) { DrawRectangle(Left, Top, BottomRight.X, BottomRight.Y, Color, Wide); } +void bitmap::DrawRectangle(v2 TopLeft, v2 BottomRight, col16 Color, truth Wide) { DrawRectangle(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y, Color, Wide); } + +void bitmap::DrawRectangle(int Left, int Top, int Right, int Bottom, col16 Color, truth Wide) +{ + DrawHorizontalLine(Left, Right, Top, Color, Wide); + DrawHorizontalLine(Left, Right, Bottom, Color, Wide); + DrawVerticalLine(Right, Top, Bottom, Color, Wide); + DrawVerticalLine(Left, Top, Bottom, Color, Wide); +} + +void bitmap::AlphaLuminanceBlit(cblitdata& BlitData) const +{ + if(BlitData.Luminance == NORMAL_LUMINANCE) + { + AlphaMaskedBlit(BlitData); + return; + } + + if(!AlphaMap) + { + LuminanceMaskedBlit(BlitData); + return; + } + + blitdata B = BlitData; + + if(!FastFlag) + { + if(!B.Border.X || !B.Border.Y) + ABORT("Zero-sized bitmap alpha blit attempt detected!"); + + if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y)) + return; + } + + packcol16** SrcImage = Image; + packalpha** SrcAlphaMap = AlphaMap; + packcol16** DestImage = B.Bitmap->Image; + int NewRedLuminance = (B.Luminance >> 7 & 0x1F800) - 0x10000; + int NewGreenLuminance = (B.Luminance >> 4 & 0xFE0) - 0x800; + int NewBlueLuminance = (B.Luminance >> 2 & 0x3F) - 0x20; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackalpha* AlphaPtr = &SrcAlphaMap[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X]; + + for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr, ++AlphaPtr) + { + LOAD_SRC(); + + if(SrcCol != B.MaskColor) + { + LOAD_DEST(); + LOAD_ALPHA(); + NEW_LUMINATE_RED(); + NEW_APPLY_ALPHA_RED(); + NEW_LUMINATE_GREEN(); + NEW_APPLY_ALPHA_GREEN(); + NEW_LUMINATE_BLUE(); + NEW_APPLY_ALPHA_BLUE(); + STORE_COLOR(); + } + } + } +} + +/* Only works for 16x16 pictures :( */ + +void bitmap::CreateFlames(rawbitmap* RawBitmap, v2 RawPos, ulong SeedNFlags, int Frame) +{ + femath::SaveSeed(); + femath::SetSeed(SeedNFlags); + int FlameTop[16], FlameBottom[16], FlamePhase[16]; + int x, y; + + for(x = 0; x < 16; ++x) + { + FlameBottom[x] = NO_FLAME; + + for(y = 0; y < 16; ++y) + if(GetPixel(x, y) != TRANSPARENT_COLOR) + { + if(1 << RawBitmap->GetMaterialColorIndex(RawPos.X + x, RawPos.Y + y) & SeedNFlags) + { + FlamePhase[x] = RAND_16; + + if(y > 1) + { + FlameBottom[x] = y - 1; + + if(y >= 5) + FlameTop[x] = (y - (RAND_32 * y >> 5)) >> 1; + else + FlameTop[x] = 0; + } + else + { + FlameBottom[x] = 1; + FlameTop[x] = 0; + } + } + + break; + } + } + + for(x = 0; x < 16; ++x) + { + if(FlameBottom[x] != NO_FLAME) + { + int Phase = (Frame + FlamePhase[x]) & 15; + int Length = FlameBottom[x] - FlameTop[x]; + int Top = FlameBottom[x] - Length + Phase * (15 - Phase) * Length / 56; + + for(y = Top; y <= FlameBottom[x]; ++y) + { + int Pos = y - Top; + PowerPutPixel(x, y, MakeRGB16(255, 255 - (Pos << 7) / Length, 0), 127 + (Pos << 6) / Length, AVERAGE_PRIORITY); + } + } + } + + femath::LoadSeed(); +} + +void bitmap::CreateSparkle(v2 SparklePos, int Frame) +{ + if(Frame) + { + int Size = (Frame - 1) * (16 - Frame) / 10; + PowerPutPixel(SparklePos.X, SparklePos.Y, WHITE, 255, SPARKLE_PRIORITY); + + for(int c = 1; c < Size; ++c) + { + int Lightness = 191 + ((Size - c) << 6) / Size; + col16 RGB = MakeRGB16(Lightness, Lightness, Lightness); + PowerPutPixel(SparklePos.X + c, SparklePos.Y, RGB, 255, SPARKLE_PRIORITY); + PowerPutPixel(SparklePos.X - c, SparklePos.Y, RGB, 255, SPARKLE_PRIORITY); + PowerPutPixel(SparklePos.X, SparklePos.Y + c, RGB, 255, SPARKLE_PRIORITY); + PowerPutPixel(SparklePos.X, SparklePos.Y - c, RGB, 255, SPARKLE_PRIORITY); + } + } +} + +void bitmap::CreateFlies(ulong Seed, int Frame, int FlyAmount) +{ + femath::SaveSeed(); + femath::SetSeed(Seed); + + for(int c = 0; c < FlyAmount; ++c) + { + double Constant = double(RAND() % 10000) / 10000 * FPI; + v2 StartPos = v2(5 + RAND() % 6, 5 + RAND() % 6); + double Temp = (double(16 - Frame) * FPI) / 16; + + if(RAND() & 1) + Temp = -Temp; + + v2 Where; + Where.X = int(StartPos.X + sin(Constant + Temp) * 3); + Where.Y = int(StartPos.Y + sin(2*(Constant + Temp)) * 3); + PowerPutPixel(Where.X, Where.Y, MakeRGB16(40, 40, 60), 255, FLY_PRIORITY); + } + + femath::LoadSeed(); +} + +void bitmap::CreateLightning(ulong Seed, col16 Color) +{ + femath::SaveSeed(); + femath::SetSeed(Seed); + v2 StartPos; + v2 Direction(0, 0); + + do + { + do + { + if(RAND() & 1) + { + if(RAND() & 1) + { + StartPos.X = 0; + Direction.X = 1; + } + else + { + StartPos.X = Size.X - 1; + Direction.X = -1; + } + + StartPos.Y = RAND() % Size.Y; + } + else + { + if(RAND() & 1) + { + StartPos.Y = 0; + Direction.Y = 1; + } + else + { + StartPos.Y = Size.Y - 1; + Direction.Y = -1; + } + + StartPos.X = RAND() % Size.X; + } + } + while(GetPixel(StartPos) != TRANSPARENT_COLOR); + } + while(!CreateLightning(StartPos, Direction, NO_LIMIT, Color)); + + femath::LoadSeed(); +} + +struct pixelvectorcontroller +{ + static truth Handler(int x, int y) + { + if(CurrentSprite->GetPixel(x, y) == TRANSPARENT_COLOR) + { + PixelVector.push_back(v2(x, y)); + return true; + } + else + return false; + } + static std::vector PixelVector; + static bitmap* CurrentSprite; +}; + +std::vector pixelvectorcontroller::PixelVector; +bitmap* pixelvectorcontroller::CurrentSprite; + +truth bitmap::CreateLightning(v2 StartPos, v2 Direction, int MaxLength, col16 Color) +{ + pixelvectorcontroller::CurrentSprite = this; + std::vector& PixelVector = pixelvectorcontroller::PixelVector; + PixelVector.clear(); + v2 LastMove(0, 0); + int Counter = 0; + + for(;;) + { + v2 Move(1 + (RAND() & 3), 1 + (RAND() & 3)); + + if(Direction.X < 0 || (!Direction.X && RAND() & 1)) + Move.X = -Move.X; + + if(Direction.Y < 0 || (!Direction.Y && RAND() & 1)) + Move.Y = -Move.Y; + + LimitRef(Move.X, -StartPos.X, Size.X - StartPos.X - 1); + LimitRef(Move.Y, -StartPos.Y, Size.X - StartPos.Y - 1); + + if(Counter < 10 && ((!Move.Y && !LastMove.Y) || (Move.Y && LastMove.Y && (Move.X << 10) / Move.Y == (LastMove.X << 10) / LastMove.Y))) + { + ++Counter; + continue; + } + + Counter = 0; + + if(!mapmath::DoLine(StartPos.X, StartPos.Y, StartPos.X + Move.X, StartPos.Y + Move.Y) + || ulong(MaxLength) <= PixelVector.size()) + { + int Limit = Min(PixelVector.size(), MaxLength); + + for(int c = 0; c < Limit; ++c) + { + PutPixel(PixelVector[c], Color); + SafeSetPriority(PixelVector[c], LIGHTNING_PRIORITY); + } + + PixelVector.clear(); + return true; + } + + StartPos += Move; + LastMove = Move; + + if((Direction.X && (!StartPos.X || StartPos.X == Size.X - 1)) || (Direction.Y && (!StartPos.Y || StartPos.Y == Size.X - 1))) + { + PixelVector.clear(); + return false; + } + } +} + +void bitmap::BlitAndCopyAlpha(bitmap* Bitmap, int Flags) const +{ + if(!FastFlag) + { + if(!AlphaMap || !Bitmap->AlphaMap) + ABORT("Attempt to blit and copy alpha without an alpha map detected!"); + + if(Flags & ROTATE && Size.X != Size.Y) + ABORT("Blit and copy alpha error: FeLib supports only square rotating!"); + + if(Size.X != Bitmap->Size.X || Size.Y != Bitmap->Size.Y) + ABORT("Blit and copy alpha attempt of noncongruent bitmaps detected!"); + } + + packcol16** SrcImage = Image; + packalpha** SrcAlphaMap = AlphaMap; + packcol16** DestImage = Bitmap->Image; + packalpha** DestAlphaMap = Bitmap->AlphaMap; + + switch(Flags & 7) + { + case NONE: + { + memcpy(DestImage[0], SrcImage[0], XSizeTimesYSize * sizeof(packcol16)); + memcpy(DestAlphaMap[0], SrcAlphaMap[0], XSizeTimesYSize * sizeof(packalpha)); + break; + } + + case MIRROR: + { + int Width = Size.X; + int Height = Size.Y; + int DestX = Width - 1; + cpackcol16* SrcPtr = SrcImage[0]; + cpackalpha* SrcAlphaPtr = SrcAlphaMap[0]; + + for(int y = 0; y < Height; ++y) + { + cpackcol16* EndPtr = SrcPtr + Width; + packcol16* DestPtr = &DestImage[y][DestX]; + packalpha* DestAlphaPtr = &DestAlphaMap[y][DestX]; + + for(; SrcPtr != EndPtr; ++SrcPtr, --DestPtr, ++SrcAlphaPtr, --DestAlphaPtr) + { + *DestPtr = *SrcPtr; + *DestAlphaPtr = *SrcAlphaPtr; + } + } + + break; + } + + case FLIP: + { + int Height = Size.Y; + int Width = Size.X; + int DestY = Height - 1; + + for(int y = 0; y < Height; ++y) + { + memcpy(DestImage[DestY - y], SrcImage[y], Width * sizeof(packcol16)); + memcpy(DestAlphaMap[DestY - y], SrcAlphaMap[y], Width * sizeof(packalpha)); + } + + break; + } + + case (MIRROR | FLIP): + { + cpackcol16* SrcPtr = SrcImage[0]; + cpackcol16* EndPtr = SrcPtr + XSizeTimesYSize; + cpackalpha* SrcAlphaPtr = SrcAlphaMap[0]; + packcol16* DestPtr = &DestImage[Size.Y - 1][Size.X - 1]; + packalpha* DestAlphaPtr = &DestAlphaMap[Size.Y - 1][Size.X - 1]; + + for(; SrcPtr != EndPtr; ++SrcPtr, --DestPtr, ++SrcAlphaPtr, --DestAlphaPtr) + { + *DestPtr = *SrcPtr; + *DestAlphaPtr = *SrcAlphaPtr; + } + + break; + } + + case ROTATE: + { + cint Width = Size.X; + cpackcol16* SrcPtr = SrcImage[0]; + cpackalpha* SrcAlphaPtr = SrcAlphaMap[0]; + packcol16* DestBase = &DestImage[0][Width - 1]; + packalpha* DestAlphaBase = &DestAlphaMap[0][Width - 1]; + + for(int y = 0; y < Width; ++y) + { + cpackcol16* EndPtr = SrcPtr + Width; + packcol16* DestPtr = DestBase - y; + packalpha* DestAlphaPtr = DestAlphaBase - y; + + for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr += Width, ++SrcAlphaPtr, DestAlphaPtr += Width) + { + *DestPtr = *SrcPtr; + *DestAlphaPtr = *SrcAlphaPtr; + } + } + + break; + } + + case (MIRROR | ROTATE): + { + cint Width = Size.X; + cpackcol16* SrcPtr = SrcImage[0]; + cpackalpha* SrcAlphaPtr = SrcAlphaMap[0]; + packcol16* DestBase = DestImage[0]; + packalpha* DestAlphaBase = DestAlphaMap[0]; + + for(int y = 0; y < Width; ++y) + { + cpackcol16* EndPtr = SrcPtr + Width; + packcol16* DestPtr = DestBase + y; + packalpha* DestAlphaPtr = DestAlphaBase + y; + + for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr += Width, ++SrcAlphaPtr, DestAlphaPtr += Width) + { + *DestPtr = *SrcPtr; + *DestAlphaPtr = *SrcAlphaPtr; + } + } + + break; + } + + case (FLIP | ROTATE): + { + cint Width = Size.X; + cpackcol16* SrcPtr = SrcImage[0]; + cpackalpha* SrcAlphaPtr = SrcAlphaMap[0]; + packcol16* DestBase = &DestImage[Width - 1][Width - 1]; + packalpha* DestAlphaBase = &DestAlphaMap[Width - 1][Width - 1]; + + for(int y = 0; y < Width; ++y) + { + cpackcol16* EndPtr = SrcPtr + Width; + packcol16* DestPtr = DestBase - y; + packalpha* DestAlphaPtr = DestAlphaBase - y; + + for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= Width, ++SrcAlphaPtr, DestAlphaPtr -= Width) + { + *DestPtr = *SrcPtr; + *DestAlphaPtr = *SrcAlphaPtr; + } + } + + break; + } + + case (MIRROR | FLIP | ROTATE): + { + cint Width = Size.X; + cpackcol16* SrcPtr = SrcImage[0]; + cpackalpha* SrcAlphaPtr = SrcAlphaMap[0]; + packcol16* DestBase = DestImage[Width - 1]; + packalpha* DestAlphaBase = DestAlphaMap[Width - 1]; + + for(int y = 0; y < Width; ++y) + { + cpackcol16* EndPtr = SrcPtr + Width; + packcol16* DestPtr = DestBase + y; + packalpha* DestAlphaPtr = DestAlphaBase + y; + + for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= Width, ++SrcAlphaPtr, DestAlphaPtr -= Width) + { + *DestPtr = *SrcPtr; + *DestAlphaPtr = *SrcAlphaPtr; + } + } + + break; + } + } +} + +void bitmap::FillAlpha(alpha Alpha) +{ + memset(AlphaMap[0], Alpha, XSizeTimesYSize); +} + +void bitmap::PowerPutPixel(int X, int Y, col16 Color, alpha Alpha, priority Priority) +{ + if(X >= 0 && Y >= 0 && X < Size.X && Y < Size.Y) + { + Image[Y][X] = Color; + + if(AlphaMap) + AlphaMap[Y][X] = Alpha; + else if(Alpha != 255) + { + CreateAlphaMap(255); + AlphaMap[Y][X] = Alpha; + } + + if(PriorityMap) + PriorityMap[Y][X] = Priority; + } +} + +void bitmap::MaskedPriorityBlit(cblitdata& BlitData) const +{ + if(!PriorityMap || !BlitData.Bitmap->PriorityMap) + { + LuminanceMaskedBlit(BlitData); + return; + } + + blitdata B = BlitData; + + if(!FastFlag) + { + if(!B.Border.X || !B.Border.Y) + ABORT("Zero-sized bitmap masked priority blit attempt detected!"); + + if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y)) + return; + } + + packcol16** SrcImage = Image; + packpriority** SrcPriorityMap = PriorityMap; + packcol16** DestImage = B.Bitmap->Image; + packpriority** DestPriorityMap = B.Bitmap->PriorityMap; + int NewRedLuminance = (B.Luminance >> 7 & 0x1F800) - 0x10000; + int NewGreenLuminance = (B.Luminance >> 4 & 0xFE0) - 0x800; + int NewBlueLuminance = (B.Luminance >> 2 & 0x3F) - 0x20; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackpriority* SrcPriorityPtr = &SrcPriorityMap[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X]; + packpriority* DestPriorityPtr = &DestPriorityMap[B.Dest.Y + y][B.Dest.X]; + + for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr, ++SrcPriorityPtr, ++DestPriorityPtr) + { + LOAD_SRC(); + + if(SrcCol != B.MaskColor) + { + priority SrcPriority = *SrcPriorityPtr; + priority DestPriority = *DestPriorityPtr; + + if((SrcPriority & 0xF) >= (DestPriority & 0xF) || (SrcPriority & 0xF0) >= (DestPriority & 0xF0)) + { + NEW_LUMINATE_RED(); + NEW_LUMINATE_GREEN(); + NEW_LUMINATE_BLUE(); + STORE_COLOR(); + *DestPriorityPtr = SrcPriority; + } + } + } + } +} + +void bitmap::AlphaPriorityBlit(cblitdata& BlitData) const +{ + if(!AlphaMap) + { + MaskedPriorityBlit(BlitData); + return; + } + + if(!PriorityMap || !BlitData.Bitmap->PriorityMap) + { + AlphaLuminanceBlit(BlitData); + return; + } + + blitdata B = BlitData; + + if(!FastFlag) + { + if(!B.Border.X || !B.Border.Y) + ABORT("Zero-sized bitmap alpha priority blit attempt detected!"); + + if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y)) + return; + } + + packcol16** SrcImage = Image; + packalpha** SrcAlphaMap = AlphaMap; + packpriority** SrcPriorityMap = PriorityMap; + packcol16** DestImage = B.Bitmap->Image; + packpriority** DestPriorityMap = B.Bitmap->PriorityMap; + int NewRedLuminance = (B.Luminance >> 7 & 0x1F800) - 0x10000; + int NewGreenLuminance = (B.Luminance >> 4 & 0xFE0) - 0x800; + int NewBlueLuminance = (B.Luminance >> 2 & 0x3F) - 0x20; + + for(int y = 0; y < B.Border.Y; ++y) + { + cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X]; + cpackalpha* AlphaPtr = &SrcAlphaMap[B.Src.Y + y][B.Src.X]; + cpackpriority* SrcPriorityPtr = &SrcPriorityMap[B.Src.Y + y][B.Src.X]; + cpackcol16* EndPtr = SrcPtr + B.Border.X; + packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X]; + packpriority* DestPriorityPtr = &DestPriorityMap[B.Dest.Y + y][B.Dest.X]; + + for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr, ++AlphaPtr, ++SrcPriorityPtr, ++DestPriorityPtr) + { + LOAD_SRC(); + + if(SrcCol != B.MaskColor) + { + priority SrcPriority = *SrcPriorityPtr; + priority DestPriority = *DestPriorityPtr; + + if((SrcPriority & 0xF) >= (DestPriority & 0xF) + || (SrcPriority & 0xF0) >= (DestPriority & 0xF0)) + { + LOAD_DEST(); + LOAD_ALPHA(); + NEW_LUMINATE_RED(); + NEW_APPLY_ALPHA_RED(); + NEW_LUMINATE_GREEN(); + NEW_APPLY_ALPHA_GREEN(); + NEW_LUMINATE_BLUE(); + NEW_APPLY_ALPHA_BLUE(); + STORE_COLOR(); + *DestPriorityPtr = SrcPriority; + } + } + } + } +} + +void bitmap::InitPriorityMap(priority InitialValue) +{ + if(!PriorityMap) + Alloc2D(PriorityMap, Size.Y, Size.X); + + memset(PriorityMap[0], InitialValue, XSizeTimesYSize); +} + +void bitmap::FillPriority(priority Priority) +{ + memset(PriorityMap[0], Priority, XSizeTimesYSize); +} + +void bitmap::FastBlitAndCopyAlpha(bitmap* Bitmap) const +{ + if(!FastFlag) + { + if(!AlphaMap || !Bitmap->AlphaMap) + ABORT("Attempt to fast blit and copy alpha without an alpha map detected!"); + + if(Size.X != Bitmap->Size.X || Size.Y != Bitmap->Size.Y) + ABORT("Fast blit and copy alpha attempt of noncongruent bitmaps detected!"); + } + + memcpy(Bitmap->Image[0], Image[0], XSizeTimesYSize * sizeof(packcol16)); + memcpy(Bitmap->AlphaMap[0], AlphaMap[0], XSizeTimesYSize * sizeof(packalpha)); +} + +void bitmap::UpdateRandMap(long Index, truth Value) +{ + long c1 = XSizeTimesYSize + Index; + RandMap[c1] = Value; + + for(long c2 = c1 >> 1; c2; c1 = c2, c2 >>= 1) + { + Value |= RandMap[c1 ^ 1]; + + if(!RandMap[c2] != !Value) + RandMap[c2] = Value; + else + return; + } +} + +void bitmap::InitRandMap() +{ + if(!RandMap) + RandMap = new truth[XSizeTimesYSize << 1]; + + memset(RandMap, 0, (XSizeTimesYSize << 1) * sizeof(truth)); +} + +v2 bitmap::RandomizePixel() const +{ + if(!RandMap[1]) + return ERROR_V2; + + long Rand = RAND(); + ulong c, RandMask = 1; + ulong MapSize = XSizeTimesYSize << 1; + + for(c = 2; c < MapSize; c <<= 1) + if(RandMap[c + 1] && (!RandMap[c] || Rand & (RandMask <<= 1))) + ++c; + + c = (c - MapSize) >> 1; + return v2(c % Size.X, c / Size.X); +} + +void bitmap::CalculateRandMap() +{ + if(!AlphaMap) + ABORT("Alpha map needed to calculate random map."); + + ulong Size = XSizeTimesYSize; + + for(ulong c = 0; c < Size; ++c) + UpdateRandMap(c, AlphaMap[0][c]); +} + +void bitmap::AlphaPutPixel(int x, int y, col16 SrcCol, col24 Luminance, alpha Alpha) +{ + int DestCol = Image[y][x]; + int NewRedLuminance = (Luminance >> 7 & 0x1F800) - 0x10000; + int NewGreenLuminance = (Luminance >> 4 & 0xFE0) - 0x800; + int NewBlueLuminance = (Luminance >> 2 & 0x3F) - 0x20; + NEW_LUMINATE_RED(); + NEW_APPLY_ALPHA_RED(); + NEW_LUMINATE_GREEN(); + NEW_APPLY_ALPHA_GREEN(); + NEW_LUMINATE_BLUE(); + NEW_APPLY_ALPHA_BLUE(); + Image[y][x] = Red|Green|Blue; + +} + +alpha bitmap::CalculateAlphaAverage() const +{ + if(!AlphaMap) + ABORT("Alpha map needed to calculate alpha average!"); + + long Alphas = 0; + long AlphaSum = 0; + ulong Size = XSizeTimesYSize; + + for(ulong c = 0; c < Size; ++c) + { + packalpha* AlphaPtr = &AlphaMap[0][c]; + + if(*AlphaPtr) + { + AlphaSum += *AlphaPtr; + ++Alphas; + } + } + + return Alphas ? AlphaSum / Alphas : 0; +} + +cachedfont::cachedfont(v2 Size) : bitmap(Size) +{ + Alloc2D(MaskMap, Size.Y, Size.X); +} + +cachedfont::cachedfont(v2 Size, col16 Color) : bitmap(Size, Color) +{ + Alloc2D(MaskMap, Size.Y, Size.X); +} + +void cachedfont::PrintCharacter(cblitdata B) const +{ + if(B.Dest.X < 0 || B.Dest.Y < 0 || B.Dest.X + 10 >= B.Bitmap->Size.X || B.Dest.Y + 9 >= B.Bitmap->Size.Y) + { + NormalMaskedBlit(B); + return; + } + + packcol16** SrcLine = &Image[B.Src.Y]; + packcol16** EndLine = SrcLine + 9; + packcol16** SrcMaskLine = &MaskMap[B.Src.Y]; + packcol16** DestLine = &B.Bitmap->Image[B.Dest.Y]; + + for(; SrcLine != EndLine; ++SrcLine, ++SrcMaskLine, ++DestLine) + { + culong* FontPtr = reinterpret_cast(*SrcLine + B.Src.X); + culong* EndPtr = FontPtr + 5; + culong* MaskPtr = reinterpret_cast(*SrcMaskLine + B.Src.X); + ulong* DestPtr = reinterpret_cast(*DestLine + B.Dest.X); + + for(; FontPtr != EndPtr; ++DestPtr, ++MaskPtr, ++FontPtr) + *DestPtr = *DestPtr & *MaskPtr | *FontPtr; + } +} + +void cachedfont::CreateMaskMap() +{ + packcol16* SrcPtr = Image[0]; + packcol16* EndPtr = SrcPtr + XSizeTimesYSize; + packcol16* MaskPtr = MaskMap[0]; + + for(; SrcPtr != EndPtr; ++SrcPtr, ++MaskPtr) + if(*SrcPtr == TRANSPARENT_COLOR) + { + *SrcPtr = 0; + *MaskPtr = 0xFFFF; + } + else + *MaskPtr = 0; +} + +cint WaveDelta[] = { 1, 2, 2, 2, 1, 0, -1, -2, -2, -2, -1 }; + +void bitmap::Wobble(int Frame, int SpeedShift, truth Horizontally) +{ + int WavePos = (Frame << SpeedShift >> 1) - 14; + + if(Horizontally) + { + for(int c = 0; c < 11; ++c) + if(WavePos + c >= 0 && WavePos + c < Size.Y) + MoveLineHorizontally(WavePos + c, WaveDelta[c]); + } + else + { + for(int c = 0; c < 11; ++c) + if(WavePos + c >= 0 && WavePos + c < Size.X) + MoveLineVertically(WavePos + c, WaveDelta[c]); + } +} + +void bitmap::MoveLineVertically(int X, int Delta) +{ + int y; + + if(Delta < 0) + { + for(y = 0; y < Size.Y + Delta; ++y) + PowerPutPixel(X, y, GetPixel(X, y - Delta), AlphaMap ? GetAlpha(X, y - Delta) : 255, AVERAGE_PRIORITY); + + for(int y = -1; y >= Delta; --y) + PowerPutPixel(X, Size.Y + y, TRANSPARENT_COLOR, 255, AVERAGE_PRIORITY); + } + else if(Delta > 0) + { + for(y = Size.Y - 1; y >= Delta; --y) + PowerPutPixel(X, y, GetPixel(X, y - Delta), AlphaMap ? GetAlpha(X, y - Delta) : 255, AVERAGE_PRIORITY); + + for(y = 0; y < Delta; ++y) + PowerPutPixel(X, y, TRANSPARENT_COLOR, 255, AVERAGE_PRIORITY); + } +} + +void bitmap::MoveLineHorizontally(int Y, int Delta) +{ + int x; + + if(Delta < 0) + { + for(x = 0; x < Size.X + Delta; ++x) + PowerPutPixel(x, Y, GetPixel(x - Delta, Y), AlphaMap ? GetAlpha(x - Delta, Y) : 255, AVERAGE_PRIORITY); + + for(x = -1; x >= Delta; --x) + PowerPutPixel(Size.X + x, Y, TRANSPARENT_COLOR, 255, AVERAGE_PRIORITY); + } + else if(Delta > 0) + { + for(x = Size.X - 1; x >= Delta; --x) + PowerPutPixel(x, Y, GetPixel(x - Delta, Y), AlphaMap ? GetAlpha(x - Delta, Y) : 255, AVERAGE_PRIORITY); + + for(x = 0; x < Delta; ++x) + PowerPutPixel(x, Y, TRANSPARENT_COLOR, 255, AVERAGE_PRIORITY); + } +} + +void bitmap::InterLace() +{ + for(int y = 0; y < Size.Y; ++y) + if(!(y % 3)) + for(int x = 0; x < Size.X; ++x) + if(Image[y][x] != 0) + Image[y][x] = 1; +} diff --git a/FeLib/Source/config.cpp b/FeLib/Source/config.cpp new file mode 100644 index 0000000..6c7e97a --- /dev/null +++ b/FeLib/Source/config.cpp @@ -0,0 +1,229 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "config.h" +#include "save.h" +#include "felist.h" +#include "feio.h" + +configoption* configsystem::Option[MAX_CONFIG_OPTIONS]; +festring configsystem::ConfigFileName; +int configsystem::Options; + +void configsystem::AddOption(configoption* O) { Option[Options++] = O; } +void configsystem::NormalStringChanger(stringoption* O, cfestring& What) +{ O->Value = What; } +void configsystem::NormalNumberChanger(numberoption* O, long What) +{ O->Value = What; } +void configsystem::NormalTruthChanger(truthoption* O, truth What) +{ O->Value = What; } + +configoption::configoption(cchar* Name, cchar* Description) +: Name(Name), Description(Description) { } + +stringoption::stringoption(cchar* Name, cchar* Desc, + cfestring& Value, + void (*ValueDisplayer)(const stringoption*, + festring&), + truth (*ChangeInterface)(stringoption*), + void (*ValueChanger)(stringoption*, + cfestring&)) +: configoption(Name, Desc), + Value(Value), ValueDisplayer(ValueDisplayer), + ChangeInterface(ChangeInterface), + ValueChanger(ValueChanger) { } + +numberoption::numberoption(cchar* Name, cchar* Desc, long Value, + void (*ValueDisplayer)(const numberoption*, + festring&), + truth (*ChangeInterface)(numberoption*), + void (*ValueChanger)(numberoption*, long)) +: configoption(Name, Desc), + Value(Value), ValueDisplayer(ValueDisplayer), + ChangeInterface(ChangeInterface), + ValueChanger(ValueChanger) { } + +scrollbaroption::scrollbaroption(cchar* Name, + cchar* Desc, long Value, + void (*ValueDisplayer)(const numberoption*, + festring&), + truth (*ChangeInterface)(numberoption*), + void (*ValueChanger)(numberoption*, long), + void (*BarHandler)(long)) +: numberoption(Name, Desc, Value, ValueDisplayer, + ChangeInterface, ValueChanger), + BarHandler(BarHandler) { } + + +truthoption::truthoption(cchar* Name, cchar* Desc, truth Value, + void (*ValueDisplayer)(const truthoption*, festring&), + truth (*ChangeInterface)(truthoption*), + void (*ValueChanger)(truthoption*, truth)) +: configoption(Name, Desc), + Value(Value), ValueDisplayer(ValueDisplayer), + ChangeInterface(ChangeInterface), + ValueChanger(ValueChanger) { } + +truth configsystem::Save() +{ + std::ofstream SaveFile(ConfigFileName.CStr(), std::ios::out); + + if(!SaveFile.is_open()) + return false; + + for(int c = 0; c < Options; ++c) + { + SaveFile << Option[c]->Name << " = "; + Option[c]->SaveValue(SaveFile); + SaveFile << ";\n"; + } + + return true; +} + +truth configsystem::Load() +{ + inputfile SaveFile(ConfigFileName, 0, false); + + if(!SaveFile.IsOpen()) + return false; + + festring Word; + + for(SaveFile.ReadWord(Word, false); + !SaveFile.Eof(); + SaveFile.ReadWord(Word, false)) + { + /* Inefficient, but speed is probably not an issue here */ + + for(int c = 0; c < Options; ++c) + if(Word == Option[c]->Name) + Option[c]->LoadValue(SaveFile); + } + + return true; +} + +void configsystem::Show(void (*BackGroundDrawer)(), + void (*ListAttributeInitializer)(felist&), + truth SlaveScreen) +{ + int Chosen; + truth TruthChange = false; + + felist List(CONST_S("Which setting do you wish to configure?")); + List.AddDescription(CONST_S("")); + List.AddDescription(CONST_S("Setting Value")); + + for(;;) + { + if(SlaveScreen) + BackGroundDrawer(); + + List.Empty(); + + for(int c = 0; c < Options; ++c) + { + festring Entry = Option[c]->Description; + Entry.Capitalize(); + Entry.Resize(60); + Option[c]->DisplayeValue(Entry); + List.AddEntry(Entry, LIGHT_GRAY); + } + + if(SlaveScreen && ListAttributeInitializer) + ListAttributeInitializer(List); + + List.SetFlags(SELECTABLE|(SlaveScreen ? DRAW_BACKGROUND_AFTERWARDS : 0) + |(!SlaveScreen && !TruthChange ? FADE : 0)); + Chosen = List.Draw(); + festring String; + + if(Chosen < Options) + TruthChange = Option[Chosen]->ActivateChangeInterface(); + else + { + Save(); + return; + } + } +} + +void configsystem::NormalStringDisplayer(const stringoption* O, + festring& Entry) +{ + if(!O->Value.IsEmpty()) + Entry << O->Value; + else + Entry << '-'; +} + +void configsystem::NormalNumberDisplayer(const numberoption* O, + festring& Entry) +{ + Entry << O->Value; +} + +void configsystem::NormalTruthDisplayer(const truthoption* O, + festring& Entry) +{ + Entry << (O->Value ? "yes" : "no"); +} + +truth configsystem::NormalTruthChangeInterface(truthoption* O) +{ + O->ChangeValue(!O->Value); + return true; +} + +truth configsystem::NormalStringChangeInterface(stringoption* O) +{ + festring String; + + if(iosystem::StringQuestion(String, CONST_S("Set new ") + + O->Description + ':', + v2(30, 30), WHITE, 0, 80, + true, true) == NORMAL_EXIT) + O->ChangeValue(String); + + return false; +} + +truth configsystem::NormalNumberChangeInterface(numberoption* O) +{ + O->ChangeValue(iosystem::NumberQuestion(CONST_S("Set new ") + + O->Description + ':', + v2(30, 30), WHITE, true)); + return false; +} + +void stringoption::SaveValue(std::ofstream& SaveFile) const +{ + SaveFile << '\"' << Value.CStr() << '\"'; +} + +void stringoption::LoadValue(inputfile& SaveFile) +{ + SaveFile.ReadWord(); + SaveFile.ReadWord(Value); +} + +/* ??? */ + +void numberoption::SaveValue(std::ofstream& SaveFile) const +{ SaveFile << Value; } +void numberoption::LoadValue(inputfile& SaveFile) +{ Value = SaveFile.ReadNumber(); } +void truthoption::SaveValue(std::ofstream& SaveFile) const +{ SaveFile << Value; } +void truthoption::LoadValue(inputfile& SaveFile) +{ Value = SaveFile.ReadNumber(); } diff --git a/FeLib/Source/error.cpp b/FeLib/Source/error.cpp new file mode 100644 index 0000000..523f43f --- /dev/null +++ b/FeLib/Source/error.cpp @@ -0,0 +1,195 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include +#include +#include +#include + +#ifdef __DJGPP__ +#include +#include +#include "graphics.h" +#endif + +#ifdef WIN32 +#include "SDL.h" +#include +#else +#include +#endif + +#ifdef VC +#include +#define set_new_handler _set_new_handler +#else +#include +#define set_new_handler std::set_new_handler +#endif + +#include "error.h" + +/* Shouldn't be initialized here! */ + +cchar* globalerrorhandler::BugMsg += "\n\nPlease send bug report to ivan-users@sourceforge.net\n" +"including a brief description of what you did, what version\n" +"you are running and which kind of system you are using."; + +#ifdef VC +int (*globalerrorhandler::OldNewHandler)(size_t) = 0; +#else +void (*globalerrorhandler::OldNewHandler)() = 0; +#endif + +#ifdef __DJGPP__ +void (*globalerrorhandler::OldSignal[SIGNALS])(int); +int globalerrorhandler::Signal[SIGNALS] += { SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGTERM, SIGINT, SIGKILL, SIGQUIT }; +#endif + +void globalerrorhandler::Install() +{ + static truth AlreadyInstalled = false; + + if(!AlreadyInstalled) + { + AlreadyInstalled = true; + OldNewHandler = set_new_handler(NewHandler); + +#ifdef __DJGPP__ + for(int c = 0; c < SIGNALS; ++c) + OldSignal[c] = signal(Signal[c], SignalHandler); +#endif + + atexit(globalerrorhandler::DeInstall); + } +} + +void globalerrorhandler::DeInstall() +{ +#ifdef __DJGPP__ + for(int c = 0; c < SIGNALS; ++c) + signal(Signal[c], OldSignal[c]); +#endif + + set_new_handler(OldNewHandler); +} + +void globalerrorhandler::Abort(cchar* Format, ...) +{ + char Buffer[512]; + + va_list AP; + va_start(AP, Format); + vsprintf(Buffer, Format, AP); + va_end(AP); + + strcat(Buffer, BugMsg); + +#ifdef WIN32 + ShowWindow(GetActiveWindow(), SW_HIDE); + MessageBox(NULL, Buffer, "Program aborted!", + MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL); +#endif +#ifdef LINUX + std::cout << Buffer << std::endl; +#endif +#ifdef __DJGPP__ + graphics::DeInit(); + std::cout << Buffer << std::endl; +#endif + + exit(4); +} + +#ifdef VC +int globalerrorhandler::NewHandler(size_t) +#else + void globalerrorhandler::NewHandler() +#endif +{ + cchar* Msg = "Fatal Error: Memory depleted.\n" + "Get more RAM and hard disk space."; +#ifdef WIN32 + ShowWindow(GetActiveWindow(), SW_HIDE); + MessageBox(NULL, Msg, "Program aborted!", MB_OK|MB_ICONEXCLAMATION); +#endif +#ifdef LINUX + std::cout << Msg << std::endl; +#endif +#ifdef __DJGPP__ + graphics::DeInit(); + std::cout << Msg << std::endl; +#endif + + exit(1); + +#ifdef VC + return 0; +#endif +} + +#ifdef __DJGPP__ + +void globalerrorhandler::SignalHandler(int Signal) +{ + static truth AlreadySignalled = false; + + if(!AlreadySignalled) + { + AlreadySignalled = true; + graphics::DeInit(); + std::cout << "Fatal Error: "; + + switch (Signal) + { + case SIGABRT: + std::cout << "Abort"; + break; + case SIGFPE: + std::cout << "Divide by zero"; + break; + case SIGILL: + std::cout << "Invalid/unknown"; + break; + case SIGSEGV: + std::cout << "Segmentation violation"; + break; + case SIGTERM: + std::cout << "Termination request"; + break; + case SIGINT: + std::cout << "Break interrupt"; + break; + case SIGKILL: + std::cout << "Kill"; + break; + case SIGQUIT: + std::cout << "Quit"; + break; + default: + std::cout << "Unknown"; + } + + std::cout << " exception signalled."; + + if(Signal != SIGINT) + std::cout << BugMsg; + + std::cout << std::endl; + } + + exit(2); +} + +#endif diff --git a/FeLib/Source/febot.cpp b/FeLib/Source/febot.cpp new file mode 100644 index 0000000..cf459ab --- /dev/null +++ b/FeLib/Source/febot.cpp @@ -0,0 +1,149 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "febot.h" +#include "femath.h" +#include "save.h" + +/* The fword associated with the empty string is called ControlFWord and is always + the first in FWordSet (since S1 < S2 for every string S2 if S1 is empty). + Fwords to which ControlFWord is linked can begin a reply and those fwords linked + to ControlFWord can end it. */ + +void febot::Initialize(ushort NewChainLength) +{ + ChainLength = NewChainLength; + WordChainSet.clear(); + WordChainSet.insert(wordchain(ChainLength)); +} + +febot::wordchain* febot::GetControlWordChain() const +{ return const_cast(&*WordChainSet.begin()); } + +febot::wordchain* febot::wordchain::GetRandomLink() const +{ return Link[RAND() % Link.size()]; } + +febot::wordchain::wordchain(const febot::wordchain* WordChain, cfestring& NewWord) : String(++WordChain->String.begin(), WordChain->String.end()) +{ + String.push_back(NewWord); +} + +/* Searches for an fword associated with String in FWordSet and returns it + or creates a new one if needed */ + +febot::wordchain* febot::CreateWordChain(const febot::wordchain* WordChain, cfestring& NewWord) +{ + return const_cast(&*WordChainSet.insert(wordchain(WordChain, NewWord)).first); +} + +void febot::Save(outputfile& SaveFile) const +{ + + + /*SaveFile << ulong(WordChainSet.size()); + fwordset::const_iterator i1;*/ + + /* Speeds up saving tremendously for large files */ + + /*std::map FWordIndexMap; + ulong c; + + for(i1 = FWordSet.begin(), c = 0; i1 != FWordSet.end(); ++i1, ++c) + { + SaveFile << i1->String; + FWordIndexMap[&*i1] = c; + } + + for(i1 = FWordSet.begin(); i1 != FWordSet.end(); ++i1) + { + SaveFile << ulong(i1->Link.size()); + + for(c = 0; c < i1->Link.size(); ++c) + SaveFile << FWordIndexMap.find(i1->Link[c])->second; + }*/ +} + +void febot::Load(inputfile& SaveFile) +{ + wordchain* Chain = CreateWordChain(GetControlWordChain(), "Jaska"); + Chain = CreateWordChain(Chain, "on"); + /*FWordSet.clear(); + ulong MapSize; + SaveFile >> MapSize;*/ + + /* Speeds up loading tremendously for large files */ + + /*std::map FWordPtrMap; + + for(ulong c = 0; c < MapSize; ++c) + FWordPtrMap[c] = const_cast(&*FWordSet.insert(FWordSet.end(), fword(ReadType(SaveFile)))); + + for(fwordset::const_iterator i1 = FWordSet.begin(); i1 != FWordSet.end(); ++i1) + { + ulong LinkSize; + SaveFile >> LinkSize; + fword* FWord = const_cast(&*i1); + FWord->Link.resize(LinkSize); + + for(ulong c = 0; c < LinkSize; ++c) + FWord->Link[c] = FWordPtrMap.find(ReadType(SaveFile))->second; + }*/ +} + +void febot::BeTalkedTo(festring String) +{ + String.PreProcessForFebot(); + + if(!String.IsEmpty()) + { + festring Word; + wordchain* OldChain = GetControlWordChain(); + + for(String.ExtractWord(Word); !Word.IsEmpty(); String.ExtractWord(Word)) + { + wordchain* NewChain = CreateWordChain(OldChain, Word); + OldChain->Link.push_back(NewChain); + OldChain = NewChain; + } + + OldChain->Link.push_back(GetControlWordChain()); + } +} + +void febot::Reply(festring& String) const +{ + String.Empty(); + + if(GetControlWordChain()->Link.empty()) + return; + + wordchain* WordChain = GetControlWordChain()->GetRandomLink(); + String << WordChain->String.back(); + + for(WordChain = WordChain->GetRandomLink(); !WordChain->String.back().IsEmpty(); WordChain = WordChain->GetRandomLink()) + String << ' ' << WordChain->String.back(); + + String.PostProcessForFebot(); +} + +bool febot::wordchain::operator<(const febot::wordchain& W) const +{ + for(std::list::const_iterator i1 = String.begin(), i2 = W.String.begin(); i1 != String.end(); ++i1, ++i2) + { + char Comp = i1->Compare(*i2); + + if(Comp) + return Comp < 0; + } + + return false; +} diff --git a/FeLib/Source/feio.cpp b/FeLib/Source/feio.cpp new file mode 100644 index 0000000..0979b04 --- /dev/null +++ b/FeLib/Source/feio.cpp @@ -0,0 +1,763 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include +#include + +#ifdef WIN32 +#include +#endif + +#ifdef LINUX +#include +#include +#include +#include +#include +#endif + +#ifdef __DJGPP__ +#include +#endif + +#include "graphics.h" +#include "feio.h" +#include "whandler.h" +#include "felist.h" +#include "rawbit.h" +#include "festring.h" +#include "bitmap.h" + +#define PENT_WIDTH 70 + +/* Prints screen full of Text in color Color. If GKey is true function + waits for keypress. BitmapEditor is a pointer to function that is + called during every fade tick. */ + +void iosystem::TextScreen(cfestring& Text, v2 Disp, + col16 Color, truth GKey, truth Fade, + bitmapeditor BitmapEditor) +{ + bitmap Buffer(RES, 0); + Buffer.ActivateFastFlag(); + festring::sizetype c; + int LineNumber = 0; + + for(c = 0; c < Text.GetSize(); ++c) + if(Text[c] == '\n') + ++LineNumber; + + LineNumber >>= 1; + char Line[200]; + int Lines = 0, LastBeginningOfLine = 0; + + for(c = 0; c < Text.GetSize(); ++c) + if(Text[c] == '\n') + { + Line[c - LastBeginningOfLine] = 0; + v2 PrintPos((RES.X >> 1) - (strlen(Line) << 2) + Disp.X, + (RES.Y << 1) / 5 - (LineNumber - Lines) * 15 + Disp.Y); + FONT->Printf(&Buffer, PrintPos, Color, Line); + ++Lines; + LastBeginningOfLine = c + 1; + } + else + Line[c - LastBeginningOfLine] = Text[c]; + + Line[c - LastBeginningOfLine] = 0; + v2 PrintPos((RES.X >> 1) - (strlen(Line) << 2) + Disp.X, + (RES.Y << 1) / 5 - (LineNumber - Lines) * 15 + Disp.Y); + FONT->Printf(&Buffer, PrintPos, Color, Line); + + if(Fade) + Buffer.FadeToScreen(BitmapEditor); + else + { + BitmapEditor(&Buffer, true); + Buffer.FastBlit(DOUBLE_BUFFER); + graphics::BlitDBToScreen(); + } + + if(GKey) + if(BitmapEditor) + while(!READ_KEY()) + BitmapEditor(DOUBLE_BUFFER, false); + else + GET_KEY(); +} + +/* Returns amount of chars cSF in string sSH */ + +int CountChars(char cSF, cfestring& sSH) +{ + int iReturnCounter = 0; + + for(festring::sizetype i = 0; i < sSH.GetSize(); ++i) + if(sSH[i] == cSF) + ++iReturnCounter; + + return iReturnCounter; +} + +/* Draws a menu on bitmap BackGround to position Pos. festring Topic + is the text that is shown before the choices '\r' is a line-ending + character. Topic must end with a '\r'. sMS is a list of choices + separated by '\r'. sMS must end with '\r'. + + Color is the col of font of sMS, SmallText1 and SmallText2. SmallText1 + is printed to the lower-left corner and SmallText2 is printed to the + lower-right. They both can have line-ending characters ('\r') and must + also always end with one. */ + +/* Warning: This function is utter garbage that just happens to work. + If you need to use this function use the comments. Don't try to + understand it. It is impossible. */ + +int iosystem::Menu(cbitmap* BackGround, v2 Pos, + cfestring& Topic, cfestring& sMS, + col16 Color, cfestring& SmallText1, + cfestring& SmallText2) +{ + if(CountChars('\r',sMS) < 1) + return (-1); + + truth bReady = false; + int iSelected = 0; + bitmap Backup(DOUBLE_BUFFER); + Backup.ActivateFastFlag(); + bitmap Buffer(RES); + Buffer.ActivateFastFlag(); + int c = 0; + + if(BackGround) + BackGround->FastBlit(&Buffer); + else + Buffer.ClearToColor(0); + + festring sCopyOfMS; + festring VeryUnGuruPrintf; + + while(!bReady) + { + clock_t StartTime = clock(); + sCopyOfMS = Topic; + int i; + + for(i = 0; i < CountChars('\r', Topic); ++i) + { + festring::sizetype RPos = sCopyOfMS.Find('\r'); + VeryUnGuruPrintf = sCopyOfMS; + VeryUnGuruPrintf.Resize(RPos); + sCopyOfMS.Erase(0,RPos+1); + v2 PrintPos(Pos.X - (VeryUnGuruPrintf.GetSize() << 2), + Pos.Y - 30 - (CountChars('\r', Topic) + + CountChars('\r', sMS)) * 25 + i * 25); + FONT->Printf(&Buffer, PrintPos, RED, "%s", VeryUnGuruPrintf.CStr()); + } + + sCopyOfMS = sMS; + + for(i = 0; i < CountChars('\r', sMS); ++i) + { + festring::sizetype RPos = sCopyOfMS.Find('\r'); + VeryUnGuruPrintf = sCopyOfMS; + VeryUnGuruPrintf.Resize(RPos); + sCopyOfMS.Erase(0,RPos+1); + int XPos = Pos.X - ((VeryUnGuruPrintf.GetSize() + 3) << 2); + int YPos = Pos.Y - CountChars('\r', sMS) * 25 + i * 50; + Buffer.Fill(XPos, YPos, ((VeryUnGuruPrintf.GetSize() + 3) << 3), 9, 0); + + if(i == iSelected) + FONT->PrintfUnshaded(&Buffer, v2(XPos + 1, YPos + 1), WHITE, + "%d. %s", i + 1, VeryUnGuruPrintf.CStr()); + else + FONT->Printf(&Buffer, v2(XPos, YPos), Color, "%d. %s", + i + 1, VeryUnGuruPrintf.CStr()); + } + + sCopyOfMS = SmallText1; + + for(i = 0; i < CountChars('\r', SmallText1); ++i) + { + festring::sizetype RPos = sCopyOfMS.Find('\r'); + VeryUnGuruPrintf = sCopyOfMS; + VeryUnGuruPrintf.Resize(RPos); + sCopyOfMS.Erase(0,RPos+1); + v2 PrintPos(3, RES.Y - CountChars('\r', SmallText1) * 10 + i * 10); + FONT->Printf(&Buffer, PrintPos, Color, "%s", VeryUnGuruPrintf.CStr()); + } + + sCopyOfMS = SmallText2; + + for(i = 0; i < CountChars('\r', SmallText2); ++i) + { + festring::sizetype RPos = sCopyOfMS.Find('\r'); + VeryUnGuruPrintf = sCopyOfMS; + VeryUnGuruPrintf.Resize(RPos); + sCopyOfMS.Erase(0,RPos+1); + v2 PrintPos(RES.X - (VeryUnGuruPrintf.GetSize() << 3) - 2, + RES.Y - CountChars('\r', SmallText2) * 10 + i * 10); + FONT->Printf(&Buffer, PrintPos, Color, "%s", VeryUnGuruPrintf.CStr()); + } + + int k; + + if(c < 5) + { + int Element = 127 - c * 25; + blitdata BlitData = { DOUBLE_BUFFER, + { 0, 0 }, + { 0, 0 }, + { RES.X, RES.Y }, + { MakeRGB24(Element, Element, Element) }, + 0, + 0 }; + Backup.LuminanceMaskedBlit(BlitData); + Buffer.SimpleAlphaBlit(DOUBLE_BUFFER, c++ * 50, 0); + graphics::BlitDBToScreen(); + while(clock() - StartTime < 0.05 * CLOCKS_PER_SEC); + k = READ_KEY(); + } + else + { + Buffer.FastBlit(DOUBLE_BUFFER); + graphics::BlitDBToScreen(); + k = GET_KEY(false); + } + + switch(k) + { + case KEY_UP: + if(iSelected > 0) + --iSelected; + else + iSelected = (CountChars('\r',sMS)-1); + break; + + case KEY_DOWN: + if(iSelected < (CountChars('\r',sMS)-1)) + ++iSelected; + else + iSelected = 0; + break; + + case 0x00D: + bReady = true; + break; + + default: + if(k > 0x30 && k < 0x31 + CountChars('\r',sMS)) + return k - 0x31; + } + } + + return iSelected; +} + +/* Asks the user a question requiring a string answer. The answer is saved + to Input. Input can also already have a default something retyped for + the user. Topic is the question or other topic for the question. Pos the + cordinates of where the question is printed on the screen. Color is the + col of all the fonts in this function. Enter is only accepted when the + answers length is between MinLetters and MaxLetters. If Fade is true the + question is asked on a black background and the transition to that is a + fade. If AllowExit is true the user can abort with the esc-key. + + The function returns ABORTED (when user aborts with esc) or + NORMAL_EXIT. */ + +int iosystem::StringQuestion(festring& Input, + cfestring& Topic, + v2 Pos, col16 Color, + festring::sizetype MinLetters, + festring::sizetype MaxLetters, + truth Fade, truth AllowExit, + stringkeyhandler StringKeyHandler) +{ + v2 V(RES.X, 9); ///??????????? + bitmap BackUp(V, 0); + blitdata B = { &BackUp, + { Pos.X, Pos.Y + 10 }, + { 0, 0 }, + { (MaxLetters << 3) + 9, 9 }, + { 0 }, + 0, + 0 }; + + if(Fade) + { + bitmap Buffer(RES, 0); + Buffer.ActivateFastFlag(); + FONT->Printf(&Buffer, Pos, Color, "%s", Topic.CStr()); + FONT->Printf(&Buffer, v2(Pos.X, Pos.Y + 10), Color, "%s_", Input.CStr()); + Buffer.FadeToScreen(); + } + else + DOUBLE_BUFFER->NormalBlit(B); + + truth TooShort = false; + FONT->Printf(DOUBLE_BUFFER, Pos, Color, "%s", Topic.CStr()); + Swap(B.Src, B.Dest); + + for(int LastKey = 0;; LastKey = 0) + { + B.Bitmap = DOUBLE_BUFFER; + BackUp.NormalBlit(B); + FONT->Printf(DOUBLE_BUFFER, v2(Pos.X, Pos.Y + 10), + Color, "%s_", Input.CStr()); + + if(TooShort) + { + FONT->Printf(DOUBLE_BUFFER, v2(Pos.X, Pos.Y + 30), + Color, "Too short!"); + TooShort = false; + } + + graphics::BlitDBToScreen(); + + if(TooShort) + DOUBLE_BUFFER->Fill(Pos.X, Pos.Y + 30, 81, 9, 0); + + /* if LastKey is less than 20 it is a control + character not available in the font */ + + while(!(IsAcceptableForStringQuestion(LastKey))) + { + LastKey = GET_KEY(false); + + if(StringKeyHandler != 0 && StringKeyHandler(LastKey, Input)) + { + LastKey = 0; + break; + } + } + + if(!LastKey) + continue; + + if(LastKey == KEY_ESC && AllowExit) + return ABORTED; + + if(LastKey == KEY_BACK_SPACE) + { + if(!Input.IsEmpty()) + Input.Resize(Input.GetSize() - 1); + + continue; + } + + if(LastKey == KEY_ENTER) + if(Input.GetSize() >= MinLetters) + break; + else + { + TooShort = true; + continue; + } + + if(LastKey >= 0x20 && Input.GetSize() < MaxLetters + && (LastKey != ' ' || !Input.IsEmpty())) + Input << char(LastKey); + } + + /* Delete all the trailing spaces */ + + festring::sizetype LastAlpha = festring::NPos; + + for(festring::sizetype c = 0; c < Input.GetSize(); ++c) + if(Input[c] != ' ') + LastAlpha = c; + + /* note: festring::NPos + 1 == 0 */ + + Input.Resize(LastAlpha + 1); + + return NORMAL_EXIT; +} + +/* Ask a question defined by Topic. This function only accepts numbers. + The question is drawn to cordinates given by Pos. All fonts are Color + coled. If Fade is true the question is asked on a black background + and the transition to that is a fade. */ + +long iosystem::NumberQuestion(cfestring& Topic, v2 Pos, col16 Color, + truth Fade, truth ReturnZeroOnEsc) +{ + v2 V(RES.X, 9); ///??????????? + bitmap BackUp(V, 0); + blitdata B = { &BackUp, + { Pos.X, Pos.Y + 10 }, + { 0, 0 }, + { 105, 9 }, + { 0 }, + 0, + 0 }; + + if(Fade) + { + bitmap Buffer(RES, 0); + Buffer.ActivateFastFlag(); + FONT->Printf(&Buffer, Pos, Color, "%s", Topic.CStr()); + FONT->Printf(&Buffer, v2(Pos.X, Pos.Y + 10), Color, "_"); + Buffer.FadeToScreen(); + } + else + DOUBLE_BUFFER->NormalBlit(B); + + festring Input; + FONT->Printf(DOUBLE_BUFFER, Pos, Color, "%s", Topic.CStr()); + Swap(B.Src, B.Dest); + + for(int LastKey = 0;; LastKey = 0) + { + B.Bitmap = DOUBLE_BUFFER; + BackUp.NormalBlit(B); + FONT->Printf(DOUBLE_BUFFER, v2(Pos.X, Pos.Y + 10), + Color, "%s_", Input.CStr()); + graphics::BlitDBToScreen(); + + while(!isdigit(LastKey) && LastKey != KEY_BACK_SPACE + && LastKey != KEY_ENTER && LastKey != KEY_ESC + && (LastKey != '-' || !Input.IsEmpty())) + LastKey = GET_KEY(false); + + if(LastKey == KEY_BACK_SPACE) + { + if(!Input.IsEmpty()) + Input.Resize(Input.GetSize() - 1); + + continue; + } + + if(LastKey == KEY_ENTER) + break; + + if(LastKey == KEY_ESC) + { + if(ReturnZeroOnEsc) + return 0; + + break; + } + + if(Input.GetSize() < 12) + Input << char(LastKey); + } + + return atoi(Input.CStr()); +} + +/* Asks a question defined by Topic and the answer is numeric. The value is + represented by a scroll bar. The topic is drawn to position Pos. Step is + the step size. Min and Max are the minimum and maximum values. If the + player aborts with the esc key AbortValue is returned. Color1 is the + left portion controls the col of left portion of the scroll bar and + Color2 the right portion. LeftKey and RightKey are the keys for changing + the scrollbar. Although '<' and '>' also work always. If Fade is true + the screen is faded to black before drawing th scrollbar. If Handler is + set it is called always when the value of the scroll bar changes. */ + +long iosystem::ScrollBarQuestion(cfestring& Topic, v2 Pos, + long StartValue, long Step, + long Min, long Max, long AbortValue, + col16 TopicColor, col16 Color1, + col16 Color2, int LeftKey, int RightKey, + truth Fade, void (*Handler)(long)) +{ + long BarValue = StartValue; + festring Input; + truth FirstTime = true; + v2 V(RES.X, 20); ///??????????? + bitmap BackUp(V, 0); + + if(Fade) + { + bitmap Buffer(RES, 0); + Buffer.ActivateFastFlag(); + FONT->Printf(&Buffer, Pos, TopicColor, + "%s %ld", Topic.CStr(), StartValue); + FONT->Printf(&Buffer, v2(Pos.X + (Topic.GetSize() << 3) + 8, Pos.Y + 1), + TopicColor, "_"); + Buffer.DrawHorizontalLine(Pos.X + 1, Pos.X + 201, + Pos.Y + 15, Color2, false); + Buffer.DrawVerticalLine(Pos.X + 201, Pos.Y + 12, + Pos.Y + 18, Color2, false); + Buffer.DrawHorizontalLine(Pos.X + 1, Pos.X + 1 + + (BarValue - Min) * 200 / (Max - Min), + Pos.Y + 15, Color1, true); + Buffer.DrawVerticalLine(Pos.X + 1, Pos.Y + 12, Pos.Y + 18, Color1, true); + Buffer.DrawVerticalLine(Pos.X + 1 + (BarValue - Min) + * 200 / (Max - Min), Pos.Y + 12, + Pos.Y + 18, Color1, true); + Buffer.FadeToScreen(); + } + else + { + blitdata B = { &BackUp, + { Pos.X, Pos.Y }, + { 0, 0 }, + { RES.X, 20 }, + { 0 }, + 0, + 0 }; + + DOUBLE_BUFFER->NormalBlit(B); + } + + blitdata B1 = { 0, + { 0, 0 }, + { Pos.X, Pos.Y }, + { ((Topic.GetSize() + 14) << 3) + 1, 10 }, + { 0 }, + 0, + 0 }; + blitdata B2 = { 0, + { 0, 10 }, + { Pos.X, Pos.Y + 10 }, + { 203, 10 }, + { 0 }, + 0, + 0 }; + + for(int LastKey = 0;; LastKey = 0) + { + if(!FirstTime) + BarValue = Input.IsEmpty() ? Min : atoi(Input.CStr()); + + if(BarValue < Min) + BarValue = Min; + + if(BarValue > Max) + BarValue = Max; + + if(Handler) + Handler(BarValue); + + B1.Bitmap = B2.Bitmap = DOUBLE_BUFFER; + BackUp.NormalBlit(B1); + BackUp.NormalBlit(B2); + + if(FirstTime) + { + FONT->Printf(DOUBLE_BUFFER, Pos, TopicColor, + "%s %ld", Topic.CStr(), StartValue); + FONT->Printf(DOUBLE_BUFFER, + v2(Pos.X + (Topic.GetSize() << 3) + 8, Pos.Y + 1), + TopicColor, "_"); + FirstTime = false; + } + else + { + FONT->Printf(DOUBLE_BUFFER, Pos, TopicColor, + "%s %s", Topic.CStr(), Input.CStr()); + FONT->Printf(DOUBLE_BUFFER, + v2(Pos.X + ((Topic.GetSize() + Input.GetSize()) << 3) + 8, + Pos.Y + 1), TopicColor, "_"); + } + + DOUBLE_BUFFER->DrawHorizontalLine(Pos.X + 1, Pos.X + 201, + Pos.Y + 15, Color2, false); + DOUBLE_BUFFER->DrawVerticalLine(Pos.X + 201, Pos.Y + 12, + Pos.Y + 18, Color2, false); + DOUBLE_BUFFER->DrawHorizontalLine(Pos.X + 1, Pos.X + 1 + + (BarValue - Min) * 200 / (Max - Min), + Pos.Y + 15, Color1, true); + DOUBLE_BUFFER->DrawVerticalLine(Pos.X + 1, Pos.Y + 12, + Pos.Y + 18, Color1, true); + DOUBLE_BUFFER->DrawVerticalLine(Pos.X + 1 + (BarValue - Min) + * 200 / (Max - Min), Pos.Y + 12, + Pos.Y + 18, Color1, true); + graphics::BlitDBToScreen(); + + while(!isdigit(LastKey) && LastKey != KEY_ESC + && LastKey != KEY_BACK_SPACE && LastKey != KEY_ENTER + && LastKey != KEY_SPACE && LastKey != '<' + && LastKey != '>' && LastKey != RightKey && LastKey != LeftKey) + LastKey = GET_KEY(false); + + if(LastKey == KEY_ESC) + { + BarValue = AbortValue; + break; + } + + if(LastKey == KEY_BACK_SPACE) + { + if(!Input.IsEmpty()) + Input.Resize(Input.GetSize() - 1); + + continue; + } + + if(LastKey == KEY_ENTER || LastKey == KEY_SPACE) + break; + + if(LastKey == '<' || LastKey == LeftKey) + { + BarValue -= Step; + + if(BarValue < Min) + BarValue = Min; + + Input.Empty(); + Input << BarValue; + continue; + } + + if(LastKey == '>' || LastKey == RightKey) + { + BarValue += Step; + + if(BarValue > Max) + BarValue = Max; + + Input.Empty(); + Input << BarValue; + continue; + } + + if(Input.GetSize() < 12) + Input << char(LastKey); + } + + return BarValue; +} + +/* DirectoryName is the directory where the savefiles are located. Returns + the selected file or "" if an error occures or if no files are found. */ + +festring iosystem::ContinueMenu(col16 TopicColor, col16 ListColor, + cfestring& DirectoryName) +{ +#ifdef WIN32 + struct _finddata_t Found; + long hFile; + int Check = 0; + festring Buffer; + felist List(CONST_S("Choose a file and be sorry:"), TopicColor); + hFile = _findfirst(festring(DirectoryName + "*.sav").CStr(), &Found); + + /* No file found */ + if(hFile == -1L) + { + TextScreen(CONST_S("You don't have any previous saves."), ZERO_V2, TopicColor); + return ""; + } + + while(!Check) + { + /* Copy all the filenames to Buffer */ + /* Buffer = Found.name; Doesn't work because of a festring bug */ + + Buffer.Empty(); + Buffer << Found.name; + List.AddEntry(Buffer, ListColor); + Check = _findnext(hFile, &Found); + } + + Check = List.Draw(); + + /* an error has occured in felist */ + + if(Check & FELIST_ERROR_BIT) + return ""; + + return List.GetEntry(Check); +#endif + +#ifdef LINUX + DIR* dp; + struct dirent* ep; + festring Buffer; + felist List(CONST_S("Choose a file and be sorry:"), TopicColor); + dp = opendir(DirectoryName.CStr()); + + if(dp) + { + while((ep = readdir(dp))) + { + /* Buffer = ep->d_name; Doesn't work because of a festring bug */ + Buffer.Empty(); + Buffer << ep->d_name; + /* Add to List all save files */ + if(Buffer.Find(".sav") != Buffer.NPos) + List.AddEntry(Buffer, ListColor); + } + + if(List.IsEmpty()) + { + TextScreen(CONST_S("You don't have any previous saves."), ZERO_V2, TopicColor); + return ""; + } + else + { + int Check = List.Draw(); + + if(Check & FELIST_ERROR_BIT) + return ""; + + return List.GetEntry(Check); + } + + } + + return ""; +#endif + +#ifdef __DJGPP__ + struct ffblk Found; + int Check = 0; + festring Buffer; + felist List(CONST_S("Choose a file and be sorry:"), TopicColor); + + /* get all filenames ending with .sav. Accepts all files even if they + FA_HIDDEN or FA_ARCH flags are set (ie. they are hidden or archives */ + + Check = findfirst(festring(DirectoryName + "*.sav").CStr(), + &Found, FA_HIDDEN | FA_ARCH); + + if(Check) + { + TextScreen(CONST_S("You don't have any previous saves."), ZERO_V2, TopicColor); + return ""; + } + + while(!Check) + { + /* Buffer = Found.ff_name; Doesn't work because of a festring bug */ + Buffer.Empty(); + Buffer << Found.ff_name; + List.AddEntry(Buffer, ListColor); + Check = findnext(&Found); + } + + Check = List.Draw(); + + if(Check & FELIST_ERROR_BIT) + return ""; + + return List.GetEntry(Check); +#endif +} + +truth iosystem::IsAcceptableForStringQuestion(char Key) +{ + if(Key == '|' || Key == '<' || Key == '>' || Key == '?' || Key == '*' + || Key == '/' || Key == '\\' || Key == ':') + return false; + + if(Key < 0x20 + && !(Key == KEY_BACK_SPACE || Key == KEY_ENTER || Key == KEY_ESC)) + return false; + + return true; +} diff --git a/FeLib/Source/felist.cpp b/FeLib/Source/felist.cpp new file mode 100644 index 0000000..4ea63dd --- /dev/null +++ b/FeLib/Source/felist.cpp @@ -0,0 +1,557 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include + +#include "felist.h" +#include "graphics.h" +#include "bitmap.h" +#include "whandler.h" +#include "rawbit.h" +#include "save.h" +#include "festring.h" + +const felist* FelistCurrentlyDrawn = 0; + +truth FelistDrawController() +{ + FelistCurrentlyDrawn->DrawPage(DOUBLE_BUFFER); + return true; +} + +struct felistentry +{ + felistentry() : ImageKey(NO_IMAGE) { } + felistentry(cfestring&, col16, uint, uint, truth); + festring String; + col16 Color; + uint Marginal; + uint ImageKey; + truth Selectable; +}; + +felistentry::felistentry(cfestring& String, col16 Color, + uint Marginal, uint ImageKey, truth Selectable) +: String(String), Color(Color), Marginal(Marginal), + ImageKey(ImageKey), Selectable(Selectable) +{ +} + +outputfile& operator<<(outputfile& SaveFile, const felistentry* Entry) +{ + SaveFile << Entry->String << Entry->Color + << Entry->Marginal << Entry->Selectable; + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, felistentry*& Entry) +{ + Entry = new felistentry; + SaveFile >> Entry->String >> Entry->Color + >> Entry->Marginal >> Entry->Selectable; + return SaveFile; +} + +struct felistdescription +{ + felistdescription() { } + felistdescription(cfestring& String, col16 Color) + : String(String), Color(Color) { } + festring String; + col16 Color; +}; + +felist::felist(cfestring& Topic, col16 TopicColor, uint Maximum) +: Maximum(Maximum), Selected(0), Pos(10, 10), Width(780), + PageLength(30), BackColor(0), Flags(SELECTABLE|FADE), + UpKey(KEY_UP), DownKey(KEY_DOWN), EntryDrawer(0) +{ + AddDescription(Topic, TopicColor); +} + +felist::~felist() +{ + Empty(); + + for(uint c = 0; c < Description.size(); ++c) + delete Description[c]; +} + +truth felist::IsEmpty() const +{ return Entry.empty(); } +uint felist::GetLength() const +{ return Entry.size(); } +uint felist::GetLastEntryIndex() const +{ return Entry.size() - 1; } +festring felist::GetEntry(uint I) const +{ return Entry[I]->String; } +col16 felist::GetColor(uint I) const +{ return Entry[I]->Color; } +void felist::SetColor(uint I, col16 What) +{ Entry[I]->Color = What; } +void felist::AddDescription(cfestring& Str, col16 Color) +{ Description.push_back(new felistdescription(Str, Color)); } + +void felist::Pop() +{ + delete Entry[GetLastEntryIndex()]; + Entry.pop_back(); +} + +uint felist::Draw() +{ + while(Entry.size() && Entry[GetLastEntryIndex()]->String.IsEmpty()) + Pop(); + + if(Entry.empty()) + return LIST_WAS_EMPTY; + + FelistCurrentlyDrawn = this; + + if(globalwindowhandler::ControlLoopsInstalled()) + globalwindowhandler::InstallControlLoop(FelistDrawController); + + bitmap BackGround(RES); + BackGround.ActivateFastFlag(); + bitmap* Buffer; + + if(Flags & FADE) + { + Buffer = new bitmap(RES, 0); + Buffer->ActivateFastFlag(); + BackGround.ClearToColor(0); + } + else + { + Buffer = DOUBLE_BUFFER; + Buffer->FastBlit(&BackGround); + } + + uint c; + uint Return, Selectables = 0; + truth JustSelectMove = false; + + for(c = 0; c < Entry.size(); ++c) + if(Entry[c]->Selectable) + ++Selectables; + + if(Selected >= Selectables) + Selected = Selectables - 1; + + if(Flags & SELECTABLE) + PageBegin = Selected - Selected % PageLength; + else if(Flags & INVERSE_MODE) + PageBegin = GetLastEntryIndex() - GetLastEntryIndex() % PageLength; + else + PageBegin = 0; + + for(;;) + { + truth AtTheEnd = DrawPage(Buffer); + + if(Flags & FADE) + { + if(JustSelectMove) + { + Buffer->FastBlit(DOUBLE_BUFFER); + graphics::BlitDBToScreen(); + } + else + Buffer->FadeToScreen(); + + JustSelectMove = false; + } + else + graphics::BlitDBToScreen(); + + uint Pressed = GET_KEY(false); + + if(Flags & SELECTABLE && Pressed > 64 + && Pressed < 91 && Pressed - 65 < PageLength + && Pressed - 65 + PageBegin < Selectables) + { + Return = Selected = Pressed - 65 + PageBegin; + break; + } + + if(Flags & SELECTABLE && Pressed > 96 + && Pressed < 123 && Pressed - 97 < PageLength + && Pressed - 97 + PageBegin < Selectables) + { + Return = Selected = Pressed - 97 + PageBegin; + break; + } + + if(Flags & SELECTABLE && Pressed == UpKey) + { + if(Selected) + { + --Selected; + + if(Selected < PageBegin) + { + BackGround.FastBlit(Buffer); + PageBegin -= PageLength; + } + else + JustSelectMove = true; + } + else + { + for(c = 0, Selected = 0; c < Entry.size(); ++c) + if(Entry[c]->Selectable) + ++Selected; + + --Selected; + + if(PageBegin == Selected - Selected % PageLength) + JustSelectMove = true; + else + { + BackGround.FastBlit(Buffer); + PageBegin = Selected - Selected % PageLength; + } + } + + continue; + } + + if(Flags & SELECTABLE && Pressed == DownKey) + { + if(!AtTheEnd || Selected != Selectables - 1) + { + ++Selected; + + if(Selected > PageBegin + PageLength - 1) + { + BackGround.FastBlit(Buffer); + PageBegin += PageLength; + } + else + JustSelectMove = true; + } + else + { + if(!PageBegin) + JustSelectMove = true; + else + BackGround.FastBlit(Buffer); + + Selected = PageBegin = 0; + } + + continue; + } + + if(Flags & SELECTABLE && Pressed == KEY_ENTER) + { + Return = Selected; + break; + } + + if(Pressed == KEY_ESC) + { + Return = ESCAPED; + break; + } + + if((AtTheEnd && !(Flags & INVERSE_MODE)) + || (!PageBegin && Flags & INVERSE_MODE)) + { + Return = NOTHING_SELECTED; + break; + } + else + { + BackGround.FastBlit(Buffer); + + if(Flags & INVERSE_MODE) + PageBegin -= PageLength; + else + PageBegin += PageLength; + + if(Flags & SELECTABLE) + Selected = PageBegin; + } + } + + if(!(Flags & FADE)) + { + if(Flags & DRAW_BACKGROUND_AFTERWARDS) + BackGround.FastBlit(DOUBLE_BUFFER); + + if(Flags & BLIT_AFTERWARDS) + graphics::BlitDBToScreen(); + } + else + delete Buffer; + + globalwindowhandler::DeInstallControlLoop(FelistDrawController); + return Return; +} + +static festring Str; + +truth felist::DrawPage(bitmap* Buffer) const +{ + uint LastFillBottom = Pos.Y + 23 + Description.size() * 10; + DrawDescription(Buffer); + + uint c, i; // c == entry index, i == selectable index + + for(c = 0, i = 0; i != PageBegin; ++c) + if(Entry[c]->Selectable) + ++i; + + while(!Entry[c]->Selectable && Entry[c]->String.IsEmpty()) ++c; + std::vector Chapter; + + for(;;) + { + Str.Empty(); + uint Marginal = Entry[c]->Marginal; + + if(Flags & SELECTABLE && Entry[c]->Selectable) + { + Str << char('A' + (i - PageBegin)) << ": "; + Marginal += 3; + } + + Str << Entry[c]->String; + + if(Entry[c]->ImageKey != NO_IMAGE) + { + if(Str.GetSize() <= (Width - 50) >> 3) + { + Buffer->Fill(Pos.X + 3, LastFillBottom, Width - 6, 20, BackColor); + + if(EntryDrawer) + EntryDrawer(Buffer, + v2(Pos.X + 13, LastFillBottom), + Entry[c]->ImageKey); + + if(Flags & SELECTABLE && Entry[c]->Selectable && Selected == i) + FONT->PrintfUnshaded(Buffer, v2(Pos.X + 38, LastFillBottom + 5), + WHITE, "%s", Str.CStr()); + else + FONT->Printf(Buffer, v2(Pos.X + 37, LastFillBottom + 4), + Entry[c]->Color, "%s", Str.CStr()); + + LastFillBottom += 20; + } + else + { + uint ChapterSize = festring::SplitString(Str, Chapter, + (Width - 50) >> 3, + Marginal); + uint PictureTop = LastFillBottom + ChapterSize * 5 - 9; + + for(uint l = 0; l < ChapterSize; ++l) + { + Buffer->Fill(Pos.X + 3, LastFillBottom, Width - 6, 10, BackColor); + + if(Flags & SELECTABLE && Entry[c]->Selectable && Selected == i) + FONT->PrintfUnshaded(Buffer, v2(Pos.X + 38, LastFillBottom + 1), + WHITE, "%s", Chapter[l].CStr()); + else + FONT->Printf(Buffer, v2(Pos.X + 37, LastFillBottom), + Entry[c]->Color, "%s", Chapter[l].CStr()); + + LastFillBottom += 10; + } + + if(EntryDrawer) + EntryDrawer(Buffer, + v2(Pos.X + 13, PictureTop), + Entry[c]->ImageKey); + } + } + else + { + uint ChapterSize = festring::SplitString(Str, Chapter, + (Width - 26) >> 3, + Marginal); + + for(uint l = 0; l < ChapterSize; ++l) + { + Buffer->Fill(Pos.X + 3, LastFillBottom, Width - 6, 10, BackColor); + + if(Flags & SELECTABLE && Entry[c]->Selectable && Selected == i) + FONT->PrintfUnshaded(Buffer, v2(Pos.X + 14, LastFillBottom + 1), + WHITE, "%s", Chapter[l].CStr()); + else + FONT->Printf(Buffer, v2(Pos.X + 13, LastFillBottom), + Entry[c]->Color, "%s", Chapter[l].CStr()); + + LastFillBottom += 10; + } + } + + if((i - PageBegin == PageLength - 1 && Entry[c]->Selectable) + || c == Entry.size() - 1) + { + if((!(Flags & INVERSE_MODE) && c != Entry.size() - 1) + || (Flags & INVERSE_MODE && PageBegin)) + { + Buffer->Fill(Pos.X + 3, LastFillBottom, Width - 6, 30, BackColor); + FONT->Printf(Buffer, v2(Pos.X + 13, LastFillBottom + 10), WHITE, + "- Press SPACE to continue, ESC to exit -"); + LastFillBottom += 30; + } + else + { + Buffer->Fill(Pos.X + 3, LastFillBottom, Width - 6, 10, BackColor); + LastFillBottom += 10; + } + + Buffer->DrawRectangle(Pos.X + 1, Pos.Y + 1, Pos.X + Width - 2, + LastFillBottom + 1, DARK_GRAY, true); + break; + } + + if(Entry[c++]->Selectable) + ++i; + } + + return c == Entry.size() - 1; +} + +void felist::DrawDescription(bitmap* Buffer) const +{ + Buffer->Fill(Pos.X + 3, Pos.Y + 3, Width - 6, 20, BackColor); + + for(uint c = 0; c < Description.size(); ++c) + { + Buffer->Fill(Pos.X + 3, Pos.Y + 13 + c * 10, Width - 6, 10, BackColor); + FONT->Printf(Buffer, v2(Pos.X + 13, Pos.Y + 13 + c * 10), + Description[c]->Color, Description[c]->String.CStr()); + } + + Buffer->Fill(Pos.X + 3, Pos.Y + 13 + Description.size() * 10, + Width - 6, 10, BackColor); +} + +/* We suppose InverseMode != false here */ + +void felist::QuickDraw(bitmap* Bitmap, uint PageLength) const +{ + static std::vector Chapter; + uint Width = Bitmap->GetSize().X; + Bitmap->Fill(3, 3, Width - 6, 20 + PageLength * 10, 0); + Bitmap->DrawRectangle(1, 1, Width - 2, + 24 + PageLength * 10, + DARK_GRAY, true); + uint LineSize = (Width - 26) >> 3; + + uint Index = 0; + uint Bottom = PageLength * 10 + 3; + + for(uint c1 = 0; c1 <= Selected; ++c1) + { + const felistentry* CurrentEntry = Entry[Selected - c1]; + uint ChapterSize = festring::SplitString(CurrentEntry->String, + Chapter, LineSize, + CurrentEntry->Marginal); + + for(uint c2 = 0; c2 < ChapterSize; ++c2) + { + col16 Color = CurrentEntry->Color; + Color = MakeRGB16(GetRed16(Color) - ((GetRed16(Color) * 3 + * Index / PageLength) >> 2), + GetGreen16(Color) - ((GetGreen16(Color) * 3 + * Index / PageLength) >> 2), + GetBlue16(Color) - ((GetBlue16(Color) * 3 + * Index / PageLength) >> 2)); + FONT->Printf(Bitmap, v2(13, Bottom), Color, "%s", + Chapter[ChapterSize - c2 - 1].CStr()); + Bottom -= 10; + + if(++Index == PageLength) + return; + } + } +} + +void felist::CreateQuickDrawFontCaches(rawbitmap* Font, + col16 Color, + uint PageLength) +{ + if(PageLength < 2) + return; + + for(uint c = 0; c < PageLength; ++c) + Font->CreateFontCache(MakeRGB16(GetRed16(Color) + - ((GetRed16(Color) * 3 + * c / PageLength) >> 2), + GetGreen16(Color) + - ((GetGreen16(Color) * 3 + * c / PageLength) >> 2), + GetBlue16(Color) + - ((GetBlue16(Color) * 3 + * c / PageLength) >> 2))); +} + +void felist::Empty() +{ + for(uint c = 0; c < Entry.size(); ++c) + delete Entry[c]; + + Entry.clear(); +} + +void felist::AddEntry(cfestring& Str, col16 Color, + uint Marginal, uint Key, truth Selectable) +{ + Entry.push_back(new felistentry(Str, Color, Marginal, Key, Selectable)); + + if(Maximum && Entry.size() > ulong(Maximum)) + { + delete Entry[0]; + Entry.erase(Entry.begin()); + } +} + +void felist::Save(outputfile& SaveFile) const +{ + SaveFile << Entry << Maximum << Selected; +} + +void felist::Load(inputfile& SaveFile) +{ + SaveFile >> Entry >> Maximum >> Selected; +} + +void felist::PrintToFile(cfestring& FileName) +{ + std::ofstream SaveFile(FileName.CStr(), std::ios::out); + + if(!SaveFile.is_open()) + return; + + uint c; + + for(c = 0; c < Description.size(); ++c) + SaveFile << Description[c]->String.CStr() << std::endl; + + SaveFile << std::endl; + + for(c = 0; c < Entry.size(); ++c) + { + if(Entry[c]->ImageKey != NO_IMAGE) + SaveFile << " "; + + SaveFile << Entry[c]->String.CStr() << std::endl; + } +} + +void felist::EmptyDescription() +{ Description.resize(1); } diff --git a/FeLib/Source/femain.cpp b/FeLib/Source/femain.cpp new file mode 100644 index 0000000..d54c563 --- /dev/null +++ b/FeLib/Source/femain.cpp @@ -0,0 +1,61 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifdef __DJGPP__ +#include +#include "graphics.h" +#endif + +#ifdef WIN32 +#include +#else +#include +#endif + +#ifdef USE_SDL +#include "SDL.h" +#endif + +#include + +#include "error.h" + +int Main(int, char**); + +int main(int argc, char* argv[]) +{ + /*try + {*/ + return Main(argc, argv); + /*} + catch(...) + { + cchar* Msg = "Fatal Error: Unknown exception thrown."; +#ifdef WIN32 + ShowWindow(GetActiveWindow(), SW_HIDE); + char Buffer[256]; + strcpy(Buffer, Msg); + strcat(Buffer, globalerrorhandler::GetBugMsg()); + MessageBox(NULL, Buffer, "Program aborted!", MB_OK|MB_ICONEXCLAMATION); +#endif +#ifdef LINUX + std::cout << Msg << globalerrorhandler::GetBugMsg() << std::endl; +#endif +#ifdef __DJGPP__ + graphics::DeInit(); + std::cout << Msg << globalerrorhandler::GetBugMsg() << std::endl; +#endif + exit(3); + } + + exit(0);*/ +} diff --git a/FeLib/Source/femath.cpp b/FeLib/Source/femath.cpp new file mode 100644 index 0000000..f00b72d --- /dev/null +++ b/FeLib/Source/femath.cpp @@ -0,0 +1,407 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include + +#include "femath.h" +#include "error.h" +#include "save.h" + +cint basequadricontroller::OrigoDeltaX[4] = { 0, 1, 0, 1 }; +cint basequadricontroller::OrigoDeltaY[4] = { 0, 0, 1, 1 }; +int basequadricontroller::OrigoX, basequadricontroller::OrigoY; +int basequadricontroller::StartX, basequadricontroller::StartY; +int basequadricontroller::XSize, basequadricontroller::YSize; +int basequadricontroller::RadiusSquare; +truth basequadricontroller::SectorCompletelyClear; + +/* A C-program for MT19937: Integer version */ +/* genrand() generates one pseudorandom unsigned integer (32bit) */ +/* which is uniformly distributed among 0 to 2^32-1 for each */ +/* call. sgenrand(seed) set initial values to the working area */ +/* of 624 words. Before genrand(), sgenrand(seed) must be */ +/* called once. (seed is any 32-bit integer except for 0). */ +/* Coded by Takuji Nishimura, considering the suggestions by */ +/* Topher Cooper and Marc Rieffel in July-Aug. 1997. */ + +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Library General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later */ +/* version. */ +/* This library 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 Library General Public License for more details. */ +/* You should have received a copy of the GNU Library General */ +/* Public License along with this library; if not, write to the */ +/* Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA */ +/* 02111-1307 USA */ + +/* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. */ +/* Any feedback is very welcome. For any question, comments, */ +/* see http://www.math.keio.ac.jp/matumoto/emt.html or email */ +/* matumoto@math.keio.ac.jp */ + +/* Period parameters */ +#define N1 624 +#define M 397 +#define MATRIX_A 0x9908b0df /* constant vector a */ +#define UPPER_MASK 0x80000000 /* most significant w-r bits */ +#define LOWER_MASK 0x7fffffff /* least significant r bits */ + +/* Tempering parameters */ +#define TEMPERING_MASK_B 0x9d2c5680 +#define TEMPERING_MASK_C 0xefc60000 +#define TEMPERING_SHIFT_U(y) (y >> 11) +#define TEMPERING_SHIFT_S(y) (y << 7) +#define TEMPERING_SHIFT_T(y) (y << 15) +#define TEMPERING_SHIFT_L(y) (y >> 18) + +ulong femath::mt[N1]; /* the array for the state vector */ +long femath::mti = N1+1; /* mti==N+1 means mt[N] is not initialized */ + +/* backups */ + +ulong femath::mtb[N1]; +long femath::mtib; + +void femath::SetSeed(ulong Seed) +{ + /* setting initial seeds to mt[N] using */ + /* the generator Line 25 of Table 1 in */ + /* [KNUTH 1981, The Art of Computer Programming */ + /* Vol. 2 (2nd Ed.), pp102] */ + + mt[0] = Seed & 0xffffffff; + + for (mti=1; mti= N1) { /* generate N words at one time */ + int kk; + + if (mti == N1+1) /* if sgenrand() has not been called, */ + SetSeed(4357); /* a default initial seed is used */ + + for (kk=0;kk> 1) ^ mag01[y & 0x1]; + } + for (;kk> 1) ^ mag01[y & 0x1]; + } + y = (mt[N1-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); + mt[N1-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1]; + + mti = 0; + } + + y = mt[mti++]; + y ^= TEMPERING_SHIFT_U(y); + y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B; + y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C; + y ^= TEMPERING_SHIFT_L(y); + + return y & 0x7FFFFFFF; +} + +int femath::WeightedRand(long* Possibility, long TotalPossibility) +{ + long Rand = RAND() % TotalPossibility, PartialSum = 0; + + for(int c = 0;; ++c) + { + PartialSum += Possibility[c]; + + if(PartialSum > Rand) + return c; + } +} + + +int femath::WeightedRand(const std::vector& Possibility, + long TotalPossibility) +{ + long Rand = RAND() % TotalPossibility, PartialSum = 0; + + for(int c = 0;; ++c) + { + PartialSum += Possibility[c]; + + if(PartialSum > Rand) + return c; + } +} + +double femath::CalculateAngle(v2 Direction) +{ + if(Direction.X < 0) + return atan(double(Direction.Y) / Direction.X) + FPI; + else if(Direction.X > 0) + { + if(Direction.Y < 0) + return atan(double(Direction.Y) / Direction.X) + 2 * FPI; + else + return atan(double(Direction.Y) / Direction.X); + } + else + { + if(Direction.Y < 0) + return 3 * FPI / 2; + else if(Direction.Y > 0) + return FPI / 2; + else + { + ABORT("Illegal direction (0, 0) passed to femath::CalculateAngle()!"); + return 0; + } + } +} + +void femath::CalculateEnvironmentRectangle(rect& Rect, + const rect& MotherRect, + v2 Origo, int Radius) +{ + Rect.X1 = Origo.X - Radius; + Rect.Y1 = Origo.Y - Radius; + Rect.X2 = Origo.X + Radius; + Rect.Y2 = Origo.Y + Radius; + + if(Rect.X1 < MotherRect.X1) + Rect.X1 = MotherRect.X1; + + if(Rect.Y1 < MotherRect.Y1) + Rect.Y1 = MotherRect.Y1; + + if(Rect.X2 > MotherRect.X2) + Rect.X2 = MotherRect.X2; + + if(Rect.Y2 > MotherRect.Y2) + Rect.Y2 = MotherRect.Y2; +} + +truth femath::Clip(int& SourceX, int& SourceY, int& DestX, int& DestY, int& Width, int& Height, int XSize, int YSize, int DestXSize, int DestYSize) +{ + /* This sentence is usually true */ + + if(SourceX >= 0 + && SourceY >= 0 + && DestX >= 0 + && DestY >= 0 + && SourceX + Width <= XSize + && SourceY + Height <= YSize + && DestX + Width <= DestXSize + && DestY + Height <= DestYSize) + return true; + + if(SourceX < 0) + { + Width += SourceX; + DestX -= SourceX; + SourceX = 0; + } + + if(SourceY < 0) + { + Height += SourceY; + DestY -= SourceY; + SourceY = 0; + } + + if(DestX < 0) + { + Width += DestX; + SourceX -= DestX; + DestX = 0; + } + + if(DestY < 0) + { + Height += DestY; + SourceY -= DestY; + DestY = 0; + } + + if(SourceX + Width > XSize) + Width = XSize - SourceX; + + if(SourceY + Height > YSize) + Height = YSize - SourceY; + + if(DestX + Width > DestXSize) + Width = DestXSize - DestX; + + if(DestY + Height > DestYSize) + Height = DestYSize - DestY; + + return Width > 0 && Height > 0; +} + +void femath::SaveSeed() +{ + mtib = mti; + + for(int c = 0; c < N1; ++c) + mtb[c] = mt[c]; +} + +void femath::LoadSeed() +{ + mti = mtib; + + for(int c = 0; c < N1; ++c) + mt[c] = mtb[c]; +} + +void ReadData(interval& I, inputfile& SaveFile) +{ + I.Min = SaveFile.ReadNumber(HIGHEST, true); + festring Word; + SaveFile.ReadWord(Word); + + if(Word == ";" || Word == ",") + I.Max = I.Min; + else if(Word == ":") + I.Max = Max(SaveFile.ReadNumber(), I.Min); + else + ABORT("Odd interval terminator %s detected, file %s line %ld!", + Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TellLine()); +} + +void ReadData(region& R, inputfile& SaveFile) +{ + ReadData(R.X, SaveFile); + ReadData(R.Y, SaveFile); +} + +outputfile& operator<<(outputfile& SaveFile, const interval& I) +{ + SaveFile.Write(reinterpret_cast(&I), sizeof(I)); + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, interval& I) +{ + SaveFile.Read(reinterpret_cast(&I), sizeof(I)); + return SaveFile; +} + +outputfile& operator<<(outputfile& SaveFile, const region& R) +{ + SaveFile.Write(reinterpret_cast(&R), sizeof(R)); + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, region& R) +{ + SaveFile.Read(reinterpret_cast(&R), sizeof(R)); + return SaveFile; +} + +long femath::SumArray(const fearray& Vector) +{ + long Sum = 0; + + for(uint c = 0; c < Vector.Size; ++c) + Sum += Vector.Data[c]; + + return Sum; +} + +void femath::GenerateFractalMap(int** Map, int Side, + int StartStep, + int Randomness) +{ + cint Limit = Side - 1; + Map[0][0] = 0; + Map[0][Limit] = 0; + Map[Limit][0] = 0; + Map[Limit][Limit] = 0; + + for(int Step = StartStep, HalfStep = Step >> 1; + HalfStep; + Step = HalfStep, HalfStep >>= 1, + Randomness = ((Randomness << 3) - Randomness) >> 3) + { + int x, y, RandMod = (Randomness << 1) + 1; + + for(x = HalfStep; x < Side; x += Step) + for(y = HalfStep; y < Side; y += Step) + Map[x][y] = ((Map[x - HalfStep][y - HalfStep] + + Map[x - HalfStep][y + HalfStep] + + Map[x + HalfStep][y - HalfStep] + + Map[x + HalfStep][y + HalfStep]) + >> 2) - Randomness + RAND() % RandMod; + + for(x = HalfStep; x < Side; x += Step) + for(y = 0; y < Side; y += Step) + { + int HeightSum = Map[x - HalfStep][y] + Map[x + HalfStep][y]; + int Neighbours = 2; + + if(y) + { + HeightSum += Map[x][y - HalfStep]; + ++Neighbours; + } + + if(y != Limit) + { + HeightSum += Map[x][y + HalfStep]; + ++Neighbours; + } + + if(Neighbours == 4) + HeightSum >>= 2; + else + HeightSum /= Neighbours; + + Map[x][y] = HeightSum - Randomness + RAND() % RandMod; + } + + for(x = 0; x < Side; x += Step) + for(y = HalfStep; y < Side; y += Step) + { + int HeightSum = Map[x][y - HalfStep] + Map[x][y + HalfStep]; + int Neighbours = 2; + + if(x) + { + HeightSum += Map[x - HalfStep][y]; + ++Neighbours; + } + + if(x != Limit) + { + HeightSum += Map[x + HalfStep][y]; + ++Neighbours; + } + + if(Neighbours == 4) + HeightSum >>= 2; + else + HeightSum /= Neighbours; + + Map[x][y] = HeightSum - Randomness + RAND() % RandMod; + } + } +} diff --git a/FeLib/Source/festring.cpp b/FeLib/Source/festring.cpp new file mode 100644 index 0000000..e1c655d --- /dev/null +++ b/FeLib/Source/festring.cpp @@ -0,0 +1,847 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include + +#include "festring.h" +#include "allocate.h" +#include "error.h" + +char** festring::IntegerMap = 0; +char* festring::EmptyString = ""; +festring::csizetype festring::NPos = festring::sizetype(-1); + +festring& festring::Append(cchar* CStr, sizetype N) +{ + sizetype OldSize = Size; + sizetype NewSize = OldSize + N; + char* OldPtr = Data; + + if(OwnsData && OldPtr && !REFS(OldPtr) && NewSize <= Reserved) + { + memcpy(OldPtr + OldSize, CStr, N); + Size = NewSize; + } + else + SlowAppend(CStr, N); + + return *this; +} + +festring& festring::operator=(cchar* CStr) +{ + sizetype NewSize = strlen(CStr); + Size = NewSize; + char* Ptr = Data; + + if(Ptr && OwnsData) + { + if(!REFS(Ptr) && NewSize <= Reserved) + { + memcpy(Ptr, CStr, NewSize); + return *this; + } + + if(!REFS(Ptr)--) + delete [] &REFS(Ptr); + } + + Data = const_cast(CStr); + OwnsData = false; + return *this; +} + +festring& festring::operator=(cfestring& Str) +{ + sizetype NewSize = Str.Size; + Size = NewSize; + char* Ptr = Data; + char* StrPtr = Str.Data; + + if(Ptr && OwnsData) + { + if(!REFS(Ptr) && NewSize <= Reserved) + { + if(StrPtr) + memcpy(Ptr, StrPtr, NewSize); + + return *this; + } + + if(!REFS(Ptr)--) + delete [] &REFS(Ptr); + } + + if((Data = StrPtr) && (OwnsData = Str.OwnsData)) + { + ++REFS(StrPtr); + Reserved = Str.Reserved; + } + + return *this; +} + +/* Size must be > 0 */ + +festring& festring::Capitalize() +{ + char* OldPtr = Data; + + if(*OldPtr > 0x60 && *OldPtr < 0x7B) + { + if(!OwnsData) + CreateOwnData(OldPtr, Size); + else if(REFS(OldPtr)) + { + --REFS(OldPtr); + CreateOwnData(OldPtr, Size); + } + + *Data ^= 0x20; + } + + return *this; +} + +void festring::CreateOwnData(cchar* CStr, sizetype N) +{ + Size = N; + Reserved = N|FESTRING_PAGE; + char* Ptr = 4 + new char[Reserved + 5]; + REFS(Ptr) = 0; + Data = Ptr; + memcpy(Ptr, CStr, N); + OwnsData = true; +} + +void festring::SlowAppend(char Char) +{ + char* OldPtr = Data; + + if(OldPtr) + { + sizetype OldSize = Size++; + sizetype NewSize = OldSize + 1; + ulong* DeletePtr = 0; + + if(OwnsData && !REFS(OldPtr)--) + DeletePtr = &REFS(OldPtr); + + Reserved = NewSize|FESTRING_PAGE; + char* NewPtr = 4 + new char[Reserved + 5]; + REFS(NewPtr) = 0; + Data = NewPtr; + memcpy(NewPtr, OldPtr, OldSize); + NewPtr[OldSize] = Char; + + if(DeletePtr) + delete [] DeletePtr; + } + else + { + Size = 1; + Reserved = FESTRING_PAGE; + char* Ptr = 4 + new char[FESTRING_PAGE + 5]; + REFS(Ptr) = 0; + Ptr[0] = Char; + Data = Ptr; + } + + OwnsData = true; +} + +void festring::SlowAppend(cchar* CStr, sizetype N) +{ + char* OldPtr = Data; + + if(OldPtr) + { + sizetype OldSize = Size; + sizetype NewSize = OldSize + N; + Size = NewSize; + ulong* DeletePtr = 0; + + if(OwnsData && !REFS(OldPtr)--) + DeletePtr = &REFS(OldPtr); + + Reserved = NewSize|FESTRING_PAGE; + char* NewPtr = 4 + new char[Reserved + 5]; + REFS(NewPtr) = 0; + Data = NewPtr; + memcpy(NewPtr, OldPtr, OldSize); + memcpy(NewPtr + OldSize, CStr, N); + OwnsData = true; + + if(DeletePtr) + delete [] DeletePtr; + } + else + CreateOwnData(CStr, N); +} + +void festring::Assign(sizetype N, char C) +{ + Size = N; + char* Ptr = Data; + + if(OwnsData && Ptr) + { + if(!REFS(Ptr) && N <= Reserved) + { + memset(Ptr, C, N); + return; + } + else + delete [] &REFS(Ptr); + } + + Reserved = N|FESTRING_PAGE; + Ptr = 4 + new char[Reserved + 5]; + REFS(Ptr) = 0; + Data = Ptr; + memset(Ptr, C, N); + OwnsData = true; +} + +void festring::Resize(sizetype N, char C) +{ + sizetype OldSize = Size; + char* OldPtr = Data; + char* NewPtr; + Size = N; + + if(OldSize < N) + { + ulong* DeletePtr = 0; + + if(OwnsData && OldPtr) + { + if(!REFS(OldPtr)) + { + if(N <= Reserved) + { + memset(OldPtr + OldSize, C, N - OldSize); + return; + } + else + DeletePtr = &REFS(OldPtr); + } + else + --REFS(OldPtr); + } + + Reserved = N|FESTRING_PAGE; + NewPtr = 4 + new char[Reserved + 5]; + REFS(NewPtr) = 0; + Data = NewPtr; + memcpy(NewPtr, OldPtr, OldSize); + memset(NewPtr + OldSize, C, N - OldSize); + OwnsData = true; + + if(DeletePtr) + delete [] DeletePtr; + } + else + { + if(OwnsData && OldPtr) + if(!REFS(OldPtr)) + return; + else + --REFS(OldPtr); + + Reserved = N|FESTRING_PAGE; + NewPtr = 4 + new char[Reserved + 5]; + REFS(NewPtr) = 0; + Data = NewPtr; + memcpy(NewPtr, OldPtr, N); + OwnsData = true; + } +} + +festring::sizetype festring::Find(char Char, sizetype Pos) const +{ + char* Ptr = Data; + + if(Ptr) + { + char* Result = static_cast(memchr(Ptr + Pos, Char, Size - Pos)); + + if(Result) + return Result - Ptr; + } + + return NPos; +} + +festring::sizetype festring::Find(cchar* CStr, + sizetype Pos, + sizetype N) const +{ + if(N) + { + char* Ptr = Data; + + if(Ptr) + { + char Char = CStr[0]; + + for(;;) + { + char* Result = static_cast(memchr(Ptr + Pos, Char, + Size - Pos)); + + if(!Result) + return NPos; + + if(!memcmp(Result, CStr, N)) + return Result - Ptr; + else + Pos = Result - Ptr + 1; + } + } + } + + return NPos; +} + +festring::sizetype festring::FindLast(char Char, sizetype Pos) const +{ + char* Ptr = Data; + + if(Ptr) + { + if(Pos >= Size) + Pos = Size - 1; + + sizetype c; + for(c = Pos; c != NPos && Ptr[c] != Char; --c); + return c; + } + else + return NPos; +} + +festring::sizetype festring::FindLast(const char* CStr, + sizetype Pos, + sizetype N) const +{ + if(N) + { + char* Ptr = Data; + + if(Ptr && Size >= N) + { + char Char = CStr[0]; + + if(Pos > Size - N) + Pos = Size - N; + + for(sizetype c = Pos; c != NPos; --c) + if(Ptr[c] == Char && !memcmp(Ptr + c, CStr, N)) + return c; + + return NPos; + } + } + + return NPos; +} + +void festring::Erase(sizetype Pos, sizetype Length) +{ + char* OldPtr = Data; + + if(OldPtr && Length) + { + sizetype OldSize = Size; + + if(Pos < OldSize) + { + truth MoveReq = Length < OldSize - Pos; + + if(OwnsData) + { + if(!REFS(OldPtr)) + { + if(MoveReq) + { + sizetype End = Pos + Length; + memmove(OldPtr + Pos, OldPtr + End, OldSize - End); + } + + Size -= Length; + return; + } + else + --REFS(OldPtr); + } + + sizetype NewSize = MoveReq ? OldSize - Length : Pos; + Size = NewSize; + Reserved = NewSize|FESTRING_PAGE; + char* Ptr = 4 + new char[Reserved + 5]; + REFS(Ptr) = 0; + Data = Ptr; + OwnsData = true; + + if(Pos) + memcpy(Ptr, OldPtr, Pos); + + if(MoveReq) + { + sizetype End = Pos + Length; + memcpy(Ptr + Pos, OldPtr + End, OldSize - End); + } + } + } +} + +void festring::Insert(sizetype Pos, cchar* CStr, sizetype N) +{ + if(N) + { + sizetype OldSize = Size; + + if(Pos < OldSize) // this implies Data != 0 + { + char* OldPtr = Data; + ulong* DeletePtr = 0; + sizetype NewSize = OldSize + N; + Size = NewSize; + + if(OwnsData) + { + if(!REFS(OldPtr)) + { + if(NewSize <= Reserved) + { + char* Ptr = OldPtr + Pos; + memmove(Ptr + N, Ptr, OldSize - Pos); + memcpy(Ptr, CStr, N); + return; + } + else + DeletePtr = &REFS(OldPtr); + } + else + --REFS(OldPtr); + } + + Reserved = NewSize|FESTRING_PAGE; + char* NewPtr = 4 + new char[Reserved + 5]; + REFS(NewPtr) = 0; + Data = NewPtr; + memcpy(NewPtr, OldPtr, Pos); + memcpy(NewPtr + Pos, CStr, N); + memcpy(NewPtr + Pos + N, OldPtr + Pos, OldSize - Pos); + OwnsData = true; + + if(DeletePtr) + delete [] DeletePtr; + } + else if(Pos == OldSize) + Append(CStr, N); + else + ABORT("Illegal festring insertion detected!"); + } +} + +/* Creates map of char representations of numbers 0-999 used by + festring::Append(long). Due to automatization, you don't need + to explicitly call it. */ + +void festring::InstallIntegerMap() +{ + Alloc2D(IntegerMap, 1000, 3); + char Ones = '0', Tens = '0', Hundreds = '0'; + + for(int c = 0; c < 1000; ++c) + { + IntegerMap[c][0] = Hundreds; + IntegerMap[c][1] = Tens; + IntegerMap[c][2] = Ones; + + if(++Ones > '9') + { + Ones = '0'; + + if(++Tens > '9') + { + Tens = '0'; + ++Hundreds; + } + } + } + + atexit(DeInstallIntegerMap); +} + +/* Deletes the integer map used by festring::Append(long). + Due to automatization, you don't need to explicitly call it. */ + +void festring::DeInstallIntegerMap() +{ + delete [] IntegerMap; + IntegerMap = 0; +} + +/* Displays numbers in the range [-2147483647, 2147483647]. + Much faster than sprintf and (nonstandard) itoa. */ + +festring& festring::Append(long Integer) +{ + if(!IntegerMap) + InstallIntegerMap(); + + char IntegerBuffer[12]; + char* BufferPtr = IntegerBuffer; + truth Negative = false; + + if(Integer < 0) + { + if(Integer < -2147483647) + return Append("-Inf", 4); + + *BufferPtr++ = '0'; + Integer = -Integer; + Negative = true; + } + else if(Integer > 2147483647) + return Append("Inf", 3); + + truth ForceZeros = false; + + if(Integer >= 2000000000) + { + *BufferPtr++ = '2'; + Integer -= 2000000000; + ForceZeros = true; + } + else if(Integer >= 1000000000) + { + *BufferPtr++ = '1'; + Integer -= 1000000000; + ForceZeros = true; + } + + if(ForceZeros || Integer >= 1000000) + { + int Temp = Integer / 1000000; + *BufferPtr++ = IntegerMap[Temp][0]; + *BufferPtr++ = IntegerMap[Temp][1]; + *BufferPtr++ = IntegerMap[Temp][2]; + Integer -= Temp * 1000000; + ForceZeros = true; + } + + if(ForceZeros || Integer >= 1000) + { + int Temp = Integer / 1000; + *BufferPtr++ = IntegerMap[Temp][0]; + *BufferPtr++ = IntegerMap[Temp][1]; + *BufferPtr++ = IntegerMap[Temp][2]; + Integer -= Temp * 1000; + } + + *BufferPtr++ = IntegerMap[Integer][0]; + *BufferPtr++ = IntegerMap[Integer][1]; + *BufferPtr++ = IntegerMap[Integer][2]; + *BufferPtr = 0; + char* EndPtr = BufferPtr; + + for(BufferPtr = IntegerBuffer; *BufferPtr == '0'; ++BufferPtr); + + if(Negative) + *--BufferPtr = '-'; + else if(!*BufferPtr) // check if the original Integer was zero + --BufferPtr; + + return Append(BufferPtr, EndPtr - BufferPtr); +} + +/* The Result string receives up to Length characters from source, + but words are left uncut if possible. */ + +void festring::SplitString(festring& Source, + festring& Result, + sizetype Length) +{ + if(Source.GetSize() <= Length) + { + Result << Source; + Source.Empty(); + return; + } + + sizetype Pos = Source.FindLast(' ', Length); + + if(Pos != NPos) + { + Result.Append(Source, Pos); + Source.Erase(0, Pos + 1); + } + else + { + Result.Append(Source, Length); + Source.Erase(0, Length); + } +} + +/* Divides Source into lines of size up to Length without cutting words + and stores them one by one to StringVector. You can also specify a + Marginal, in which case a number of spaces is inserted in the + beginning of each line except the first. It returns the number of + created lines. */ + +int festring::SplitString(cfestring& Source, + std::vector& StringVector, + sizetype Length, sizetype Marginal) +{ + if(!Length) + ABORT("Illegal Length 0 passed to festring::SplitString()!"); + + if(Marginal >= Length) + ABORT("Illegal festring::SplitString() call:" + "Marginal must be less than Length!"); + + festring CopyOfSource(Source); + + if(StringVector.empty()) + StringVector.push_back(festring()); + else + StringVector[0].Empty(); + + SplitString(CopyOfSource, StringVector[0], Length); + sizetype Size = 1; + + while(!CopyOfSource.IsEmpty()) + { + if(StringVector.size() <= Size) + StringVector.push_back(festring()); + + festring& String = StringVector[Size++]; + String.Assign(Marginal, ' '); + SplitString(CopyOfSource, String, Length - Marginal); + } + + return Size; +} + +char Capitalize(char Char) +{ return Char > 0x60 && Char < 0x7B ? Char ^ 0x20 : Char; } + +/* Returns the position of the first occurance of What in Where + starting at Begin or after it, ignoring the case of letters. + If the search fails, festring::NPos is returned instead. */ + +festring::sizetype festring::IgnoreCaseFind(cfestring& Where, + cfestring& What, + sizetype Begin) +{ + if(What.IsEmpty()) + return Begin; + + for(; Where.GetSize() >= What.GetSize() + Begin; ++Begin) + if(::Capitalize(Where[Begin]) == ::Capitalize(What[0])) + { + truth Equal = true; + + for(sizetype c = 1; c < What.GetSize(); ++c) + if(::Capitalize(Where[Begin + c]) != ::Capitalize(What[c])) + { + Equal = false; + break; + } + + if(Equal) + return Begin; + } + + return NPos; +} + +/* Replaces all occurances of What in Where after Begin with With */ + +void festring::SearchAndReplace(festring& Where, cfestring& What, + cfestring& With, sizetype Begin) +{ + for(sizetype Pos = Where.Find(What, Begin); + Pos != NPos; Pos = Where.Find(What, Pos)) + { + Where.Erase(Pos, What.GetSize()); + Where.Insert(Pos, With); + } +} + +/* Returns whether First is behind Second in alphabetical order, + ignoring case */ + +bool festring::IgnoreCaseCompare(cfestring& First, + cfestring& Second) +{ + for(sizetype Pos = 0; + Pos < First.GetSize() && Pos < Second.GetSize(); ++Pos) + { + char Char1 = ::Capitalize(First[Pos]); + char Char2 = ::Capitalize(Second[Pos]); + + if(Char1 != Char2) + return Char1 < Char2; + } + + return First.GetSize() < Second.GetSize(); +} + +/* Sorry for ugliness */ + +void festring::PreProcessForFebot() +{ + sizetype c, d, Length; + + for(c = 0, Length = 0; + c < Size && (Data[c] == ' ' || Data[c] == '\t'); ++c) + ++Length; + + Erase(0, Length); + + if(!Size) + return; + + for(c = Size - 1, Length = 0; + Data[c] == ' ' || Data[c] == '\t'; + --c) + ++Length; + + Erase(c + 1, Length); + + for(c = 0; c < Size - 1; ++c) + { + char Char = Data[c + 1]; + + if(Data[c] == '\t') + Data[c] = ' '; + else if(Data[c] == '\"' || Data[c] == '(' || Data[c] == ')') + { + Erase(c--, 1); + continue; + } + + if(Data[c] == ' ') + { + if(Char == ' ' || Char == '\t') + { + for(d = c + 2, Length = 1; + d < Size && (Data[d] == ' ' || Data[d] == '\t'); ++d) + ++Length; + + Erase(c + 1, Length); + } + } + else if((Char == '.' || Char == '!' || Char == '?') + && Data[c] != '.' && Data[c] != '!' && Data[c] != '?' + && (c == Size - 2 || Data[c + 2] == ' ' || Data[c + 2] == '\t')) + Insert(c+++1, " ", 1); + } + + if(Data[c] == '\"' || Data[c] == '(' || Data[c] == ')') + Erase(c--, 1); + + if(!ispunct(Data[c])) + *this << ' ' << '.'; +} + +/* Sorry for ugliness */ + +void festring::PostProcessForFebot() +{ + Capitalize(); + truth CapitalizeNextChar = false; + + for(sizetype c = 0; c < Size - 1; ++c) + { + char Char1 = Data[c]; + + if(Char1 == ' ') + { + char Char2 = Data[c + 1]; + + if((Char2 == '.' || Char2 == '!' || Char2 == '?') + && (c == Size - 2 || Data[c + 2] == ' ' || Data[c + 2] == '\t')) + { + Erase(c++, 1); + CapitalizeNextChar = true; + } + } + else if((Char1 == '.' || Char1 == '!' || Char1 == '?') + && (c == Size - 2 || Data[c + 2] == ' ' || Data[c + 2] == '\t')) + CapitalizeNextChar = true; + /* Erase() guarantees that OwnsData != false && REFS(Data) == 0 */ + else if(CapitalizeNextChar) + { + if(Char1 > 0x60 && Char1 < 0x7B) + Data[c] &= ~0x20; + + CapitalizeNextChar = false; + } + } +} + +/* Erases the first word of the sentence and places it to To. + Should currently be used only by strings processed for Febot */ + +void festring::ExtractWord(festring& To) +{ + for(sizetype c = 0; c < Size; ++c) + if(Data[c] == ' ') + { + To.Empty(); + To.Append(&Data[c + 1], Size - c - 1); + Erase(c, Size - c); + SwapData(To); + return; + } + + To = *this; + Empty(); +} + +void festring::SwapData(festring& Str) +{ + char*const TData = Data; + csizetype TSize = Size; + csizetype TReserved = Reserved; + ctruth TOwnsData = OwnsData; + Data = Str.Data; + Size = Str.Size; + Reserved = Str.Reserved; + OwnsData = Str.OwnsData; + Str.Data = TData; + Str.Size = TSize; + Str.Reserved = TReserved; + Str.OwnsData = TOwnsData; +} + +long festring::GetCheckSum() const +{ + long Counter = 0; + + for(ushort c = 0; c < GetSize(); ++c) + Counter = long(Data[c]); + + return Counter; +} + +void festring::EnsureOwnsData() +{ + if(!OwnsData) + CreateOwnData(Data, Size); +} diff --git a/FeLib/Source/fetime.cpp b/FeLib/Source/fetime.cpp new file mode 100644 index 0000000..f457fd3 --- /dev/null +++ b/FeLib/Source/fetime.cpp @@ -0,0 +1,41 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* This file might have some problems with some obscure systems. + But it also should be rather easy to fix. */ + +#include + +#include "fetime.h" +#include "festring.h" + +time_t time::GetZeroTime() { return 0; } + +time_t time::TimeAdd(time_t A, time_t B) +{ + return A + B; +} + +time_t time::TimeDifference(time_t A, time_t B) +{ + return A - B; +} + +festring time::VerbalizeAsTimeSpent(time_t) +{ + return "mur"; +} + +festring time::VerbalizeAsCalenderTime(time_t) +{ + return "murimuri"; +} diff --git a/FeLib/Source/graphics.cpp b/FeLib/Source/graphics.cpp new file mode 100644 index 0000000..3946746 --- /dev/null +++ b/FeLib/Source/graphics.cpp @@ -0,0 +1,287 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifdef USE_SDL +#include "SDL.h" +#endif + +#ifdef __DJGPP__ +#include +#include +#include +#endif + +#include "graphics.h" +#include "bitmap.h" +#include "whandler.h" +#include "error.h" +#include "rawbit.h" + +void (*graphics::SwitchModeHandler)(); + +#ifdef USE_SDL +SDL_Surface* graphics::Screen; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +SDL_Surface* graphics::TempSurface; +#endif +#endif + +#ifdef __DJGPP__ +ulong graphics::BufferSize; +ushort graphics::ScreenSelector = 0; +graphics::vesainfo graphics::VesaInfo; +graphics::modeinfo graphics::ModeInfo; +#endif + +bitmap* graphics::DoubleBuffer; +v2 graphics::Res; +int graphics::ColorDepth; +rawbitmap* graphics::DefaultFont = 0; + +void graphics::Init() +{ + static truth AlreadyInstalled = false; + + if(!AlreadyInstalled) + { + AlreadyInstalled = true; + +#ifdef USE_SDL + if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE)) + ABORT("Can't initialize SDL."); +#endif + +#ifdef __DJGPP__ + VesaInfo.Retrieve(); +#endif + + atexit(graphics::DeInit); + } +} + +void graphics::DeInit() +{ + delete DefaultFont; + DefaultFont = 0; + +#ifdef USE_SDL +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + SDL_FreeSurface(TempSurface); +#endif + SDL_Quit(); +#endif + +#ifdef __DJGPP__ + if(ScreenSelector) + { + __dpmi_free_ldt_descriptor(ScreenSelector); + ScreenSelector = 0; + textmode(0x3); + } +#endif +} + +#ifdef USE_SDL + +void graphics::SetMode(cchar* Title, cchar* IconName, + v2 NewRes, truth FullScreen) +{ + if(IconName) + { + SDL_Surface* Icon = SDL_LoadBMP(IconName); + SDL_SetColorKey(Icon, SDL_SRCCOLORKEY, + SDL_MapRGB(Icon->format, 255, 255, 255)); + SDL_WM_SetIcon(Icon, NULL); + } + + ulong Flags = SDL_SWSURFACE; + + if(FullScreen) + { + SDL_ShowCursor(SDL_DISABLE); + Flags |= SDL_FULLSCREEN; + } + + Screen = SDL_SetVideoMode(NewRes.X, NewRes.Y, 16, Flags); + + if(!Screen) + ABORT("Couldn't set video mode."); + + SDL_WM_SetCaption(Title, 0); + globalwindowhandler::Init(); + DoubleBuffer = new bitmap(NewRes); + Res = NewRes; + ColorDepth = 16; + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + + Uint32 rmask, gmask, bmask; + rmask = 0xF800; + gmask = 0x7E0; + bmask = 0x1F; + + TempSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, Res.X, Res.Y, 16, + rmask, gmask, bmask, 0); + + if(!TempSurface) + ABORT("CreateRGBSurface failed: %s\n", SDL_GetError()); + +#endif +} + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + +void graphics::BlitDBToScreen() +{ + SDL_LockSurface(TempSurface); + packcol16* SrcPtr = DoubleBuffer->GetImage()[0]; + packcol16* DestPtr = static_cast(TempSurface->pixels); + ulong ScreenYMove = (TempSurface->pitch >> 1); + ulong LineSize = Res.X << 1; + + for(int y = 0; y < Res.Y; ++y, SrcPtr += Res.X, DestPtr += ScreenYMove) + memcpy(DestPtr, SrcPtr, LineSize); + + SDL_UnlockSurface(TempSurface); + SDL_Surface* S = SDL_DisplayFormat(TempSurface); + SDL_BlitSurface(S, NULL, Screen, NULL); + SDL_FreeSurface(S); + SDL_UpdateRect(Screen, 0, 0, Res.X, Res.Y); +} + +#else + +void graphics::BlitDBToScreen() +{ + if(SDL_MUSTLOCK(Screen) && SDL_LockSurface(Screen) < 0) + ABORT("Can't lock screen"); + + packcol16* SrcPtr = DoubleBuffer->GetImage()[0]; + packcol16* DestPtr = static_cast(Screen->pixels); + ulong ScreenYMove = (Screen->pitch >> 1); + ulong LineSize = Res.X << 1; + + for(int y = 0; y < Res.Y; ++y, SrcPtr += Res.X, DestPtr += ScreenYMove) + memcpy(DestPtr, SrcPtr, LineSize); + + if(SDL_MUSTLOCK(Screen)) + SDL_UnlockSurface(Screen); + + SDL_UpdateRect(Screen, 0, 0, Res.X, Res.Y); +} + +#endif + +void graphics::SwitchMode() +{ + ulong Flags; + + if(Screen->flags & SDL_FULLSCREEN) + { + SDL_ShowCursor(SDL_ENABLE); + Flags = SDL_SWSURFACE; + } + else + { + SDL_ShowCursor(SDL_DISABLE); + Flags = SDL_SWSURFACE|SDL_FULLSCREEN; + } + + if(SwitchModeHandler) + SwitchModeHandler(); + + Screen = SDL_SetVideoMode(Res.X, Res.Y, ColorDepth, Flags); + + if(!Screen) + ABORT("Couldn't toggle fullscreen mode."); + + BlitDBToScreen(); +} + +#endif + +void graphics::LoadDefaultFont(cfestring& FileName) +{ + DefaultFont = new rawbitmap(FileName); +} + +#ifdef __DJGPP__ + +void graphics::SetMode(cchar*, cchar*, v2 NewRes, truth) +{ + ulong Mode; + + for(Mode = 0; Mode < 0x10000; ++Mode) + { + ModeInfo.Retrieve(Mode); + + if(ModeInfo.Attribs1 & 0x01 + && ModeInfo.Attribs1 & 0xFF + && ModeInfo.Width == NewRes.X + && ModeInfo.Height == NewRes.Y + && ModeInfo.BitsPerPixel == 16) + break; + } + + if(Mode == 0x10000) + ABORT("Resolution %dx%d not supported!", NewRes.X, NewRes.Y); + + __dpmi_regs Regs; + Regs.x.ax = 0x4F02; + Regs.x.bx = Mode | 0x4000; + __dpmi_int(0x10, &Regs); + Res.X = ModeInfo.Width; + Res.Y = ModeInfo.Height; + BufferSize = Res.Y * ModeInfo.BytesPerLine; + delete DoubleBuffer; + DoubleBuffer = new bitmap(Res); + __dpmi_meminfo MemoryInfo; + MemoryInfo.size = BufferSize; + MemoryInfo.address = ModeInfo.PhysicalLFBAddress; + __dpmi_physical_address_mapping(&MemoryInfo); + __dpmi_lock_linear_region(&MemoryInfo); + ScreenSelector = __dpmi_allocate_ldt_descriptors(1); + __dpmi_set_segment_base_address(ScreenSelector, MemoryInfo.address); + __dpmi_set_segment_limit(ScreenSelector, BufferSize - 1); +} + +void graphics::BlitDBToScreen() +{ + movedata(_my_ds(), ulong(DoubleBuffer->GetImage()[0]), + ScreenSelector, 0, BufferSize); +} + +void graphics::vesainfo::Retrieve() +{ + Signature = 0x32454256; + dosmemput(this, sizeof(vesainfo), __tb); + __dpmi_regs Regs; + Regs.x.ax = 0x4F00; + Regs.x.di = __tb & 0x000F; + Regs.x.es = (__tb >> 4) & 0xFFFF; + __dpmi_int(0x10, &Regs); + dosmemget(__tb, sizeof(vesainfo), this); +} + +void graphics::modeinfo::Retrieve(ushort Mode) +{ + __dpmi_regs Regs; + Regs.x.ax = 0x4F01; + Regs.x.cx = Mode; + Regs.x.di = __tb & 0x000F; + Regs.x.es = (__tb >> 4) & 0xFFFF; + dosmemput(this, sizeof(modeinfo), __tb); + __dpmi_int(0x10, &Regs); + dosmemget(__tb, sizeof(modeinfo), this); +} + +#endif diff --git a/FeLib/Source/hscore.cpp b/FeLib/Source/hscore.cpp new file mode 100644 index 0000000..4cf8e26 --- /dev/null +++ b/FeLib/Source/hscore.cpp @@ -0,0 +1,185 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "hscore.h" +#include "save.h" +#include "felist.h" +#include "feio.h" +#include "femath.h" + +/* Increment this if changes make highscores incompatible */ +#define HIGH_SCORE_VERSION 121 + +cfestring& highscore::GetEntry(int I) const { return Entry[I]; } +long highscore::GetScore(int I) const { return Score[I]; } +long highscore::GetSize() const { return Entry.size(); } + +highscore::highscore(cfestring& File) : LastAdd(0xFF), Version(HIGH_SCORE_VERSION) { Load(File); } + +truth highscore::Add(long NewScore, cfestring& NewEntry, + time_t NewTime, long NewRandomID) +{ + for(uint c = 0; c < Score.size(); ++c) + if(Score[c] < NewScore) + { + Entry.insert(Entry.begin() + c, NewEntry); + Score.insert(Score.begin() + c, NewScore); + Time.insert(Time.begin() + c, NewTime); + RandomID.insert(RandomID.begin() + c, NewRandomID); + + if(Score.size() > MAX_HIGHSCORES) + { + Entry.resize(MAX_HIGHSCORES, festring()); + Score.resize(MAX_HIGHSCORES); + Time.resize(MAX_HIGHSCORES); + RandomID.resize(MAX_HIGHSCORES); + } + + LastAdd = c; + return true; + } + + if(Score.size() < MAX_HIGHSCORES) + { + LastAdd = Score.size(); + Entry.push_back(NewEntry); + Score.push_back(NewScore); + Time.push_back(NewTime); + RandomID.push_back(NewRandomID); + return true; + } + else + { + LastAdd = MAX_HIGHSCORES; + return false; + } +} + +void highscore::Draw() const +{ + if(Score.empty()) + { + iosystem::TextScreen(CONST_S("There are no entries yet. " + "Play a game to correct this.")); + return; + } + + if(GetVersion() != HIGH_SCORE_VERSION) + { + iosystem::TextScreen(CONST_S("The highscore file is for an other version of IVAN.")); + return; + } + + felist List(CONST_S("Adventurers' Hall of Fame")); + festring Desc; + + for(uint c = 0; c < Score.size(); ++c) + { + Desc.Empty(); + Desc << c + 1; + Desc.Resize(5, ' '); + Desc << Score[c]; + Desc.Resize(13, ' '); + Desc << Entry[c]; + List.AddEntry(Desc, c == uint(LastAdd) ? WHITE : LIGHT_GRAY, 13); + } + + List.SetFlags(FADE); + List.SetPageLength(40); + List.Draw(); +} + +void highscore::Save(cfestring& File) const +{ + outputfile HighScore(File); + long CheckSum = HIGH_SCORE_VERSION + LastAdd; + for(ushort c = 0; c < Score.size(); ++c) + { + CheckSum += Score[c] + Entry[c].GetCheckSum() + RandomID[c]; + } + + HighScore << ushort(HIGH_SCORE_VERSION) << Score + << Entry << Time << RandomID << LastAdd << CheckSum; +} + +/* This function needs much more error handling */ +void highscore::Load(cfestring& File) +{ + { + inputfile HighScore(File, 0, false); + + if(!HighScore.IsOpen()) + return; + + HighScore.Get(); + + if(HighScore.Eof()) + return; + } + + inputfile HighScore(File, 0, false); + HighScore >> Version; + HighScore >> Score >> Entry >> Time >> RandomID >> LastAdd; +} + +truth highscore::MergeToFile(highscore* To) const +{ + truth MergedSomething = false; + + for(uint c = 0; c < Score.size(); ++c) + if(!To->Find(Score[c], Entry[c], Time[c], RandomID[c])) + { + To->Add(Score[c], Entry[c], Time[c], RandomID[c]); + MergedSomething = true; + } + + return MergedSomething; +} + +truth highscore::Add(long NewScore, cfestring& NewEntry) +{ + return Add(NewScore, NewEntry, time(0), RAND()); +} + +/* Because of major stupidity this return the number of NEXT + from the right entry, 0 = not found */ + +int highscore::Find(long AScore, cfestring& AEntry, + time_t ATime, long ARandomID) +{ + for(uint c = 0; c < Score.size(); ++c) + { + if(AScore == Score[c] && Entry[c] == AEntry + && ATime == Time[c] && ARandomID == RandomID[c]) + return c + 1; + } + + return 0; +} + +truth highscore::LastAddFailed() const +{ return LastAdd == MAX_HIGHSCORES; } + +void highscore::Clear() +{ + Entry.clear(); + Score.clear(); + Time.clear(); + RandomID.clear(); + Version = HIGH_SCORE_VERSION; + LastAdd = 0xFF; +} + +truth highscore::CheckVersion() const +{ + return Version == HIGH_SCORE_VERSION; +} diff --git a/FeLib/Source/rawbit.cpp b/FeLib/Source/rawbit.cpp new file mode 100644 index 0000000..f96d44e --- /dev/null +++ b/FeLib/Source/rawbit.cpp @@ -0,0 +1,806 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include + +#include "allocate.h" +#include "rawbit.h" +#include "bitmap.h" +#include "save.h" +#include "femath.h" + +void rawbitmap::MaskedBlit(bitmap* Bitmap, packcol16* Color) const { MaskedBlit(Bitmap, ZERO_V2, ZERO_V2, Size, Color); } + +rawbitmap::rawbitmap(cfestring& FileName) +{ + inputfile File(FileName.CStr(), 0, false); + + if(!File.IsOpen()) + ABORT("Bitmap %s not found!", FileName.CStr()); + + File.SeekPosEnd(-768); + Palette = new uchar[768]; + File.Read(reinterpret_cast(Palette), 768); + File.SeekPosBegin(8); + Size.X = File.Get(); + Size.X += (File.Get() << 8) + 1; + Size.Y = File.Get(); + Size.Y += (File.Get() << 8) + 1; + File.SeekPosBegin(128); + Alloc2D(PaletteBuffer, Size.Y, Size.X); + paletteindex* Buffer = PaletteBuffer[0]; + paletteindex* End = &PaletteBuffer[Size.Y - 1][Size.X]; + + while(Buffer != End) + { + int Char1 = File.Get(); + + if(Char1 > 192) + { + int Char2 = File.Get(); + + for(; Char1 > 192; Char1--) + *Buffer++ = Char2; + } + else + *Buffer++ = Char1; + } +} + +rawbitmap::rawbitmap(v2 Size) : Size(Size) +{ + Palette = new uchar[768]; + Alloc2D(PaletteBuffer, Size.Y, Size.X); +} + +rawbitmap::~rawbitmap() +{ + delete [] Palette; + delete [] PaletteBuffer; + + for(fontcache::iterator i = FontCache.begin(); i != FontCache.end(); ++i) + { + delete i->second.first; + delete i->second.second; + } +} + +/* A lousy bitmap saver that uses the pcx format but doesn't do any compression. */ + +void rawbitmap::Save(cfestring& FileName) +{ + char PCXHeader[128]; + memset(PCXHeader, 0, 128); + *((ulong*)PCXHeader) = 0x0801050A; + PCXHeader[65] = 0x01; + PCXHeader[66] = Size.X & 0xFF; + PCXHeader[67] = (Size.X >> 8) & 0xFF; + PCXHeader[0x08] = (Size.X - 1) & 0xFF; + PCXHeader[0x09] = ((Size.X - 1) >> 8) & 0xFF; + PCXHeader[0x0A] = (Size.Y - 1) & 0xFF; + PCXHeader[0x0B] = ((Size.Y - 1) >> 8) & 0xFF; + outputfile SaveFile(FileName); + SaveFile.Write(PCXHeader, 128); + paletteindex* Buffer = PaletteBuffer[0]; + paletteindex* End = &PaletteBuffer[Size.Y - 1][Size.X]; + + while(Buffer != End) + { + paletteindex Char = *Buffer++; + + if(Char >= 192) + SaveFile.Put(char(193)); + + SaveFile.Put(Char); + } + + SaveFile.Write(reinterpret_cast(Palette), 768); +} + +void rawbitmap::MaskedBlit(bitmap* Bitmap, v2 Src, v2 Dest, v2 Border, packcol16* Color) const +{ + if(!femath::Clip(Src.X, Src.Y, Dest.X, Dest.Y, Border.X, Border.Y, Size.X, Size.Y, Bitmap->GetSize().X, Bitmap->GetSize().Y)) + return; + + paletteindex* Buffer = &PaletteBuffer[Src.Y][Src.X]; + packcol16* DestBuffer = &Bitmap->GetImage()[Dest.Y][Dest.X]; + int BitmapXSize = Bitmap->GetSize().X; + uchar* Palette = this->Palette; // eliminate the efficiency cost of dereferencing + + for(int y = 0; y < Border.Y; ++y) + { + for(int x = 0; x < Border.X; ++x) + { + int PaletteElement = Buffer[x]; + + if(PaletteElement >= 192) + { + int ThisColor = Color[(PaletteElement - 192) >> 4]; + int Index = PaletteElement & 15; + int Red = (ThisColor >> 8 & 0xF8) * Index; + int Green = (ThisColor >> 3 & 0xFC) * Index; + int Blue = (ThisColor << 3 & 0xF8) * Index; + + if(Red > 0x7FF) + Red = 0x7FF; + + if(Green > 0x7FF) + Green = 0x7FF; + + if(Blue > 0x7FF) + Blue = 0x7FF; + + DestBuffer[x] = (Red << 5 & 0xF800) | (Green & 0x7E0) | (Blue >> 6 & 0x1F); + } + else + { + int PaletteIndex = PaletteElement + (PaletteElement << 1); + int ThisColor = ((Palette[PaletteIndex] & 0xFFF8) << 8) + | ((Palette[PaletteIndex + 1] & 0xFFFC) << 3) + | (Palette[PaletteIndex + 2] >> 3); + + if(ThisColor != TRANSPARENT_COLOR) + DestBuffer[x] = ThisColor; + } + } + + DestBuffer += BitmapXSize; + Buffer += Size.X; + } +} + +cachedfont* rawbitmap::Colorize(cpackcol16* Color, alpha BaseAlpha, cpackalpha* Alpha) const +{ + cachedfont* Bitmap = new cachedfont(Size); + paletteindex* Buffer = PaletteBuffer[0]; + packcol16* DestBuffer = Bitmap->GetImage()[0]; + uchar* Palette = this->Palette; // eliminate the efficiency cost of dereferencing + packalpha* AlphaMap; + truth UseAlpha; + + if(BaseAlpha != 255 || (Alpha && (Alpha[0] != 255 || Alpha[1] != 255 || Alpha[2] != 255 || Alpha[3] != 255))) + { + Bitmap->CreateAlphaMap(BaseAlpha); + AlphaMap = Bitmap->GetAlphaMap()[0]; + UseAlpha = true; + } + else + { + AlphaMap = 0; + UseAlpha = false; + } + + int BitmapXSize = Bitmap->GetSize().X; + + for(int y = 0; y < Size.Y; ++y) + { + for(int x = 0; x < Size.X; ++x) + { + int PaletteElement = Buffer[x]; + + if(PaletteElement >= 192) + { + int ColorIndex = (PaletteElement - 192) >> 4; + int ThisColor = Color[ColorIndex]; + + if(ThisColor != TRANSPARENT_COLOR) + { + int Index = PaletteElement & 15; + int Red = (ThisColor >> 8 & 0xF8) * Index; + int Green = (ThisColor >> 3 & 0xFC) * Index; + int Blue = (ThisColor << 3 & 0xF8) * Index; + + if(Red > 0x7FF) + Red = 0x7FF; + + if(Green > 0x7FF) + Green = 0x7FF; + + if(Blue > 0x7FF) + Blue = 0x7FF; + + DestBuffer[x] = (Red << 5 & 0xF800) + | (Green & 0x7E0) + | (Blue >> 6 & 0x1F); + + if(UseAlpha) + AlphaMap[x] = Alpha[ColorIndex]; + } + else + DestBuffer[x] = TRANSPARENT_COLOR; + } + else + { + int PaletteIndex = PaletteElement + (PaletteElement << 1); + DestBuffer[x] = ((Palette[PaletteIndex] & 0xFFF8) << 8) + | ((Palette[PaletteIndex + 1] & 0xFFFC) << 3) + | (Palette[PaletteIndex + 2] >> 3); + } + } + + DestBuffer += BitmapXSize; + AlphaMap += BitmapXSize; + Buffer += Size.X; + } + + return Bitmap; +} + +bitmap* rawbitmap::Colorize(v2 Pos, v2 Border, v2 Move, cpackcol16* Color, alpha BaseAlpha, cpackalpha* Alpha, cuchar* RustData, truth AllowReguralColors) const +{ + bitmap* Bitmap = new bitmap(Border); + v2 TargetPos(0, 0); + + if(Move.X || Move.Y) + { + Bitmap->ClearToColor(TRANSPARENT_COLOR); + + if(Move.X < 0) + { + Pos.X -= Move.X; + Border.X += Move.X; + } + else if(Move.X > 0) + { + TargetPos.X = Move.X; + Border.X -= Move.X; + } + + if(Move.Y < 0) + { + Pos.Y -= Move.Y; + Border.Y += Move.Y; + } + else if(Move.Y > 0) + { + TargetPos.Y = Move.Y; + Border.Y -= Move.Y; + } + } + + paletteindex* Buffer = &PaletteBuffer[Pos.Y][Pos.X]; + packcol16* DestBuffer = &Bitmap->GetImage()[TargetPos.Y][TargetPos.X]; + int BitmapXSize = Bitmap->GetSize().X; + uchar* Palette = this->Palette; // eliminate the efficiency cost of dereferencing + packalpha* AlphaMap; + truth UseAlpha; + + if(BaseAlpha != 255 || (Alpha && (Alpha[0] != 255 || Alpha[1] != 255 || Alpha[2] != 255 || Alpha[3] != 255))) + { + Bitmap->CreateAlphaMap(BaseAlpha); + AlphaMap = &Bitmap->GetAlphaMap()[TargetPos.Y][TargetPos.X]; + UseAlpha = true; + } + else + { + AlphaMap = 0; + UseAlpha = false; + } + + truth Rusted = RustData && (RustData[0] || RustData[1] || RustData[2] || RustData[3]); + ulong RustSeed[4]; + + if(Rusted) + { + RustSeed[0] = (RustData[0] & 0xFC) >> 2; + RustSeed[1] = (RustData[1] & 0xFC) >> 2; + RustSeed[2] = (RustData[2] & 0xFC) >> 2; + RustSeed[3] = (RustData[3] & 0xFC) >> 2; + } + + for(int y = 0; y < Border.Y; ++y) + { + for(int x = 0; x < Border.X; ++x) + { + int PaletteElement = Buffer[x]; + + if(PaletteElement >= 192) + { + int ColorIndex = (PaletteElement - 192) >> 4; + int ThisColor = Color[ColorIndex]; + + if(ThisColor != TRANSPARENT_COLOR) + { + int Index = PaletteElement & 15; + int Red = (ThisColor >> 8 & 0xF8) * Index; + int Green = (ThisColor >> 3 & 0xFC) * Index; + int Blue = (ThisColor << 3 & 0xF8) * Index; + + if(Rusted && RustData[ColorIndex] + && (RustData[ColorIndex] & 3UL) + > (RustSeed[ColorIndex] = RustSeed[ColorIndex] * 1103515245 + 12345) >> 30) + { + Green = ((Green << 1) + Green) >> 2; + Blue >>= 1; + } + + if(Red > 0x7FF) + Red = 0x7FF; + + if(Green > 0x7FF) + Green = 0x7FF; + + if(Blue > 0x7FF) + Blue = 0x7FF; + + DestBuffer[x] = (Red << 5 & 0xF800) + | (Green & 0x7E0) + | (Blue >> 6 & 0x1F); + + if(UseAlpha) + AlphaMap[x] = Alpha[ColorIndex]; + } + else + DestBuffer[x] = TRANSPARENT_COLOR; + } + else if(AllowReguralColors) + { + int PaletteIndex = PaletteElement + (PaletteElement << 1); + DestBuffer[x] = ((Palette[PaletteIndex] & 0xFFF8) << 8) + | ((Palette[PaletteIndex + 1] & 0xFFFC) << 3) + | (Palette[PaletteIndex + 2] >> 3); + } + else + DestBuffer[x] = TRANSPARENT_COLOR; + } + + DestBuffer += BitmapXSize; + AlphaMap += BitmapXSize; + Buffer += Size.X; + } + + return Bitmap; +} + +void rawbitmap::Printf(bitmap* Bitmap, v2 Pos, packcol16 Color, cchar* Format, ...) const +{ + char Buffer[256]; + + va_list AP; + va_start(AP, Format); + vsprintf(Buffer, Format, AP); + va_end(AP); + + fontcache::const_iterator Iterator = FontCache.find(Color); + + if(Iterator == FontCache.end()) + { + packcol16 ShadeCol = MakeShadeColor(Color); + + for(int c = 0; Buffer[c]; ++c) + { + v2 F(((Buffer[c] - 0x20) & 0xF) << 4, (Buffer[c] - 0x20) & 0xF0); + MaskedBlit(Bitmap, F, v2(Pos.X + (c << 3) + 1, Pos.Y + 1), v2(8, 8), &ShadeCol); + MaskedBlit(Bitmap, F, v2(Pos.X + (c << 3), Pos.Y), v2(8, 8), &Color); + } + } + else + { + const cachedfont* Font = Iterator->second.first; + blitdata B = { Bitmap, + { 0, 0 }, + { Pos.X, Pos.Y }, + { 9, 9 }, + { 0 }, + TRANSPARENT_COLOR, + 0 }; + + for(int c = 0; Buffer[c]; ++c, B.Dest.X += 8) + { + B.Src.X = ((Buffer[c] - 0x20) & 0xF) << 4; + B.Src.Y = (Buffer[c] - 0x20) & 0xF0; + Font->PrintCharacter(B); + } + } +} + +void rawbitmap::PrintfUnshaded(bitmap* Bitmap, v2 Pos, packcol16 Color, cchar* Format, ...) const +{ + char Buffer[256]; + + va_list AP; + va_start(AP, Format); + vsprintf(Buffer, Format, AP); + va_end(AP); + + fontcache::const_iterator Iterator = FontCache.find(Color); + + if(Iterator == FontCache.end()) + { + for(int c = 0; Buffer[c]; ++c) + { + v2 F(((Buffer[c] - 0x20) & 0xF) << 4, (Buffer[c] - 0x20) & 0xF0); + MaskedBlit(Bitmap, F, v2(Pos.X + (c << 3), Pos.Y), v2(8, 8), &Color); + } + } + else + { + const cachedfont* Font = Iterator->second.second; + blitdata B = { Bitmap, + { 0, 0 }, + { Pos.X, Pos.Y }, + { 9, 9 }, + { 0 }, + TRANSPARENT_COLOR, + 0 }; + + for(int c = 0; Buffer[c]; ++c, B.Dest.X += 8) + { + B.Src.X = ((Buffer[c] - 0x20) & 0xF) << 4; + B.Src.Y = (Buffer[c] - 0x20) & 0xF0; + Font->PrintCharacter(B); + } + } +} + +void rawbitmap::AlterGradient(v2 Pos, v2 Border, int MColor, int Amount, truth Clip) +{ + int ColorMin = 192 + (MColor << 4); + int ColorMax = 207 + (MColor << 4); + + if(Clip) + { + for(int x = Pos.X; x < Pos.X + Border.X; ++x) + for(int y = Pos.Y; y < Pos.Y + Border.Y; ++y) + { + int Pixel = PaletteBuffer[y][x]; + + if(Pixel >= ColorMin && Pixel <= ColorMax) + { + int NewPixel = Pixel + Amount; + + if(NewPixel < ColorMin) + NewPixel = ColorMin; + + if(NewPixel > ColorMax) + NewPixel = ColorMax; + + PaletteBuffer[y][x] = NewPixel; + } + } + } + else + { + int x; + + for(x = Pos.X; x < Pos.X + Border.X; ++x) + for(int y = Pos.Y; y < Pos.Y + Border.Y; ++y) + { + int Pixel = PaletteBuffer[y][x]; + + if(Pixel >= ColorMin && Pixel <= ColorMax) + { + int NewPixel = Pixel + Amount; + + if(NewPixel < ColorMin) + return; + + if(NewPixel > ColorMax) + return; + } + } + + for(x = Pos.X; x < Pos.X + Border.X; ++x) + for(int y = Pos.Y; y < Pos.Y + Border.Y; ++y) + { + int Pixel = PaletteBuffer[y][x]; + + if(Pixel >= ColorMin && Pixel <= ColorMax) + PaletteBuffer[y][x] = Pixel + Amount; + } + } +} + +void rawbitmap::SwapColors(v2 Pos, v2 Border, int Color1, int Color2) +{ + if(Color1 > 3 || Color2 > 3) + ABORT("Illgal col swap!"); + + for(int x = Pos.X; x < Pos.X + Border.X; ++x) + for(int y = Pos.Y; y < Pos.Y + Border.Y; ++y) + { + paletteindex& Pixel = PaletteBuffer[y][x]; + + if(Pixel >= 192 + (Color1 << 4) && Pixel <= 207 + (Color1 << 4)) + Pixel += (Color2 - Color1) << 4; + else if(Pixel >= 192 + (Color2 << 4) && Pixel <= 207 + (Color2 << 4)) + Pixel += (Color1 - Color2) << 4; + } +} + +/* TempBuffer must be an array of Border.X * Border.Y paletteindices */ + +void rawbitmap::Roll(v2 Pos, v2 Border, v2 Move, paletteindex* TempBuffer) +{ + int x, y; + + for(x = Pos.X; x < Pos.X + Border.X; ++x) + for(y = Pos.Y; y < Pos.Y + Border.Y; ++y) + { + int XPos = x + Move.X, YPos = y + Move.Y; + + if(XPos < Pos.X) + XPos += Border.X; + + if(YPos < Pos.Y) + YPos += Border.Y; + + if(XPos >= Pos.X + Border.X) + XPos -= Border.X; + + if(YPos >= Pos.Y + Border.Y) + YPos -= Border.Y; + + TempBuffer[(YPos - Pos.Y) * Border.X + XPos - Pos.X] = PaletteBuffer[y][x]; + } + + for(x = Pos.X; x < Pos.X + Border.X; ++x) + for(y = Pos.Y; y < Pos.Y + Border.Y; ++y) + PaletteBuffer[y][x] = TempBuffer[(y - Pos.Y) * Border.X + x - Pos.X]; +} + +void rawbitmap::CreateFontCache(packcol16 Color) +{ + if(FontCache.find(Color) != FontCache.end()) + return; + + packcol16 ShadeColor = MakeShadeColor(Color); + cachedfont* Font = new cachedfont(Size, TRANSPARENT_COLOR); + MaskedBlit(Font, ZERO_V2, v2(1, 1), v2(Size.X - 1, Size.Y - 1), &ShadeColor); + cachedfont* UnshadedFont = Colorize(&Color); + + blitdata B = { Font, + { 0, 0 }, + { 0, 0 }, + { Size.X, Size.Y }, + { 0 }, + TRANSPARENT_COLOR, + 0 }; + + UnshadedFont->NormalMaskedBlit(B); + Font->CreateMaskMap(); + UnshadedFont->CreateMaskMap(); + FontCache[Color] = std::make_pair(Font, UnshadedFont); +} + +/* returns ERROR_V2 if fails find Pos else returns pos */ + +v2 rawbitmap::RandomizeSparklePos(cv2* ValidityArray, v2* PossibleBuffer, v2 Pos, v2 Border, int ValidityArraySize, int SparkleFlags) const +{ + if(!SparkleFlags) + return ERROR_V2; + + /* Don't use { } to initialize, or GCC optimizations will produce code that crashes! */ + + v2* BadPossible[4]; + BadPossible[0] = PossibleBuffer; + BadPossible[1] = BadPossible[0] + ((Border.X + Border.Y) << 1) - 4; + BadPossible[2] = BadPossible[1] + ((Border.X + Border.Y) << 1) - 12; + BadPossible[3] = BadPossible[2] + ((Border.X + Border.Y) << 1) - 20; + v2* PreferredPossible = BadPossible[3] + ((Border.X + Border.Y) << 1) - 28; + int Preferred = 0; + int Bad[4] = { 0, 0, 0, 0 }; + int XMax = Pos.X + Border.X; + int YMax = Pos.Y + Border.Y; + + for(int c = 0; c < ValidityArraySize; ++c) + { + v2 V = ValidityArray[c] + Pos; + int Entry = PaletteBuffer[V.Y][V.X]; + + if(IsMaterialColor(Entry) && 1 << GetMaterialColorIndex(Entry) & SparkleFlags) + { + int MinDist = 0x7FFF; + + if(V.X < Pos.X + 4) + MinDist = Min(V.X - Pos.X, MinDist); + + if(V.X >= XMax - 4) + MinDist = Min(XMax - V.X - 1, MinDist); + + if(V.Y < Pos.Y + 4) + MinDist = Min(V.Y - Pos.Y, MinDist); + + if(V.Y >= YMax - 4) + MinDist = Min(YMax - V.Y - 1, MinDist); + + if(MinDist >= 4) + PreferredPossible[Preferred++] = V; + else + BadPossible[MinDist][Bad[MinDist]++] = V; + } + } + + v2 Return; + + if(Preferred) + Return = PreferredPossible[RAND() % Preferred] - Pos; + else if(Bad[3]) + Return = BadPossible[3][RAND() % Bad[3]] - Pos; + else if(Bad[2]) + Return = BadPossible[2][RAND() % Bad[2]] - Pos; + else if(Bad[1]) + Return = BadPossible[1][RAND() % Bad[1]] - Pos; + else if(Bad[0]) + Return = BadPossible[0][RAND() % Bad[0]] - Pos; + else + Return = ERROR_V2; + + return Return; +} + +truth rawbitmap::IsTransparent(v2 Pos) const +{ + return PaletteBuffer[Pos.Y][Pos.X] == TRANSPARENT_PALETTE_INDEX; +} + +truth rawbitmap::IsMaterialColor1(v2 Pos) const +{ + int P = PaletteBuffer[Pos.Y][Pos.X]; + return P >= 192 && P < 208; +} + +void rawbitmap::CopyPaletteFrom(rawbitmap* Bitmap) +{ + memcpy(Palette, Bitmap->Palette, 768); +} + +void rawbitmap::Clear() +{ + memset(PaletteBuffer[0], TRANSPARENT_PALETTE_INDEX, Size.X * Size.Y * sizeof(paletteindex)); +} + +void rawbitmap::NormalBlit(rawbitmap* Bitmap, v2 Src, v2 Dest, v2 Border, int Flags) const +{ + paletteindex** SrcBuffer = PaletteBuffer; + paletteindex** DestBuffer = Bitmap->PaletteBuffer; + + switch(Flags & 7) + { + case NONE: + { + if(Size.X == Bitmap->Size.X && Size.Y == Bitmap->Size.Y) + memcpy(DestBuffer[0], SrcBuffer[0], Size.X * Size.Y * sizeof(paletteindex)); + else + { + cint Bytes = Border.X * sizeof(paletteindex); + + for(int y = 0; y < Border.Y; ++y) + memcpy(&DestBuffer[Dest.Y + y][Dest.X], &SrcBuffer[Src.Y + y][Src.X], Bytes); + } + + break; + } + + case MIRROR: + { + Dest.X += Border.X - 1; + + for(int y = 0; y < Border.Y; ++y) + { + cpaletteindex* SrcPtr = &SrcBuffer[Src.Y + y][Src.X]; + cpaletteindex* EndPtr = SrcPtr + Border.X; + paletteindex* DestPtr = &DestBuffer[Dest.Y + y][Dest.X]; + + for(; SrcPtr != EndPtr; ++SrcPtr, --DestPtr) + *DestPtr = *SrcPtr; + } + + break; + } + + case FLIP: + { + Dest.Y += Border.Y - 1; + cint Bytes = Border.X * sizeof(paletteindex); + + for(int y = 0; y < Border.Y; ++y) + memcpy(&DestBuffer[Dest.Y - y][Dest.X], &SrcBuffer[Src.Y + y][Src.X], Bytes); + + break; + } + + case (MIRROR | FLIP): + { + Dest.X += Border.X - 1; + Dest.Y += Border.Y - 1; + + for(int y = 0; y < Border.Y; ++y) + { + cpaletteindex* SrcPtr = &SrcBuffer[Src.Y + y][Src.X]; + cpaletteindex* EndPtr = SrcPtr + Border.X; + paletteindex* DestPtr = &DestBuffer[Dest.Y - y][Dest.X]; + + for(; SrcPtr != EndPtr; ++SrcPtr, --DestPtr) + *DestPtr = *SrcPtr; + } + + break; + } + + case ROTATE: + { + Dest.X += Border.X - 1; + int TrueDestXMove = Bitmap->Size.X; + paletteindex* DestBase = &DestBuffer[Dest.Y][Dest.X]; + + for(int y = 0; y < Border.Y; ++y) + { + cpaletteindex* SrcPtr = &SrcBuffer[Src.Y + y][Src.X]; + cpaletteindex* EndPtr = SrcPtr + Border.X; + paletteindex* DestPtr = DestBase - y; + + for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr += TrueDestXMove) + *DestPtr = *SrcPtr; + } + + break; + } + + case (MIRROR | ROTATE): + { + int TrueDestXMove = Bitmap->Size.X; + paletteindex* DestBase = &DestBuffer[Dest.Y][Dest.X]; + + for(int y = 0; y < Border.Y; ++y) + { + cpaletteindex* SrcPtr = &SrcBuffer[Src.Y + y][Src.X]; + cpaletteindex* EndPtr = SrcPtr + Border.X; + paletteindex* DestPtr = DestBase + y; + + for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr += TrueDestXMove) + *DestPtr = *SrcPtr; + } + + break; + } + + case (FLIP | ROTATE): + { + Dest.X += Border.X - 1; + Dest.Y += Border.Y - 1; + int TrueDestXMove = Bitmap->Size.X; + paletteindex* DestBase = &DestBuffer[Dest.Y][Dest.X]; + + for(int y = 0; y < Border.Y; ++y) + { + cpaletteindex* SrcPtr = &SrcBuffer[Src.Y + y][Src.X]; + cpaletteindex* EndPtr = SrcPtr + Border.X; + paletteindex* DestPtr = DestBase - y; + + for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= TrueDestXMove) + *DestPtr = *SrcPtr; + } + + break; + } + + case (MIRROR | FLIP | ROTATE): + { + Dest.Y += Border.Y - 1; + int TrueDestXMove = Bitmap->Size.X; + paletteindex* DestBase = &DestBuffer[Dest.Y][Dest.X]; + + for(int y = 0; y < Border.Y; ++y) + { + cpaletteindex* SrcPtr = &SrcBuffer[Src.Y + y][Src.X]; + cpaletteindex* EndPtr = SrcPtr + Border.X; + paletteindex* DestPtr = DestBase + y; + + for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= TrueDestXMove) + *DestPtr = *SrcPtr; + } + + break; + } + } +} diff --git a/FeLib/Source/save.cpp b/FeLib/Source/save.cpp new file mode 100644 index 0000000..7a15d31 --- /dev/null +++ b/FeLib/Source/save.cpp @@ -0,0 +1,645 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include + +#include "save.h" +#include "femath.h" + +outputfile::outputfile(cfestring& FileName, truth AbortOnErr) +: Buffer(fopen(FileName.CStr(), "wb")), FileName(FileName) +{ + if(AbortOnErr && !IsOpen()) + ABORT("Can't open %s for output!", FileName.CStr()); +} + +outputfile::~outputfile() +{ + if(Buffer) + fclose(Buffer); +} + +void outputfile::ReOpen() +{ + fclose(Buffer); + Buffer = fopen(FileName.CStr(), "ab"); +} + +inputfile::inputfile(cfestring& FileName, + const valuemap* ValueMap, + truth AbortOnErr) +: Buffer(fopen(FileName.CStr(), "rb")), + FileName(FileName), ValueMap(ValueMap) +{ + if(AbortOnErr && !IsOpen()) + ABORT("File %s not found!", FileName.CStr()); +} + +inputfile::~inputfile() +{ + if(Buffer) + fclose(Buffer); +} + +festring inputfile::ReadWord(truth AbortOnEOF) +{ + static festring ToReturn; + ReadWord(ToReturn, AbortOnEOF); + return ToReturn; +} + +#define MODE_WORD 1 +#define MODE_NUMBER 2 + +#define PUNCT_RETURN 0 +#define PUNCT_CONTINUE 1 + +int inputfile::HandlePunct(festring& String, int Char, int Mode) +{ + if(Char == '/') + { + if(!feof(Buffer)) + { + Char = fgetc(Buffer); + + if(Char == '*') + { + long StartPos = TellPos(); + int OldChar = 0, CommentLevel = 1; + + for(;;) + { + if(feof(Buffer)) + ABORT("Unterminated comment in file %s, beginning at line %ld!", + FileName.CStr(), TellLineOfPos(StartPos)); + + Char = fgetc(Buffer); + + if(OldChar != '*' || Char != '/') + { + if(OldChar != '/' || Char != '*') + OldChar = Char; + else + { + ++CommentLevel; + OldChar = 0; + } + } + else + { + if(!--CommentLevel) + break; + else + OldChar = 0; + } + } + + return PUNCT_CONTINUE; + } + else + { + ungetc(Char, Buffer); + clearerr(Buffer); + } + } + + if(Mode) + ungetc('/', Buffer); + else + String << '/'; + + return PUNCT_RETURN; + } + + if(Mode) + { + ungetc(Char, Buffer); + return PUNCT_RETURN; + } + + if(Char == '"') + { + long StartPos = TellPos(); + int OldChar = 0; + + for(;;) + { + if(feof(Buffer)) + ABORT("Unterminated string in file %s, beginning at line %ld!", + FileName.CStr(), TellLineOfPos(StartPos)); + + Char = fgetc(Buffer); + + if(Char != '"') + { + String << char(Char); + OldChar = Char; + } + else if(OldChar == '\\') + { + String[String.GetSize() - 1] = '"'; + OldChar = 0; + } + else + return PUNCT_RETURN; + } + } + + String << char(Char); + return PUNCT_RETURN; +} + +void inputfile::ReadWord(festring& String, truth AbortOnEOF) +{ + int Mode = 0; + String.Empty(); + + for(int Char = fgetc(Buffer); !feof(Buffer); Char = fgetc(Buffer)) + { + if(isalpha(Char) || Char == '_') + { + if(!Mode) + Mode = MODE_WORD; + else if(Mode == MODE_NUMBER) + { + ungetc(Char, Buffer); + return; + } + + String << char(Char); + continue; + } + + if(isdigit(Char)) + { + if(!Mode) + Mode = MODE_NUMBER; + else if(Mode == MODE_WORD) + { + ungetc(Char, Buffer); + return; + } + + String << char(Char); + continue; + } + + if((Char == ' ' || Char == '\n') && Mode) + return; + + if(ispunct(Char) && HandlePunct(String, Char, Mode) == PUNCT_RETURN) + return; + } + + if(AbortOnEOF) + ABORT("Unexpected end of file %s!", FileName.CStr()); + + if(Mode) + clearerr(Buffer); +} + +char inputfile::ReadLetter(truth AbortOnEOF) +{ + for(int Char = fgetc(Buffer); !feof(Buffer); Char = fgetc(Buffer)) + { + if(isalpha(Char) || isdigit(Char)) + return Char; + + if(ispunct(Char)) + { + if(Char == '/') + { + if(!feof(Buffer)) + { + Char = fgetc(Buffer); + + if(Char == '*') + { + long StartPos = TellPos(); + int OldChar = 0, CommentLevel = 1; + + for(;;) + { + if(feof(Buffer)) + ABORT("Unterminated comment in file %s, " + "beginning at line %ld!", + FileName.CStr(), TellLineOfPos(StartPos)); + + Char = fgetc(Buffer); + + if(OldChar != '*' || Char != '/') + { + if(OldChar != '/' || Char != '*') + OldChar = Char; + else + { + ++CommentLevel; + OldChar = 0; + } + } + else + { + if(!--CommentLevel) + break; + else + OldChar = 0; + } + } + + continue; + } + else + ungetc(Char, Buffer); + } + + return '/'; + } + + return Char; + } + } + + if(AbortOnEOF) + ABORT("Unexpected end of file %s!", FileName.CStr()); + + return 0; +} + +/* Reads a number or a formula from inputfile. Valid values could be for + instance "3", "5 * 4+5", "2+Variable%4" etc. */ + +long inputfile::ReadNumber(int CallLevel, truth PreserveTerminator) +{ + long Value = 0; + static festring Word; + truth NumberCorrect = false; + + for(;;) + { + ReadWord(Word); + char First = Word[0]; + + if(isdigit(First)) + { + Value = atoi(Word.CStr()); + NumberCorrect = true; + continue; + } + + if(Word.GetSize() == 1) + { + if(First == ';' || First == ',' || First == ':') + { + if(CallLevel != HIGHEST || PreserveTerminator) + ungetc(First, Buffer); + + return Value; + } + + if(First == ')') + { + if((CallLevel != HIGHEST && CallLevel != 4) || PreserveTerminator) + ungetc(')', Buffer); + + return Value; + } + + if(First == '~') + { + Value = ~ReadNumber(4); + NumberCorrect = true; + continue; + } + + /* Convert this into an inline function! */ + +#define CHECK_OP(op, cl)\ + if(First == #op[0])\ + if(cl < CallLevel)\ + {\ + Value op##= ReadNumber(cl);\ + NumberCorrect = true;\ + continue;\ + }\ + else\ + {\ + ungetc(#op[0], Buffer);\ + return Value;\ + } + + CHECK_OP(&, 1); CHECK_OP(|, 1); CHECK_OP(^, 1); + CHECK_OP(*, 2); CHECK_OP(/, 2); CHECK_OP(%, 2); + CHECK_OP(+, 3); CHECK_OP(-, 3); + + if(First == '<') + { + char Next = Get(); + + if(Next == '<') + if(1 < CallLevel) + { + Value <<= ReadNumber(1); + NumberCorrect = true; + continue; + } + else + { + ungetc('<', Buffer); + ungetc('<', Buffer); + return Value; + } + else + ungetc(Next, Buffer); + } + + if(First == '>') + { + char Next = Get(); + + if(Next == '>') + if(1 < CallLevel) + { + Value >>= ReadNumber(1); + NumberCorrect = true; + continue; + } + else + { + ungetc('>', Buffer); + ungetc('>', Buffer); + return Value; + } + else + ungetc(Next, Buffer); + } + + if(First == '(') + if(NumberCorrect) + { + ungetc('(', Buffer); + return Value; + } + else + { + Value = ReadNumber(4); + NumberCorrect = false; + continue; + } + + if(First == '=' && CallLevel == HIGHEST) + continue; + + if(First == '#') // for #defines + { + ungetc('#', Buffer); + return Value; + } + } + + if(Word == "rgb") + { + int Bits = ReadNumber(); + + if(Bits == 16) + { + int Red = ReadNumber(); + int Green = ReadNumber(); + int Blue = ReadNumber(); + Value = MakeRGB16(Red, Green, Blue); + } + else if(Bits == 24) + { + int Red = ReadNumber(); + int Green = ReadNumber(); + int Blue = ReadNumber(); + Value = MakeRGB24(Red, Green, Blue); + } + else + ABORT("Illegal RGB bit size %d in file %s, line %ld!", + Bits, FileName.CStr(), TellLine()); + + NumberCorrect = true; + continue; + } + + if(Word == "true") + { + Value = 1; + NumberCorrect = true; + continue; + } + + if(Word == "false") + { + Value = 0; + NumberCorrect = true; + continue; + } + + if(ValueMap) + { + valuemap::const_iterator Iterator = ValueMap->find(Word); + + if(Iterator != ValueMap->end()) + { + Value = Iterator->second; + NumberCorrect = true; + continue; + } + } + + ABORT("Odd numeric value \"%s\" encountered in file %s, line %ld!", + Word.CStr(), FileName.CStr(), TellLine()); + } +} + +v2 inputfile::ReadVector2d() +{ + v2 Vector; + Vector.X = ReadNumber(); + Vector.Y = ReadNumber(); + return Vector; +} + +rect inputfile::ReadRect() +{ + rect Rect; + Rect.X1 = ReadNumber(); + Rect.Y1 = ReadNumber(); + Rect.X2 = ReadNumber(); + Rect.Y2 = ReadNumber(); + return Rect; +} + +outputfile& operator<<(outputfile& SaveFile, cfestring& String) +{ + ushort Length = String.GetSize(); + SaveFile << Length; + + if(Length) + SaveFile.Write(String.CStr(), Length); + + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, festring& String) +{ + char* RealBuffer, StackBuffer[1024]; + ushort Length; + SaveFile >> Length; + RealBuffer = Length < 1024 ? StackBuffer : new char[Length + 1]; + String.Empty(); + + if(Length) + { + SaveFile.Read(RealBuffer, Length); + RealBuffer[Length] = 0; + String << RealBuffer; + } + + if(Length >= 1024) + delete [] RealBuffer; + + return SaveFile; +} + +outputfile& operator<<(outputfile& SaveFile, cchar* String) +{ + ushort Length = String ? strlen(String) : 0; + SaveFile << Length; + + if(Length) + SaveFile.Write(String, Length); + + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, char*& String) +{ + ushort Length; + SaveFile >> Length; + + if(Length) + { + String = new char[Length + 1]; + SaveFile.Read(String, Length); + String[Length] = 0; + } + else + String = 0; + + return SaveFile; +} + +void ReadData(festring& String, inputfile& SaveFile) +{ + SaveFile.ReadWord(String); + + if(String == "=") + SaveFile.ReadWord(String); + + SaveFile.ReadWord(); +} + +void ReadData(fearray& Array, inputfile& SaveFile) +{ + Array.Clear(); + static festring Word; + SaveFile.ReadWord(Word); + + if(Word == "=") + SaveFile.ReadWord(Word); + + if(Word == "=") + { + Array.Allocate(1); + Array.Data[0] = SaveFile.ReadNumber(); + return; + } + + if(Word != "{") + ABORT("Array syntax error \"%s\" found in file %s, line %ld!", + Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TellLine()); + + fearray::sizetype Size = SaveFile.ReadNumber(); + Array.Allocate(Size); + + for(fearray::sizetype c = 0; c < Size; ++c) + Array.Data[c] = SaveFile.ReadNumber(); + + if(SaveFile.ReadWord() != "}") + ABORT("Illegal array terminator \"%s\" " + "encountered in file %s, line %ld!", + Word.CStr(), SaveFile.GetFileName().CStr(), + SaveFile.TellLine()); +} + +void ReadData(fearray& Array, inputfile& SaveFile) +{ + Array.Clear(); + static festring Word; + SaveFile.ReadWord(Word); + + if(Word == "=") + SaveFile.ReadWord(Word); + + if(Word == "=") + { + Array.Allocate(1); + SaveFile.ReadWord(Array.Data[0]); + + if(SaveFile.ReadWord() != ";") + ABORT("Array syntax error \"%s\" found in file %s, line %ld!", + Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TellLine()); + + return; + } + + if(Word != "{") + ABORT("Array syntax error \"%s\" found in file %s, line %ld!", + Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TellLine()); + + fearray::sizetype Size = SaveFile.ReadNumber(); + Array.Allocate(Size); + + for(fearray::sizetype c = 0; c < Size; ++c) + { + SaveFile.ReadWord(Array.Data[c]); + SaveFile.ReadWord(Word); + + if(Word != "," && Word != ";") + ABORT("Array syntax error \"%s\" found in file %s, line %ld!", + Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TellLine()); + } + + if(SaveFile.ReadWord() != "}") + ABORT("Illegal array terminator \"%s\" " + "encountered in file %s, line %ld!", + Word.CStr(), SaveFile.GetFileName().CStr(), + SaveFile.TellLine()); +} + +ulong inputfile::TellLineOfPos(long Pos) +{ + ulong Line = 1; + long BackupPos = TellPos(); + SeekPosBegin(0); + + while(TellPos() != Pos) + if(fgetc(Buffer) == '\n') + ++Line; + + if(TellPos() != BackupPos) + SeekPosBegin(BackupPos); + + return Line; +} diff --git a/FeLib/Source/whandler.cpp b/FeLib/Source/whandler.cpp new file mode 100644 index 0000000..125d3be --- /dev/null +++ b/FeLib/Source/whandler.cpp @@ -0,0 +1,295 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "whandler.h" +#include "graphics.h" +#include "error.h" +#include "bitmap.h" +#include "festring.h" + +truth (*globalwindowhandler::ControlLoop[MAX_CONTROLS])(); +int globalwindowhandler::Controls = 0; +ulong globalwindowhandler::Tick; +truth globalwindowhandler::ControlLoopsEnabled = true; + +void globalwindowhandler::InstallControlLoop(truth (*What)()) +{ + if(Controls == MAX_CONTROLS) + ABORT("Animation control frenzy!"); + + ControlLoop[Controls++] = What; +} + +void globalwindowhandler::DeInstallControlLoop(truth (*What)()) +{ + int c; + + for(c = 0; c < Controls; ++c) + if(ControlLoop[c] == What) + break; + + if(c != Controls) + { + --Controls; + + for(; c < Controls; ++c) + ControlLoop[c] = ControlLoop[c + 1]; + } +} + +#ifdef __DJGPP__ + +#include +#include + +int globalwindowhandler::GetKey(truth EmptyBuffer) +{ + if(EmptyBuffer) + while(kbhit()) + getkey(); + + int Key = 0; + + while(!Key) + { + while(!kbhit()) + if(Controls && ControlLoopsEnabled) + { + static ulong LastTick = 0; + UpdateTick(); + + if(LastTick != Tick) + { + LastTick = Tick; + truth Draw = false; + + for(int c = 0; c < Controls; ++c) + if(ControlLoop[c]()) + Draw = true; + + if(Draw) + graphics::BlitDBToScreen(); + } + } + + Key = getkey(); + + if(Key == K_Control_Print) + { + DOUBLE_BUFFER->Save("Scrshot.bmp"); + Key = 0; + } + } + + return Key; +} + +int globalwindowhandler::ReadKey() +{ + return kbhit() ? getkey() : 0; +} + +#endif + +#ifdef USE_SDL + +#include + +std::vector globalwindowhandler::KeyBuffer; +truth (*globalwindowhandler::QuitMessageHandler)() = 0; + +void globalwindowhandler::Init() +{ + SDL_EnableUNICODE(1); + SDL_EnableKeyRepeat(500, 30); +} + +int globalwindowhandler::GetKey(truth EmptyBuffer) +{ + SDL_Event Event; + + if(EmptyBuffer) + { + while(SDL_PollEvent(&Event)) + ProcessMessage(&Event); + + KeyBuffer.clear(); + } + + for(;;) + if(!KeyBuffer.empty()) + { + int Key = KeyBuffer[0]; + KeyBuffer.erase(KeyBuffer.begin()); + + if(Key > 0xE000) + return Key - 0xE000; + + if(Key && Key < 0x81) + return Key; + } + else + { + if(SDL_PollEvent(&Event)) + ProcessMessage(&Event); + else + { + if(SDL_GetAppState() & SDL_APPACTIVE + && Controls && ControlLoopsEnabled) + { + static ulong LastTick = 0; + UpdateTick(); + + if(LastTick != Tick) + { + LastTick = Tick; + truth Draw = false; + + for(int c = 0; c < Controls; ++c) + if(ControlLoop[c]()) + Draw = true; + + if(Draw) + graphics::BlitDBToScreen(); + } + + SDL_Delay(10); + } + else + { + SDL_WaitEvent(&Event); + ProcessMessage(&Event); + } + } + } +} + +int globalwindowhandler::ReadKey() +{ + SDL_Event Event; + + if(SDL_GetAppState() & SDL_APPACTIVE) + { + while(SDL_PollEvent(&Event)) + ProcessMessage(&Event); + } + else + { + SDL_WaitEvent(&Event); + ProcessMessage(&Event); + } + + return KeyBuffer.size() ? GetKey(false) : 0; +} + +void globalwindowhandler::ProcessMessage(SDL_Event* Event) +{ + int KeyPressed; + + switch(Event->active.type) + { + case SDL_VIDEOEXPOSE: + graphics::BlitDBToScreen(); + break; + case SDL_QUIT: + if(!QuitMessageHandler || QuitMessageHandler()) + exit(0); + + return; + case SDL_KEYDOWN: + switch(Event->key.keysym.sym) + { + case SDLK_RETURN: + case SDLK_KP_ENTER: + if(Event->key.keysym.mod & KMOD_ALT) + { + graphics::SwitchMode(); + return; + } + else + KeyPressed = KEY_ENTER; //Event->key.keysym.unicode; + + break; + case SDLK_DOWN: + case SDLK_KP2: + KeyPressed = KEY_DOWN + 0xE000; + break; + case SDLK_UP: + case SDLK_KP8: + KeyPressed = KEY_UP + 0xE000; + break; + case SDLK_RIGHT: + case SDLK_KP6: + KeyPressed = KEY_RIGHT + 0xE000; + break; + case SDLK_LEFT: + case SDLK_KP4: + KeyPressed = KEY_LEFT + 0xE000; + break; + case SDLK_HOME: + case SDLK_KP7: + KeyPressed = KEY_HOME + 0xE000; + break; + case SDLK_END: + case SDLK_KP1: + KeyPressed = KEY_END + 0xE000; + break; + case SDLK_PAGEUP: + case SDLK_KP9: + KeyPressed = KEY_PAGE_UP + 0xE000; + break; + case SDLK_KP3: + case SDLK_PAGEDOWN: + KeyPressed = KEY_PAGE_DOWN + 0xE000; + break; + case SDLK_KP5: + KeyPressed = '.'; + break; + case SDLK_SYSREQ: + case SDLK_PRINT: +#ifdef WIN32 + DOUBLE_BUFFER->Save("Scrshot.bmp"); +#else + DOUBLE_BUFFER->Save(festring(getenv("HOME")) + "/Scrshot.bmp"); +#endif + return; + case SDLK_e: + if(Event->key.keysym.mod & KMOD_ALT + && (Event->key.keysym.mod & KMOD_LCTRL + || Event->key.keysym.mod & KMOD_RCTRL)) + { + KeyPressed = '\177'; + break; + } + default: + KeyPressed = Event->key.keysym.unicode; + + if(!KeyPressed) + return; + } + + if(std::find(KeyBuffer.begin(), KeyBuffer.end(), KeyPressed) + == KeyBuffer.end()) + KeyBuffer.push_back(KeyPressed); + + break; + } +} + +// returns true if shift is being pressed +// else false +truth globalwindowhandler::ShiftIsDown() { + return false; + +} +#endif + diff --git a/FeMath/CVS/Entries b/FeMath/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FeMath/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FeMath/CVS/Entries.Log b/FeMath/CVS/Entries.Log new file mode 100644 index 0000000..b7481e5 --- /dev/null +++ b/FeMath/CVS/Entries.Log @@ -0,0 +1,2 @@ +A D/Include//// +A D/Source//// diff --git a/FeMath/CVS/Repository b/FeMath/CVS/Repository new file mode 100644 index 0000000..d7fb3ad --- /dev/null +++ b/FeMath/CVS/Repository @@ -0,0 +1 @@ +ivan/FeMath diff --git a/FeMath/CVS/Root b/FeMath/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeMath/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeMath/Include/CVS/Entries b/FeMath/Include/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FeMath/Include/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FeMath/Include/CVS/Repository b/FeMath/Include/CVS/Repository new file mode 100644 index 0000000..8b69eac --- /dev/null +++ b/FeMath/Include/CVS/Repository @@ -0,0 +1 @@ +ivan/FeMath/Include diff --git a/FeMath/Include/CVS/Root b/FeMath/Include/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeMath/Include/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeMath/Source/CVS/Entries b/FeMath/Source/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FeMath/Source/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FeMath/Source/CVS/Repository b/FeMath/Source/CVS/Repository new file mode 100644 index 0000000..2964eb8 --- /dev/null +++ b/FeMath/Source/CVS/Repository @@ -0,0 +1 @@ +ivan/FeMath/Source diff --git a/FeMath/Source/CVS/Root b/FeMath/Source/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeMath/Source/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeWin/CVS/Entries b/FeWin/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FeWin/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FeWin/CVS/Entries.Log b/FeWin/CVS/Entries.Log new file mode 100644 index 0000000..b7481e5 --- /dev/null +++ b/FeWin/CVS/Entries.Log @@ -0,0 +1,2 @@ +A D/Include//// +A D/Source//// diff --git a/FeWin/CVS/Repository b/FeWin/CVS/Repository new file mode 100644 index 0000000..87c2d83 --- /dev/null +++ b/FeWin/CVS/Repository @@ -0,0 +1 @@ +ivan/FeWin diff --git a/FeWin/CVS/Root b/FeWin/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeWin/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeWin/Include/CVS/Entries b/FeWin/Include/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FeWin/Include/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FeWin/Include/CVS/Repository b/FeWin/Include/CVS/Repository new file mode 100644 index 0000000..ab4c81f --- /dev/null +++ b/FeWin/Include/CVS/Repository @@ -0,0 +1 @@ +ivan/FeWin/Include diff --git a/FeWin/Include/CVS/Root b/FeWin/Include/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeWin/Include/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/FeWin/Source/CVS/Entries b/FeWin/Source/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/FeWin/Source/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/FeWin/Source/CVS/Repository b/FeWin/Source/CVS/Repository new file mode 100644 index 0000000..fb1cf91 --- /dev/null +++ b/FeWin/Source/CVS/Repository @@ -0,0 +1 @@ +ivan/FeWin/Source diff --git a/FeWin/Source/CVS/Root b/FeWin/Source/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/FeWin/Source/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/Graphics/CVS/Entries b/Graphics/CVS/Entries new file mode 100644 index 0000000..600cbe8 --- /dev/null +++ b/Graphics/CVS/Entries @@ -0,0 +1,17 @@ +/Char.pcx/1.86/Mon Jul 25 11:55:59 2005/-kb/ +/Cursor.pcx/1.6/Thu Aug 24 06:10:46 2006/-kb/ +/Effect.pcx/1.4/Fri Dec 10 09:09:34 2004/-kb/ +/Enner.pcx/1.1/Sun Sep 3 18:05:49 2006/-kb/ +/FOW.pcx/1.3/Fri Dec 10 09:09:34 2004/-kb/ +/Font.pcx/1.8/Fri Dec 10 09:09:34 2004/-kb/ +/GLTerra.pcx/1.12/Fri Dec 10 09:09:34 2004/-kb/ +/Humanoid.pcx/1.48/Tue Aug 22 19:45:08 2006/-kb/ +/Icon.bmp/1.4/Sun Aug 3 20:37:14 2003/-kb/ +/Item.pcx/1.91/Tue Aug 22 19:45:08 2006/-kb/ +/Makefile.am/1.14/Sun Jul 16 14:32:19 2006// +/Menu.pcx/1.10/Fri Dec 10 09:09:38 2004/-kb/ +/OLTerra.pcx/1.21/Tue Aug 22 19:45:08 2006/-kb/ +/Smiley.pcx/1.1/Sun Jul 16 14:30:12 2006/-kb/ +/Symbol.pcx/1.23/Fri Mar 4 13:08:15 2005/-kb/ +/WTerra.pcx/1.4/Fri Dec 10 09:09:39 2004/-kb/ +D diff --git a/Graphics/CVS/Repository b/Graphics/CVS/Repository new file mode 100644 index 0000000..72e5585 --- /dev/null +++ b/Graphics/CVS/Repository @@ -0,0 +1 @@ +ivan/Graphics diff --git a/Graphics/CVS/Root b/Graphics/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/Graphics/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/Graphics/Char.pcx b/Graphics/Char.pcx new file mode 100644 index 0000000..2b09053 Binary files /dev/null and b/Graphics/Char.pcx differ diff --git a/Graphics/Cursor.pcx b/Graphics/Cursor.pcx new file mode 100644 index 0000000..2a293fc Binary files /dev/null and b/Graphics/Cursor.pcx differ diff --git a/Graphics/Effect.pcx b/Graphics/Effect.pcx new file mode 100644 index 0000000..f3b9ffe Binary files /dev/null and b/Graphics/Effect.pcx differ diff --git a/Graphics/Enner.pcx b/Graphics/Enner.pcx new file mode 100644 index 0000000..de2ade6 Binary files /dev/null and b/Graphics/Enner.pcx differ diff --git a/Graphics/FOW.pcx b/Graphics/FOW.pcx new file mode 100644 index 0000000..b6762ac Binary files /dev/null and b/Graphics/FOW.pcx differ diff --git a/Graphics/Font.pcx b/Graphics/Font.pcx new file mode 100644 index 0000000..e5cb28c Binary files /dev/null and b/Graphics/Font.pcx differ diff --git a/Graphics/GLTerra.pcx b/Graphics/GLTerra.pcx new file mode 100644 index 0000000..d803da1 Binary files /dev/null and b/Graphics/GLTerra.pcx differ diff --git a/Graphics/Humanoid.pcx b/Graphics/Humanoid.pcx new file mode 100644 index 0000000..282876c Binary files /dev/null and b/Graphics/Humanoid.pcx differ diff --git a/Graphics/Icon.bmp b/Graphics/Icon.bmp new file mode 100644 index 0000000..89af80a Binary files /dev/null and b/Graphics/Icon.bmp differ diff --git a/Graphics/Item.pcx b/Graphics/Item.pcx new file mode 100644 index 0000000..cd2a55e Binary files /dev/null and b/Graphics/Item.pcx differ diff --git a/Graphics/Makefile.am b/Graphics/Makefile.am new file mode 100644 index 0000000..467941f --- /dev/null +++ b/Graphics/Makefile.am @@ -0,0 +1,3 @@ +graphicdir = $(datadir)/ivan/Graphics +graphic_DATA = Char.pcx Cursor.pcx Effect.pcx Font.pcx FOW.pcx GLTerra.pcx Humanoid.pcx Icon.bmp Item.pcx Menu.pcx OLTerra.pcx Smiley.pcx Symbol.pcx WTerra.pcx +EXTRA_DIST = Char.pcx Cursor.pcx Effect.pcx Font.pcx FOW.pcx GLTerra.pcx Humanoid.pcx Icon.bmp Item.pcx Menu.pcx OLTerra.pcx Smiley.pcx Symbol.pcx WTerra.pcx diff --git a/Graphics/Makefile.in b/Graphics/Makefile.in new file mode 100644 index 0000000..353dacb --- /dev/null +++ b/Graphics/Makefile.in @@ -0,0 +1,215 @@ +# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMDEPBACKSLASH = @AMDEPBACKSLASH@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CC = @CC@ +CCAS = @CCAS@ +CCASFLAGS = @CCASFLAGS@ +CXX = @CXX@ +CYGPATH_W = @CYGPATH_W@ +DEPDIR = @DEPDIR@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +SDL_CFLAGS = @SDL_CFLAGS@ +SDL_CONFIG = @SDL_CONFIG@ +SDL_LIBS = @SDL_LIBS@ +STRIP = @STRIP@ +VERSION = @VERSION@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +graphicdir = $(datadir)/ivan/Graphics +graphic_DATA = Char.pcx Cursor.pcx Effect.pcx Font.pcx FOW.pcx GLTerra.pcx Humanoid.pcx Icon.bmp Item.pcx Menu.pcx OLTerra.pcx Symbol.pcx WTerra.pcx +EXTRA_DIST = Char.pcx Cursor.pcx Effect.pcx Font.pcx FOW.pcx GLTerra.pcx Humanoid.pcx Icon.bmp Item.pcx Menu.pcx OLTerra.pcx Symbol.pcx WTerra.pcx +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = +DATA = $(graphic_DATA) + +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu Graphics/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES) + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +install-graphicDATA: $(graphic_DATA) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(graphicdir) + @list='$(graphic_DATA)'; for p in $$list; do \ + if test -f $(srcdir)/$$p; then \ + echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(graphicdir)/$$p"; \ + $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(graphicdir)/$$p; \ + else if test -f $$p; then \ + echo " $(INSTALL_DATA) $$p $(DESTDIR)$(graphicdir)/$$p"; \ + $(INSTALL_DATA) $$p $(DESTDIR)$(graphicdir)/$$p; \ + fi; fi; \ + done + +uninstall-graphicDATA: + @$(NORMAL_UNINSTALL) + list='$(graphic_DATA)'; for p in $$list; do \ + rm -f $(DESTDIR)$(graphicdir)/$$p; \ + done +tags: TAGS +TAGS: + + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = Graphics + +distdir: $(DISTFILES) + here=`cd $(top_builddir) && pwd`; \ + top_distdir=`cd $(top_distdir) && pwd`; \ + distdir=`cd $(distdir) && pwd`; \ + cd $(top_srcdir) \ + && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu Graphics/Makefile + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$d/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: +install-exec: install-exec-am + +install-data-am: install-graphicDATA +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-graphicDATA +uninstall: uninstall-am +all-am: Makefile $(DATA) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(graphicdir) + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: uninstall-graphicDATA install-graphicDATA tags distdir info-am \ +info dvi-am dvi check check-am installcheck-am installcheck \ +install-exec-am install-exec install-data-am install-data install-am \ +install uninstall-am uninstall all-redirect all-am all installdirs \ +mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Graphics/Menu.pcx b/Graphics/Menu.pcx new file mode 100644 index 0000000..dbf1c70 Binary files /dev/null and b/Graphics/Menu.pcx differ diff --git a/Graphics/OLTerra.pcx b/Graphics/OLTerra.pcx new file mode 100644 index 0000000..244211d Binary files /dev/null and b/Graphics/OLTerra.pcx differ diff --git a/Graphics/Smiley.pcx b/Graphics/Smiley.pcx new file mode 100644 index 0000000..3e38a4b Binary files /dev/null and b/Graphics/Smiley.pcx differ diff --git a/Graphics/Symbol.pcx b/Graphics/Symbol.pcx new file mode 100644 index 0000000..9ef5671 Binary files /dev/null and b/Graphics/Symbol.pcx differ diff --git a/Graphics/WTerra.pcx b/Graphics/WTerra.pcx new file mode 100644 index 0000000..9202bdf Binary files /dev/null and b/Graphics/WTerra.pcx differ diff --git a/IVAN.exe b/IVAN.exe new file mode 100644 index 0000000..7f8f6d8 Binary files /dev/null and b/IVAN.exe differ diff --git a/Include/CVS/Entries b/Include/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/Include/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/Include/CVS/Repository b/Include/CVS/Repository new file mode 100644 index 0000000..25b0645 --- /dev/null +++ b/Include/CVS/Repository @@ -0,0 +1 @@ +ivan/Include diff --git a/Include/CVS/Root b/Include/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/Include/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/KNOWN_ISSUES b/KNOWN_ISSUES new file mode 100644 index 0000000..8e0e581 --- /dev/null +++ b/KNOWN_ISSUES @@ -0,0 +1,18 @@ +Bugs that will crash CLIVAN: + +- Praying to Silva while on the 5th floor or the 6th floor in gloomy caves, DON'T DO IT. The earthquake bug still exists. + +- Wishing for a flaming sword results in a crash + +- When editing the dungeon script files, one must presently specify GenerateWards to be true or false, otherwise the game will crash. + + +Bugs that won't crash CLIVAN: + +- It is known that certain hostile dolphins will follow the player to other levels + + +Submit any bugs that occur to Warheck, or lampshade at www.attnam.com + +I would recommend keeping the AutoSave count at 1. Because I've had some crashes that's happened for no reason. + diff --git a/LICENSING b/LICENSING new file mode 100644 index 0000000..8260cd2 --- /dev/null +++ b/LICENSING @@ -0,0 +1,25 @@ +Licensing +--------- + +All files contained in this release are part of Continuation of Lampshade's +Iter Vehemens ad Necem. + +Continuation of Lampshade's Iter Vehemens ad Necem is Copyright (C) 2011, +Ryan van Herel. + +Iter Vehemens ad Necem 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. + +Iter Vehemens ad Necem 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 Iter Vehemens ad Necem; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +The GNU General Public License can be found in the file `COPYING' in +this release. diff --git a/Main/CVS/Entries b/Main/CVS/Entries new file mode 100644 index 0000000..2384365 --- /dev/null +++ b/Main/CVS/Entries @@ -0,0 +1,2 @@ +/Makefile.am/1.3/Wed Apr 2 12:37:42 2003// +D diff --git a/Main/CVS/Entries.Log b/Main/CVS/Entries.Log new file mode 100644 index 0000000..26e88ec --- /dev/null +++ b/Main/CVS/Entries.Log @@ -0,0 +1,3 @@ +A D/Include//// +A D/Resource//// +A D/Source//// diff --git a/Main/CVS/Repository b/Main/CVS/Repository new file mode 100644 index 0000000..1d39719 --- /dev/null +++ b/Main/CVS/Repository @@ -0,0 +1 @@ +ivan/Main diff --git a/Main/CVS/Root b/Main/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/Main/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/Main/Include/CVS/Entries b/Main/Include/CVS/Entries new file mode 100644 index 0000000..72df84a --- /dev/null +++ b/Main/Include/CVS/Entries @@ -0,0 +1,56 @@ +/Makefile.am/1.19/Sat Jul 23 23:58:57 2005// +/action.h/1.21/Tue May 10 20:32:59 2005// +/actions.h/1.23/Tue May 10 20:32:59 2005// +/area.h/1.49/Wed Sep 29 21:54:33 2004// +/balance.h/1.4/Fri Oct 29 12:16:02 2004// +/bodypart.h/1.63/Mon Aug 21 18:58:55 2006// +/char.h/1.161/Tue Aug 22 12:18:40 2006// +/command.h/1.33/Tue May 10 20:33:00 2005// +/confdef.h/1.46/Tue Aug 22 19:45:08 2006// +/cont.h/1.25/Wed Sep 29 21:54:33 2004// +/database.h/1.19/Tue May 10 20:33:00 2005// +/dungeon.h/1.40/Tue May 10 20:33:00 2005// +/entity.h/1.38/Tue May 10 20:33:00 2005// +/fluid.h/1.29/Tue May 10 20:33:00 2005// +/game.h/1.186/Sun Sep 3 18:02:55 2006// +/gear.h/1.39/Tue Aug 22 19:45:08 2006// +/god.h/1.45/Tue May 10 20:33:01 2005// +/gods.h/1.14/Tue May 10 20:33:01 2005// +/human.h/1.81/Thu Aug 17 11:27:19 2006// +/iconf.h/1.9/Sun Jul 24 17:13:53 2005// +/id.h/1.29/Mon Aug 21 18:58:55 2006// +/igraph.h/1.82/Thu Aug 24 06:10:46 2006// +/iloops.h/1.6/Tue May 10 20:33:01 2005// +/item.h/1.110/Mon Aug 21 18:58:55 2006// +/ivandef.h/1.172/Thu Aug 24 06:10:46 2006// +/level.h/1.99/Tue May 10 20:33:02 2005// +/lock.h/1.2/Sun Jul 24 00:11:18 2005// +/lsquare.h/1.137/Tue May 10 20:33:02 2005// +/lterra.h/1.47/Wed Aug 16 10:16:00 2006// +/lterras.h/1.40/Mon Jul 25 10:50:02 2005// +/materia.h/1.47/Mon Aug 21 18:58:55 2006// +/materias.h/1.21/Mon Aug 21 18:58:55 2006// +/message.h/1.25/Tue May 10 20:33:03 2005// +/miscitem.h/1.78/Mon Aug 21 18:58:55 2006// +/nonhuman.h/1.62/Sun Jul 24 15:05:22 2005// +/object.h/1.98/Tue May 10 20:33:03 2005// +/pool.h/1.23/Wed Sep 29 21:54:34 2004// +/proto.h/1.68/Tue May 10 20:33:03 2005// +/rain.h/1.8/Wed Sep 29 21:54:34 2004// +/room.h/1.24/Tue May 10 20:33:03 2005// +/rooms.h/1.17/Tue May 10 20:33:03 2005// +/script.h/1.108/Sun Sep 3 18:02:55 2006// +/slot.h/1.33/Tue May 10 20:33:03 2005// +/smoke.h/1.14/Tue May 10 20:33:03 2005// +/square.h/1.74/Tue May 10 20:33:03 2005// +/stack.h/1.114/Mon Aug 21 18:58:55 2006// +/team.h/1.27/Wed Sep 29 21:54:35 2004// +/terra.h/1.35/Tue May 10 20:33:03 2005// +/trap.h/1.13/Fri Jul 22 18:56:46 2005// +/traps.h/1.15/Tue May 10 20:33:03 2005// +/worldmap.h/1.50/Tue May 10 20:33:03 2005// +/wskill.h/1.41/Tue May 10 20:33:03 2005// +/wsquare.h/1.41/Wed Sep 29 21:54:35 2004// +/wterra.h/1.16/Tue May 10 20:33:03 2005// +/wterras.h/1.16/Tue May 10 20:33:03 2005// +D diff --git a/Main/Include/CVS/Repository b/Main/Include/CVS/Repository new file mode 100644 index 0000000..b329329 --- /dev/null +++ b/Main/Include/CVS/Repository @@ -0,0 +1 @@ +ivan/Main/Include diff --git a/Main/Include/CVS/Root b/Main/Include/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/Main/Include/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/Main/Include/Makefile.am b/Main/Include/Makefile.am new file mode 100644 index 0000000..fefa60b --- /dev/null +++ b/Main/Include/Makefile.am @@ -0,0 +1 @@ +noinst_HEADERS = action.h actions.h area.h balance.h bodypart.h char.h command.h confdef.h cont.h database.h dungeon.h entity.h fluid.h game.h gear.h god.h gods.h human.h iconf.h id.h igraph.h item.h ivandef.h level.h lock.h iloops.h lsquare.h lterra.h lterras.h materia.h materias.h message.h miscitem.h nonhuman.h object.h pool.h proto.h rain.h room.h rooms.h script.h slot.h smoke.h square.h stack.h team.h terra.h trap.h traps.h worldmap.h wskill.h wsquare.h wterra.h wterras.h diff --git a/Main/Include/Makefile.in b/Main/Include/Makefile.in new file mode 100644 index 0000000..892b2cf --- /dev/null +++ b/Main/Include/Makefile.in @@ -0,0 +1,221 @@ +# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMDEPBACKSLASH = @AMDEPBACKSLASH@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CC = @CC@ +CCAS = @CCAS@ +CCASFLAGS = @CCASFLAGS@ +CXX = @CXX@ +CYGPATH_W = @CYGPATH_W@ +DEPDIR = @DEPDIR@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +SDL_CFLAGS = @SDL_CFLAGS@ +SDL_CONFIG = @SDL_CONFIG@ +SDL_LIBS = @SDL_LIBS@ +STRIP = @STRIP@ +VERSION = @VERSION@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +noinst_HEADERS = action.h actions.h area.h balance.h bodypart.h char.h command.h confdef.h cont.h database.h dungeon.h entity.h fluid.h game.h gear.h god.h gods.h human.h iconf.h id.h igraph.h item.h ivandef.h level.h iloops.h lsquare.h lterra.h lterras.h materia.h materias.h message.h miscitem.h nonhuman.h object.h pool.h proto.h rain.h room.h rooms.h script.h slot.h smoke.h square.h stack.h team.h terra.h trap.h traps.h worldmap.h wskill.h wsquare.h wterra.h wterras.h +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = +HEADERS = $(noinst_HEADERS) + +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu Main/Include/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES) + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags -o $$here/TAGS $(ETAGS_ARGS) $$tags $$unique $(LISP)) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = Main/Include + +distdir: $(DISTFILES) + here=`cd $(top_builddir) && pwd`; \ + top_distdir=`cd $(top_distdir) && pwd`; \ + distdir=`cd $(distdir) && pwd`; \ + cd $(top_srcdir) \ + && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu Main/Include/Makefile + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$d/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: +uninstall: uninstall-am +all-am: Makefile $(HEADERS) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-tags mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-tags clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-tags distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-tags maintainer-clean-generic \ + distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: tags mostlyclean-tags distclean-tags clean-tags \ +maintainer-clean-tags distdir info-am info dvi-am dvi check check-am \ +installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Main/Include/action.h b/Main/Include/action.h new file mode 100644 index 0000000..9ab4164 --- /dev/null +++ b/Main/Include/action.h @@ -0,0 +1,99 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __ACTION_H__ +#define __ACTION_H__ + +#include "typedef.h" +#include "ivandef.h" + +class character; +class action; +class outputfile; +class inputfile; + +typedef action* (*actionspawner)(character*); + +class actionprototype +{ + public: + actionprototype(actionspawner, cchar*); + action* SpawnAndLoad(inputfile&) const; + cchar* GetClassID() const { return ClassID; } + int GetIndex() const { return Index; } + private: + int Index; + actionspawner Spawner; + cchar* ClassID; +}; + +class action +{ + public: + typedef actionprototype prototype; + action() : Actor(0), Flags(0) { } + virtual ~action() { } + virtual void Handle() = 0; + virtual void Terminate(truth); + character* GetActor() const { return Actor; } + void SetActor(character* What) { Actor = What; } + virtual truth IsVoluntary() const { return true; } + virtual truth AllowUnconsciousness() const { return true; } + virtual truth AllowFoodConsumption() const { return true; } + virtual truth TryDisplace() { return true; } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth IsRest() const { return false; } + virtual const prototype* GetProtoType() const = 0; + int GetType() const { return GetProtoType()->GetIndex(); } + virtual cchar* GetDescription() const = 0; + truth InDNDMode() const { return Flags & IN_DND_MODE; } + void ActivateInDNDMode() { Flags |= IN_DND_MODE; } + virtual truth ShowEnvironment() const { return true; } + virtual cchar* GetDeathExplanation() const { return ""; } + virtual truth CanBeTalkedTo() const { return true; } + virtual truth IsUnconsciousness() const { return false; } + protected: + character* Actor; + ulong Flags; +}; + +template +class actionsysbase : public base +{ + public: + typedef actionsysbase mybase; + static type* Spawn(character* Actor) + { + type* T = new type; + T->Actor = Actor; + return T; + } + virtual const actionprototype* GetProtoType() const { return &ProtoType; } + static const actionprototype ProtoType; +}; + +#ifdef __FILE_OF_STATIC_ACTION_PROTOTYPE_DEFINITIONS__ +#define ACTION_PROTO(name)\ +template<> const actionprototype\ + name##sysbase::ProtoType((actionspawner)(&name##sysbase::Spawn), #name); +#else +#define ACTION_PROTO(name) +#endif + +#define ACTION(name, base)\ +class name;\ +typedef actionsysbase name##sysbase;\ +ACTION_PROTO(name)\ +class name : public name##sysbase + +#endif diff --git a/Main/Include/actions.h b/Main/Include/actions.h new file mode 100644 index 0000000..be25495 --- /dev/null +++ b/Main/Include/actions.h @@ -0,0 +1,127 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __ACTIONS_H__ +#define __ACTIONS_H__ + +#include "action.h" +#include "festring.h" +#include "v2.h" + +ACTION(unconsciousness, action) +{ + public: + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void Handle(); + void SetCounter(int What) { Counter = What; } + virtual truth IsVoluntary() const { return false; } + virtual void Terminate(truth); + virtual truth AllowUnconsciousness() const { return false; } + virtual cchar* GetDescription() const; + virtual cchar* GetDeathExplanation() const; + virtual truth CanBeTalkedTo() const { return false; } + virtual truth IsUnconsciousness() const { return true; } + void RaiseCounterTo(int); + protected: + int Counter; +}; + +ACTION(consume, action) +{ + public: + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void Handle(); + virtual void Terminate(truth); + void SetConsumingID(ulong What) { ConsumingID = What; } + virtual truth AllowUnconsciousness() const { return false; } + virtual truth AllowFoodConsumption() const { return false; } + virtual cchar* GetDescription() const; + virtual void SetDescription(cfestring&); + protected: + festring Description; + ulong ConsumingID; +}; + +ACTION(rest, action) +{ + public: + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void Handle(); + void SetGoalHP(int What) { GoalHP = What; } + virtual void Terminate(truth); + virtual truth IsRest() const { return true; } + virtual cchar* GetDescription() const; + void SetMinToStop(int What) { MinToStop = What; } + protected: + int GoalHP; + int MinToStop; +}; + +ACTION(dig, action) +{ + public: + dig() : RightBackupID(0), LeftBackupID(0) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void Handle(); + void SetSquareDug(v2 What) { SquareDug = What; } + virtual void Terminate(truth); + void SetRightBackupID(ulong What) { RightBackupID = What; } + void SetLeftBackupID(ulong What) { LeftBackupID = What; } + virtual truth TryDisplace() { return false; } + virtual cchar* GetDescription() const; + virtual truth ShowEnvironment() const { return false; } + void SetMoveDigger(truth What) { MoveDigger = What; } + protected: + ulong RightBackupID; + ulong LeftBackupID; + v2 SquareDug; + truth MoveDigger; +}; + +ACTION(go, action) +{ + public: + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void Handle(); + int GetDirection() const { return Direction; } + void SetDirection(int What) { Direction = What; } + truth IsWalkingInOpen() const { return WalkingInOpen; } + void SetIsWalkingInOpen(truth What) { WalkingInOpen = What; } + virtual truth TryDisplace(); + virtual cchar* GetDescription() const; + virtual truth ShowEnvironment() const { return false; } + protected: + int Direction; + truth WalkingInOpen; +}; + +ACTION(study, action) +{ + public: + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void Handle(); + virtual void Terminate(truth); + void SetLiteratureID(ulong What) { LiteratureID = What; } + virtual cchar* GetDescription() const; + void SetCounter(int What) { Counter = What; } + protected: + ulong LiteratureID; + int Counter; +}; + +#endif diff --git a/Main/Include/area.h b/Main/Include/area.h new file mode 100644 index 0000000..891c5c9 --- /dev/null +++ b/Main/Include/area.h @@ -0,0 +1,57 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __AREA_H__ +#define __AREA_H__ + +#include + +#include "rect.h" + +class character; +class square; +class bitmap; +class outputfile; +class inputfile; + +class area +{ + public: + area(); + area(int, int); + virtual ~area(); + virtual void Draw(truth) const = 0; + void Save(outputfile&) const; + void Load(inputfile&); + int GetFlag(v2 Pos) const { return FlagMap[Pos.X][Pos.Y]; } + void AddFlag(v2 Pos, int What) { FlagMap[Pos.X][Pos.Y] |= What; } + square* GetSquare(v2 Pos) const { return Map[Pos.X][Pos.Y]; } + square* GetSquare(int x, int y) const { return Map[x][y]; } + int GetXSize() const { return XSize; } + int GetYSize() const { return YSize; } + void SendNewDrawRequest(); + void Initialize(int, int); + square* GetNeighbourSquare(v2, int) const; + truth IsValidPos(v2 Pos) const { return Pos.X >= 0 && Pos.Y >= 0 && Pos.X < XSize && Pos.Y < YSize; } + truth IsValidPos(int X, int Y) const { return X >= 0 && Y >= 0 && X < XSize && Y < YSize; } + const rect& GetBorder() const { return Border; } + void SetEntryPos(int, v2); + protected: + square*** Map; + uchar** FlagMap; + int XSize, YSize; + ulong XSizeTimesYSize; + rect Border; + std::map EntryMap; +}; + +#endif diff --git a/Main/Include/balance.h b/Main/Include/balance.h new file mode 100644 index 0000000..5006680 --- /dev/null +++ b/Main/Include/balance.h @@ -0,0 +1,32 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __BALANCE_H__ +#define __BALANCE_H__ + +/* + * Global balance defines for the project IVAN. + * This file is created to hold macros that affect the game balance + */ + +#define GLOBAL_WEAK_BODYPART_HIT_MODIFIER 10. + +#define EDIT_ATTRIBUTE_DAY_MIN 10 // last day there is no monster attribute plus +#define EDIT_ATTRIBUTE_DAY_SHIFT 2 // attribute plus = floor((day - min) / 2^shift) + +#define DANGER_PLUS_DAY_MIN 5 // last day monster danger plus is zero +#define DANGER_PLUS_MULTIPLIER .001 // increases danger plus + +#define KAMIKAZE_INVISIBILITY_DAY_MIN 30 // last day no dwarf can be generated invisible +#define KAMIKAZE_INVISIBILITY_DAY_MAX 50 // after this, all dwarves are invisible + +#endif diff --git a/Main/Include/bodypart.h b/Main/Include/bodypart.h new file mode 100644 index 0000000..e642f97 --- /dev/null +++ b/Main/Include/bodypart.h @@ -0,0 +1,760 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __BODYPART_H__ +#define __BODYPART_H__ + +#include "item.h" +#include "save.h" + +class humanoid; +class sweaponskill; + +struct scar +{ + int Severity; + mutable bitmap* PanelBitmap; +}; + +outputfile& operator<<(outputfile&, const scar&); +inputfile& operator>>(inputfile&, scar&); + +struct damageid +{ + int SrcID, Amount; +}; + +RAW_SAVE_LOAD(damageid) + +ITEM(bodypart, item) +{ + public: + friend class corpse; + bodypart() : Master(0) { } + bodypart(const bodypart&); + virtual ~bodypart(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual int GetGraphicsContainerIndex() const; + character* GetMaster() const { return Master; } + humanoid* GetHumanoidMaster() const; + void SetMaster(character* What) { Master = What; } + virtual int GetStrengthValue() const; + int GetMaxHP() const { return MaxHP; } + void SetHP(int); + int GetHP() const { return HP; } + void EditHP(int, int); + void IncreaseHP(); + virtual int GetTotalResistance(int) const { return 0; } + virtual truth ReceiveDamage(character*, int, int, int); + cfestring& GetOwnerDescription() const { return OwnerDescription; } + void SetOwnerDescription(cfestring& What) { OwnerDescription = What; } + truth IsUnique() const { return Flags & UNIQUE; } + void SetIsUnique(truth); + virtual void DropEquipment(stack* = 0) { } + virtual void InitSpecialAttributes() { } + virtual void SignalEquipmentAdd(gearslot*); + virtual void SignalEquipmentRemoval(gearslot*, citem*); + virtual void Mutate(); + long GetBodyPartVolume() const { return BodyPartVolume; } + long GetCarriedWeight() const { return CarriedWeight; } + virtual item* GetEquipment(int) const { return 0; } + virtual int GetEquipments() const { return 0; } + virtual void CalculateVolumeAndWeight(); + virtual void CalculateEmitation(); + void CalculateMaxHP(ulong = MAY_CHANGE_HPS|CHECK_USABILITY); + virtual void SignalVolumeAndWeightChange(); + void FastRestoreHP(); + void RestoreHP(); + virtual void CalculateDamage() { } + virtual void CalculateToHitValue() { } + virtual void CalculateAPCost() { } + void CalculateAttackInfo(); + double GetTimeToDie(int, double, double, truth, truth) const; + virtual double GetRoughChanceToHit(double, double) const; + cfestring& GetBodyPartName() const { return GetNameSingular(); } + void RandomizePosition(); + void ResetPosition() { SpecialFlags &= ~0x7; } + virtual void SignalSpoil(material*); + virtual truth CanBePiledWith(citem*, ccharacter*) const; + truth IsAlive() const; + void SpillBlood(int); + void SpillBlood(int, v2); + virtual void Be(); + int GetConditionColorIndex() const; + void SetBitmapPos(v2 What) { BitmapPos = What; } + void SetSpecialFlags(int What) { SpecialFlags = What; } + void SetWobbleData(int What) { WobbleData = What; } + void SetMaterialColorB(col16 What) { ColorB = What; } + void SetMaterialColorC(col16 What) { ColorC = What; } + void SetMaterialColorD(col16 What) { ColorD = What; } + virtual void SignalEnchantmentChange(); + virtual void CalculateAttributeBonuses() { } + virtual void SignalSpoilLevelChange(material*); + virtual truth CanBeEatenByAI(ccharacter*) const; + virtual truth DamageArmor(character*, int, int) { return false; } + truth CanBeSevered(int) const; + virtual truth EditAllAttributes(int) { return false; } + virtual void Draw(blitdata&) const; + void SetSparkleFlags(int); + virtual int GetSpecialFlags() const; + virtual truth IsRepairable(ccharacter*) const; + truth IsWarm() const; + truth UseMaterialAttributes() const; + truth CanRegenerate() const; + truth CanHaveParasite() const; + virtual square* GetSquareUnder(int = 0) const; + virtual lsquare* GetLSquareUnder(int = 0) const; + virtual item* GetArmorToReceiveFluid(truth) const { return 0; } + virtual void SpillFluid(character*, liquid*, int = 0); + void StayOn(liquid*); + void SetBloodMaterial(int What) { BloodMaterial = What; } + int GetBloodMaterial() const { return BloodMaterial; } + liquid* CreateBlood(long) const; + virtual truth UpdateArmorPictures() { return false; } + virtual void DrawArmor(blitdata&) const { } + virtual void UpdatePictures(); + item* GetExternalBodyArmor() const; + item* GetExternalCloak() const; + item* GetExternalHelmet() const; + item* GetExternalBelt() const; + virtual void ReceiveAcid(material*, cfestring&, long); + virtual truth ShowFluids() const { return false; } + virtual void TryToRust(long); + virtual truth AllowFluidBe() const; + virtual material* RemoveMaterial(material*); + virtual void CopyAttributes(const bodypart*) { } + virtual void DestroyBodyPart(stack*); + virtual void SetLifeExpectancy(int, int); + virtual void SpecialEatEffect(character*, int); + virtual character* GetBodyPartMaster() const { return Master; } + virtual truth AllowFluids() const { return true; } + truth IsBadlyHurt() const { return Flags & BADLY_HURT; } + truth IsStuck() const { return Flags & STUCK; } + truth IsUsable() const { return !(Flags & (BADLY_HURT|STUCK)); } + virtual void SignalPossibleUsabilityChange() { UpdateFlags(); } + void SetIsInfectedByLeprosy(truth); + virtual int GetSparkleFlags() const; + virtual truth MaterialIsChangeable(ccharacter*) const; + virtual void RemoveRust(); + virtual item* Fix(); + virtual long GetFixPrice() const; + virtual truth IsFixableBySmith(ccharacter*) const; + virtual truth IsFixableByTailor(ccharacter*) const; + virtual void SignalMaterialChange(); + void SetNormalMaterial(int What) { NormalMaterial = What; } + virtual truth IsBroken() const { return HP < MaxHP; } + virtual truth IsDestroyable(ccharacter*) const; + void DrawScars(cblitdata&) const; + static truth DamageTypeCanScar(int); + void GenerateScar(int, int); + int CalculateScarAttributePenalty(int) const; + protected: + virtual alpha GetMaxAlpha() const; + virtual void GenerateMaterials() { } + virtual void AddPostFix(festring&, int) const; + virtual truth ShowMaterial() const; + virtual int GetArticleMode() const; + virtual col16 GetMaterialColorA(int) const; + virtual col16 GetMaterialColorB(int) const { return ColorB; } + virtual col16 GetMaterialColorC(int) const { return ColorC; } + virtual col16 GetMaterialColorD(int) const { return ColorD; } + virtual v2 GetBitmapPos(int) const { return BitmapPos; } + virtual int GetWobbleData() const { return WobbleData; } + void UpdateArmorPicture(graphicdata&, item*, int, v2 (item::*)(int) const, truth = false) const; + void DrawEquipment(const graphicdata&, blitdata&) const; + void UpdateFlags(); + truth MasterIsAnimated() const; + void SignalAnimationStateChange(truth); + virtual truth AddAdjective(festring&, truth) const; + void RemoveDamageIDs(int); + void AddDamageID(int, int); + festring OwnerDescription; + character* Master; + long CarriedWeight; + long BodyPartVolume; + packv2 BitmapPos; + packcol16 ColorB; + packcol16 ColorC; + packcol16 ColorD; + ushort SpecialFlags; + short HP; + short MaxHP; + short BloodMaterial; + short NormalMaterial; + uchar SpillBloodCounter; + uchar WobbleData; + std::vector Scar; + std::deque DamageID; +}; + +ITEM(head, bodypart) +{ + public: + head(); + head(const head&); + virtual ~head(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual int GetTotalResistance(int) const; + void SetHelmet(item* What) { HelmetSlot.PutInItem(What); } + item* GetHelmet() const { return *HelmetSlot; } + void SetAmulet(item* What) { AmuletSlot.PutInItem(What); } + item* GetAmulet() const { return *AmuletSlot; } + virtual void DropEquipment(stack* = 0); + virtual int GetBodyPartIndex() const; + double GetBiteDamage() const { return BiteDamage; } + int GetBiteMinDamage() const; + int GetBiteMaxDamage() const; + double GetBiteToHitValue() const { return BiteToHitValue; } + long GetBiteAPCost() const { return BiteAPCost; } + virtual void InitSpecialAttributes(); + virtual item* GetEquipment(int) const; + virtual int GetEquipments() const { return 2; } + int GetBaseBiteStrength() const { return BaseBiteStrength; } + void SetBaseBiteStrength(long What) { BaseBiteStrength = What; } + virtual void CalculateDamage(); + virtual void CalculateToHitValue(); + virtual void CalculateAPCost(); + virtual truth DamageArmor(character*, int, int); + virtual head* Behead(); + virtual item* GetArmorToReceiveFluid(truth) const; + virtual void SignalPossibleUsabilityChange(); + protected: + void UpdateHeadArmorPictures(graphicdata&) const; + gearslot HelmetSlot; + gearslot AmuletSlot; + int BaseBiteStrength; + double BiteToHitValue; + double BiteDamage; + long BiteAPCost; +}; + +ITEM(torso, bodypart) +{ + public: + virtual int GetBodyPartIndex() const; + virtual double GetRoughChanceToHit(double, double) const; +}; + +ITEM(normaltorso, torso) +{ + public: + virtual int GetGraphicsContainerIndex() const; + virtual int GetTotalResistance(int) const; +}; + +ITEM(humanoidtorso, torso) +{ + public: + humanoidtorso(); + humanoidtorso(const humanoidtorso&); + virtual ~humanoidtorso(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual int GetTotalResistance(int) const; + void SetBodyArmor(item* What) { BodyArmorSlot.PutInItem(What); } + item* GetBodyArmor() const { return *BodyArmorSlot; } + void SetCloak(item* What) { CloakSlot.PutInItem(What); } + item* GetCloak() const { return *CloakSlot; } + void SetBelt(item* What) { BeltSlot.PutInItem(What); } + item* GetBelt() const { return *BeltSlot; } + virtual void DropEquipment(stack* = 0); + virtual item* GetEquipment(int) const; + virtual int GetEquipments() const { return 3; } + virtual void SignalEquipmentAdd(gearslot*); + virtual void SignalVolumeAndWeightChange(); + virtual truth DamageArmor(character*, int, int); + virtual item* GetArmorToReceiveFluid(truth) const; + protected: + void UpdateTorsoArmorPictures(graphicdata&, graphicdata&, graphicdata&) const; + gearslot BodyArmorSlot; + gearslot CloakSlot; + gearslot BeltSlot; +}; + +ITEM(arm, bodypart) +{ + public: + arm() : StrengthBonus(0), DexterityBonus(0) { } + arm(const arm&); + virtual ~arm(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual int GetTotalResistance(int) const; + double GetWieldedDamage() const; + double GetWieldedToHitValue() const; + void SetWielded(item* What) { WieldedSlot.PutInItem(What); } + item* GetWielded() const { return *WieldedSlot; } + void SetGauntlet(item* What) { GauntletSlot.PutInItem(What); } + item* GetGauntlet() const { return *GauntletSlot; } + void SetRing(item* What) { RingSlot.PutInItem(What); } + item* GetRing() const { return *RingSlot; } + virtual void DropEquipment(stack* = 0); + double GetUnarmedToHitValue() const; + double GetUnarmedDamage() const; + void Hit(character*, v2, int, int = 0); + int GetAttribute(int, truth = true) const; + truth EditAttribute(int, int); + void EditExperience(int, double, double); + void SetStrength(int What) { StrengthExperience = What * EXP_MULTIPLIER; } + void SetDexterity(int What) { DexterityExperience = What * EXP_MULTIPLIER; } + virtual void InitSpecialAttributes(); + virtual void Mutate(); + virtual arm* GetPairArm() const { return 0; } + long GetWieldedAPCost() const; + long GetUnarmedAPCost() const; + virtual item* GetEquipment(int) const; + virtual int GetEquipments() const { return 3; } + int GetBaseUnarmedStrength() const { return BaseUnarmedStrength; } + void SetBaseUnarmedStrength(long What) { BaseUnarmedStrength = What; } + virtual void CalculateDamage(); + virtual void CalculateToHitValue(); + virtual void CalculateAPCost(); + double GetDamage() const { return Damage; } + int GetMinDamage() const; + int GetMaxDamage() const; + double GetToHitValue() const { return ToHitValue; } + long GetAPCost() const { return APCost; } + truth PairArmAllowsMelee() const; + virtual void SignalVolumeAndWeightChange(); + truth TwoHandWieldIsActive() const; + double GetBlockChance(double) const; + int GetBlockCapability() const; + void WieldedSkillHit(int); + double GetBlockValue() const; + void ApplyEquipmentAttributeBonuses(item*); + virtual void CalculateAttributeBonuses(); + int GetWieldedHitStrength() const; + virtual void SignalEquipmentAdd(gearslot*); + virtual void SignalEquipmentRemoval(gearslot*, citem*); + void ApplyDexterityPenalty(item*); + void ApplyStrengthBonus(item*); + void ApplyDexterityBonus(item*); + virtual truth DamageArmor(character*, int, int); + truth CheckIfWeaponTooHeavy(cchar*) const; + virtual truth EditAllAttributes(int); + void AddAttackInfo(felist&) const; + void AddDefenceInfo(felist&) const; + void UpdateWieldedPicture(); + void DrawWielded(blitdata&) const; + virtual truth IsRightArm() const { return 0; } + virtual void UpdatePictures(); + virtual double GetTypeDamage(ccharacter*) const; + virtual item* GetArmorToReceiveFluid(truth) const; + virtual void CopyAttributes(const bodypart*); + double GetStrengthExperience() const { return StrengthExperience; } + double GetDexterityExperience() const { return DexterityExperience; } + virtual void SignalPossibleUsabilityChange(); + virtual truth IsAnimated() const; + truth HasSadistWeapon() const; + protected: + virtual sweaponskill** GetCurrentSWeaponSkill() const { return 0; } + void UpdateArmArmorPictures(graphicdata&, graphicdata&, int) const; + int GetCurrentSWeaponSkillBonus() const; + gearslot WieldedSlot; + gearslot GauntletSlot; + gearslot RingSlot; + double StrengthExperience; + double DexterityExperience; + int BaseUnarmedStrength; + double Damage; + double ToHitValue; + long APCost; + int StrengthBonus; + int DexterityBonus; + graphicdata WieldedGraphicData; +}; + +ITEM(rightarm, arm) +{ + public: + rightarm(); + rightarm(const rightarm&); + virtual int GetBodyPartIndex() const; + virtual arm* GetPairArm() const; + virtual truth IsRightArm() const { return true; } + virtual int GetSpecialFlags() const; + protected: + virtual sweaponskill** GetCurrentSWeaponSkill() const; +}; + +ITEM(leftarm, arm) +{ + public: + leftarm(); + leftarm(const leftarm&); + virtual int GetBodyPartIndex() const; + virtual arm* GetPairArm() const; + virtual truth IsRightArm() const { return false; } + virtual int GetSpecialFlags() const; + protected: + virtual sweaponskill** GetCurrentSWeaponSkill() const; +}; + +ITEM(groin, bodypart) +{ + public: + virtual int GetTotalResistance(int) const; + virtual int GetBodyPartIndex() const; + virtual truth DamageArmor(character*, int, int); + virtual int GetSpecialFlags() const; + virtual item* GetArmorToReceiveFluid(truth) const; + void UpdateGroinArmorPictures(graphicdata&) const; +}; + +ITEM(leg, bodypart) +{ + public: + leg() : StrengthBonus(0), AgilityBonus(0) { } + leg(const leg&); + virtual ~leg(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual int GetTotalResistance(int) const; + void SetBoot(item* What) { BootSlot.PutInItem(What); } + item* GetBoot() const { return *BootSlot; } + virtual void DropEquipment(stack* = 0); + double GetKickToHitValue() const { return KickToHitValue; } + double GetKickDamage() const { return KickDamage; } + int GetKickMinDamage() const; + int GetKickMaxDamage() const; + int GetAttribute(int, truth = true) const; + truth EditAttribute(int, int); + void EditExperience(int, double, double); + virtual void InitSpecialAttributes(); + virtual void Mutate(); + long GetKickAPCost() const { return KickAPCost; } + virtual item* GetEquipment(int) const; + virtual int GetEquipments() const { return 1; } + long GetBaseKickStrength() const { return BaseKickStrength; } + void SetBaseKickStrength(long What) { BaseKickStrength = What; } + virtual void CalculateDamage(); + virtual void CalculateToHitValue(); + virtual void CalculateAPCost(); + void ApplyEquipmentAttributeBonuses(item*); + virtual void CalculateAttributeBonuses(); + virtual void SignalEquipmentAdd(gearslot*); + void ApplyAgilityPenalty(item*); + void ApplyStrengthBonus(item*); + void ApplyAgilityBonus(item*); + virtual void SignalVolumeAndWeightChange(); + virtual truth DamageArmor(character*, int, int); + virtual truth EditAllAttributes(int); + void AddAttackInfo(felist&) const; + virtual item* GetArmorToReceiveFluid(truth) const; + virtual void CopyAttributes(const bodypart*); + double GetStrengthExperience() const { return StrengthExperience; } + double GetAgilityExperience() const { return AgilityExperience; } + virtual void SignalPossibleUsabilityChange(); + protected: + void UpdateLegArmorPictures(graphicdata&, graphicdata&, int) const; + gearslot BootSlot; + double StrengthExperience; + double AgilityExperience; + int BaseKickStrength; + double KickDamage; + double KickToHitValue; + long KickAPCost; + int StrengthBonus; + int AgilityBonus; +}; + +ITEM(rightleg, leg) +{ + public: + rightleg(); + rightleg(const rightleg&); + virtual int GetBodyPartIndex() const; + virtual int GetSpecialFlags() const; + protected: +}; + +ITEM(leftleg, leg) +{ + public: + leftleg(); + leftleg(const leftleg&); + virtual int GetBodyPartIndex() const; + virtual int GetSpecialFlags() const; + protected: +}; + +ITEM(corpse, item) +{ + public: + corpse() { } + corpse(const corpse&); + virtual ~corpse(); + virtual int GetOfferValue(int) const; + virtual double GetWeaponStrength() const; + virtual truth CanBeEatenByAI(ccharacter*) const; + virtual int GetStrengthValue() const; + character* GetDeceased() const { return Deceased; } + void SetDeceased(character*); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth IsDestroyable(ccharacter*) const; + virtual long GetTruePrice() const; + virtual int GetMaterials() const { return 2; } + virtual truth RaiseTheDead(character*); + virtual void CalculateVolumeAndWeight(); + virtual void CalculateEmitation(); + virtual void SignalSpoil(material*); + virtual truth CanBePiledWith(citem*, ccharacter*) const; + virtual int GetSpoilLevel() const; + virtual material* GetMaterial(int) const; + virtual head* Behead(); + virtual truth CanBeCloned() const; + virtual int GetAttachedGod() const; + virtual void PreProcessForBone(); + virtual void PostProcessForBone(); + virtual void FinalProcessForBone(); + virtual truth SuckSoul(character*, character* = 0); + virtual character* TryNecromancy(character*); + virtual void Cannibalize(); + virtual material* GetConsumeMaterial(ccharacter*, materialpredicate) const; + virtual truth DetectMaterial(cmaterial*) const; + virtual void SetLifeExpectancy(int, int); + virtual void Be(); + virtual void SignalDisappearance(); + virtual truth IsValuable() const; + virtual truth AddRustLevelDescription(festring&, truth) const { return false; } + virtual truth Necromancy(character*); + virtual int GetSparkleFlags() const; + virtual truth IsRusted() const { return false; } + virtual truth CanBeHardened(ccharacter*) const { return false; } + protected: + virtual void GenerateMaterials() { } + virtual col16 GetMaterialColorA(int) const; + virtual col16 GetMaterialColorB(int) const; + virtual alpha GetAlphaA(int) const; + virtual alpha GetAlphaB(int) const; + virtual truth ShowMaterial() const { return false; } + virtual void AddPostFix(festring&, int) const; + virtual v2 GetBitmapPos(int) const; + virtual int GetSize() const; + virtual int GetArticleMode() const; + virtual int GetRustDataA() const; + virtual truth AddStateDescription(festring&, truth) const; + character* Deceased; +}; + +ITEM(eddytorso, normaltorso) +{ + protected: + virtual int GetClassAnimationFrames() const { return 8; } + virtual v2 GetBitmapPos(int) const; +}; + +ITEM(largetorso, normaltorso) +{ + public: + virtual void SignalStackAdd(stackslot*, void (stack::*)(item*, truth)); + virtual int GetSquareIndex(v2) const; + virtual void Draw(blitdata&) const; + virtual void CalculateSquaresUnder() { SquaresUnder = 4; } + protected: + virtual v2 GetBitmapPos(int I) const { return GetLargeBitmapPos(BitmapPos, I); } + virtual void ModifyAnimationFrames(int& AF) const { AF <<= 2; } +}; + +ITEM(largecorpse, corpse) +{ + public: + virtual void SignalStackAdd(stackslot*, void (stack::*)(item*, truth)); + virtual int GetSquareIndex(v2) const; + virtual void Draw(blitdata&) const; + virtual void CalculateSquaresUnder() { SquaresUnder = 4; } + protected: + virtual v2 GetBitmapPos(int I) const { return GetLargeBitmapPos(item::GetBitmapPos(I), I); } + virtual void ModifyAnimationFrames(int& AF) const { AF <<= 2; } +}; + +ITEM(ennerhead, head) +{ + protected: + virtual int GetClassAnimationFrames() const { return 32; } + virtual v2 GetBitmapPos(int) const; +}; + +ITEM(playerkindhead, head) +{ + public: + playerkindhead() { } + playerkindhead(const playerkindhead& Head) : mybase(Head) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth UpdateArmorPictures(); + virtual void DrawArmor(blitdata&) const; + virtual truth ShowFluids() const { return true; } + virtual truth IsAnimated() const { return true; } + protected: + graphicdata HelmetGraphicData; +}; + +ITEM(playerkindtorso, humanoidtorso) +{ + public: + playerkindtorso() { } + playerkindtorso(const playerkindtorso& Torso) : mybase(Torso) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth UpdateArmorPictures(); + virtual void DrawArmor(blitdata&) const; + virtual void SignalVolumeAndWeightChange(); + virtual truth ShowFluids() const { return true; } + virtual truth IsAnimated() const { return true; } + protected: + graphicdata TorsoArmorGraphicData; + graphicdata CloakGraphicData; + graphicdata BeltGraphicData; +}; + +ITEM(playerkindrightarm, rightarm) +{ + public: + playerkindrightarm() { } + playerkindrightarm(const playerkindrightarm& Arm) : mybase(Arm) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth UpdateArmorPictures(); + virtual void DrawArmor(blitdata&) const; + virtual truth ShowFluids() const { return true; } + virtual truth IsAnimated() const { return true; } + protected: + graphicdata ArmArmorGraphicData; + graphicdata GauntletGraphicData; +}; + +ITEM(playerkindleftarm, leftarm) +{ + public: + playerkindleftarm() { } + playerkindleftarm(const playerkindleftarm& Arm) : mybase(Arm) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth UpdateArmorPictures(); + virtual void DrawArmor(blitdata&) const; + virtual truth ShowFluids() const { return true; } + virtual truth IsAnimated() const { return true; } + protected: + graphicdata ArmArmorGraphicData; + graphicdata GauntletGraphicData; +}; + +ITEM(playerkindgroin, groin) +{ + public: + playerkindgroin() { } + playerkindgroin(const playerkindgroin& Groin) : mybase(Groin) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth UpdateArmorPictures(); + virtual void DrawArmor(blitdata&) const; + virtual truth ShowFluids() const { return true; } + virtual truth IsAnimated() const { return true; } + protected: + graphicdata GroinArmorGraphicData; +}; + +ITEM(playerkindrightleg, rightleg) +{ + public: + playerkindrightleg() { } + playerkindrightleg(const playerkindrightleg& Leg) : mybase(Leg) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth UpdateArmorPictures(); + virtual void DrawArmor(blitdata&) const; + virtual truth ShowFluids() const { return true; } + virtual truth IsAnimated() const { return true; } + protected: + graphicdata LegArmorGraphicData; + graphicdata BootGraphicData; +}; + +ITEM(playerkindleftleg, leftleg) +{ + public: + playerkindleftleg() { } + playerkindleftleg(const playerkindleftleg& Leg) : mybase(Leg) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth UpdateArmorPictures(); + virtual void DrawArmor(blitdata&) const; + virtual truth ShowFluids() const { return true; } + virtual truth IsAnimated() const { return true; } + protected: + graphicdata LegArmorGraphicData; + graphicdata BootGraphicData; +}; + +ITEM(magicmushroomtorso, normaltorso) +{ + protected: + virtual int GetClassAnimationFrames() const { return 64; } + virtual v2 GetBitmapPos(int) const; +}; + +ITEM(menatrixtorso, largetorso) +{ +}; + +ITEM(dogtorso, normaltorso) +{ + protected: + virtual void Draw(blitdata&) const; + virtual int GetClassAnimationFrames() const { return 16; } + virtual v2 GetBitmapPos(int) const; +}; + +ITEM(blinkdogtorso, dogtorso) +{ + protected: + virtual alpha GetAlphaA(int) const; + virtual int GetClassAnimationFrames() const { return 64; } +}; + +ITEM(mysticfrogtorso, normaltorso) +{ + public: + virtual truth AllowAlphaEverywhere() const { return true; } + protected: + virtual int GetClassAnimationFrames() const { return 128; } + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; +}; + +ITEM(lobhsetorso, largetorso) +{ + protected: + virtual int GetClassAnimationFrames() const { return 32; } + virtual col16 GetMaterialColorD(int) const; +}; + +ITEM(battorso, normaltorso) +{ + protected: + virtual int GetClassAnimationFrames() const { return 16; } + virtual v2 GetBitmapPos(int) const; +}; + +ITEM(spidertorso, normaltorso) +{ + protected: + virtual int GetClassAnimationFrames() const { return 16; } + virtual v2 GetBitmapPos(int) const; +}; + +#endif + diff --git a/Main/Include/char.h b/Main/Include/char.h new file mode 100644 index 0000000..81fb7e8 --- /dev/null +++ b/Main/Include/char.h @@ -0,0 +1,1252 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __CHAR_H__ +#define __CHAR_H__ + +#include "bodypart.h" +#include "script.h" +#include "wskill.h" +#include "save.h" + +#define CHAR_PERSONAL_PRONOUN GetPersonalPronoun(true).CStr() +#define CHAR_POSSESSIVE_PRONOUN GetPossessivePronoun(true).CStr() +#define CHAR_OBJECT_PRONOUN GetObjectPronoun(true).CStr() +#define CHAR_PERSONAL_PRONOUN_THIRD_PERSON_VIEW GetPersonalPronoun(false).CStr() +#define CHAR_POSSESSIVE_PRONOUN_THIRD_PERSON_VIEW GetPossessivePronoun(false).CStr() +#define CHAR_OBJECT_PRONOUN_THIRD_PERSON_VIEW GetObjectPronoun(false).CStr() + +class go; +class team; +class wsquare; +class cweaponskill; +class action; +class characterprototype; +class web; +class mindworm; + +struct homedata; +struct trapdata; +struct blitdata; + +typedef std::vector > blockvector; +typedef truth (item::*sorter)(ccharacter*) const; +typedef truth (character::*petmanagementfunction)(); +typedef character* (*characterspawner)(int, int); +typedef character* (*charactercloner)(ccharacter*); + +inline int APBonus(int Attribute) +{ + return Attribute >= 10 ? 90 + Attribute : 50 + Attribute * 5; +} + +struct expid +{ + bool operator<(expid) const; + int ActID, SrcID; +}; + +inline bool expid::operator<(expid E) const +{ + return ActID != E.ActID ? ActID < E.ActID : SrcID < E.SrcID; +} + +RAW_SAVE_LOAD(expid) + +typedef std::map expmodifiermap; + +struct characterdatabase : public databasebase +{ + typedef characterprototype prototype; + void InitDefaults(const prototype*, int); + truth AllowRandomInstantiation() const { return CanBeGenerated && !IsUnique; } + void PostProcess(); + const prototype* ProtoType; + double NaturalExperience[ATTRIBUTES]; + ulong Flags; + truth IsAbstract; + truth CanRead; + truth CanBeGenerated; + truth CanOpen; + truth IsUnique; + truth IsNameable; + truth IsPolymorphable; + truth CanUseEquipment; + truth CanKick; + truth CanTalk; + truth CanBeWished; + truth CreateDivineConfigurations; + truth CreateGolemMaterialConfigurations; + truth CanBeCloned; + truth CanZap; + truth HasALeg; + truth IgnoreDanger; + truth IsExtraCoward; + truth SpillsBlood; + truth HasEyes; + truth HasHead; + truth CanThrow; + truth UsesNutrition; + truth BodyPartsDisappearWhenSevered; + truth CanBeConfused; + truth CanApply; + truth BiteCapturesBodyPart; + truth IsPlant; + truth DestroysWalls; + truth IsRooted; + truth HasSecondaryMaterial; + truth IsImmuneToLeprosy; + truth AutomaticallySeen; + truth CanHear; + truth WillCarryItems; + truth Sweats; + truth IsImmuneToItemTeleport; + truth AlwaysUseMaterialAttributes; + truth IsEnormous; + truth IsExtraFragile; + truth AllowUnconsciousness; + truth CanChoke; + truth IsImmuneToStickiness; + truth ForceCustomStandVerb; + truth VomittingIsUnhealthy; + int DefaultEndurance; + int DefaultPerception; + int DefaultIntelligence; + int DefaultWisdom; + int DefaultWillPower; + int DefaultCharisma; + int DefaultMana; + int DefaultArmStrength; + int DefaultLegStrength; + int DefaultDexterity; + int DefaultAgility; + long DefaultMoney; + int TotalSize; + int Sex; + int CriticalModifier; + festring StandVerb; + int Frequency; + int EnergyResistance; + int FireResistance; + int PoisonResistance; + int ElectricityResistance; + int AcidResistance; + int ConsumeFlags; + long TotalVolume; + packv2 HeadBitmapPos; + packv2 TorsoBitmapPos; + packv2 ArmBitmapPos; + packv2 LegBitmapPos; + packv2 RightArmBitmapPos; + packv2 LeftArmBitmapPos; + packv2 RightLegBitmapPos; + packv2 LeftLegBitmapPos; + packv2 GroinBitmapPos; + packcol16 ClothColor; + packcol16 SkinColor; + packcol16 CapColor; + packcol16 HairColor; + packcol16 EyeColor; + packcol16 TorsoMainColor; + packcol16 BeltColor; + packcol16 BootColor; + packcol16 TorsoSpecialColor; + packcol16 ArmMainColor; + packcol16 GauntletColor; + packcol16 ArmSpecialColor; + packcol16 LegMainColor; + packcol16 LegSpecialColor; + col24 BaseEmitation; + truth UsesLongArticle; + festring Adjective; + truth UsesLongAdjectiveArticle; + festring NameSingular; + festring NamePlural; + festring PostFix; + int ArticleMode; + int BaseUnarmedStrength; + int BaseBiteStrength; + int BaseKickStrength; + int AttackStyle; + long ClassStates; + long WhatThrowItemTypesToThrow; + fearray Alias; + contentscript Helmet; + contentscript Amulet; + contentscript Cloak; + contentscript BodyArmor; + contentscript Belt; + contentscript RightWielded; + contentscript LeftWielded; + contentscript RightRing; + contentscript LeftRing; + contentscript RightGauntlet; + contentscript LeftGauntlet; + contentscript RightBoot; + contentscript LeftBoot; + int AttributeBonus; + fearray KnownCWeaponSkills; + fearray CWeaponSkillHits; + int RightSWeaponSkillHits; + int LeftSWeaponSkillHits; + int PanicLevel; + fearray > Inventory; + int DangerModifier; + festring DefaultName; + fearray FriendlyReplies; + fearray HostileReplies; + int FleshMaterial; + festring DeathMessage; + int HPRequirementForGeneration; + int DayRequirementForGeneration; + int AttackWisdomLimit; + int AttachedGod; + packv2 WieldedPosition; + int NaturalSparkleFlags; + int MoveType; + int BloodMaterial; + int VomitMaterial; + int PolymorphIntelligenceRequirement; + ulong DefaultCommandFlags; + ulong ConstantCommandFlags; + festring ForceVomitMessage; + int DefaultSweatMaterial; + fearray ScienceTalkAdjectiveAttribute; + fearray ScienceTalkSubstantiveAttribute; + fearray ScienceTalkPrefix; + fearray ScienceTalkName; + int ScienceTalkPossibility; + int ScienceTalkIntelligenceModifier; + int ScienceTalkWisdomModifier; + int ScienceTalkCharismaModifier; + int ScienceTalkIntelligenceRequirement; + int ScienceTalkWisdomRequirement; + int ScienceTalkCharismaRequirement; + int DisplacePriority; + festring RunDescriptionLineOne; + festring RunDescriptionLineTwo; + truth AllowPlayerToChangeEquipment; + int TamingDifficulty; + truth IsMasochist; + truth IsSadist; + truth IsCatacombCreature; + truth CreateUndeadConfigurations; + truth UndeadVersions; + int UndeadAttributeModifier; + int UndeadVolumeModifier; + truth UndeadCopyMaterials; + truth CanBeGeneratedOnlyInTheCatacombs; + truth IsAlcoholic; + truth IsImmuneToWhipOfThievery; + truth IsRangedAttacker; + int WhatCategoryToThrow; + int WhatWeaponConfigToThrow; +}; + +class characterprototype +{ + public: + friend class databasecreator; + friend class protosystem; + characterprototype(const characterprototype*, characterspawner, charactercloner, cchar*); + character* Spawn(int Config = 0, int SpecialFlags = 0) const { return Spawner(Config, SpecialFlags); } + character* SpawnAndLoad(inputfile&) const; + character* Clone(ccharacter* Char) const { return Cloner(Char); } + int GetIndex() const { return Index; } + const characterprototype* GetBase() const { return Base; } + cchar* GetClassID() const { return ClassID; } + int CreateSpecialConfigurations(characterdatabase**, int, int); + const characterdatabase* ChooseBaseForConfig(characterdatabase** TempConfig, int, int) { return *TempConfig; } + const characterdatabase*const* GetConfigData() const { return ConfigData; } + int GetConfigSize() const { return ConfigSize; } + private: + int Index; + const characterprototype* Base; + characterdatabase** ConfigData; + characterdatabase** ConfigTable[CONFIG_TABLE_SIZE]; + int ConfigSize; + characterspawner Spawner; + charactercloner Cloner; + cchar* ClassID; +}; + +class character : public entity, public id +{ + public: + friend class databasecreator; + friend class corpse; + typedef characterprototype prototype; + typedef characterdatabase database; + character(); + character(ccharacter&); + virtual ~character(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth CanWield() const { return false; } + virtual truth Catches(item*) { return false; } + truth CheckDeath(cfestring&, ccharacter* = 0, ulong = 0); + truth DodgesFlyingItem(item*, double); + virtual truth Hit(character*, v2, int, int = 0) = 0; + truth ReadItem(item*); + truth TestForPickup(item*) const; + void ThrowItem(int, item*); + truth TryMove(v2, truth, truth); + truth HasHeadOfElpuri() const; + truth HasGoldenEagleShirt() const; + truth HasPetrussNut() const; + truth HasCurdledBlood() const; + truth HasOmmelBlood() const; + truth CurdleOmmelBlood() const; + truth RemoveCurdledOmmelBlood(); + truth RemoveEncryptedScroll(); + truth RemoveMondedrPass(); + truth RemoveRingOfThieves(); + truth IsPlayer() const { return Flags & C_PLAYER; } + truth Engrave(cfestring&); + void AddScoreEntry(cfestring&, double = 1., truth = true) const; + long GetAP() const { return AP; } + long GetNP() const { return NP; } + stack* GetStack() const { return Stack; } + int GetBurdenState() const { return BurdenState; } + truth MakesBurdened(long What) const { return long(GetCarryingStrength()) * 2500 < What; } + virtual int TakeHit(character*, item*, bodypart*, v2, double, double, int, int, int, truth, truth); + int GetLOSRange() const; + int GetLOSRangeSquare() const { return GetLOSRange() * GetLOSRange(); } + int GetESPRange() const { return GetAttribute(INTELLIGENCE) / 3; } + int GetESPRangeSquare() const { return GetESPRange() * GetESPRange(); } + int GetTeleportRange() const { return GetAttribute(INTELLIGENCE); } + int GetTeleportRangeSquare() const { return GetTeleportRange() * GetTeleportRange(); } + void AddMissMessage(ccharacter*) const; + void AddPrimitiveHitMessage(ccharacter*, cfestring&, cfestring&, int) const; + void AddWeaponHitMessage(ccharacter*, citem*, int, truth = false) const; + virtual void BeTalkedTo(); + void ReceiveDarkness(long); + void Die(ccharacter* = 0, cfestring& = CONST_S(""), ulong = 0); + void HasBeenHitByItem(character*, item*, int, double, int); + void Hunger(); + void Move(v2, truth, truth = false); + virtual truth MoveRandomly(); + void ReceiveNutrition(long); + void ReceiveOmmelUrine(long); + void ReceivePepsi(long); + void ReceiveSchoolFood(long); + void Regenerate(); + void SetAP(long What) { AP = What; } + void SetNP(long); + void Vomit(v2, int, truth = true); + virtual void Be(); + truth Polymorph(character*, int); + void BeKicked(character*, item*, bodypart*, v2, double, double, int, int, truth, truth); + void FallTo(character*, v2); + truth CheckCannibalism(cmaterial*) const; + void ActivateTemporaryState(long What) { TemporaryState |= What; } + void DeActivateTemporaryState(long What) { TemporaryState &= ~What; } + void ActivateEquipmentState(long What) { EquipmentState |= What; } + void DeActivateEquipmentState(long What) { EquipmentState &= ~What; } + truth TemporaryStateIsActivated(long What) const { return TemporaryState & What; } + truth EquipmentStateIsActivated(long What) const { return EquipmentState & What; } + truth StateIsActivated(long What) const { return TemporaryState & What || EquipmentState & What; } + truth LoseConsciousness(int, truth = false); + void SetTemporaryStateCounter(long, int); + void DeActivateVoluntaryAction(cfestring& = CONST_S("")); + void ActionAutoTermination(); + team* GetTeam() const { return Team; } + void SetTeam(team*); + void ChangeTeam(team*); + virtual int GetMoveEase() const; + double GetDodgeValue() const { return DodgeValue; } + long GetMoney() const { return Money; } + void SetMoney(long What) { Money = What; } + void EditMoney(long What) { Money += What; } + truth ChangeSweatMaterial(int) const; + int GetCurrentSweatMaterial() const { return CurrentSweatMaterial; } + void SetInitialSweatMaterial(int What) { CurrentSweatMaterial = What; } + void EditCurrentSweatMaterial(int What) { CurrentSweatMaterial = What; } + truth Displace(character*, truth = false); + truth CheckStarvationDeath(cfestring&); + void ShowNewPosInfo() const; + void Hostility(character*); + stack* GetGiftStack() const; + truth MoveRandomlyInRoom(); + std::list::iterator GetTeamIterator(); + void SetTeamIterator(std::list::iterator); + void ReceiveKoboldFlesh(long); + truth ChangeRandomAttribute(int); + int RandomizeReply(long&, int); + virtual void CreateInitialEquipment(int); + void DisplayInfo(festring&); + virtual truth SpecialEnemySightedReaction(character*) { return false; } + void TestWalkability(); + void EditAP(long); + void EditNP(long); + void SetSize(int); + virtual int GetSize() const; + torso* GetTorso() const { return static_cast(*BodyPartSlot[TORSO_INDEX]); } + humanoidtorso* GetHumanoidTorso() const { return static_cast(*BodyPartSlot[TORSO_INDEX]); } + void SetTorso(torso* What) { SetBodyPart(TORSO_INDEX, What); } + bodypart* GetBodyPart(int I) const { return static_cast(*BodyPartSlot[I]); } + void SetBodyPart(int, bodypart*); + void SetMainMaterial(material*, int = 0); + void ChangeMainMaterial(material*, int = 0); + void SetSecondaryMaterial(material*, int = 0); + void ChangeSecondaryMaterial(material*, int = 0); + void RestoreHP(); + void RestoreLivingHP(); + void RestoreStamina() { Stamina = MaxStamina; } + virtual truth ReceiveDamage(character*, int, int, int = ALL, int = 8, truth = false, truth = false, truth = false, truth = true); + virtual int ReceiveBodyPartDamage(character*, int, int, int, int = 8, truth = false, truth = false, truth = true, truth = false); + virtual truth BodyPartIsVital(int) const { return true; } + void RestoreBodyParts(); + cfestring& GetAssignedName() const { return AssignedName; } + void SetAssignedName(cfestring& What) { AssignedName = What; } + festring GetDescription(int) const; + festring GetPersonalPronoun(truth = true) const; + festring GetPossessivePronoun(truth = true) const; + festring GetObjectPronoun(truth = true) const; + virtual truth BodyPartCanBeSevered(int) const; + virtual void AddName(festring&, int) const; + void ReceiveHeal(long); + virtual item* GetMainWielded() const { return 0; } + virtual item* GetSecondaryWielded() const { return 0; } + int GetHungerState() const; + truth ConsumeItem(item*, cfestring&); + virtual truth CanConsume(material*) const; + action* GetAction() const { return Action; } + void SetAction(action* What) { Action = What; } + virtual void SwitchToDig(item*, v2) { } + virtual void SetRightWielded(item*) { } + virtual void SetLeftWielded(item*) { } + void GoOn(go*, truth = false); + virtual truth CheckKick() const; + virtual int OpenMultiplier() const { return 2; } + virtual int CloseMultiplier() const { return 2; } + virtual truth CheckThrow() const; + virtual truth CheckOffer() const { return true; } + int GetTemporaryStateCounter(long) const; + void EditTemporaryStateCounter(long, int); + static truth AllowDamageTypeBloodSpill(int); + int GetResistance(int) const; + virtual int GetGlobalResistance(int Type) const { return GetResistance(Type); } + virtual cchar* GetEquipmentName(int) const; + virtual bodypart* GetBodyPartOfEquipment(int) const { return 0; } + virtual item* GetEquipment(int) const { return 0; } + virtual int GetEquipments() const { return 0; } + virtual sorter EquipmentSorter(int) const { return 0; } + virtual void SetEquipment(int, item*) { } + void AddHealingLiquidConsumeEndMessage() const; + void AddSchoolFoodConsumeEndMessage() const; + void AddSchoolFoodHitMessage() const; + void AddOmmelConsumeEndMessage() const; + void AddPepsiConsumeEndMessage() const; + void AddFrogFleshConsumeEndMessage() const; + void AddKoboldFleshConsumeEndMessage() const; + void AddKoboldFleshHitMessage() const; + void AddBoneConsumeEndMessage() const; + void AddBlackUnicornConsumeEndMessage() const; + void AddGrayUnicornConsumeEndMessage() const; + void AddWhiteUnicornConsumeEndMessage() const; + void AddOmmelBoneConsumeEndMessage() const; + void AddLiquidHorrorConsumeEndMessage() const; + void PrintInfo() const; + virtual item* SevereBodyPart(int, truth = false, stack* = 0); + virtual truth TryToRiseFromTheDead(); + virtual truth RaiseTheDead(character*); + bodypart* CreateBodyPart(int, int = 0); + virtual truth EquipmentIsAllowed(int) const { return true; } + truth CanUseEquipment(int) const; + const database* GetDataBase() const { return DataBase; } + void SetParameters(int) { } + virtual double GetNaturalExperience(int) const; + DATA_BASE_VALUE(const prototype*, ProtoType); + DATA_BASE_VALUE(int, Config); + DATA_BASE_VALUE(int, DefaultEndurance); + DATA_BASE_VALUE(int, DefaultPerception); + DATA_BASE_VALUE(int, DefaultIntelligence); + DATA_BASE_VALUE(int, DefaultWisdom); + DATA_BASE_VALUE(int, DefaultWillPower); + DATA_BASE_VALUE(int, DefaultCharisma); + DATA_BASE_VALUE(int, DefaultMana); + DATA_BASE_VALUE(int, DefaultArmStrength); + DATA_BASE_VALUE(int, DefaultLegStrength); + DATA_BASE_VALUE(int, DefaultDexterity); + DATA_BASE_VALUE(int, DefaultAgility); + DATA_BASE_VALUE(long, DefaultMoney); + DATA_BASE_VALUE(int, TotalSize); + DATA_BASE_TRUTH(CanRead); + DATA_BASE_VALUE(int, Sex); + DATA_BASE_TRUTH(CanBeGenerated); + DATA_BASE_VALUE(int, CriticalModifier); + DATA_BASE_TRUTH(CanOpen); + DATA_BASE_VALUE(int, EnergyResistance); + DATA_BASE_VALUE(int, FireResistance); + DATA_BASE_VALUE(int, PoisonResistance); + DATA_BASE_VALUE(int, ElectricityResistance); + DATA_BASE_VALUE(int, AcidResistance); + DATA_BASE_VALUE(int, ConsumeFlags); + DATA_BASE_VALUE(long, TotalVolume); + virtual DATA_BASE_VALUE(v2, HeadBitmapPos); + virtual DATA_BASE_VALUE(v2, TorsoBitmapPos); + virtual DATA_BASE_VALUE(v2, ArmBitmapPos); + virtual DATA_BASE_VALUE(v2, LegBitmapPos); + virtual DATA_BASE_VALUE(v2, RightArmBitmapPos); + virtual DATA_BASE_VALUE(v2, LeftArmBitmapPos); + virtual DATA_BASE_VALUE(v2, RightLegBitmapPos); + virtual DATA_BASE_VALUE(v2, LeftLegBitmapPos); + virtual DATA_BASE_VALUE(v2, GroinBitmapPos); + virtual DATA_BASE_VALUE(col16, ClothColor); + virtual DATA_BASE_VALUE(col16, SkinColor); + virtual DATA_BASE_VALUE(col16, CapColor); + virtual DATA_BASE_VALUE(col16, HairColor); + virtual DATA_BASE_VALUE(col16, EyeColor); + virtual DATA_BASE_VALUE(col16, TorsoMainColor); + virtual DATA_BASE_VALUE(col16, BeltColor); + virtual DATA_BASE_VALUE(col16, BootColor); + virtual DATA_BASE_VALUE(col16, TorsoSpecialColor); + virtual DATA_BASE_VALUE(col16, ArmMainColor); + virtual DATA_BASE_VALUE(col16, GauntletColor); + virtual DATA_BASE_VALUE(col16, ArmSpecialColor); + virtual DATA_BASE_VALUE(col16, LegMainColor); + virtual DATA_BASE_VALUE(col16, LegSpecialColor); + virtual DATA_BASE_TRUTH(IsNameable); + virtual DATA_BASE_VALUE(col24, BaseEmitation); // devirtualize ASAP + DATA_BASE_TRUTH(UsesLongArticle); + DATA_BASE_VALUE(cfestring&, Adjective); + DATA_BASE_TRUTH(UsesLongAdjectiveArticle); + DATA_BASE_VALUE(cfestring&, NameSingular); + DATA_BASE_VALUE(cfestring&, NamePlural); + DATA_BASE_VALUE(cfestring&, PostFix); + DATA_BASE_VALUE(int, ArticleMode); + DATA_BASE_TRUTH(CanZap); + virtual DATA_BASE_TRUTH(IsPolymorphable); + DATA_BASE_VALUE(int, BaseUnarmedStrength); + DATA_BASE_VALUE(int, BaseBiteStrength); + DATA_BASE_VALUE(int, BaseKickStrength); + DATA_BASE_VALUE(int, AttackStyle); + DATA_BASE_TRUTH(CanUseEquipment); + DATA_BASE_TRUTH(CanKick); + DATA_BASE_TRUTH(CanTalk); + DATA_BASE_TRUTH(CanBeWished); + DATA_BASE_VALUE(long, ClassStates); + DATA_BASE_VALUE(long, WhatThrowItemTypesToThrow) + DATA_BASE_VALUE(const fearray&, Alias); + DATA_BASE_TRUTH(CreateGolemMaterialConfigurations); + DATA_BASE_VALUE(const fearray&, KnownCWeaponSkills); + DATA_BASE_VALUE(const fearray&, CWeaponSkillHits); + DATA_BASE_VALUE(int, RightSWeaponSkillHits); + DATA_BASE_VALUE(int, LeftSWeaponSkillHits); + DATA_BASE_VALUE(int, PanicLevel); + DATA_BASE_TRUTH(CanBeCloned); + DATA_BASE_VALUE(cfestring&, DefaultName); + DATA_BASE_VALUE(const fearray&, FriendlyReplies); + DATA_BASE_VALUE(const fearray&, HostileReplies); + DATA_BASE_VALUE(int, FleshMaterial); + virtual DATA_BASE_TRUTH(HasALeg); + virtual DATA_BASE_VALUE(cfestring&, DeathMessage); + DATA_BASE_VALUE(int, HPRequirementForGeneration); + DATA_BASE_TRUTH(IsExtraCoward); + DATA_BASE_TRUTH(SpillsBlood); + DATA_BASE_TRUTH(HasEyes); + virtual DATA_BASE_TRUTH(HasHead); + DATA_BASE_TRUTH(CanThrow); + DATA_BASE_TRUTH(UsesNutrition); + DATA_BASE_VALUE(int, AttackWisdomLimit); + DATA_BASE_TRUTH(IsUnique); + DATA_BASE_VALUE(int, AttachedGod); + DATA_BASE_TRUTH(BodyPartsDisappearWhenSevered); + DATA_BASE_VALUE(int, Frequency); + DATA_BASE_TRUTH(CanBeConfused); + DATA_BASE_TRUTH(CanApply); + DATA_BASE_VALUE(v2, WieldedPosition); + virtual DATA_BASE_VALUE(int, NaturalSparkleFlags); + DATA_BASE_TRUTH(IgnoreDanger); + DATA_BASE_TRUTH(BiteCapturesBodyPart); + DATA_BASE_TRUTH(IsPlant); + DATA_BASE_TRUTH(DestroysWalls); + DATA_BASE_TRUTH(IsRooted); + DATA_BASE_VALUE(int, BloodMaterial); + DATA_BASE_VALUE(int, VomitMaterial); + DATA_BASE_TRUTH(AutomaticallySeen); + DATA_BASE_VALUE(ulong, DefaultCommandFlags); + DATA_BASE_TRUTH(WillCarryItems); + DATA_BASE_VALUE(int, DefaultSweatMaterial); + DATA_BASE_TRUTH(Sweats); + DATA_BASE_TRUTH(IsImmuneToItemTeleport); + DATA_BASE_TRUTH(AlwaysUseMaterialAttributes); + DATA_BASE_TRUTH(IsEnormous); + DATA_BASE_VALUE(const fearray&, ScienceTalkAdjectiveAttribute); + DATA_BASE_VALUE(const fearray&, ScienceTalkSubstantiveAttribute); + DATA_BASE_VALUE(const fearray&, ScienceTalkPrefix); + DATA_BASE_VALUE(const fearray&, ScienceTalkName); + DATA_BASE_VALUE(int, ScienceTalkPossibility); + DATA_BASE_VALUE(int, ScienceTalkIntelligenceModifier); + DATA_BASE_VALUE(int, ScienceTalkWisdomModifier); + DATA_BASE_VALUE(int, ScienceTalkCharismaModifier); + DATA_BASE_VALUE(int, ScienceTalkIntelligenceRequirement); + DATA_BASE_VALUE(int, ScienceTalkWisdomRequirement); + DATA_BASE_VALUE(int, ScienceTalkCharismaRequirement); + DATA_BASE_TRUTH(IsExtraFragile); + DATA_BASE_TRUTH(IsImmuneToStickiness); + DATA_BASE_VALUE(festring, ForceVomitMessage); + DATA_BASE_TRUTH(CanChoke); + DATA_BASE_VALUE(int, DisplacePriority); + DATA_BASE_VALUE(cfestring&, RunDescriptionLineOne); + DATA_BASE_VALUE(cfestring&, RunDescriptionLineTwo); + DATA_BASE_TRUTH(ForceCustomStandVerb); + DATA_BASE_TRUTH(VomittingIsUnhealthy); + DATA_BASE_TRUTH(AllowPlayerToChangeEquipment); + DATA_BASE_VALUE(int, TamingDifficulty); + DATA_BASE_TRUTH(IsMasochist); + DATA_BASE_TRUTH(IsSadist); + DATA_BASE_TRUTH(IsRangedAttacker); + DATA_BASE_TRUTH(IsCatacombCreature); + DATA_BASE_TRUTH(CreateUndeadConfigurations); + DATA_BASE_TRUTH(UndeadVersions); + DATA_BASE_VALUE(int, UndeadAttributeModifier); + DATA_BASE_VALUE(int, UndeadVolumeModifier); + DATA_BASE_TRUTH(UndeadCopyMaterials); + DATA_BASE_TRUTH(CanBeGeneratedOnlyInTheCatacombs); + DATA_BASE_TRUTH(IsAlcoholic); + DATA_BASE_TRUTH(IsImmuneToWhipOfThievery); + int GetType() const { return GetProtoType()->GetIndex(); } + void TeleportRandomly(truth = false); + void DoDetecting(); + truth TeleportNear(character*); + virtual void InitSpecialAttributes() { } + virtual void Kick(lsquare*, int, truth = false) = 0; + virtual int GetAttribute(int, truth = true) const; + virtual truth EditAttribute(int, int); + virtual void EditExperience(int, double, double); + truth RawEditAttribute(double&, int) const; + void DrawPanel(truth) const; + virtual int DrawStats(truth) const = 0; + virtual int GetCarryingStrength() const = 0; + static truth DamageTypeAffectsInventory(int); + virtual int GetRandomStepperBodyPart() const; + entity* GetMotherEntity() const { return MotherEntity; } + void SetMotherEntity(entity* What) { MotherEntity = What; } + virtual int CheckForBlock(character*, item*, double, int, int, int); + int CheckForBlockWithArm(character*, item*, arm*, double, int, int, int); + void AddBlockMessage(ccharacter*, citem*, cfestring&, truth) const; + character* GetPolymorphBackup() const { return PolymorphBackup; } + void SetPolymorphBackup(character* What) { PolymorphBackup = What; } + cweaponskill* GetCWeaponSkill(int I) const { return &CWeaponSkill[I]; } + virtual truth AddSpecialSkillInfo(felist&) const { return false; } + virtual truth CheckBalance(double); + long GetStateAPGain(long) const; + virtual long GetMoveAPRequirement(int) const; + virtual void SignalEquipmentAdd(int); + virtual void SignalEquipmentRemoval(int, citem*); + void BeginTemporaryState(long, int); + void GainIntrinsic(long); + void HandleStates(); + void PrintBeginPolymorphControlMessage() const; + void PrintEndPolymorphControlMessage() const; + void PrintBeginLifeSaveMessage() const; + void PrintEndLifeSaveMessage() const; + void PrintBeginLycanthropyMessage() const; + void PrintEndLycanthropyMessage() const; + void PrintBeginHasteMessage() const; + void PrintEndHasteMessage() const; + void PrintBeginSlowMessage() const; + void PrintEndSlowMessage() const; + void PrintBeginSearchingMessage() const; + void PrintEndSearchingMessage() const; + void PrintBeginHiccupsMessage() const; + void PrintEndHiccupsMessage() const; + void PrintBeginVampirismMessage() const; + void PrintEndVampirismMessage() const; + void EndPolymorph(); + character* ForceEndPolymorph(); + void LycanthropyHandler(); + void SearchingHandler(); + void SaveLife(); + void BeginInvisibility(); + void BeginInfraVision(); + void BeginESP(); + void EndInvisibility(); + void EndInfraVision(); + void EndESP(); + void HiccupsHandler(); + void VampirismHandler(); + character* PolymorphRandomly(int, int, int); + virtual truth EquipmentEasilyRecognized(int) const { return true; } + void StartReading(item*, long); + void DexterityAction(int); + void IntelligenceAction(int); + virtual void SWeaponSkillTick() { } + void PrintBeginInvisibilityMessage() const; + void PrintEndInvisibilityMessage() const; + void PrintBeginInfraVisionMessage() const; + void PrintEndInfraVisionMessage() const; + void PrintBeginESPMessage() const; + void PrintEndESPMessage() const; + truth CanBeSeenByPlayer(truth = false, truth = false) const; + truth CanBeSeenBy(ccharacter*, truth = false, truth = false) const; + void AttachBodyPart(bodypart*); + truth HasAllBodyParts() const; + bodypart* FindRandomOwnBodyPart(truth) const; + bodypart* GenerateRandomBodyPart(); + void PrintBeginPoisonedMessage() const; + void PrintEndPoisonedMessage() const; + truth IsWarm() const; + void CalculateEquipmentState(); + void Draw(blitdata&) const; + virtual void DrawBodyParts(blitdata&) const; + god* GetMasterGod() const; + void PoisonedHandler(); + void PrintBeginTeleportMessage() const; + void PrintEndTeleportMessage() const; + void TeleportHandler(); + void PrintBeginDetectMessage() const; + void PrintEndDetectMessage() const; + void DetectHandler(); + void PrintEndTeleportControlMessage() const; + void PrintBeginTeleportControlMessage() const; + void PolymorphHandler(); + void PrintEndPolymorphMessage() const; + void PrintBeginPolymorphMessage() const; + virtual void DisplayStethoscopeInfo(character*) const; + virtual truth CanUseStethoscope(truth) const; + virtual truth IsUsingArms() const; + virtual truth IsUsingLegs() const; + virtual truth IsUsingHead() const; + dungeon* GetDungeon() const { return static_cast(GetSquareUnder()->GetArea())->GetDungeon(); } + level* GetLevel() const { return static_cast(GetSquareUnder()->GetArea()); } + area* GetArea() const { return GetSquareUnder()->GetArea(); } + virtual square* GetNeighbourSquare(int) const; + virtual lsquare* GetNeighbourLSquare(int) const; + virtual wsquare* GetNeighbourWSquare(int) const; + stack* GetStackUnder(int I = 0) const { return static_cast(GetSquareUnder(I))->GetStack(); } + square* GetNearSquare(v2 Pos) const { return GetSquareUnder()->GetArea()->GetSquare(Pos); } + square* GetNearSquare(int x, int y) const { return GetSquareUnder()->GetArea()->GetSquare(x, y); } + lsquare* GetNearLSquare(v2 Pos) const { return static_cast(GetSquareUnder()->GetArea()->GetSquare(Pos)); } + lsquare* GetNearLSquare(int x, int y) const { return static_cast(GetSquareUnder()->GetArea()->GetSquare(x, y)); } + wsquare* GetNearWSquare(v2) const; + wsquare* GetNearWSquare(int, int) const; + v2 GetPos(int I = 0) const { return GetSquareUnder(I)->GetPos(); } + square* GetSquareUnder(int I = 0) const { return !MotherEntity ? SquareUnder[I] : MotherEntity->GetSquareUnderEntity(I); } + virtual square* GetSquareUnderEntity(int I = 0) const { return GetSquareUnder(I); } + lsquare* GetLSquareUnder(int I = 0) const { return static_cast(GetSquareUnder(I)); } + int GetRandomNonVitalBodyPart() const; + void TeleportSomePartsAway(int); + virtual void SignalVolumeAndWeightChange(); + virtual void SignalBodyPartVolumeAndWeightChange() { } + void CalculateVolumeAndWeight(); + long GetVolume() const { return Volume; } + long GetBodyVolume() const { return BodyVolume; } + long GetWeight() const { return Weight; } + long GetCarriedWeight() const { return CarriedWeight; } + virtual void SignalEmitationIncrease(col24); + virtual void SignalEmitationDecrease(col24); + void CalculateEmitation(); + void CalculateAll(); + void CalculateHP(); + void CalculateMaxHP(); + int GetHP() const { return HP; } + int GetMaxHP() const { return MaxHP; } + void CalculateBodyPartMaxHPs(ulong = MAY_CHANGE_HPS|CHECK_USABILITY); + truth IsInitializing() const { return Flags & C_INITIALIZING; } + truth IsInNoMsgMode() const { return Flags & C_IN_NO_MSG_MODE; } + truth ActivateRandomState(int, int, long = 0); + long GetRandomState(int) const; + truth GainRandomIntrinsic(int); + virtual void CalculateBattleInfo() = 0; + void CalculateBurdenState(); + virtual void CalculateDodgeValue(); + virtual void CalculateBodyParts() { BodyParts = 1; } + virtual void CalculateAllowedWeaponSkillCategories(); + int GetBodyParts() const { return BodyParts; } + int GetAllowedWeaponSkillCategories() const { return AllowedWeaponSkillCategories; } + double GetRelativeDanger(ccharacter*, truth = false) const; + double GetTimeToDie(ccharacter*, int, double, truth, truth) const; + virtual double GetTimeToKill(ccharacter*, truth) const = 0; + virtual void AddSpecialEquipmentInfo(festring&, int) const { } + virtual festring GetBodyPartName(int, truth = false) const; + item* SearchForItem(ulong) const; + truth SearchForItem(citem*) const; + item* SearchForItem(const sweaponskill*) const; + truth ContentsCanBeSeenBy(ccharacter*) const; + festring GetBeVerb() const; + virtual void CreateBlockPossibilityVector(blockvector&, double) const { } + virtual truth SpecialUnarmedEffect(character*, v2, int, int, truth) { return false; } + virtual truth SpecialKickEffect(character*, v2, int, int, truth) { return false; } + virtual truth SpecialBiteEffect(character*, v2, int, int, truth) { return false; } + truth HitEffect(character*, item*, v2, int, int, int, truth); + void WeaponSkillHit(item*, int, int); + character* Duplicate(ulong = 0); + room* GetRoom(int I = 0) const { return GetLSquareUnder(I)->GetRoom(); } + truth TryToEquip(item*); + truth TryToConsume(item*); + truth TryToAddToInventory(item*); + void UpdateESPLOS() const; + int GetCWeaponSkillLevel(citem*) const; + virtual int GetSWeaponSkillLevel(citem*) const { return 0; } + void PrintBeginPanicMessage() const; + void PrintEndPanicMessage() const; + void CheckPanic(int); + character* DuplicateToNearestSquare(character*, ulong = 0); + void SignalSpoil(); + void SignalSpoilLevelChange(); + virtual truth UseMaterialAttributes() const = 0; + truth IsPolymorphed() const { return Flags & C_POLYMORPHED; } + truth IsInBadCondition() const { return HP * 3 < MaxHP; } + truth IsInBadCondition(int HP) const { return HP * 3 < MaxHP; } + int GetCondition() const; + void UpdatePictures(); + truth CanHeal() const; + void SetGoingTo(v2); + int GetRelation(ccharacter*) const; + truth CalculateAttributeBonuses(); + void ApplyEquipmentAttributeBonuses(item*); + void ReceiveAntidote(long); + void AddAntidoteConsumeEndMessage() const; + truth IsDead() const; + void AddOriginalBodyPartID(int, ulong); + void AddToInventory(const fearray >&, int); + truth HasHadBodyPart(citem*) const; + void ProcessAndAddMessage(festring) const; + virtual truth CheckZap(); + void SetEndurance(int); + void SetPerception(int); + void SetIntelligence(int); + void SetWisdom(int); + void SetWillPower(int); + void SetCharisma(int); + void SetMana(int); + void DamageAllItems(character*, int, int); + truth Equips(citem*) const; + void PrintBeginConfuseMessage() const; + void PrintEndConfuseMessage() const; + v2 ApplyStateModification(v2) const; + void AddConfuseHitMessage() const; + item* SelectFromPossessions(cfestring&, sorter = 0); + truth SelectFromPossessions(itemvector&, cfestring&, int, sorter = 0); + truth EquipsSomething(sorter = 0); + truth CheckTalk(); + virtual truth CanCreateBodyPart(int) const { return true; } + virtual truth HandleCharacterBlockingTheWay(character*, v2, int) { return false; } + virtual festring& ProcessMessage(festring&) const; + virtual truth IsHumanoid() const { return false; } + virtual truth IsHuman() const { return false; } + truth IsOnGround() const; + virtual truth CheckIfEquipmentIsNotUsable(int) const { return false; } + virtual truth MoveTowardsHomePos(); + virtual void SetWayPoints(const fearray&) { } + truth TryToChangeEquipment(stack*, stack*, int); + void PrintBeginParasitizedMessage() const; + void PrintEndParasitizedMessage() const; + void ParasitizedHandler(); + truth CanFollow() const; + truth LeftOversAreUnique() const; + virtual festring GetKillName() const; + festring GetPanelName() const; + virtual void AddSpecialStethoscopeInfo(felist&) const = 0; + virtual item* GetPairEquipment(int) const { return 0; } + bodypart* HealHitPoint(); + void CreateHomeData(); + room* GetHomeRoom() const; + truth HomeDataIsValid() const; + void SetHomePos(v2); + void RemoveHomeData(); + ulong GetID() const { return ID; } + void AddESPConsumeMessage() const; + const std::list& GetOriginalBodyPartID(int) const; + void GetHitByExplosion(const explosion*, int); + truth CanBePoisoned() const { return TorsoIsAlive(); } + truth CanBeParasitized() const { return TorsoIsAlive(); } + void SortAllItems(const sortdata&); + character* GetRandomNeighbourEnemy() const; + void Search(int); + character* GetRandomNeighbour(int = (HOSTILE|UNCARING|FRIEND)) const; + virtual truth IsRetreating() const; + virtual truth IsMushroom() const { return false; } + void ResetStates(); + virtual head* Behead() { return 0; } + void PrintBeginGasImmunityMessage() const; + void PrintEndGasImmunityMessage() const; + void ShowAdventureInfo() const; + virtual truth BoundToUse(citem*, int) const { return false; } + virtual truth IsBananaGrower() const { return false; } + virtual int GetRandomApplyBodyPart() const; + DATA_BASE_VALUE(int, WhatCategoryToThrow); + DATA_BASE_VALUE(int, WhatWeaponConfigToThrow); +#ifdef WIZARD + virtual void AddAttributeInfo(festring&) const; + virtual void AddAttackInfo(felist&) const = 0; + virtual void AddDefenceInfo(felist&) const; + virtual void DetachBodyPart(); +#endif + void ReceiveHolyBanana(long); + void AddHolyBananaConsumeEndMessage() const; + void ReceiveHolyMango(long); + void AddHolyMangoConsumeEndMessage() const; + virtual truth PreProcessForBone(); + truth PostProcessForBone(double&, int&); + truth PostProcessForBone(); + virtual void FinalProcessForBone(); + virtual truth EditAllAttributes(int); + virtual void SetSoulID(ulong); + virtual truth SuckSoul(character*) { return false; } + virtual truth MustBeRemovedFromBone() const; + truth TorsoIsAlive() const { return GetTorso()->IsAlive(); } + truth PictureUpdatesAreForbidden() const { return Flags & C_PICTURE_UPDATES_FORBIDDEN; } + virtual int GetUsableArms() const { return 0; } + truth IsPet() const; + virtual void PutTo(v2); + void PutTo(lsquare*); + void PutNear(v2); + void PutToOrNear(v2); + virtual void Remove(); + truth IsSmall() const { return SquaresUnder == 1; } + truth IsOver(v2) const; + truth IsOver(citem*) const; + truth SquareUnderCanBeSeenByPlayer(truth) const; + truth SquareUnderCanBeSeenBy(ccharacter*, truth) const; + int GetDistanceSquareFrom(ccharacter*) const; + virtual truth CanTheoreticallyMoveOn(const lsquare*) const; + virtual truth CanMoveOn(const lsquare*) const; + virtual truth CanMoveOn(const square*) const; + truth CanMoveOn(const olterrain*) const; + truth CanMoveOn(const oterrain*) const; + truth IsMainPos(v2 What) const { return GetPos() == What; } + virtual void CalculateSquaresUnder() { SquaresUnder = 1; } + int GetSquaresUnder() const { return SquaresUnder; } + virtual int GetSquareIndex(v2) const { return 0; } + virtual int GetNeighbourSquares() const { return 8; } + virtual int GetExtendedNeighbourSquares() const { return 9; } + virtual int CalculateNewSquaresUnder(lsquare**, v2) const; + virtual truth IsFreeForMe(square*) const; + void SendNewDrawRequest() const; + square* GetNaturalNeighbourSquare(int I) const { return character::GetNeighbourSquare(I); } + lsquare* GetNaturalNeighbourLSquare(int I) const { return character::GetNeighbourLSquare(I); } + void SignalStepFrom(lsquare**); + virtual void SpecialBodyDefenceEffect(character*, bodypart*, int) { } + virtual int GetSumOfAttributes() const; + truth IsGoingSomeWhere() const { return GoingTo != ERROR_V2; } + virtual truth CreateRoute(); + void TerminateGoingTo(); + virtual truth IsSpy() const { return false; } + truth CheckForFood(int); + truth CheckForFoodInSquare(v2); + virtual truth CheckIfSatiated() { return GetNP() > SATIATED_LEVEL; } + virtual void SignalNaturalGeneration() { } + virtual truth IsBunny() const { return false; } + void SetConfig(int, int = 0); + bodypartslot* GetBodyPartSlot(int I) { return &BodyPartSlot[I]; } + virtual truth CheckConsume(cfestring&) const; + virtual int GetTameSymbolSquareIndex() const { return 0; } + virtual int GetFlySymbolSquareIndex() const { return 0; } + virtual int GetSwimmingSymbolSquareIndex() const { return 0; } + virtual int GetUnconsciousSymbolSquareIndex() const { return 0; } + virtual truth PlaceIsIllegal(v2 Pos, v2 Illegal) const { return Pos == Illegal; } + liquid* CreateBlood(long) const; + void SpillFluid(character*, liquid*, int = 0); + virtual void StayOn(liquid*); + virtual head* GetVirtualHead() const { return 0; } + truth IsAlly(ccharacter*) const; + virtual truth CanVomit() const { return TorsoIsAlive(); } + ulong GetLastAcidMsgMin() const { return LastAcidMsgMin; } + void SetLastAcidMsgMin(ulong What) { LastAcidMsgMin = What; } + virtual truth AllowSpoil() const { return false; } + void Disappear(corpse*, cchar*, truth (item::*)() const); + void ResetSpoiling(); + virtual character* GetLeader() const; + int GetMoveType() const; + virtual truth IsSumoWrestler() const { return false; } + void InitMaterials(const materialscript*, const materialscript*, truth) { } + item* SearchForItem(ccharacter*, sorter) const; + virtual character* CreateZombie() const { return 0; } + virtual festring GetZombieDescription() const; + virtual truth CanAttack() const { return true; } + truth DetectMaterial(cmaterial*) const; + truth CheckIfTooScaredToHit(ccharacter*) const; + void PrintBeginLevitationMessage() const; + void PrintEndLevitationMessage() const; + void EditDealExperience(long); + int RawEditExperience(double&, double, double, double) const; + virtual void LeprosyHandler(); + virtual void TryToInfectWithLeprosy(ccharacter*); + void PrintBeginLeprosyMessage() const; + void PrintEndLeprosyMessage() const; + void SignalGeneration(); + void CheckIfSeen(); + void SignalSeen(); + truth HasBeenSeen() const; + int GetPolymorphIntelligenceRequirement() const; + void RemoveAllItems(); + int CalculateWeaponSkillHits(ccharacter*) const; + void DonateEquipmentTo(character*); + void ReceivePeaSoup(long); + void AddPeaSoupConsumeEndMessage() const; + void CalculateMaxStamina(); + void EditStamina(int, truth); + void RegenerateStamina(); + void BeginPanic(); + void EndPanic(); + int GetTirednessState() const; + int GetStamina() const { return Stamina; } + int GetMaxStamina() const { return MaxStamina; } + void SetGenerationDanger(double What) { GenerationDanger = What; } + double GetGenerationDanger() const { return GenerationDanger; } + void ReceiveBlackUnicorn(long); + void ReceiveGrayUnicorn(long); + void ReceiveWhiteUnicorn(long); + void DecreaseStateCounter(long, int); + truth IsImmuneToLeprosy() const; + bodypart* SearchForOriginalBodyPart(int) const; + void SetLifeExpectancy(int, int); + virtual void DuplicateEquipment(character*, ulong); + virtual void SignalDisappearance(); + virtual truth HornOfFearWorks() const; + virtual truth CanHear() const; + void BeginLeprosy(); + void EndLeprosy(); + void ReceiveOmmelCerumen(long); + void ReceiveOmmelSweat(long); + void ReceiveOmmelTears(long); + void ReceiveOmmelSnot(long); + void ReceiveOmmelBone(long); + truth IsSameAs(ccharacter*) const; + ulong GetCommandFlags() const; + void SetCommandFlags(ulong What) { CommandFlags = What; } + ulong GetPossibleCommandFlags() const; + ulong GetConstantCommandFlags() const; + virtual truth AllowEquipment(citem*, int) const { return true; } + truth ChatMenu(); + truth ChangePetEquipment(); + truth TakePetItems(); + truth GivePetItems(); + truth IssuePetCommands(); + truth ChatIdly(); + truth EquipmentScreen(stack*, stack*); + ulong GetManagementFlags() const; + cchar* GetVerbalBurdenState() const; + col16 GetVerbalBurdenStateColor() const; + virtual int GetAttributeAverage() const; + virtual cfestring& GetStandVerb() const; + virtual truth CheckApply() const; + virtual truth CanForceVomit() const { return CanVomit(); } + void EndLevitation(); + virtual truth CanMove() const; + void CalculateEnchantments(); + truth GetNewFormForPolymorphWithControl(character*&); + liquid* CreateSweat(long) const; + truth IsTemporary() const; + truth TeleportRandomItem(truth); + truth HasClearRouteTo(v2) const; + virtual truth IsTransparent() const; + void SignalPossibleTransparencyChange(); + int GetCursorData() const; + void TryToName(); + double GetSituationDanger(ccharacter*, v2, v2, truth) const; + virtual void ModifySituationDanger(double&) const; + void LycanthropySituationDangerModifier(double&) const; + void PoisonedSituationDangerModifier(double&) const; + void PolymorphingSituationDangerModifier(double&) const; + void PanicSituationDangerModifier(double&) const; + void ConfusedSituationDangerModifier(double&) const; + void ParasitizedSituationDangerModifier(double&) const; + void LeprosySituationDangerModifier(double&) const; + void HiccupsSituationDangerModifier(double&) const; + void VampirismSituationDangerModifier(double&) const; + truth TryToTalkAboutScience(); + truth IsUsingWeaponOfCategory(int) const; + virtual truth IsKamikazeDwarf() const { return false; } + void AddRandomScienceName(festring&) const; + truth IsStuck() const { return truth(TrapData); } + festring GetTrapDescription() const; + truth TryToUnStickTraps(v2); + void RemoveTrap(ulong); + void AddTrap(ulong, ulong); + truth IsStuckToTrap(ulong) const; + void RemoveTraps(); + void RemoveTraps(int); + int RandomizeHurtBodyPart(ulong) const; + virtual int RandomizeTryToUnStickBodyPart(ulong) const { return NONE_INDEX; } + truth BodyPartIsStuck(int) const; + void PrintAttribute(cchar*, int, int, int) const; + virtual truth AllowUnconsciousness() const; + truth CanPanic() const; + int GetRandomBodyPart(ulong = ALL_BODYPART_FLAGS) const; + virtual truth CanChokeOnWeb(web*) const { return CanChoke(); } + virtual truth BrainsHurt() const { return false; } + truth IsSwimming() const; + truth IsAnimated() const; + virtual truth IsPlayerKind() const { return false; } + truth HasBodyPart(sorter) const; + truth PossessesItem(sorter) const; + truth IsFlying() const { return GetMoveType() & FLY; } + virtual cchar* GetRunDescriptionLine(int) const; + void VomitAtRandomDirection(int); + virtual truth SpecialSaveLife() { return false; } + void RemoveLifeSavers(); + virtual ccharacter* FindCarrier() const; + virtual cchar* GetNormalDeathMessage() const; + virtual bool IsConscious() const; + void ForcePutNear(v2); + virtual void ApplySpecialAttributeBonuses() { } + void ReceiveMustardGas(int, long); + void ReceiveMustardGasLiquid(int, long); + truth IsBadPath(v2) const; + double& GetExpModifierRef(expid); + truth ForgetRandomThing(); + void ApplyAllGodsKnownBonus(); + item* GiveMostExpensiveItem(character*); + void ReceiveItemAsPresent(item*); + item* FindMostExpensiveItem() const; + void ReceiveSirenSong(character* Siren); + character* GetNearestEnemy() const; + truth IsInfectedByMindWorm() const { return !CounterToMindWormHatch; } + void SetCounterToMindWormHatch(int What) { CounterToMindWormHatch = What; } + truth MindWormCanPenetrateSkull(mindworm*) const; + truth CanTameWithDulcis(const character*) const; + truth CanTameWithLyre(const character*) const; + truth CanTameWithScroll(const character*) const; + truth IsCharmable() const { return GetTamingDifficulty() != NO_TAMING; } + truth CheckSadism(); + virtual truth HasSadistAttackMode() const { return IsUsingLegs(); } + truth CheckForBeverage(); + void Haste(); + void Slow(); + truth CheckThrowItemOpportunity(); + truth CheckAIZapOpportunity(); + truth CheckInventoryForItemToThrow(item*); + //virtual truth HasRangedAttackMode() const { return IsUsingArms(); } + virtual void SurgicallyDetachBodyPart(); + protected: + static truth DamageTypeDestroysBodyPart(int); + virtual void LoadSquaresUnder(); + virtual bodypart* MakeBodyPart(int) const; + virtual void SpecialTurnHandler() { } + void Initialize(int, int); + virtual void PostConstruct() { } + void LoadDataBaseStats(); + virtual v2 GetBodyPartBitmapPos(int, truth = false) const; + virtual col16 GetBodyPartColorA(int, truth = false) const; + virtual col16 GetBodyPartColorB(int, truth = false) const; + virtual col16 GetBodyPartColorC(int, truth = false) const; + virtual col16 GetBodyPartColorD(int, truth = false) const; + virtual int GetBodyPartSparkleFlags(int) const; + virtual long GetBodyPartSize(int, int) const; + virtual long GetBodyPartVolume(int) const; + void UpdateBodyPartPicture(int I, truth); + int ChooseBodyPartToReceiveHit(double, double); + virtual void CreateBodyParts(int); + virtual material* CreateBodyPartMaterial(int, long) const; + virtual truth ShowClassDescription() const { return true; } + void SeekLeader(ccharacter*); + virtual truth CheckForUsefulItemsOnGround(truth = true); + truth CheckForDoors(); + truth CheckForEnemies(truth, truth, truth, truth = false); + truth FollowLeader(character*); + void StandIdleAI(); + virtual void CreateCorpse(lsquare*); + void GetPlayerCommand(); + virtual void GetAICommand(); + truth MoveTowardsTarget(truth); + virtual cchar* FirstPersonUnarmedHitVerb() const; + virtual cchar* FirstPersonCriticalUnarmedHitVerb() const; + virtual cchar* ThirdPersonUnarmedHitVerb() const; + virtual cchar* ThirdPersonCriticalUnarmedHitVerb() const; + virtual cchar* FirstPersonKickVerb() const; + virtual cchar* FirstPersonCriticalKickVerb() const; + virtual cchar* ThirdPersonKickVerb() const; + virtual cchar* ThirdPersonCriticalKickVerb() const; + virtual cchar* FirstPersonBiteVerb() const; + virtual cchar* FirstPersonCriticalBiteVerb() const; + virtual cchar* ThirdPersonBiteVerb() const; + virtual cchar* ThirdPersonCriticalBiteVerb() const; + virtual cchar* UnarmedHitNoun() const; + virtual cchar* KickNoun() const; + virtual cchar* BiteNoun() const; + virtual truth AttackIsBlockable(int) const { return true; } + virtual truth AttackMayDamageArmor() const { return true; } + virtual int GetSpecialBodyPartFlags(int) const { return ST_NORMAL; } + virtual int GetBodyPartWobbleData(int) const { return 0; } + virtual int ModifyBodyPartHitPreference(int, int Modifier) const { return Modifier; } + virtual int ModifyBodyPartToHitChance(int, int Chance) const { return Chance; } + virtual truth CanPanicFromSeveredBodyPart() const { return true; } + virtual void SpecialBodyPartSeverReaction() { } + truth AttackAdjacentEnemyAI(); + double RandomizeBabyExperience(double); + static truth IsLimbIndex(int); + virtual truth AllowExperience() const { return true; } + virtual const prototype* FindProtoType() const { return &ProtoType; } + static const prototype ProtoType; + stack* Stack; + long NP, AP; + long TemporaryState; + int TemporaryStateCounter[STATES]; + team* Team; + v2 GoingTo; + long Money; + int CurrentSweatMaterial; + std::list::iterator TeamIterator; + bodypartslot* BodyPartSlot; + festring AssignedName; + action* Action; + const database* DataBase; + double BaseExperience[BASE_ATTRIBUTES]; + std::list* OriginalBodyPartID; + entity* MotherEntity; + character* PolymorphBackup; + cweaponskill* CWeaponSkill; + long EquipmentState; + square** SquareUnder; + long Volume; + long Weight; + long CarriedWeight; + long BodyVolume; + int HP; + int MaxHP; + int BurdenState; + double DodgeValue; + int AllowedWeaponSkillCategories; + int BodyParts; + long RegenerationCounter; + int AttributeBonus[BASE_ATTRIBUTES]; + int CarryingBonus; + homedata* HomeData; + ulong ID; + int SquaresUnder; + std::vector Route; + std::set Illegal; + ulong LastAcidMsgMin; + int Stamina; + int MaxStamina; + int BlocksSinceLastTurn; + double GenerationDanger; + ulong CommandFlags; + ulong WarnFlags; + int ScienceTalks; + trapdata* TrapData; + expmodifiermap ExpModifierMap; + int CounterToMindWormHatch; +}; + +#ifdef __FILE_OF_STATIC_CHARACTER_PROTOTYPE_DEFINITIONS__ +#define CHARACTER_PROTO(name, base)\ +template<> const characterprototype\ + name##sysbase::ProtoType(&base::ProtoType,\ + (characterspawner)(&name##sysbase::Spawn),\ + (charactercloner)(&name##sysbase::Clone), #name); +#else +#define CHARACTER_PROTO(name, base) +#endif + +#define CHARACTER(name, base)\ +class name;\ +typedef sysbase name##sysbase;\ +CHARACTER_PROTO(name, base)\ +class name : public name##sysbase + +#endif diff --git a/Main/Include/command.h b/Main/Include/command.h new file mode 100644 index 0000000..a74767b --- /dev/null +++ b/Main/Include/command.h @@ -0,0 +1,104 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __COMMAND_H__ +#define __COMMAND_H__ + +#include "ivandef.h" + +typedef truth (item::*sorter)(ccharacter*) const; + +class command +{ + public: + command(truth (*)(character*), cchar*, char, char, truth, truth = false); + truth (*GetLinkedFunction() const)(character*) { return LinkedFunction; } + cchar* GetDescription() const { return Description; } + char GetKey() const; + truth IsUsableInWilderness() const { return UsableInWilderness; } + truth IsWizardModeFunction() const { return WizardModeFunction; } + private: + truth (*LinkedFunction)(character*); + cchar* Description; + char Key1; + char Key2; + truth UsableInWilderness; + truth WizardModeFunction; +}; + +class commandsystem +{ + public: + static command* GetCommand(int I) { return Command[I]; } + private: + static truth Apply(character*); + static truth Close(character*); + static truth Eat(character*); + static truth Drink(character*); + static truth Dip(character*); + static truth DrawMessageHistory(character*); + static truth Drop(character*); + static truth ForceVomit(character*); + static truth GoDown(character*); + static truth GoUp(character*); + static truth Kick(character*); + static truth Look(character*); + static truth NOP(character*); + static truth Offer(character*); + static truth Open(character*); + static truth PickUp(character*); + static truth Pray(character*); + static truth Quit(character*); + static truth Read(character*); + static truth Save(character*); + static truth ShowInventory(character*); + static truth ShowKeyLayout(character*); + static truth ShowWeaponSkills(character*); + static truth Talk(character*); + static truth Throw(character*); + static truth EquipmentScreen(character*); + static truth WhatToEngrave(character*); + static truth Zap(character*); + static truth Rest(character*); + static truth Sit(character*); + static truth Go(character*); + static truth ShowConfigScreen(character*); + static truth ScrollMessagesDown(character*); + static truth ScrollMessagesUp(character*); + static truth WieldInRightArm(character*); + static truth WieldInLeftArm(character*); + static truth AssignName(character*); + static truth Search(character*); + static truth Consume(character*, cchar*, sorter); +#ifdef WIZARD + static truth WizardMode(character*); + static truth RaiseStats(character*); + static truth LowerStats(character*); + static truth SeeWholeMap(character*); + static truth WalkThroughWalls(character*); + static truth RaiseGodRelations(character*); + static truth LowerGodRelations(character*); + static truth GainDivineKnowledge(character*); + static truth GainAllItems(character*); + static truth SecretKnowledge(character*); + static truth DetachBodyPart(character*); + static truth SummonMonster(character*); + static truth LevelTeleport(character*); + static truth Possess(character*); + static truth Polymorph(character*); +#endif + static truth ToggleRunning(character*); + static truth IssueCommand(character*); + static command* Command[]; +}; + +#endif diff --git a/Main/Include/confdef.h b/Main/Include/confdef.h new file mode 100644 index 0000000..074edf5 --- /dev/null +++ b/Main/Include/confdef.h @@ -0,0 +1,499 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __CONFDEF_H__ +#define __CONFDEF_H__ + +/* + * Configuration defines of IVAN + */ + +#define SOLID_ID (1 << 12) + +#define VALPURIUM (SOLID_ID + 1) +#define GRAVEL (SOLID_ID + 2) +#define MORAINE (SOLID_ID + 3) +#define OCTIRON (SOLID_ID + 4) +#define GLASS (SOLID_ID + 5) +#define PARCHMENT (SOLID_ID + 6) +#define CLOTH (SOLID_ID + 7) +#define MITHRIL (SOLID_ID + 8) +#define MARBLE (SOLID_ID + 9) +#define GOLD (SOLID_ID + 10) +#define GRASS (SOLID_ID + 11) +#define LEATHER (SOLID_ID + 12) +#define LEAF (SOLID_ID + 13) +#define FABRIC (SOLID_ID + 14) +#define PALM_LEAF (SOLID_ID + 15) +#define SULFUR (SOLID_ID + 16) +#define UNICORN_HORN (SOLID_ID + 17) +#define DIAMOND (SOLID_ID + 18) +#define SILVER (SOLID_ID + 19) +#define SAPPHIRE (SOLID_ID + 20) +#define RUBY (SOLID_ID + 21) +#define BRONZE (SOLID_ID + 22) +#define COPPER (SOLID_ID + 23) +#define TIN (SOLID_ID + 24) +#define SPIDER_SILK (SOLID_ID + 25) +#define KEVLAR (SOLID_ID + 26) +#define OMMEL_HAIR (SOLID_ID + 27) +#define HARDENED_LEATHER (SOLID_ID + 28) +#define TROLL_HIDE (SOLID_ID + 29) +#define NYMPH_HAIR (SOLID_ID + 30) +#define ANGEL_HAIR (SOLID_ID + 31) +#define PHOENIX_FEATHER (SOLID_ID + 32) +#define GOLDEN_EAGLE_FEATHER (SOLID_ID + 33) +#define ICE (SOLID_ID + 34) +#define DRAGON_HIDE (SOLID_ID + 35) +#define ARCANITE (SOLID_ID + 36) +#define ILLITHIUM (SOLID_ID + 37) +#define BALSA_WOOD (SOLID_ID + 38) +#define PINE_WOOD (SOLID_ID + 39) +#define FIR_WOOD (SOLID_ID + 40) +#define BIRCH_WOOD (SOLID_ID + 41) +#define OAK_WOOD (SOLID_ID + 42) +#define TEAK_WOOD (SOLID_ID + 43) +#define EBONY_WOOD (SOLID_ID + 44) +#define BLUE_CRYSTAL (SOLID_ID + 45) +#define PURPLE_CRYSTAL (SOLID_ID + 46) +#define GREEN_CRYSTAL (SOLID_ID + 47) +#define SAND_STONE (SOLID_ID + 48) +#define LIME_STONE (SOLID_ID + 49) +#define CALCITE (SOLID_ID + 50) +#define OBSIDIAN (SOLID_ID + 51) +#define GNEISS (SOLID_ID + 52) +#define SLATE (SOLID_ID + 53) +#define GRANITE (SOLID_ID + 54) +#define BASALT (SOLID_ID + 55) +#define MILKY_QUARTZ (SOLID_ID + 56) +#define FLINT (SOLID_ID + 57) +#define QUARTZITE (SOLID_ID + 58) +#define AMETHYST (SOLID_ID + 59) +#define CITRINE (SOLID_ID + 60) +#define ROSE_QUARTZ (SOLID_ID + 61) +#define JASPER (SOLID_ID + 62) +#define ROCK_CRYSTAL (SOLID_ID + 63) +#define DARK_GRASS (SOLID_ID + 64) +#define LEAD (SOLID_ID + 65) +#define BLACK_GRANITE (SOLID_ID + 66) +#define BLACK_LEATHER (SOLID_ID + 67) +#define FLAWLESS_DIAMOND (SOLID_ID + 68) +#define EMERALD (SOLID_ID + 69) +#define SUN_CRYSTAL (SOLID_ID + 70) +#define BLACK_DIAMOND (SOLID_ID + 71) +#define PSYPHER (SOLID_ID + 72) +#define EXTRA_HARD_BASALT (SOLID_ID + 73) +#define DEAD_GRASS (SOLID_ID + 74) +#define KAURI_WOOD (SOLID_ID + 75) +#define RATA_WOOD (SOLID_ID + 76) +#define NEPHRITE (SOLID_ID + 77) +#define HESSIAN_CLOTH (SOLID_ID + 78) + +#define ORGANIC_ID (2 << 12) + +#define BANANA_FLESH (ORGANIC_ID + 1) +#define SCHOOL_FOOD (ORGANIC_ID + 2) +#define BANANA_PEEL (ORGANIC_ID + 3) +#define KIWI_FLESH (ORGANIC_ID + 4) +#define PINEAPPLE_FLESH (ORGANIC_ID + 5) +#define PLANT_FIBER (ORGANIC_ID + 6) +#define MUTANT_PLANT_FIBER (ORGANIC_ID + 7) +#define BONE (ORGANIC_ID + 8) +#define BREAD (ORGANIC_ID + 9) +#define HOLY_BANANA_FLESH (ORGANIC_ID + 10) +#define CARROT_FLESH (ORGANIC_ID + 11) +#define OMMEL_CERUMEN (ORGANIC_ID + 12) +#define OMMEL_BONE (ORGANIC_ID + 13) +#define OMMEL_TOOTH (ORGANIC_ID + 14) +#define MANGO_FLESH (ORGANIC_ID + 15) +#define HOLY_MANGO_FLESH (ORGANIC_ID + 16) + +#define GAS_ID (3 << 12) + +#define AIR (GAS_ID + 1) +#define MAGICAL_AIR (GAS_ID + 2) +#define SMOKE (GAS_ID + 3) +#define SKUNK_SMELL (GAS_ID + 4) +#define GHOST (GAS_ID + 5) +#define MAGIC_VAPOUR (GAS_ID + 6) +#define EVIL_WONDER_STAFF_VAPOUR (GAS_ID + 7) +#define GOOD_WONDER_STAFF_VAPOUR (GAS_ID + 8) +#define FART (GAS_ID + 9) +#define MUSTARD_GAS (GAS_ID + 10) + +#define LIQUID_ID (4 << 12) + +#define OMMEL_URINE (LIQUID_ID + 1) +#define PEPSI (LIQUID_ID + 2) +#define WATER (LIQUID_ID + 3) +#define HEALING_LIQUID (LIQUID_ID + 4) +#define BLOOD (LIQUID_ID + 5) +#define BROWN_SLIME (LIQUID_ID + 6) +#define POISON_LIQUID (LIQUID_ID + 7) +#define VALDEMAR (LIQUID_ID + 8) +#define ANTIDOTE_LIQUID (LIQUID_ID + 9) +#define VODKA (LIQUID_ID + 10) +#define TROLL_BLOOD (LIQUID_ID + 11) +#define DARK_FROG_BLOOD (LIQUID_ID + 12) +#define SPIDER_BLOOD (LIQUID_ID + 13) +#define VOMIT (LIQUID_ID + 14) +#define ACIDOUS_BLOOD (LIQUID_ID + 15) +#define SULPHURIC_ACID (LIQUID_ID + 16) +#define DOG_DROOL (LIQUID_ID + 17) +#define PEA_SOUP (LIQUID_ID + 18) +#define OMMEL_SWEAT (LIQUID_ID + 19) +#define OMMEL_TEARS (LIQUID_ID + 20) +#define OMMEL_SNOT (LIQUID_ID + 21) +#define SWEAT (LIQUID_ID + 22) +#define GLOWING_BLOOD (LIQUID_ID + 23) +#define YELLOW_SLIME (LIQUID_ID + 24) +#define SICK_BLOOD (LIQUID_ID + 25) +#define MUSTARD_GAS_LIQUID (LIQUID_ID + 26) +#define LIQUID_HORROR (LIQUID_ID + 27) +#define VINEGAR (LIQUID_ID + 28) +#define OMMEL_BLOOD (LIQUID_ID + 29) +#define CURDLED_OMMEL_BLOOD (LIQUID_ID + 30) + +#define FLESH_ID (5 << 12) + +#define GOBLINOID_FLESH (FLESH_ID + 1) +#define PORK (FLESH_ID + 2) +#define BEEF (FLESH_ID + 3) +#define FROG_FLESH (FLESH_ID + 4) +#define ELPURI_FLESH (FLESH_ID + 5) +#define HUMAN_FLESH (FLESH_ID + 6) +#define DOLPHIN_FLESH (FLESH_ID + 7) +#define BEAR_FLESH (FLESH_ID + 8) +#define WOLF_FLESH (FLESH_ID + 9) +#define DOG_FLESH (FLESH_ID + 10) +#define ENNER_BEAST_FLESH (FLESH_ID + 11) +#define SPIDER_FLESH (FLESH_ID + 12) +#define JACKAL_FLESH (FLESH_ID + 13) +#define MUTANT_ASS_FLESH (FLESH_ID + 14) +#define BAT_FLESH (FLESH_ID + 15) +#define WERE_WOLF_FLESH (FLESH_ID + 16) +#define KOBOLD_FLESH (FLESH_ID + 17) +#define GIBBERLING_FLESH (FLESH_ID + 18) +#define CAT_FLESH (FLESH_ID + 19) +#define RAT_FLESH (FLESH_ID + 20) +#define ANGEL_FLESH (FLESH_ID + 21) +#define DWARF_FLESH (FLESH_ID + 22) +#define DAEMON_FLESH (FLESH_ID + 23) +#define MAMMOTH_FLESH (FLESH_ID + 24) +#define BLACK_UNICORN_FLESH (FLESH_ID + 25) +#define GRAY_UNICORN_FLESH (FLESH_ID + 26) +#define WHITE_UNICORN_FLESH (FLESH_ID + 27) +#define LION_FLESH (FLESH_ID + 28) +#define BUFFALO_FLESH (FLESH_ID + 29) +#define SNAKE_FLESH (FLESH_ID + 30) +#define ORC_FLESH (FLESH_ID + 31) +#define OSTRICH_FLESH (FLESH_ID + 32) +#define CHAMELEON_FLESH (FLESH_ID + 33) +#define FLOATING_EYE_FLESH (FLESH_ID + 34) +#define MUSHROOM_FLESH (FLESH_ID + 35) +#define MOOSE_FLESH (FLESH_ID + 36) +#define MAGPIE_FLESH (FLESH_ID + 37) +#define SKUNK_FLESH (FLESH_ID + 38) +#define HEDGEHOG_FLESH (FLESH_ID + 39) +#define MUTANT_BUNNY_FLESH (FLESH_ID + 40) +#define HATTIFATTENER_FLESH (FLESH_ID + 41) +#define BLINK_DOG_FLESH (FLESH_ID + 42) +#define MAGIC_MUSHROOM_FLESH (FLESH_ID + 43) +#define SICK_SPIDER_FLESH (FLESH_ID + 44) +#define MIND_WORM_FLESH (FLESH_ID + 45) +#define MUTANT_HEDGEHOG_FLESH (FLESH_ID + 46) +#define EAGLE_FLESH (FLESH_ID + 47) +#define KABOUTER_FLESH (FLESH_ID + 48) +#define ULDRA_FLESH (FLESH_ID + 49) +#define OKAPI_FLESH (FLESH_ID + 50) +#define VAMPIRE_FLESH (FLESH_ID + 51) +#define MOUSE_FLESH (FLESH_ID + 52) +#define FOX_FLESH (FLESH_ID + 53) +#define THUNDER_BIRD_FLESH (FLESH_ID + 54) + +#define POWDER_ID (6 << 12) + +#define GUN_POWDER (POWDER_ID + 1) +#define SNOW (POWDER_ID + 2) +#define SAND (POWDER_ID + 3) + +#define IRON_ALLOY_ID (7 << 12) + +#define IRON (IRON_ALLOY_ID + 1) +#define STEEL (IRON_ALLOY_ID + 2) +#define METEORIC_STEEL (IRON_ALLOY_ID + 3) +#define ADAMANT (IRON_ALLOY_ID + 4) + +#define LONG_SWORD 1 +#define TWO_HANDED_SWORD 2 +#define TWO_HANDED_SCIMITAR 3 +#define SPEAR 4 +#define AXE 5 +#define HALBERD 6 +#define MACE 7 +#define WAR_HAMMER 8 +#define SICKLE 9 +#define DAGGER 10 +#define SHORT_SWORD 11 +#define BASTARD_SWORD 12 +#define BATTLE_AXE 13 +#define SCYTHE 14 +#define QUARTER_STAFF 15 +#define HAMMER 16 +#define KNIGHT_SWORD 17 +#define KATANA 18 +#define SPETUM 19 +#define TIP_SWORD 20 +#define GREAT_AXE 21 +#define RAPIER 22 + +#define GOROVITS_HAMMER 1 +#define GOROVITS_SICKLE 2 +#define GOROVITS_SCIMITAR 3 + +#define CHAIN_MAIL 1 +#define PLATE_MAIL 2 +#define ARMOR_OF_GREAT_HEALTH 3 +#define DRAGON_CUIRASS 4 +#define FILTHY_TUNIC 5 + +#define CHEAP 1 +#define EXPENSIVE 2 + +#define WAND_OF_POLYMORPH 1 +#define WAND_OF_STRIKING 2 +#define WAND_OF_FIRE_BALLS 3 +#define WAND_OF_TELEPORTATION 4 +#define WAND_OF_HASTE 5 +#define WAND_OF_SLOW 6 +#define WAND_OF_RESURRECTION 7 +#define WAND_OF_DOOR_CREATION 8 +#define WAND_OF_INVISIBILITY 9 +#define WAND_OF_CLONING 10 +#define WAND_OF_LIGHTNING 11 +#define WAND_OF_ACID_RAIN 12 +#define WAND_OF_MIRRORING 13 +#define WAND_OF_NECROMANCY 14 + +#define RUNED_WHIP 1 + +#define BIG_MINE 1 + +#define CLOAK_OF_INVISIBILITY 1 +#define CLOAK_OF_FIRE_RESISTANCE 2 +#define CLOAK_OF_ELECTRICITY_RESISTANCE 3 +#define CLOAK_OF_ACID_RESISTANCE 4 + +#define BOOT_OF_STRENGTH 1 +#define BOOT_OF_AGILITY 2 +#define BOOT_OF_KICKING 3 + +#define GAUNTLET_OF_STRENGTH 1 +#define GAUNTLET_OF_DEXTERITY 2 + +#define RING_OF_FIRE_RESISTANCE 1 +#define RING_OF_POLYMORPH_CONTROL 2 +#define RING_OF_INFRA_VISION 3 +#define RING_OF_TELEPORTATION 4 +#define RING_OF_TELEPORT_CONTROL 5 +#define RING_OF_POLYMORPH 6 +#define RING_OF_POISON_RESISTANCE 7 +#define RING_OF_INVISIBILITY 8 +#define RING_OF_ELECTRICITY_RESISTANCE 9 +#define RING_OF_SEARCHING 10 +#define RING_OF_ACID_RESISTANCE 11 + +#define AMULET_OF_LIFE_SAVING 1 +#define AMULET_OF_ESP 2 +#define AMULET_OF_WARDING 3 +#define AMULET_OF_VANITY 4 + +#define FULL_HELMET 1 +#define HELM_OF_PERCEPTION 2 +#define HELM_OF_UNDERSTANDING 3 +#define HELM_OF_BRILLIANCE 4 +#define HELM_OF_ATTRACTIVITY 5 +#define GOROVITS_FAMILY_GAS_MASK 6 + +#define BELT_OF_CARRYING 1 +#define BELT_OF_LEVITATION 2 + +#define SMALL_CHEST 1 +#define CHEST 2 +#define LARGE_CHEST 3 +#define STRONG_BOX 4 + +#define BRAVERY 1 +#define FEAR 2 +#define CONFUSION 3 + +#define ROOKIE 1 +#define VETERAN 2 +#define EUNUCH 3 +#define PATROL 4 +#define SHOP 5 +#define ELITE 6 +#define MASTER 7 +#define GRAND_MASTER 8 +#define MONDEDR_GUARD 9 +#define DWARVEN_GUARD 10 +#define SENTINEL 11 +#define FOREST_SHOP 12 +#define ENQUIOX 128 + + + +#define DARK 1 +#define GREATER_DARK 2 +#define GIANT_DARK 3 +#define LIGHT 4 +#define GREATER_LIGHT 5 +#define GIANT_LIGHT 6 + +#define WARRIOR 1 +#define WAR_LORD 2 + +#define BERSERKER 1 +#define BUTCHER 2 +#define PRINCE 3 +#define KING 4 +#define JAILER 5 +#define PRISON_WARDEN 6 + +#define CONICAL 1 +#define FLAT 2 + +#define LARGE 1 +#define GIANT 2 + +#define BLACK_BEAR 1 +#define GRIZZLY_BEAR 2 +#define CAVE_BEAR 3 +#define POLAR_BEAR 4 + +#define ZOMBIE_OF_KHAZ_ZADM 1 + +#define TORTURING_CHIEF 1 +#define WHIP_CHAMPION 2 +#define WAR_LADY 3 +#define QUEEN 4 + +#define CHIEFTAIN 1 +#define LORD 2 +#define PATRIARCH 3 + +#define AMBULATORY 1 + +#define GREATER 1 +#define GIANT 2 +#define LILY 3 +#define SHAMBLING 4 + +#define SLAUGHTERER 1 +#define SQUAD_LEADER 2 +#define OFFICER 3 +#define GENERAL 4 +#define MARSHAL 5 + +#define APPRENTICE 1 +#define BATTLE_MAGE 2 +#define ELDER 3 +#define ARCH_MAGE 4 + +#define ROVER 1 +#define BAND_LEADER 2 + +#define FIELD_MOUSE 1 +#define LABORATORY_MOUSE 2 + +#define THIN_PIG 1 + +#define STARVED_OX 1 + +#define FLOATIE 1 + +/* Least significant bit defines sex */ + +#define BABY_MALE 2 +#define BABY_FEMALE 3 +#define ADULT_MALE 4 +#define ADULT_FEMALE 5 + +#define APPRENTICE_NECROMANCER 1 +#define MASTER_NECROMANCER 2 + +#define HUSBAND 1 +#define WIFE 2 +#define CHILD 3 + +#define PARQUET 1 +#define FLOOR 2 +#define GROUND 3 +#define GRASS_TERRAIN 4 +#define LANDING_SITE 5 +#define SNOW_TERRAIN 6 +#define DARK_GRASS_TERRAIN 7 +#define SAND_TERRAIN 8 + +#define POOL 1 +#define UNDERGROUND_LAKE 2 + +#define BRICK_FINE 1 +#define BRICK_PROPAGANDA 2 +#define BRICK_OLD 3 +#define BRICK_PRIMITIVE 4 +#define BRICK_PRIMITIVE_PROPAGANDA 5 +#define STONE_WALL 6 +#define ICE_WALL 7 +#define BROKEN_WALL 8 + +#define PINE 1 +#define FIR 2 +#define HOLY_TREE 3 +#define CARPET 4 +#define COUCH 5 +#define DOUBLE_BED 6 +#define POOL_BORDER 7 +#define POOL_CORNER 8 +#define PALM 9 +#define SNOW_PINE 10 +#define SNOW_FIR 11 +#define ANVIL 12 +#define SHARD 13 +#define CACTUS 14 +#define OAK 15 +#define BIRCH 16 +#define TEAK 17 +#define DWARF_BIRCH 18 + +#define SNOW_BOULDER 4 + +#define OREE_LAIR_ENTRY 300 +#define OREE_LAIR_EXIT 400 +#define SUMO_ARENA_ENTRY 700 +#define SUMO_ARENA_EXIT 800 + +#define BOOK_CASE 1 +#define CHEST_OF_DRAWERS 2 +#define SHELF 3 + +#define BROKEN_BARWALL 1 +#define SECRET_DOOR 2 + +#define ROOM_NORMAL 1 +#define ROOM_SHOP 2 +#define ROOM_CATHEDRAL 3 +#define ROOM_LIBRARY 4 +#define ROOM_BANANA_DROP_AREA 5 +#define ROOM_SUMO_ARENA 6 + +#endif diff --git a/Main/Include/cont.h b/Main/Include/cont.h new file mode 100644 index 0000000..2290e93 --- /dev/null +++ b/Main/Include/cont.h @@ -0,0 +1,54 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __CONT_H__ +#define __CONT_H__ + +#include + +#include "v2.h" +#include "festring.h" + +class outputfile; +class inputfile; + +class continent +{ + public: + friend class worldmap; + continent(); + continent(int); + void AttachTo(continent*); + void Add(v2); + void Save(outputfile&) const; + void Load(inputfile&); + long GetSize() const; + int GetIndex() const { return Index; } + void GenerateInfo(); + festring GetName() const { return Name; } + int GetGTerrainAmount(int) const; + v2 GetRandomMember(int); + v2 GetMember(int) const; + private: + static uchar** TypeBuffer; + static short** AltitudeBuffer; + static uchar** ContinentBuffer; + festring Name; + std::vector Member; + std::vector GTerrainAmount; + int Index; +}; + +outputfile& operator<<(outputfile&, const continent*); +inputfile& operator>>(inputfile&, continent*&); + +#endif diff --git a/Main/Include/database.h b/Main/Include/database.h new file mode 100644 index 0000000..792882b --- /dev/null +++ b/Main/Include/database.h @@ -0,0 +1,53 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __DATABASE_H__ +#define __DATABASE_H__ + +#include + +#include "typedef.h" + +class inputfile; +class festring; + +template struct databasememberbase +{ + virtual void ReadData(database&, inputfile&) = 0; +}; + +template class databasecreator +{ + public: + typedef typename type::database database; + typedef typename type::prototype prototype; + typedef std::map*> databasemembermap; + static void ReadFrom(inputfile&); + static void FindDataBase(const database*&, const prototype*, int); + static void InstallDataBase(type*, int); + static void CreateDataBaseMemberMap(); + static int CreateDivineConfigurations(const prototype*, database**, int); + private: + static truth AnalyzeData(inputfile&, cfestring&, database&); + static void CheckDefaults(cfestring&, database&); + static void CreateLTerrainDataBaseMemberMap(); + static void SetBaseValue(cfestring&, databasememberbase*, database&); + static databasemembermap& GetDataBaseMemberMap(); +}; + +class databasesystem +{ + public: + static void Initialize(); +}; + +#endif diff --git a/Main/Include/dungeon.h b/Main/Include/dungeon.h new file mode 100644 index 0000000..ee1b236 --- /dev/null +++ b/Main/Include/dungeon.h @@ -0,0 +1,61 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __DUNGEON_H__ +#define __DUNGEON_H__ + +#include "v2.h" + +class level; +class outputfile; +class inputfile; +class dungeonscript; +class levelscript; +class festring; + +class dungeon +{ + public: + dungeon(); + dungeon(int); + ~dungeon(); + truth PrepareLevel(int, truth = true); + void SaveLevel(cfestring&, int, truth = true); + level* LoadLevel(cfestring&, int); + level* GetLevel(int I) const { return Level[I]; } + int GetLevels() const; + void Save(outputfile&) const; + void Load(inputfile&); + void SetIndex(int What) { Index = What; } + int GetIndex() const { return Index; } + const levelscript* GetLevelScript(int); + v2 GetWorldMapPos() { return WorldMapPos; } + void SetWorldMapPos(v2 What) { WorldMapPos = What; } + festring GetLevelDescription(int); + festring GetShortLevelDescription(int); + level* LoadLevel(inputfile&, int); + truth IsGenerated(int I) const { return Generated[I]; } + void SetIsGenerated(int I, truth What) { Generated[I] = What; } + int GetLevelTeleportDestination(int) const; + private: + void Initialize(); + const dungeonscript* DungeonScript; + level** Level; + int Index; + truth* Generated; + v2 WorldMapPos; +}; + +outputfile& operator<<(outputfile&, const dungeon*); +inputfile& operator>>(inputfile&, dungeon*&); + +#endif diff --git a/Main/Include/entity.h b/Main/Include/entity.h new file mode 100644 index 0000000..3fa9216 --- /dev/null +++ b/Main/Include/entity.h @@ -0,0 +1,75 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __ENTITY_H__ +#define __ENTITY_H__ + +#include + +#include "ivandef.h" + +#define EXISTS 1 +#define HAS_BE 2 +#define ENTITY_FLAGS 3 + +class square; +struct v2; + +class entity +{ + public: + friend class pool; + entity(int); + entity(const entity&); + virtual ~entity(); + virtual void Be() { } + truth Exists() const { return Flags & EXISTS; } + void SendToHell(); + truth IsEnabled() const { return Flags & HAS_BE; } + void Enable(); + void Disable(); + virtual square* GetSquareUnderEntity(int = 0) const = 0; + virtual void SignalVolumeAndWeightChange() { } + col24 GetEmitation() const { return Emitation; } + virtual void SignalEmitationIncrease(col24) { } + virtual void SignalEmitationDecrease(col24) { } + virtual truth ContentsCanBeSeenBy(ccharacter*) const { return false; } + virtual truth AllowSpoil() const { return false; } + virtual void SignalSpoil(material*) { } + virtual void SignalSpoilLevelChange(material*) { } + virtual truth IsOnGround() const = 0; + virtual truth AllowContentEmitation() const { return true; } + virtual void SignalRustLevelChange() { } + virtual material* RemoveMaterial(material*) { return 0; } + virtual character* TryNecromancy(character*) { return 0; } + virtual void SignalDisappearance() { } + virtual void SpecialEatEffect(character*, int) { } + virtual ulong GetTrapID() const { return 0; } + virtual ulong GetVictimID() const { return 0; } + virtual void AddTrapName(festring&, int) const { } + virtual void UnStick() { } + virtual void UnStick(int) { } + virtual truth TryToUnStick(character*, v2); + virtual int GetTrapType() const { return 0; } + void AddFlags(ulong What) { Flags |= What; } + void RemoveFlags(ulong What) { Flags &= ~What; } + virtual truth IsStuckTo(ccharacter*) const { return false; } + virtual ccharacter* FindCarrier() const { return 0; } + protected: + col24 Emitation; + ulong Flags; + private: + entity* Last; + entity* Next; +}; + +#endif diff --git a/Main/Include/fluid.h b/Main/Include/fluid.h new file mode 100644 index 0000000..a7cadd8 --- /dev/null +++ b/Main/Include/fluid.h @@ -0,0 +1,125 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __FLUID_H__ +#define __FLUID_H__ + +#include "lsquare.h" +#include "trap.h" + +class bitmap; + +typedef truth (rawbitmap::*pixelpredicate)(v2) const; + +class fluid : public entity +{ + public: + /* Come To The Dark Side */ + fluid* Next; + public: + fluid(); + fluid(liquid*, lsquare*); + fluid(liquid*, item*, cfestring&, truth); + virtual ~fluid(); + virtual void Be(); + void Save(outputfile&) const; + void Load(inputfile&); + void SimpleDraw(blitdata&) const; + void Draw(blitdata&) const; + liquid* GetLiquid() const { return Liquid; } + virtual square* GetSquareUnderEntity(int = 0) const { return LSquareUnder; } + square* GetSquareUnder() const { return LSquareUnder; } + void SetLSquareUnder(lsquare* What) { LSquareUnder = What; } + lsquare* GetLSquareUnder() const { return LSquareUnder; } + virtual truth IsOnGround() const { return true; } + void AddLiquid(long); + void AddLiquidAndVolume(long); + virtual void SignalVolumeAndWeightChange(); + void SetMotherItem(item*); + static void AddFluidInfo(const fluid*, festring&); + void CheckGearPicture(v2, int, truth); + void DrawGearPicture(blitdata&, int) const; + truth FadePictures(); + void DrawBodyArmorPicture(blitdata&, int) const; + void Redistribute(); + virtual material* RemoveMaterial(material*); + void Destroy(); + cfestring& GetLocationName() const { return LocationName; } + truth IsInside() const { return Flags & FLUID_INSIDE; } + truth UseImage() const; + virtual int GetTrapType() const { return Liquid->GetType() | FLUID_TRAP; } + virtual ulong GetTrapID() const { return TrapData.TrapID; } + virtual ulong GetVictimID() const { return TrapData.VictimID; } + virtual void AddTrapName(festring&, int) const; + virtual void UnStick() { TrapData.VictimID = 0; } + virtual void UnStick(int I) { TrapData.BodyParts &= ~(1 << I); } + void StepOnEffect(character*); + virtual truth TryToUnStick(character*, v2); + virtual void PreProcessForBone(); + virtual void PostProcessForBone(); + virtual truth IsStuckTo(ccharacter*) const; + truth IsDangerous(ccharacter*) const; + protected: + trapdata TrapData; + struct imagedata + { + imagedata(truth = true); + ~imagedata(); + void Animate(blitdata&, int) const; + void AddLiquidToPicture(const rawbitmap*, long, long, col16, pixelpredicate); + void Save(outputfile&) const; + void Load(inputfile&); + truth Fade(); + void Clear(truth); + /* Only pictures of fluids not on ground have their RandMaps initialized, + since they are animated. Note that the picture is always unrotated. */ + bitmap* Picture; + /* Used by Animate() */ + mutable int DripTimer; + mutable v2 DripPos; + mutable col16 DripColor; + mutable alpha DripAlpha; + /* Sum of all alphas of Picture. The volume of the liquid is currently + proportional to AlphaSum of the fluid's Image, limiting it + considerably. */ + long AlphaSum; + /* AlphaSum / (non-transparent pixels in Picture), used to synchronise + gear pictures with the main image */ + packalpha AlphaAverage; + /* The position of a gear picture in humanoid.pcx which binds the fluid; + remembered so that it can be easily determined whether the fluid needs + to be redistributed due to a major graphics change */ + v2 ShadowPos; + /* Animation of gear items needs to know whether the raw picture is + rotated somehow. Currently this is the case only for an item + in the left hand of a character. */ + int SpecialFlags; + }; + liquid* Liquid; + lsquare* LSquareUnder; + /* MotherItem == 0 means that the fluid is on ground */ + item* MotherItem; + imagedata Image; + /* Data of pictures shown over the player if he equips the item which + the fluid covers. Note that these are not destroyed when the armor + or weapon is unequipped. There is no real need, since the existence + of the fluid is very temporary anyway. */ + imagedata* GearImage; + ulong Flags; + festring LocationName; + static const long BodyArmorPartPixels[]; +}; + +outputfile& operator<<(outputfile&, const fluid*); +inputfile& operator>>(inputfile&, fluid*&); + +#endif diff --git a/Main/Include/game.h b/Main/Include/game.h new file mode 100644 index 0000000..fc2949d --- /dev/null +++ b/Main/Include/game.h @@ -0,0 +1,622 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __GAME_H__ +#define __GAME_H__ + +#include +#include +#include + +#include "femath.h" +#include "festring.h" +#include "ivandef.h" + +#ifndef LIGHT_BORDER +#define LIGHT_BORDER 80 +#endif + +#define PLAYER game::GetPlayer() + +class area; +class level; +class dungeon; +class felist; +class team; +class character; +class gamescript; +class item; +class outputfile; +class inputfile; +class worldmap; +class god; +class square; +class wsquare; +class lsquare; +class bitmap; +class festring; +class rain; +class liquid; +class entity; +class olterrain; +struct explosion; + +typedef std::map valuemap; +typedef truth (*stringkeyhandler)(int, festring&); +typedef v2 (*positionkeyhandler)(v2, int); +typedef void (*positionhandler)(v2); +typedef void (*bitmapeditor)(bitmap*, truth); + +struct homedata +{ + v2 Pos; + int Dungeon; + int Level; + int Room; +}; + +outputfile& operator<<(outputfile&, const homedata*); +inputfile& operator>>(inputfile&, homedata*&); + +#ifdef VC +#pragma pack(1) +#endif + +struct configid +{ + configid() { } + configid(int Type, int Config) : Type(Type), Config(Config) { } + bool operator<(const configid& CI) const { return memcmp(this, &CI, sizeof(configid)) < 0; } + int Type NO_ALIGNMENT; + int Config NO_ALIGNMENT; +}; + +#ifdef VC +#pragma pack() +#endif + +outputfile& operator<<(outputfile&, const configid&); +inputfile& operator>>(inputfile&, configid&); + +struct dangerid +{ + dangerid() { } + dangerid(double NakedDanger, double EquippedDanger) : NakedDanger(NakedDanger), EquippedDanger(EquippedDanger) { } + double NakedDanger; + double EquippedDanger; +}; + +outputfile& operator<<(outputfile&, const dangerid&); +inputfile& operator>>(inputfile&, dangerid&); + +struct ivantime +{ + int Day; + int Hour; + int Min; +}; + +struct massacreid +{ + massacreid() { } + massacreid(int Type, int Config, cfestring& Name) + : Type(Type), Config(Config), Name(Name) { } + bool operator<(const massacreid&) const; + int Type; + int Config; + festring Name; +}; + +inline bool massacreid::operator<(const massacreid& MI) const +{ + if(Type != MI.Type) + return Type < MI.Type; + + if(Config != MI.Config) + return Config < MI.Config; + + return Name < MI.Name; +} + +outputfile& operator<<(outputfile&, const massacreid&); +inputfile& operator>>(inputfile&, massacreid&); + +struct killreason +{ + killreason() { } + killreason(cfestring& String, int Amount) : String(String), Amount(Amount) { } + festring String; + int Amount; +}; + +outputfile& operator<<(outputfile&, const killreason&); +inputfile& operator>>(inputfile&, killreason&); + +struct killdata +{ + killdata(int Amount = 0, double DangerSum = 0) : Amount(Amount), DangerSum(DangerSum) { } + int Amount; + double DangerSum; + std::vector Reason; +}; + +outputfile& operator<<(outputfile&, const killdata&); +inputfile& operator>>(inputfile&, killdata&); + +typedef std::map dangermap; +typedef std::map characteridmap; +typedef std::map itemidmap; +typedef std::map trapidmap; +typedef std::map massacremap; +typedef std::map boneidmap; +typedef std::vector itemvector; +typedef std::vector itemvectorvector; +typedef std::vector charactervector; + +class quitrequest { }; +class areachangerequest { }; + +class game +{ + public: + static truth Init(cfestring& = CONST_S("")); + static void DeInit(); + static void Run(); + static int GetMoveCommandKey(int); + static cv2 GetMoveVector(int I) { return MoveVector[I]; } + static cv2 GetRelativeMoveVector(int I) { return RelativeMoveVector[I]; } + static cv2 GetBasicMoveVector(int I) { return BasicMoveVector[I]; } + static cv2 GetLargeMoveVector(int I) { return LargeMoveVector[I]; } + static area* GetCurrentArea() { return CurrentArea; } + static level* GetCurrentLevel() { return CurrentLevel; } + static uchar*** GetLuxTable() { return LuxTable; } + static character* GetPlayer() { return Player; } + static void SetPlayer(character*); + static v2 GetCamera() { return Camera; } + static void UpdateCameraX(); + static void UpdateCameraY(); + static truth IsLoading() { return Loading; } + static void SetIsLoading(truth What) { Loading = What; } + static truth ForceJumpToPlayerBe() { return JumpToPlayerBe; } + static void SetForceJumpToPlayerBe(truth What) { JumpToPlayerBe = What; } + static level* GetLevel(int); + static void InitLuxTable(); + static void DeInitLuxTable(); + static cchar* Insult(); + static truth TruthQuestion(cfestring&, int = 0, int = 0); + static void DrawEverything(); + static truth Save(cfestring& = SaveName("")); + static int Load(cfestring& = SaveName("")); + static truth IsRunning() { return Running; } + static void SetIsRunning(truth What) { Running = What; } + static void UpdateCameraX(int); + static void UpdateCameraY(int); + static int GetCurrentLevelIndex() { return CurrentLevelIndex; } + static int GetMoveCommandKeyBetweenPoints(v2, v2); + static void DrawEverythingNoBlit(truth = false); + static god* GetGod(int I) { return God[I]; } + static cchar* GetAlignment(int I) { return Alignment[I]; } + static void ApplyDivineTick(); + static void ApplyDivineAlignmentBonuses(god*, int, truth); + static v2 GetDirectionVectorForKey(int); + static festring SaveName(cfestring& = CONST_S("")); + static void ShowLevelMessage(); + static double GetMinDifficulty(); + static void TriggerQuestForGoldenEagleShirt(); + static void CalculateGodNumber(); + static void IncreaseTick() { ++Tick; } + static ulong GetTick() { return Tick; } + static festring GetAutoSaveFileName() { return AutoSaveFileName; } + static int DirectionQuestion(cfestring&, truth = true, truth = false); + static void RemoveSaves(truth = true); + static truth IsInWilderness() { return InWilderness; } + static void SetIsInWilderness(truth What) { InWilderness = What; } + static worldmap* GetWorldMap() { return WorldMap; } + static void SetAreaInLoad(area* What) { AreaInLoad = What; } + static void SetSquareInLoad(square* What) { SquareInLoad = What; } + static area* GetAreaInLoad() { return AreaInLoad; } + static square* GetSquareInLoad() { return SquareInLoad; } + static int GetLevels(); + static dungeon* GetCurrentDungeon() { return Dungeon[CurrentDungeonIndex]; } + static dungeon* GetDungeon(int I) { return Dungeon[I]; } + static int GetCurrentDungeonIndex() { return CurrentDungeonIndex; } + static void InitDungeons(); + static truth OnScreen(v2); + static void DoEvilDeed(int); + static void SaveWorldMap(cfestring& = SaveName(""), truth = true); + static worldmap* LoadWorldMap(cfestring& = SaveName("")); + static void UpdateCamera(); + static ulong CreateNewCharacterID(character*); + static ulong CreateNewItemID(item*); + static ulong CreateNewTrapID(entity*); + static team* GetTeam(int I) { return Team[I]; } + static int GetTeams() { return Teams; } + static void Hostility(team*, team*); + static void CreateTeams(); + static festring StringQuestion(cfestring&, col16, festring::sizetype, festring::sizetype, truth, stringkeyhandler = 0); + static long NumberQuestion(cfestring&, int, truth = false); + static ulong IncreaseLOSTick(); + static ulong GetLOSTick() { return LOSTick; } + static void SendLOSUpdateRequest() { LOSUpdateRequested = true; } + static void RemoveLOSUpdateRequest() { LOSUpdateRequested = false; } + static character* GetPetrus() { return Petrus; } + static void SetPetrus(character* What) { Petrus = What; } + static truth HandleQuitMessage(); + static int GetDirectionForVector(v2); + static cchar* GetVerbalPlayerAlignment(); + static void CreateGods(); + static int GetScreenXSize() { return 42; } + static int GetScreenYSize() { return 26; } + static v2 CalculateScreenCoordinates(v2); + static void BusyAnimation(); + static void BusyAnimation(bitmap*, truth); + static v2 PositionQuestion(cfestring&, v2, positionhandler = 0, positionkeyhandler = 0, truth = true); + static void LookHandler(v2); + static int AskForKeyPress(cfestring&); + static truth AnimationController(); + static gamescript* GetGameScript() { return GameScript; } + static void InitScript(); + static valuemap& GetGlobalValueMap() { return GlobalValueMap; } + static void InitGlobalValueMap(); + static void TextScreen(cfestring&, v2 = ZERO_V2, col16 = 0xFFFF, truth = true, truth = true, bitmapeditor = 0); + static void SetCursorPos(v2 What) { CursorPos = What; } + static truth DoZoom() { return Zoom; } + static void SetDoZoom(truth What) { Zoom = What; } + static int KeyQuestion(cfestring&, int, int, ...); + static v2 LookKeyHandler(v2, int); + static v2 NameKeyHandler(v2, int); + static void End(festring, truth = true, truth = true); + static int CalculateRoughDirection(v2); + static long ScrollBarQuestion(cfestring&, long, long, long, long, long, col16, col16, col16, void (*)(long) = 0); + static truth IsGenerating() { return Generating; } + static void SetIsGenerating(truth What) { Generating = What; } + static void CalculateNextDanger(); + static int Menu(bitmap*, v2, cfestring&, cfestring&, col16, cfestring& = "", cfestring& = ""); + static void InitDangerMap(); + static const dangermap& GetDangerMap(); + static truth TryTravel(int, int, int, truth = false, truth = true); + static truth LeaveArea(charactervector&, truth, truth); + static void EnterArea(charactervector&, int, int); + static int CompareLights(col24, col24); + static int CompareLightToInt(col24, col24); + static void CombineLights(col24&, col24); + static col24 CombineConstLights(col24, col24); + static truth IsDark(col24); + static void SetStandardListAttributes(felist&); + static double GetAveragePlayerArmStrengthExperience() { return AveragePlayerArmStrengthExperience; } + static double GetAveragePlayerLegStrengthExperience() { return AveragePlayerLegStrengthExperience; } + static double GetAveragePlayerDexterityExperience() { return AveragePlayerDexterityExperience; } + static double GetAveragePlayerAgilityExperience() { return AveragePlayerAgilityExperience; } + static void InitPlayerAttributeAverage(); + static void UpdatePlayerAttributeAverage(); + static void CallForAttention(v2, int); + static character* SearchCharacter(ulong); + static item* SearchItem(ulong); + static entity* SearchTrap(ulong); + static void AddCharacterID(character*, ulong); + static void RemoveCharacterID(ulong); + static void AddItemID(item*, ulong); + static void RemoveItemID(ulong); + static void UpdateItemID(item*, ulong); + static void AddTrapID(entity*, ulong); + static void RemoveTrapID(ulong); + static void UpdateTrapID(entity*, ulong); + static int GetStoryState() { return StoryState; } + static void SetStoryState(int What) { StoryState = What; } + static int GetMondedrPass() { return MondedrPass; } + static void SetMondedrPass(int What) { MondedrPass = What; } + static int GetRingOfThieves() { return RingOfThieves; } + static void SetRingOfThieves(int What) { RingOfThieves = What; } + static int GetMasamune() { return Masamune; } + static void SetMasamune(int What) { Masamune = What; } + static int GetMuramasa() { return Muramasa; } + static void SetMuramasa(int What) { Muramasa = What; } + static int GetLoricatusHammer() { return LoricatusHammer; } + static void SetLoricatusHammer(int What) { LoricatusHammer = What; } + static int GetOmmelBloodMission() { return OmmelBloodMission; } + static void SetOmmelBloodMission(int What) { OmmelBloodMission = What; } + + static int GetRegiiTalkState() { return RegiiTalkState; } + static void SetRegiiTalkState(int What) { RegiiTalkState = What; } + + static void SetIsInGetCommand(truth What) { InGetCommand = What; } + static truth IsInGetCommand() { return InGetCommand; } + static festring GetHomeDir(); + static festring GetSaveDir(); + static festring GetGameDir(); + static festring GetBoneDir(); + static truth PlayerWasHurtByExplosion() { return PlayerHurtByExplosion; } + static void SetPlayerWasHurtByExplosion(truth What) { PlayerHurtByExplosion = What; } + static void SetCurrentArea(area* What) { CurrentArea = What; } + static void SetCurrentLevel(level* What) { CurrentLevel = What; } + static void SetCurrentWSquareMap(wsquare*** What) { CurrentWSquareMap = What; } + static void SetCurrentLSquareMap(lsquare*** What) { CurrentLSquareMap = What; } + static festring& GetDefaultPolymorphTo() { return DefaultPolymorphTo; } + static festring& GetDefaultSummonMonster() { return DefaultSummonMonster; } + static festring& GetDefaultChangeMaterial() { return DefaultChangeMaterial; } + static festring& GetDefaultDetectMaterial() { return DefaultDetectMaterial; } + static void SignalDeath(ccharacter*, ccharacter*, festring); + static void DisplayMassacreLists(); + static void DisplayMassacreList(const massacremap&, cchar*, long); + static truth MassacreListsEmpty(); +#ifdef WIZARD + static void ActivateWizardMode() { WizardMode = true; } + static truth WizardModeIsActive() { return WizardMode; } + static void SeeWholeMap(); + static int GetSeeWholeMapCheatMode() { return SeeWholeMapCheatMode; } + static truth GoThroughWallsCheatIsActive() { return GoThroughWallsCheat; } + static void GoThroughWalls() { GoThroughWallsCheat = !GoThroughWallsCheat; } +#else + static truth WizardModeIsActive() { return false; } + static int GetSeeWholeMapCheatMode() { return 0; } + static truth GoThroughWallsCheatIsActive() { return false; } +#endif + static truth WizardModeIsReallyActive() { return WizardMode; } + static void CreateBone(); + static int GetQuestMonstersFound() { return QuestMonstersFound; } + static void SignalQuestMonsterFound() { ++QuestMonstersFound; } + static void SetQuestMonstersFound(int What) { QuestMonstersFound = What; } + static truth PrepareRandomBone(int); + static boneidmap& GetBoneItemIDMap() { return BoneItemIDMap; } + static boneidmap& GetBoneCharacterIDMap() { return BoneCharacterIDMap; } + static double CalculateAverageDanger(const charactervector&, character*); + static double CalculateAverageDangerOfAllNormalEnemies(); + static character* CreateGhost(); + static truth TooGreatDangerFound() { return TooGreatDangerFoundTruth; } + static void SetTooGreatDangerFound(truth What) { TooGreatDangerFoundTruth = What; } + static void CreateBusyAnimationCache(); + static long GetScore(); + static truth TweraifIsFree(); + static truth IsXMas(); + static int AddToItemDrawVector(const itemvector&); + static void ClearItemDrawVector(); + static void ItemEntryDrawer(bitmap*, v2, uint); + static int AddToCharacterDrawVector(character*); + static void ClearCharacterDrawVector(); + static void CharacterEntryDrawer(bitmap*, v2, uint); + static void GodEntryDrawer(bitmap*, v2, uint); + static itemvectorvector& GetItemDrawVector() { return ItemDrawVector; } + static charactervector& GetCharacterDrawVector() { return CharacterDrawVector; } + static truth IsSumoWrestling() { return SumoWrestling; } + static void SetIsSumoWrestling(truth What) { SumoWrestling = What; } + static truth AllowHostilities() { return !SumoWrestling; } + static truth AllBodyPartsVanish() { return SumoWrestling; } + static truth TryToEnterSumoArena(); + static truth TryToExitSumoArena(); + static truth EndSumoWrestling(int); + static character* GetSumo(); + static cfestring& GetPlayerName() { return PlayerName; } + static rain* ConstructGlobalRain(); + static void SetGlobalRainLiquid(liquid* What) { GlobalRainLiquid = What; } + static void SetGlobalRainSpeed(v2 What) { GlobalRainSpeed = What; } + static truth PlayerIsSumoChampion() { return PlayerSumoChampion; } + static truth PlayerIsSolicitusChampion() { return PlayerSolicitusChampion; } + static void MakePlayerSolicitusChampion() { PlayerSolicitusChampion = true; } + static v2 GetSunLightDirectionVector(); + static int CalculateMinimumEmitationRadius(col24); + static ulong IncreaseSquarePartEmitationTicks(); + static cint GetLargeMoveDirection(int I) { return LargeMoveDirection[I]; } + static void Wish(character*, cchar*, cchar*); + static festring DefaultQuestion(festring, festring&, stringkeyhandler = 0); + static void GetTime(ivantime&); + static long GetTurn() { return Turn; } + static void IncreaseTurn() { ++Turn; } + static int GetTotalMinutes() { return Tick * 60 / 2000; } + static truth PolymorphControlKeyHandler(int, festring&); + static ulong* GetEquipmentMemory() { return EquipmentMemory; } + static truth PlayerIsRunning(); + static void SetPlayerIsRunning(truth What) { PlayerRunning = What; } + static truth FillPetVector(cchar*); + static truth CommandQuestion(); + static void NameQuestion(); + static v2 CommandKeyHandler(v2, int); + static void CommandScreen(cfestring&, ulong, ulong, ulong&, ulong&); + static truth CommandAll(); + static double GetDangerFound() { return DangerFound; } + static void SetDangerFound(double What) { DangerFound = What; } + static col16 GetAttributeColor(int); + static void UpdateAttributeMemory(); + static void InitAttributeMemory(); + static void TeleportHandler(v2); + static void PetHandler(v2); + static truth SelectPet(int); + static double GetGameSituationDanger(); + static olterrain* GetMonsterPortal() { return MonsterPortal; } + static void SetMonsterPortal(olterrain* What) { MonsterPortal = What; } + static truth GetCausePanicFlag() { return CausePanicFlag; } + static void SetCausePanicFlag(truth What) { CausePanicFlag = What; } + static long GetTimeSpent(); + static void AddSpecialCursor(v2, int); + static void RemoveSpecialCursors(); + static void LearnAbout(god*); + static truth PlayerKnowsAllGods(); + static void AdjustRelationsToAllGods(int); + static void SetRelationsToAllGods(int); + static void ShowDeathSmiley(bitmap*, truth); + static void SetEnterImage(cbitmap* What) { EnterImage = What; } + static void SetEnterTextDisplacement(v2 What) + { + EnterTextDisplacement = What; + } + private: + static void UpdateCameraCoordinate(int&, int, int, int); + static cchar* const Alignment[]; + static god** God; + static int CurrentLevelIndex; + static int CurrentDungeonIndex; + static cint MoveNormalCommandKey[]; + static cint MoveAbnormalCommandKey[]; + static cv2 MoveVector[]; + static cv2 RelativeMoveVector[]; + static cv2 BasicMoveVector[]; + static cv2 LargeMoveVector[]; + static uchar*** LuxTable; + static truth Running; + static character* Player; + static v2 Camera; + static ulong Tick; + static festring AutoSaveFileName; + static truth InWilderness; + static worldmap* WorldMap; + static area* AreaInLoad; + static square* SquareInLoad; + static dungeon** Dungeon; + static ulong NextCharacterID; + static ulong NextItemID; + static ulong NextTrapID; + static team** Team; + static ulong LOSTick; + static truth LOSUpdateRequested; + static character* Petrus; + static truth Loading; + static truth JumpToPlayerBe; + static gamescript* GameScript; + static valuemap GlobalValueMap; + static v2 CursorPos; + static truth Zoom; + static truth Generating; + static dangermap DangerMap; + static int NextDangerIDType; + static int NextDangerIDConfigIndex; + static double AveragePlayerArmStrengthExperience; + static double AveragePlayerLegStrengthExperience; + static double AveragePlayerDexterityExperience; + static double AveragePlayerAgilityExperience; + static characteridmap CharacterIDMap; + static itemidmap ItemIDMap; + static trapidmap TrapIDMap; + static int Teams; + static int Dungeons; + static int StoryState; + static int MondedrPass; + static int RingOfThieves; + static int Masamune; + static int Muramasa; + static int LoricatusHammer; + static int OmmelBloodMission; + static int RegiiTalkState; + static truth InGetCommand; + static truth PlayerHurtByExplosion; + static area* CurrentArea; + static level* CurrentLevel; + static wsquare*** CurrentWSquareMap; + static lsquare*** CurrentLSquareMap; + static festring DefaultPolymorphTo; + static festring DefaultSummonMonster; + static festring DefaultWish; + static festring DefaultChangeMaterial; + static festring DefaultDetectMaterial; + static massacremap PlayerMassacreMap; + static massacremap PetMassacreMap; + static massacremap MiscMassacreMap; + static long PlayerMassacreAmount; + static long PetMassacreAmount; + static long MiscMassacreAmount; + static truth WizardMode; + static int SeeWholeMapCheatMode; + static truth GoThroughWallsCheat; + static int QuestMonstersFound; + static boneidmap BoneItemIDMap; + static boneidmap BoneCharacterIDMap; + static truth TooGreatDangerFoundTruth; + static bitmap* BusyAnimationCache[32]; + static itemvectorvector ItemDrawVector; + static charactervector CharacterDrawVector; + static truth SumoWrestling; + static festring PlayerName; + static liquid* GlobalRainLiquid; + static v2 GlobalRainSpeed; + static long GlobalRainTimeModifier; + static truth PlayerSumoChampion; + static truth PlayerSolicitusChampion; + static ulong SquarePartEmitationTick; + static cint LargeMoveDirection[]; + static long Turn; + static ulong EquipmentMemory[MAX_EQUIPMENT_SLOTS]; + static truth PlayerRunning; + static character* LastPetUnderCursor; + static charactervector PetVector; + static double DangerFound; + static int OldAttribute[ATTRIBUTES]; + static int NewAttribute[ATTRIBUTES]; + static int LastAttributeChangeTick[ATTRIBUTES]; + static int NecroCounter; + static int CursorData; + static olterrain* MonsterPortal; + static truth CausePanicFlag; + static time_t TimePlayedBeforeLastLoad; + static time_t LastLoad; + static time_t GameBegan; + static std::vector SpecialCursorPos; + static std::vector SpecialCursorData; + static truth PlayerHasReceivedAllGodsKnownBonus; + static cbitmap* EnterImage; + static v2 EnterTextDisplacement; +}; + +inline void game::CombineLights(col24& L1, col24 L2) +{ + if(L2) + { + if(L1) + { + long Red1 = L1 & 0xFF0000, Red2 = L2 & 0xFF0000; + long New = Red1 >= Red2 ? Red1 : Red2; + long Green1 = L1 & 0xFF00, Green2 = L2 & 0xFF00; + New |= Green1 >= Green2 ? Green1 : Green2; + long Blue1 = L1 & 0xFF, Blue2 = L2 & 0xFF; + L1 = Blue1 >= Blue2 ? New | Blue1 : New | Blue2; + } + else + L1 = L2; + } +} + +inline col24 game::CombineConstLights(col24 L1, col24 L2) +{ + CombineLights(L1, L2); + return L1; +} + +inline truth game::IsDark(col24 Light) +{ + return !Light + || ((Light & 0xFF0000) < (LIGHT_BORDER << 16) + && (Light & 0x00FF00) < (LIGHT_BORDER << 8) + && (Light & 0x0000FF) < LIGHT_BORDER); +} + +inline int game::CompareLights(col24 L1, col24 L2) +{ + if(L1) + { + if((L1 & 0xFF0000) > (L2 & 0xFF0000) + || (L1 & 0x00FF00) > (L2 & 0x00FF00) + || (L1 & 0x0000FF) > (L2 & 0x0000FF)) + return 1; + else if((L1 & 0xFF0000) == (L2 & 0xFF0000) + || (L1 & 0x00FF00) == (L2 & 0x00FF00) + || (L1 & 0x0000FF) == (L2 & 0x0000FF)) + return 0; + else + return -1; + } + else + return -int(!!L2); +} + +inline v2 game::CalculateScreenCoordinates(v2 Pos) +{ + return v2((Pos.X - Camera.X + 1) << 4, (Pos.Y - Camera.Y + 2) << 4); +} + +#endif diff --git a/Main/Include/gear.h b/Main/Include/gear.h new file mode 100644 index 0000000..fcea222 --- /dev/null +++ b/Main/Include/gear.h @@ -0,0 +1,594 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __GEAR_H__ +#define __GEAR_H__ + +#include "item.h" + +ITEM(meleeweapon, item) +{ + public: + meleeweapon() { } + meleeweapon(const meleeweapon&); + virtual ~meleeweapon(); + virtual truth HitEffect(character*, character*, v2, int, int, truth); + virtual void DipInto(liquid*, character*); + virtual long GetPrice() const; + virtual truth IsDippable(ccharacter*) const; + virtual material* GetSecondaryMaterial() const { return SecondaryMaterial; } + virtual void SetSecondaryMaterial(material*, int = 0); + virtual void ChangeSecondaryMaterial(material*, int = 0); + void InitMaterials(material*, material*, truth = true); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual int GetMaterials() const { return 2; } + virtual void AddInventoryEntry(ccharacter*, festring&, int, truth) const; + virtual void SignalSpoil(material*); + virtual void Be(); + virtual truth IsWeapon(ccharacter*) const { return true; } + virtual int GetEnchantment() const { return Enchantment; } + virtual void SetEnchantment(int); + virtual void EditEnchantment(int); + virtual int GetStrengthValue() const; + virtual truth IsFixableBySmith(ccharacter*) const { return IsBroken() || IsRusted(); } + virtual truth IsFixableByTailor(ccharacter*) const { return IsBroken(); } + virtual double GetTHVBonus() const; + virtual double GetDamageBonus() const; + virtual int GetSpoilLevel() const; + virtual material* GetMaterial(int) const; + virtual void TryToRust(long); + virtual material* GetConsumeMaterial(ccharacter*, materialpredicate = TrueMaterialPredicate) const; + virtual pixelpredicate GetFluidPixelAllowedPredicate() const; + virtual material* RemoveMaterial(material*); + material* RemoveMainMaterial(); + material* RemoveSecondaryMaterial(); + virtual v2 GetWieldedBitmapPos(int) const; + virtual void CalculateEmitation(); + virtual void InitMaterials(const materialscript*, const materialscript*, truth); + virtual item* Fix(); + virtual void CalculateEnchantment(); + virtual truth AllowFluids() const { return true; } + virtual int GetSparkleFlags() const; + protected: + virtual long GetMaterialPrice() const; + virtual truth CalculateHasBe() const; + virtual void PostConstruct(); + virtual void AddPostFix(festring&, int) const; + virtual void GenerateMaterials(); + virtual col16 GetMaterialColorB(int) const; + virtual col16 GetMaterialColorC(int) const; + virtual alpha GetAlphaB(int) const; + virtual int GetRustDataB() const; + virtual col16 GetDripColor() const; + virtual truth AllowRegularColors() const; + material* SecondaryMaterial; + int Enchantment; +}; + +ITEM(justifier, meleeweapon) +{ + public: + virtual truth AllowAlphaEverywhere() const { return true; } + protected: + virtual int GetClassAnimationFrames() const { return 32; } + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; +}; + +ITEM(neercseulb, meleeweapon) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); + virtual truth AllowAlphaEverywhere() const { return true; } + protected: + virtual int GetClassAnimationFrames() const { return 32; } + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; +}; + +ITEM(pickaxe, meleeweapon) +{ + public: + virtual truth Apply(character*); + virtual truth IsAppliable(ccharacter*) const; +}; + +ITEM(whip, meleeweapon) +{ + public: + virtual truth IsWhip() const { return true; } + protected: + virtual int GetFormModifier() const; +}; + +ITEM(flamingsword, meleeweapon) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); + virtual int GetSpecialFlags() const; +}; + +ITEM(mjolak, meleeweapon) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); +}; + +ITEM(vacuumblade, meleeweapon) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); +}; + +ITEM(vermis, meleeweapon) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); +}; + +ITEM(turox, meleeweapon) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); +}; + +//entropyaxe +ITEM(eptyron, meleeweapon) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); + virtual void BlockEffect(character*, character*, item*, int Type); + virtual truth AllowAlphaEverywhere() const { return true; } + //virtual void Break(character*, int); //should it have a break effect?? + protected: + virtual int GetClassAnimationFrames() const; + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; +}; + +ITEM(taiaha, meleeweapon) +{ + public: + //virtual truth HitEffect(character*, character*, v2, int, int, truth); + //virtual void BlockEffect(character*, character*, item*, int Type); + virtual truth AllowAlphaEverywhere() const { return true; } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void ChargeFully(character*) { TimesUsed = 0; } + virtual truth IsAppliable(ccharacter*) const { return false; } + virtual truth IsZappable(ccharacter*) const { return true; } + virtual truth IsChargeable(ccharacter*) const { return true; } + virtual truth ReceiveDamage(character*, int, int, int); + virtual truth Zap(character*, v2, int); + virtual void AddInventoryEntry(ccharacter*, festring&, int, truth) const; //this? + //virtual long GetPrice() const; + virtual truth IsExplosive() const { return true; } + protected: + virtual int GetClassAnimationFrames() const; + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; + virtual void PostConstruct(); + void BreakEffect(character*, cfestring&); + //ulong GetSpecialParameters() const; + int Charges; + int TimesUsed; +}; + +ITEM(whipofthievery, whip) +{ + public: + virtual long GetPrice() const; + virtual truth HitEffect(character*, character*, v2, int, int, truth); + protected: + virtual truth CleptiaHelps(ccharacter*, ccharacter*) const; +}; + +ITEM(gleipnir, whipofthievery) +{ +}; + +ITEM(gorovitsweapon, meleeweapon) +{ + public: + virtual truth IsGorovitsFamilyRelic() const { return true; } + virtual truth AllowAlphaEverywhere() const { return true; } + protected: + virtual int GetClassAnimationFrames() const { return 32; } + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; +}; + +ITEM(thunderhammer, meleeweapon) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); + virtual int GetSpecialFlags() const; + virtual truth ReceiveDamage(character*, int, int, int); +}; + +ITEM(saalthul, meleeweapon) +{ +}; + +ITEM(armor, item) +{ + public: + virtual long GetPrice() const; + virtual void AddInventoryEntry(ccharacter*, festring&, int, truth) const; + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth IsArmor(ccharacter*) const { return true; } + virtual int GetEnchantment() const { return Enchantment; } + virtual void SetEnchantment(int); + virtual void EditEnchantment(int); + virtual int GetStrengthValue() const; + virtual truth CanBePiledWith(citem*, ccharacter*) const; + virtual int GetInElasticityPenalty(int) const; + virtual int GetCarryingBonus() const; + virtual truth IsFixableBySmith(ccharacter*) const { return IsBroken() || IsRusted(); } + virtual truth IsFixableByTailor(ccharacter*) const { return IsBroken(); } + virtual truth AllowFluids() const { return true; } + virtual void CalculateEnchantment(); + virtual double GetTHVBonus() const; + virtual double GetDamageBonus() const; + protected: + virtual void AddPostFix(festring&, int) const; + virtual void PostConstruct(); + int Enchantment; +}; + +ITEM(bodyarmor, armor) +{ + public: + virtual long GetPrice() const; + virtual truth IsBodyArmor(ccharacter*) const { return true; } + virtual truth IsInCorrectSlot(int) const; + protected: + virtual cchar* GetBreakVerb() const; + virtual truth AddAdjective(festring&, truth) const; + virtual cfestring& GetNameSingular() const; +}; + +ITEM(goldeneagleshirt, bodyarmor) +{ + public: + virtual truth IsGoldenEagleShirt() const { return true; } + virtual truth IsConsumable() const { return false; } + virtual truth AllowAlphaEverywhere() const { return true; } + protected: + virtual int GetClassAnimationFrames() const { return 32; } + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; +}; + +ITEM(shield, armor) +{ + public: + virtual long GetPrice() const; + virtual truth IsShield(ccharacter*) const { return true; } + virtual void AddInventoryEntry(ccharacter*, festring&, int, truth) const; +}; + +ITEM(cloak, armor) +{ + public: + virtual long GetPrice() const; + virtual truth IsCloak(ccharacter*) const { return true; } + virtual truth IsInCorrectSlot(int) const; + virtual truth ReceiveDamage(character*, int, int, int); + protected: + virtual int GetSpecialFlags() const; + virtual cchar* GetBreakVerb() const; + virtual truth AddAdjective(festring&, truth) const; + virtual col16 GetMaterialColorB(int) const; +}; + +ITEM(boot, armor) +{ + public: + virtual long GetPrice() const; + virtual truth IsBoot(ccharacter*) const { return true; } + virtual truth IsInCorrectSlot(int) const; +}; + +ITEM(gauntlet, armor) +{ + public: + virtual long GetPrice() const; + virtual truth IsGauntlet(ccharacter*) const { return true; } + virtual truth IsInCorrectSlot(int) const; +}; + +ITEM(belt, armor) +{ + public: + virtual long GetPrice() const; + virtual truth IsBelt(ccharacter*) const { return true; } + virtual int GetFormModifier() const; + virtual truth IsInCorrectSlot(int) const; + virtual col16 GetMaterialColorB(int Frame) const { return GetMaterialColorA(Frame); } +}; + +typedef lockable lockablebelt; + +ITEM(ring, item) +{ + public: + virtual truth IsRing(ccharacter*) const { return true; } + virtual truth IsInCorrectSlot(int) const; + virtual truth IsLuxuryItem(ccharacter*) const { return true; } + protected: + virtual col16 GetMaterialColorB(int) const; +}; + +ITEM(ringofthieves, ring) +{ + public: + virtual truth IsRingOfThieves() const { return true; } +}; + +ITEM(amulet, item) +{ + public: + virtual truth IsAmulet(ccharacter*) const { return true; } + virtual truth IsInCorrectSlot(int) const; + virtual truth IsLuxuryItem(ccharacter*) const { return true; } + protected: + virtual col16 GetMaterialColorB(int) const; +}; +/* +ITEM(amuletofvanity, amulet) // to make amulet enchantable, these are the possible extras below +{ + public: + //virtual long GetPrice() const; + virtual void AddInventoryEntry(ccharacter*, festring&, int, truth) const; + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + //virtual truth IsArmor(ccharacter*) const { return true; } + virtual int GetEnchantment() const { return Enchantment; } + virtual void SetEnchantment(int); + virtual void EditEnchantment(int); + //virtual int GetStrengthValue() const; + //virtual truth CanBePiledWith(citem*, ccharacter*) const; + //virtual int GetInElasticityPenalty(int) const; + //virtual int GetCarryingBonus() const; + //virtual truth IsFixableBySmith(ccharacter*) const { return IsBroken() || IsRusted(); } + //virtual truth IsFixableByTailor(ccharacter*) const { return IsBroken(); } + //virtual truth AllowFluids() const { return true; } + virtual void CalculateEnchantment(); + //virtual double GetTHVBonus() const; + //virtual double GetDamageBonus() const; + protected: + virtual void AddPostFix(festring&, int) const; //? + virtual void PostConstruct(); //? + int Enchantment; +}; +*/ +/* +ITEM(collar, item) +{ + public: + virtual truth IsAmulet(ccharacter*) const { return true; } + virtual truth IsInCorrectSlot(int) const; + virtual truth IsLuxuryItem(ccharacter*) const { return true; } + protected: + virtual col16 GetMaterialColorB(int) const; +};*/ + +ITEM(helmet, armor) +{ + public: + virtual truth IsGorovitsFamilyRelic() const; + virtual long GetPrice() const; + virtual truth IsHelmet(ccharacter*) const { return true; } + virtual truth IsInCorrectSlot(int) const; + protected: + virtual col16 GetMaterialColorB(int) const; + virtual col16 GetMaterialColorC(int) const; +}; + +ITEM(chameleonwhip, whip) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); + protected: + virtual truth ScabiesHelps(ccharacter*, ccharacter*) const; +}; + +ITEM(wondersmellstaff, meleeweapon) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); + virtual truth AllowAlphaEverywhere() const { return true; } + virtual void Break(character*, int); + protected: + virtual int GetClassAnimationFrames() const; + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; +}; + +ITEM(decosadshirt, bodyarmor) +{ + public: + decosadshirt(); + virtual void Be(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + ulong GetEquippedTicks() { return EquippedTicks; } + void SetEquippedTicks(ulong What) { EquippedTicks = What; } + virtual truth IsDecosAdShirt(ccharacter*) const { return true; } + protected: + virtual truth CalculateHasBe() const { return true; } + ulong EquippedTicks; +}; + +ITEM(weepblade, meleeweapon) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); +}; + +ITEM(daggerofvenom, meleeweapon) +{ + public: + daggerofvenom() { Enable(); } + virtual void Be(); +protected: + virtual truth CalculateHasBe() const { return true; } +}; + + +ITEM(acidshield, shield) +{ + public: + virtual void BlockEffect(character*, character*, item*, int Type); +}; + +ITEM(chastitybelt, lockablebelt) +{ + public: + virtual int GetFormModifier() const { return item::GetFormModifier(); } +}; + +ITEM(sunsword, meleeweapon) +{ +}; + +ITEM(rescuethepeasant, meleeweapon) +{ +}; + +ITEM(vormav, meleeweapon) +{ +}; + +ITEM(cronus, meleeweapon) +{ +}; + +ITEM(defender, meleeweapon) +{ +}; + +ITEM(aethier, meleeweapon) +{ +}; + +ITEM(aegis, shield) +{ +}; + +ITEM(phoenixshield, shield) +{ +}; + +ITEM(belderiver, meleeweapon) +{ + public: + virtual truth AllowAlphaEverywhere() const { return true; } + protected: + virtual int GetClassAnimationFrames() const { return 32; } + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; +}; + +ITEM(loricatushammer, meleeweapon) +{ + public: + virtual truth AllowAlphaEverywhere() const { return true; } + protected: + virtual int GetClassAnimationFrames() const { return 32; } + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; +}; + +ITEM(goldenjaguarshirt, bodyarmor) +{ + public: + virtual truth IsConsumable() const { return false; } + virtual truth AllowAlphaEverywhere() const { return true; } + protected: + virtual int GetClassAnimationFrames() const { return 32; } + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; +}; + +ITEM(demonhead, meleeweapon) +{ + public: + virtual truth AllowAlphaEverywhere() const { return true; } + protected: + virtual int GetClassAnimationFrames() const { return 32; } + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; +}; + +ITEM(smite, meleeweapon) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); + virtual int GetSpecialFlags() const; + virtual truth ReceiveDamage(character*, int, int, int); +}; + +ITEM(muramasa, meleeweapon) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); + virtual truth AllowAlphaEverywhere() const { return true; } + protected: + virtual int GetClassAnimationFrames() const { return 32; } + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; +}; + +ITEM(masamune, meleeweapon) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); + virtual truth AllowAlphaEverywhere() const { return true; } + protected: + virtual int GetClassAnimationFrames() const { return 32; } + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; +}; + +ITEM(zulfiqar, meleeweapon) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); +}; + +ITEM(tipswordofpenetration, meleeweapon) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); +}; + +ITEM(maingauche, meleeweapon) +{ +}; + +ITEM(lynslag, meleeweapon) +{ +}; + +ITEM(thievesgirdle, belt) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); + protected: + virtual truth CleptiaHelps(ccharacter*, ccharacter*) const; +}; + +#endif diff --git a/Main/Include/god.h b/Main/Include/god.h new file mode 100644 index 0000000..663d140 --- /dev/null +++ b/Main/Include/god.h @@ -0,0 +1,106 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __GOD_H__ +#define __GOD_H__ + +#include "ivandef.h" + +class outputfile; +class inputfile; +class god; +class liquid; +class team; +struct materialdatabase; + +typedef god* (*godspawner)(); + +class godprototype +{ + public: + godprototype(godspawner, cchar*); + god* Spawn() const { return Spawner(); } + god* SpawnAndLoad(inputfile&) const; + cchar* GetClassID() const { return ClassID; } + int GetIndex() const { return Index; } + private: + int Index; + godspawner Spawner; + cchar* ClassID; +}; + +class god +{ + public: + typedef godprototype prototype; + god(); + virtual void Pray(); + virtual cchar* GetName() const = 0; + virtual cchar* GetDescription() const = 0; + cchar* GetPersonalPronoun() const; + cchar* GetObjectPronoun() const; + virtual int GetAlignment() const = 0; + festring GetCompleteDescription() const; + void ApplyDivineTick(); + void AdjustRelation(god*, int, truth); + void AdjustRelation(int); + void AdjustTimer(long); + void Save(outputfile&) const; + void Load(inputfile&); + void SetRelation(int Value) { Relation = Value; } + void SetTimer(long Value) { Timer = Value; } + truth ReceiveOffer(item*); + virtual int GetBasicAlignment() const; + int GetRelation() const { return Relation; } + void PrintRelation() const; + void SetIsKnown(truth What) { Known = What; } + truth IsKnown() const { return Known; } + void PlayerKickedAltar() { AdjustRelation(-100); } + void PlayerKickedFriendsAltar() { AdjustRelation(-50); } + virtual truth PlayerVomitedOnAltar(liquid*); + character* CreateAngel(team*, int = 0); + virtual col16 GetColor() const = 0; + virtual col16 GetEliteColor() const = 0; + virtual const prototype* GetProtoType() const = 0; + int GetType() const { return GetProtoType()->GetIndex(); } + virtual truth ForceGiveBodyPart() const { return false; } + virtual truth HealRegeneratingBodyParts() const { return false; } + virtual truth LikesMaterial(const materialdatabase*, ccharacter*) const; + truth TryToAttachBodyPart(character*); + truth TryToHardenBodyPart(character*); + virtual truth MutatesBodyParts() const { return false; } + virtual int GetSex() const = 0; + void SignalRandomAltarGeneration(const std::vector&); + virtual truth LikesVomit() const { return false; } + protected: + virtual void PrayGoodEffect() = 0; + virtual void PrayBadEffect() = 0; + int Relation, LastPray; + long Timer; + truth Known; +}; + +#ifdef __FILE_OF_STATIC_GOD_PROTOTYPE_DEFINITIONS__ +#define GOD_PROTO(name)\ +template<> const godprototype\ + name##sysbase::ProtoType((godspawner)(&name##sysbase::Spawn), #name); +#else +#define GOD_PROTO(name) +#endif + +#define GOD(name, base)\ +class name;\ +typedef simplesysbase name##sysbase;\ +GOD_PROTO(name)\ +class name : public name##sysbase + +#endif diff --git a/Main/Include/gods.h b/Main/Include/gods.h new file mode 100644 index 0000000..0f403f3 --- /dev/null +++ b/Main/Include/gods.h @@ -0,0 +1,269 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __GODS_H__ +#define __GODS_H__ + +#include "god.h" + +GOD(valpurus, god) +{ + public: + virtual cchar* GetName() const; + virtual cchar* GetDescription() const; + virtual int GetAlignment() const; + virtual void Pray(); + virtual int GetBasicAlignment() const; + virtual col16 GetColor() const; + virtual col16 GetEliteColor() const; + virtual int GetSex() const { return MALE; } + protected: + virtual void PrayGoodEffect(); + virtual void PrayBadEffect(); +}; + +GOD(legifer, god) +{ + public: + virtual cchar* GetName() const; + virtual cchar* GetDescription() const; + virtual int GetAlignment() const; + virtual int GetBasicAlignment() const; + virtual col16 GetColor() const; + virtual col16 GetEliteColor() const; + virtual int GetSex() const { return MALE; } + protected: + virtual void PrayGoodEffect(); + virtual void PrayBadEffect(); +}; + +GOD(atavus, god) +{ + public: + virtual cchar* GetName() const; + virtual cchar* GetDescription() const; + virtual int GetAlignment() const; + virtual int GetBasicAlignment() const; + virtual col16 GetColor() const; + virtual col16 GetEliteColor() const; + virtual truth LikesMaterial(const materialdatabase*, ccharacter*) const; + virtual int GetSex() const { return MALE; } + protected: + virtual void PrayGoodEffect(); + virtual void PrayBadEffect(); +}; + +GOD(dulcis, god) +{ + public: + virtual cchar* GetName() const; + virtual cchar* GetDescription() const; + virtual int GetAlignment() const; + virtual int GetBasicAlignment() const; + virtual col16 GetColor() const; + virtual col16 GetEliteColor() const; + virtual int GetSex() const { return FEMALE; } + protected: + virtual void PrayGoodEffect(); + virtual void PrayBadEffect(); +}; + +GOD(seges, god) +{ + public: + virtual cchar* GetName() const; + virtual cchar* GetDescription() const; + virtual int GetAlignment() const; + virtual int GetBasicAlignment() const; + virtual col16 GetColor() const; + virtual col16 GetEliteColor() const; + virtual truth ForceGiveBodyPart() const { return true; } + virtual truth HealRegeneratingBodyParts() const { return true; } + virtual truth LikesMaterial(const materialdatabase*, ccharacter*) const; + virtual int GetSex() const { return FEMALE; } + protected: + virtual void PrayGoodEffect(); + virtual void PrayBadEffect(); +}; + +GOD(sophos, god) +{ + public: + virtual cchar* GetName() const; + virtual cchar* GetDescription() const; + virtual int GetAlignment() const; + virtual int GetBasicAlignment() const; + virtual col16 GetColor() const; + virtual col16 GetEliteColor() const; + virtual int GetSex() const { return MALE; } + protected: + virtual void PrayGoodEffect(); + virtual void PrayBadEffect(); +}; + +GOD(silva, god) +{ + public: + virtual cchar* GetName() const; + virtual cchar* GetDescription() const; + virtual int GetAlignment() const; + virtual int GetBasicAlignment() const; + virtual col16 GetColor() const; + virtual col16 GetEliteColor() const; + virtual int GetSex() const { return FEMALE; } + protected: + virtual void PrayGoodEffect(); + virtual void PrayBadEffect(); +}; + +GOD(loricatus, god) +{ + public: + virtual cchar* GetName() const; + virtual cchar* GetDescription() const; + virtual int GetAlignment() const; + virtual int GetBasicAlignment() const; + virtual col16 GetColor() const; + virtual col16 GetEliteColor() const; + virtual int GetSex() const { return MALE; } + protected: + virtual void PrayGoodEffect(); + virtual void PrayBadEffect(); +}; + +GOD(mellis, god) +{ + public: + virtual cchar* GetName() const; + virtual cchar* GetDescription() const; + virtual int GetAlignment() const; + virtual int GetBasicAlignment() const; + virtual col16 GetColor() const; + virtual col16 GetEliteColor() const; + virtual int GetSex() const { return MALE; } + protected: + virtual void PrayGoodEffect(); + virtual void PrayBadEffect(); +}; + +GOD(cleptia, god) +{ + public: + virtual cchar* GetName() const; + virtual cchar* GetDescription() const; + virtual int GetAlignment() const; + virtual int GetBasicAlignment() const; + virtual col16 GetColor() const; + virtual col16 GetEliteColor() const; + virtual int GetSex() const { return FEMALE; } + protected: + virtual void PrayGoodEffect(); + virtual void PrayBadEffect(); +}; + +GOD(nefas, god) +{ + public: + virtual cchar* GetName() const; + virtual cchar* GetDescription() const; + virtual int GetAlignment() const; + virtual int GetBasicAlignment() const; + virtual col16 GetColor() const; + virtual col16 GetEliteColor() const; + virtual int GetSex() const { return FEMALE; } + protected: + virtual void PrayGoodEffect(); + virtual void PrayBadEffect(); +}; + +GOD(scabies, god) +{ + public: + virtual cchar* GetName() const; + virtual cchar* GetDescription() const; + virtual int GetAlignment() const; + virtual int GetBasicAlignment() const; + virtual truth PlayerVomitedOnAltar(liquid*); + virtual col16 GetColor() const; + virtual col16 GetEliteColor() const; + virtual truth LikesMaterial(const materialdatabase*, ccharacter*) const; + virtual truth MutatesBodyParts() const { return true; } + virtual int GetSex() const { return FEMALE; } + virtual truth LikesVomit() const { return true; } + protected: + virtual void PrayGoodEffect(); + virtual void PrayBadEffect(); +}; + +GOD(infuscor, god) +{ + public: + virtual cchar* GetName() const; + virtual cchar* GetDescription() const; + virtual int GetAlignment() const; + virtual int GetBasicAlignment() const; + virtual col16 GetColor() const; + virtual col16 GetEliteColor() const; + virtual int GetSex() const { return FEMALE; } + protected: + virtual void PrayGoodEffect(); + virtual void PrayBadEffect(); +}; + +GOD(cruentus, god) +{ + public: + virtual cchar* GetName() const; + virtual cchar* GetDescription() const; + virtual int GetAlignment() const; + virtual int GetBasicAlignment() const; + virtual col16 GetColor() const; + virtual col16 GetEliteColor() const; + virtual int GetSex() const { return MALE; } + protected: + virtual void PrayGoodEffect(); + virtual void PrayBadEffect(); +}; + +GOD(mortifer, god) +{ + public: + virtual cchar* GetName() const; + virtual cchar* GetDescription() const; + virtual int GetAlignment() const; + virtual void Pray(); + virtual int GetBasicAlignment() const; + virtual col16 GetColor() const; + virtual col16 GetEliteColor() const; + virtual int GetSex() const { return MALE; } + protected: + virtual void PrayGoodEffect(); + virtual void PrayBadEffect(); +}; +/* +GOD(solicitu, god) +{ + public: + virtual cchar* GetName() const; + virtual cchar* GetDescription() const; + virtual int GetAlignment() const; + virtual void Pray(); + virtual int GetBasicAlignment() const; + virtual col16 GetColor() const; + virtual col16 GetEliteColor() const; + virtual int GetSex() const { return MALE; } + protected: + virtual void PrayGoodEffect(); + virtual void PrayBadEffect(); +}; +*/ +#endif diff --git a/Main/Include/human.h b/Main/Include/human.h new file mode 100644 index 0000000..bc9ed06 --- /dev/null +++ b/Main/Include/human.h @@ -0,0 +1,884 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __HUMAN_H__ +#define __HUMAN_H__ + +#include "char.h" + +CHARACTER(humanoid, character) +{ + public: + friend class rightarm; + friend class leftarm; + humanoid() : CurrentRightSWeaponSkill(0), CurrentLeftSWeaponSkill(0) { } + humanoid(const humanoid&); + virtual ~humanoid(); + virtual truth CanWield() const; + virtual truth Hit(character*, v2, int, int = 0); + virtual int GetSize() const; + head* GetHead() const { return static_cast(*BodyPartSlot[HEAD_INDEX]); } + arm* GetRightArm() const { return static_cast(*BodyPartSlot[RIGHT_ARM_INDEX]); } + arm* GetLeftArm() const { return static_cast(*BodyPartSlot[LEFT_ARM_INDEX]); } + groin* GetGroin() const { return static_cast(*BodyPartSlot[GROIN_INDEX]); } + leg* GetRightLeg() const { return static_cast(*BodyPartSlot[RIGHT_LEG_INDEX]); } + leg* GetLeftLeg() const { return static_cast(*BodyPartSlot[LEFT_LEG_INDEX]); } + item* GetHelmet() const { return GetHead() ? GetHead()->GetHelmet() : 0; } + item* GetAmulet() const { return GetHead() ? GetHead()->GetAmulet() : 0; } + item* GetCloak() const { return GetHumanoidTorso() ? GetHumanoidTorso()->GetCloak() : 0; } + item* GetBodyArmor() const { return GetHumanoidTorso() ? GetHumanoidTorso()->GetBodyArmor() : 0; } + item* GetBelt() const { return GetHumanoidTorso() ? GetHumanoidTorso()->GetBelt() : 0; } + item* GetRightWielded() const { return GetRightArm() ? GetRightArm()->GetWielded() : 0; } + item* GetLeftWielded() const { return GetLeftArm() ? GetLeftArm()->GetWielded() : 0; } + item* GetRightRing() const { return GetRightArm() ? GetRightArm()->GetRing() : 0; } + item* GetLeftRing() const { return GetLeftArm() ? GetLeftArm()->GetRing() : 0; } + item* GetRightGauntlet() const { return GetRightArm() ? GetRightArm()->GetGauntlet() : 0; } + item* GetLeftGauntlet() const { return GetLeftArm() ? GetLeftArm()->GetGauntlet() : 0; } + item* GetRightBoot() const { return GetRightLeg() ? GetRightLeg()->GetBoot() : 0; } + item* GetLeftBoot() const { return GetLeftLeg() ? GetLeftLeg()->GetBoot() : 0; } + void SetHelmet(item* What) { GetHead()->SetHelmet(What); } + void SetAmulet(item* What) { GetHead()->SetAmulet(What); } + void SetCloak(item* What) { GetHumanoidTorso()->SetCloak(What); } + void SetBodyArmor(item* What) { GetHumanoidTorso()->SetBodyArmor(What); } + void SetBelt(item* What) { GetHumanoidTorso()->SetBelt(What); } + void SetRightWielded(item* What) { GetRightArm()->SetWielded(What); } + void SetLeftWielded(item* What) { GetLeftArm()->SetWielded(What); } + void SetRightRing(item* What) { GetRightArm()->SetRing(What); } + void SetLeftRing(item* What) { GetLeftArm()->SetRing(What); } + void SetRightGauntlet(item* What) { GetRightArm()->SetGauntlet(What); } + void SetLeftGauntlet(item* What) { GetLeftArm()->SetGauntlet(What); } + void SetRightBoot(item* What) { GetRightLeg()->SetBoot(What); } + void SetLeftBoot(item* What) { GetLeftLeg()->SetBoot(What); } + arm* GetMainArm() const; + arm* GetSecondaryArm() const; + virtual truth ReceiveDamage(character*, int, int, int = ALL, int = 8, truth = false, truth = false, truth = false, truth = true); + virtual truth BodyPartIsVital(int) const; + virtual truth BodyPartCanBeSevered(int) const; + virtual item* GetMainWielded() const; + virtual item* GetSecondaryWielded() const; + virtual cchar* GetEquipmentName(int) const; + virtual bodypart* GetBodyPartOfEquipment(int) const; + virtual item* GetEquipment(int) const; + virtual int GetEquipments() const { return 13; } + virtual void SwitchToDig(item*, v2); + virtual int GetUsableLegs() const; + virtual int GetUsableArms() const; + virtual truth CheckKick() const; + virtual int OpenMultiplier() const; + virtual int CloseMultiplier() const; + virtual truth CheckThrow() const; + virtual truth CheckOffer() const; + virtual sorter EquipmentSorter(int) const; + virtual void SetEquipment(int, item*); + virtual void DrawSilhouette(truth) const; + virtual int GetGlobalResistance(int) const; + virtual truth TryToRiseFromTheDead(); + virtual truth HandleNoBodyPart(int); + virtual void Kick(lsquare*, int, truth = false); + virtual double GetTimeToKill(ccharacter*, truth) const; + virtual int GetAttribute(int, truth = true) const; + virtual truth EditAttribute(int, int); + virtual void EditExperience(int, double, double); + virtual int DrawStats(truth) const; + virtual void Bite(character*, v2, int, truth = false); + virtual int GetCarryingStrength() const; + virtual int GetRandomStepperBodyPart() const; + virtual int CheckForBlock(character*, item*, double, int, int, int); + virtual truth AddSpecialSkillInfo(felist&) const; + virtual truth CheckBalance(double); + virtual long GetMoveAPRequirement(int) const; + virtual v2 GetEquipmentPanelPos(int) const; + virtual truth EquipmentEasilyRecognized(int) const; + sweaponskill* GetCurrentRightSWeaponSkill() const { return CurrentRightSWeaponSkill; } + void SetCurrentRightSWeaponSkill(sweaponskill* What) { CurrentRightSWeaponSkill = What; } + sweaponskill* GetCurrentLeftSWeaponSkill() const { return CurrentLeftSWeaponSkill; } + void SetCurrentLeftSWeaponSkill(sweaponskill* What) { CurrentLeftSWeaponSkill = What; } + virtual void SWeaponSkillTick(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void SignalEquipmentAdd(int); + virtual void SignalEquipmentRemoval(int, citem*); + virtual void DrawBodyParts(blitdata&) const; + virtual truth CanUseStethoscope(truth) const; + virtual truth IsUsingArms() const; + virtual truth IsUsingLegs() const; + virtual truth IsUsingHead() const; + virtual void CalculateBattleInfo(); + virtual void CalculateBodyParts(); + virtual void CalculateAllowedWeaponSkillCategories(); + virtual truth HasALeg() const { return HasAUsableLeg(); } + virtual void AddSpecialEquipmentInfo(festring&, int) const; + virtual void CreateInitialEquipment(int); + virtual festring GetBodyPartName(int, truth = false) const; + virtual void CreateBlockPossibilityVector(blockvector&, double) const; + virtual item* SevereBodyPart(int, truth = false, stack* = 0); + virtual int GetSWeaponSkillLevel(citem*) const; + virtual truth UseMaterialAttributes() const; + virtual void CalculateDodgeValue(); + virtual truth CheckZap(); + virtual truth IsHumanoid() const { return true; } + virtual truth CheckTalk(); + virtual truth CheckIfEquipmentIsNotUsable(int) const; + virtual void AddSpecialStethoscopeInfo(felist&) const; + virtual item* GetPairEquipment(int) const; + virtual truth HasHead() const { return truth(GetHead()); } + virtual cfestring& GetStandVerb() const; + virtual head* Behead(); + virtual void AddAttributeInfo(festring&) const; + virtual void AddAttackInfo(felist&) const; + virtual void AddDefenceInfo(felist&) const; + virtual void DetachBodyPart(); + virtual void SurgicallyDetachBodyPart(); + virtual int GetRandomApplyBodyPart() const; + void EnsureCurrentSWeaponSkillIsCorrect(sweaponskill*&, citem*); + virtual int GetSumOfAttributes() const; + virtual truth CheckConsume(cfestring&) const; + virtual truth CanConsume(material*) const; + virtual truth PreProcessForBone(); + virtual void FinalProcessForBone(); + virtual void StayOn(liquid*); + virtual head* GetVirtualHead() const { return GetHead(); } + virtual character* CreateZombie() const; + virtual void LeprosyHandler(); + virtual void DropRandomNonVitalBodypart(); + virtual void DropBodyPart(int); + virtual void DuplicateEquipment(character*, ulong); + virtual int GetAttributeAverage() const; + virtual truth CanVomit() const; + virtual truth CheckApply() const; + virtual truth CanForceVomit() const { return TorsoIsAlive() && HasAUsableArm(); } + virtual truth IsTransparent() const; + virtual void ModifySituationDanger(double&) const; + virtual int RandomizeTryToUnStickBodyPart(ulong) const; + virtual truth AllowUnconsciousness() const; + virtual truth CanChokeOnWeb(web*) const; + virtual truth BrainsHurt() const; + virtual cchar* GetRunDescriptionLine(int) const; + virtual cchar* GetNormalDeathMessage() const; + virtual void ApplySpecialAttributeBonuses(); + virtual truth MindWormCanPenetrateSkull(mindworm*) const; + truth HasSadistWeapon() const; + virtual truth HasSadistAttackMode() const; + virtual truth CheckThrowItemOpportunity(); + virtual truth CheckAIZapOpportunity(); + protected: + virtual v2 GetBodyPartBitmapPos(int, truth = false) const; + virtual col16 GetBodyPartColorB(int, truth = false) const; + virtual col16 GetBodyPartColorC(int, truth = false) const; + virtual col16 GetBodyPartColorD(int, truth = false) const; + virtual int GetBodyPartSparkleFlags(int) const; + virtual long GetBodyPartSize(int, int) const; + virtual long GetBodyPartVolume(int) const; + virtual bodypart* MakeBodyPart(int) const; + virtual cfestring& GetDeathMessage() const; + virtual v2 GetDrawDisplacement(int) const { return ZERO_V2; } + truth HasAUsableArm() const; + truth HasAUsableLeg() const; + truth HasTwoUsableLegs() const; + truth CanAttackWithAnArm() const; + truth RightArmIsUsable() const; + truth LeftArmIsUsable() const; + truth RightLegIsUsable() const; + truth LeftLegIsUsable() const; + std::list SWeaponSkill; + sweaponskill* CurrentRightSWeaponSkill; + sweaponskill* CurrentLeftSWeaponSkill; + static cint DrawOrder[]; + virtual truth SpecialBiteEffect(character*, v2, int, int, truth); +}; + +CHARACTER(playerkind, humanoid) +{ + public: + playerkind(); + playerkind(const playerkind&); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void SetSoulID(ulong); + virtual truth SuckSoul(character*); + virtual truth TryToRiseFromTheDead(); + virtual void FinalProcessForBone(); + virtual void BeTalkedTo(); + virtual truth IsHuman() const { return true; } + virtual col16 GetHairColor() const { return HairColor; } + virtual col16 GetEyeColor() const { return EyeColor; } + virtual v2 GetHeadBitmapPos() const; + virtual v2 GetRightArmBitmapPos() const; + virtual v2 GetLeftArmBitmapPos() const; + virtual int GetNaturalSparkleFlags() const; + virtual truth IsPlayerKind() const { return true; } + virtual double GetNaturalExperience(int) const; + protected: + virtual bodypart* MakeBodyPart(int) const; + virtual void PostConstruct(); + ulong SoulID; + col16 HairColor; + col16 EyeColor; + int Talent; + int Weakness; + truth IsBonePlayer; + truth IsClone; +}; + +CHARACTER(petrus, humanoid) +{ + public: + petrus(); // there is a petrus + virtual ~petrus(); // if there is no petrus, then librarian's betalkedto replies change + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void BeTalkedTo(); + truth HealFully(character*); + virtual void FinalProcessForBone(); + virtual truth MoveTowardsHomePos(); + protected: + virtual void CreateCorpse(lsquare*); + virtual void GetAICommand(); + ulong LastHealed; +}; + +CHARACTER(mysteryman, humanoid) +{ + public: + virtual void BeTalkedTo(); +}; + +CHARACTER(denim, humanoid) +{ + protected: + virtual void GetAICommand(); +}; + +CHARACTER(raven, humanoid) +{ + public: + virtual void BeTalkedTo(); + protected: + virtual void GetAICommand(); +}; + +CHARACTER(wisefarmer, humanoid) +{ + protected: + virtual void GetAICommand(); + public: + virtual v2 GetHeadBitmapPos() const; + virtual v2 GetRightArmBitmapPos() const; + virtual v2 GetLeftArmBitmapPos() const { return GetRightArmBitmapPos(); } +}; + +CHARACTER(vulcan, humanoid) +{ + protected: + virtual void GetAICommand(); +}; + +CHARACTER(farmer, humanoid) +{ + public: + virtual v2 GetHeadBitmapPos() const; + virtual v2 GetRightArmBitmapPos() const; + virtual v2 GetLeftArmBitmapPos() const { return GetRightArmBitmapPos(); } +}; + +CHARACTER(bum, humanoid) +{ +}; + +CHARACTER(guard, humanoid) +{ + public: + guard() : NextWayPoint(0) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void GetAICommand(); + virtual void SetWayPoints(const fearray&); + virtual truth MoveTowardsHomePos(); + virtual void BeTalkedTo(); + protected: + std::vector WayPoints; + uint NextWayPoint; +}; + +CHARACTER(shopkeeper, humanoid) +{ + public: + shopkeeper(); + protected: + virtual void GetAICommand() { StandIdleAI(); } +}; + +CHARACTER(priest, humanoid) +{ + protected: + virtual void GetAICommand() { StandIdleAI(); } + virtual void BeTalkedTo(); +}; + +CHARACTER(doctor, humanoid) +{ + protected: + virtual void BeTalkedTo(); +}; + +CHARACTER(oree, humanoid) +{ + public: + virtual void Bite(character*, v2, int, truth = false); + virtual void GetAICommand(); + protected: + virtual cchar* FirstPersonBiteVerb() const; + virtual cchar* FirstPersonCriticalBiteVerb() const; + virtual cchar* ThirdPersonBiteVerb() const; + virtual cchar* ThirdPersonCriticalBiteVerb() const; + virtual cchar* BiteNoun() const; + void CallForMonsters(); +}; + +CHARACTER(darkknight, humanoid) +{ + protected: + virtual int ModifyBodyPartHitPreference(int, int) const; + virtual int ModifyBodyPartToHitChance(int, int) const; + virtual truth CanPanicFromSeveredBodyPart() const { return false; } + virtual void SpecialBodyPartSeverReaction(); +}; + +CHARACTER(ennerbeast, humanoid) +{ + public: + virtual truth Hit(character*, v2, int, int = 0); + virtual truth MustBeRemovedFromBone() const; + protected: + virtual bodypart* MakeBodyPart(int) const; + virtual void GetAICommand(); + virtual truth AttackIsBlockable(int) const { return false; } +}; + +CHARACTER(skeleton, humanoid) +{ + public: + virtual void BeTalkedTo(); + virtual item* SevereBodyPart(int, truth = false, stack* = 0); + virtual truth BodyPartIsVital(int) const; + virtual long GetBodyPartVolume(int) const; + protected: + virtual void CreateCorpse(lsquare*); +}; + +CHARACTER(goblin, humanoid) +{ +}; + +CHARACTER(golem, humanoid) +{ + public: + golem(); + virtual truth MoveRandomly(); + virtual truth CheckForUsefulItemsOnGround(truth = true) { return false; } + virtual void BeTalkedTo(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + void SetItemVolume(long What) { ItemVolume = What; } + protected: + virtual truth AddAdjective(festring&, truth) const; + virtual material* CreateBodyPartMaterial(int, long) const; + virtual void CreateCorpse(lsquare*); + long ItemVolume; +}; + +CHARACTER(communist, humanoid) +{ + public: + virtual truth MoveRandomly(); + virtual void BeTalkedTo(); + virtual truth BoundToUse(citem*, int) const; + virtual truth MustBeRemovedFromBone() const; + protected: + virtual truth ShowClassDescription() const; +}; + +CHARACTER(hunter, humanoid) +{ + public: + virtual void BeTalkedTo(); + protected: + virtual void CreateBodyParts(int); +}; + +CHARACTER(slave, playerkind) +{ + public: + virtual void BeTalkedTo(); + virtual void GetAICommand(); + virtual col16 GetHairColor() const { return humanoid::GetHairColor(); } + virtual col16 GetEyeColor() const { return humanoid::GetEyeColor(); } + protected: + virtual void PostConstruct(); +}; + +CHARACTER(assassin, humanoid) +{ + public: + virtual void BeTalkedTo(); +}; + +CHARACTER(petrusswife, humanoid) +{ + public: + petrusswife() : GiftTotal(0) { } + virtual truth MoveRandomly() { return MoveRandomlyInRoom(); } + virtual truth SpecialEnemySightedReaction(character*); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void BeTalkedTo(); + protected: + int GiftTotal; +}; + +CHARACTER(housewife, humanoid) +{ + public: + virtual truth SpecialEnemySightedReaction(character*); + protected: + virtual int GetHairColor() const; + virtual v2 GetHeadBitmapPos() const; +}; + +CHARACTER(femaleslave, humanoid) +{ + public: + virtual void BeTalkedTo(); + protected: + virtual void GetAICommand() { StandIdleAI(); } +}; + +CHARACTER(librarian, humanoid) +{ + public: + virtual void BeTalkedTo(); + protected: + virtual void GetAICommand() { StandIdleAI(); } +}; + +CHARACTER(zombie, humanoid) +{ + public: + virtual void BeTalkedTo(); + virtual truth BodyPartIsVital(int) const; + virtual void CreateBodyParts(int); + virtual truth AllowSpoil() const { return true; } + void SetDescription(cfestring What) { Description = What; } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual festring GetZombieDescription() const; + protected: + virtual void PostConstruct(); + virtual void AddPostFix(festring&, int) const; + virtual void GetAICommand(); + virtual truth AllowExperience() const { return false; } + festring Description; +}; + +CHARACTER(imp, humanoid) +{ +}; + +CHARACTER(mistress, humanoid) +{ + public: + virtual int TakeHit(character*, item*, bodypart*, v2, double, double, int, int, int, truth, truth); + virtual truth ReceiveDamage(character*, int, int, int = ALL, int = 8, truth = false, truth = false, truth = false, truth = true); + virtual truth AllowEquipment(citem*, int) const; +}; + +CHARACTER(werewolfhuman, humanoid) +{ +}; + +CHARACTER(werewolfwolf, humanoid) +{ + public: + virtual festring GetKillName() const; +}; + +CHARACTER(kobold, humanoid) +{ +}; + +CHARACTER(kabouter, humanoid) +{ + public: + kabouter() : LastTamed(0) { } + virtual void Load(inputfile&); + virtual void Save(outputfile&) const; +// virtual int GetAttribute(int, truth = true) const; + virtual void FinalProcessForBone(); + protected: + virtual void GetAICommand(); + int GetSpellAPCost() const; + ulong LastTamed; +}; + +CHARACTER(uldra, humanoid) +{ + protected: + virtual void GetAICommand(); + int GetSpellAPCost() const; +}; + +CHARACTER(vampire, humanoid) +{ + protected: + virtual truth SpecialBiteEffect(character*, v2, int, int, truth); +}; + +CHARACTER(gibberling, humanoid) +{ +}; + +CHARACTER(angel, humanoid) +{ + public: + angel() : LastHealed(0) { } + virtual void Load(inputfile&); + virtual void Save(outputfile&) const; + virtual truth AttachBodyPartsOfFriendsNear(); + virtual truth BodyPartIsVital(int) const; + virtual int GetAttribute(int, truth = true) const; + virtual col24 GetBaseEmitation() const; + virtual truth CanCreateBodyPart(int) const; + virtual cfestring& GetStandVerb() const { return character::GetStandVerb(); } + virtual void FinalProcessForBone(); + virtual void CreateInitialEquipment(int); + protected: + virtual int GetTorsoMainColor() const; + virtual int GetArmMainColor() const; + virtual void GetAICommand(); + ulong LastHealed; +}; + +//probably do this, and override the torso main colours etc +CHARACTER(insudo, angel) +{ + public: + //angel() : LastHealed(0) { } + //virtual void Load(inputfile&); + //virtual void Save(outputfile&) const; + //virtual truth AttachBodyPartsOfFriendsNear(); + //virtual truth BodyPartIsVital(int) const; + //virtual int GetAttribute(int, truth = true) const; + virtual col24 GetBaseEmitation() const { return MakeRGB24(120, 120, 150); } + //virtual truth CanCreateBodyPart(int) const; + //virtual cfestring& GetStandVerb() const { return character::GetStandVerb(); } + //virtual void FinalProcessForBone(); + virtual void CreateInitialEquipment(int); //has a bespoke function to handle this, need to adapt from the archangel code + protected: + virtual int GetTorsoMainColor() const { return MakeRGB16(120, 120, 120); } + virtual int GetArmMainColor() const { return MakeRGB16(120, 120, 120); } + virtual void GetAICommand(); + //ulong LastHealed; +}; + +CHARACTER(kamikazedwarf, humanoid) +{ + public: + virtual truth Hit(character*, v2, int, int = 0); + virtual truth CheckForUsefulItemsOnGround(truth = true) { return false; } + virtual void GetAICommand(); + virtual void CreateInitialEquipment(int); + virtual truth IsKamikazeDwarf() const { return true; } + virtual void SingRandomSong(); + protected: + virtual int GetTorsoMainColor() const; + virtual int GetGauntletColor() const; + virtual int GetLegMainColor() const; + virtual v2 GetDrawDisplacement(int) const; + virtual int GetWSkillHits() const { return 10000; } + virtual truth IsElite() const { return false; } + virtual truth IsRookie() const { return false; } + virtual truth IsGrenadier() const { return false; } +}; + +CHARACTER(axethrowerdwarf, humanoid) +{ + public: + //virtual truth Hit(character*, v2, int, int = 0); + //virtual truth CheckForUsefulItemsOnGround(truth = true) { return false; } + virtual void GetAICommand(); + virtual void CreateInitialEquipment(int); + //virtual truth IsKamikazeDwarf() const { return true; } + //virtual void SingRandomSong(); + protected: + virtual int GetTorsoMainColor() const; + virtual int GetGauntletColor() const; + virtual int GetLegMainColor() const; + virtual v2 GetDrawDisplacement(int) const; + virtual int GetWSkillHits() const { return 10000; } + virtual truth IsElite() const { return false; } +}; + +CHARACTER(rookiekamikazedwarf, kamikazedwarf) +{ + public: + virtual void GetAICommand(); + virtual truth CheckForUsefulItemsOnGround(truth = true); + //virtual void CreateInitialEquipment(int); + protected: + //virtual void PostConstruct(); + //virtual int GetTorsoMainColor() const; + //virtual int GetGauntletColor() const; + //virtual int GetLegMainColor() const; + //virtual int GetWSkillHits() const { return 10000; } + virtual truth IsRookie() const { return true; } +}; + +CHARACTER(grenadierdwarf, kamikazedwarf) +{ + public: + virtual void GetAICommand(); + //virtual void CreateInitialEquipment(int); + protected: + virtual void PostConstruct(); + virtual int GetTorsoMainColor() const; + virtual int GetGauntletColor() const; + virtual int GetLegMainColor() const; + virtual int GetWSkillHits() const { return 50000; } + virtual truth IsGrenadier() const { return true; } +}; + +CHARACTER(shaman, humanoid) +{ + public: + + protected: + virtual void GetAICommand(); + int GetSpellAPCost() const; + virtual void PostConstruct(); +}; + +CHARACTER(warlock, humanoid) +{ + protected: + virtual void GetAICommand(); + int GetSpellAPCost() const; +}; + +CHARACTER(alchemist, humanoid) +{ + protected: + virtual void GetAICommand(); + int GetSpellAPCost() const; +}; + +CHARACTER(genie, humanoid) +{ + public: + virtual truth BodyPartIsVital(int) const; + virtual int GetAttribute(int, truth = true) const; + virtual truth CanCreateBodyPart(int) const; + virtual cfestring& GetStandVerb() const { return character::GetStandVerb(); } +}; + +CHARACTER(reaper, genie) +{ +}; + +CHARACTER(orc, humanoid) +{ + protected: + virtual void PostConstruct(); +}; + +CHARACTER(cossack, humanoid) +{ +}; + +CHARACTER(bananagrower, humanoid) +{ + public: + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void BeTalkedTo(); + virtual festring& ProcessMessage(festring&) const; + virtual truth IsBananaGrower() const { return true; } + festring GetProfession() const { return Profession; } + protected: + virtual truth HandleCharacterBlockingTheWay(character*, v2, int); + virtual void PostConstruct(); + virtual void GetAICommand(); + void RandomizeProfession(); + festring Profession; + truth HasDroppedBananas; + truth FeedingSumo; +}; + +CHARACTER(mangogrower, humanoid) +{ +}; + +CHARACTER(imperialist, humanoid) +{ + public: + virtual void GetAICommand() { StandIdleAI(); } + virtual void BeTalkedTo(); + virtual void DisplayStethoscopeInfo(character*) const; +}; + +CHARACTER(regii, humanoid) +{ + public: + virtual void GetAICommand() { StandIdleAI(); } + virtual void BeTalkedTo(); + //virtual void DisplayStethoscopeInfo(character*) const; +}; + +CHARACTER(UTFAOfficial, humanoid) +{ + public: + virtual void GetAICommand() { StandIdleAI(); } +}; + +CHARACTER(smith, humanoid) +{ + public: + virtual void BeTalkedTo(); + virtual void GetAICommand() { StandIdleAI(); } +}; + +CHARACTER(elder, humanoid) +{ + protected: + virtual void GetAICommand(); + virtual void CreateBodyParts(int); +}; + +CHARACTER(encourager, humanoid) +{ + /*public: + encourager() : LastHit(0) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void FinalProcessForBone(); + protected: + virtual void GetAICommand(); + ulong LastHit;*/ + protected: + virtual void GetAICommand() { StandIdleAI(); } +}; + +CHARACTER(darkmage, humanoid) +{ + protected: + virtual void GetAICommand(); + int GetSpellAPCost() const; +}; + +CHARACTER(necromancer, humanoid) +{ + public: + virtual truth TryToRaiseZombie(); + virtual void RaiseSkeleton(); + protected: + virtual void GetAICommand(); + int GetSpellAPCost() const; +}; + +CHARACTER(sumowrestler, humanoid) +{ + public: + virtual void BeTalkedTo(); + virtual truth CheckIfSatiated() { return GetNP() > BLOATED_LEVEL; } + virtual truth IsSumoWrestler() const { return true; } + virtual truth EquipmentIsAllowed(int) const; + protected: + virtual void GetAICommand(); +}; + +CHARACTER(tourist, humanoid) +{ + public: + virtual character* GetLeader() const; + protected: + virtual void GetAICommand(); +}; + +CHARACTER(veterankamikazedwarf, kamikazedwarf) +{ + protected: + virtual void PostConstruct(); + virtual int GetTorsoMainColor() const; + virtual int GetGauntletColor() const; + virtual int GetLegMainColor() const; + virtual int GetWSkillHits() const { return 50000; } + virtual truth IsElite() const { return true; } +}; + +CHARACTER(archangel, angel) +{ + public: + virtual void CreateInitialEquipment(int); + protected: + virtual int GetTorsoMainColor() const; + virtual int GetArmMainColor() const; +}; + +CHARACTER(tailor, humanoid) +{ + public: + virtual void BeTalkedTo(); + virtual void GetAICommand() { StandIdleAI(); } +}; + + +CHARACTER(siren, humanoid) +{ + public: + virtual void GetAICommand(); + protected: + virtual truth TryToSing(); +}; + + +CHARACTER(punisher, humanoid) +{ + +}; + +CHARACTER(rogue, humanoid) +{ + public: + virtual void GetAICommand(); + virtual truth IsRetreating() const; +}; + +CHARACTER(forestman, humanoid) +{ + public: + virtual character* GetLeader() const; + protected: + virtual void GetAICommand(); +}; + +CHARACTER(morbe, humanoid) +{ + public: + virtual void BeTalkedTo(); + //virtual void FinalProcessForBone(); + //virtual truth MoveTowardsHomePos(); // may need this in times to come + protected: + virtual void CreateCorpse(lsquare*); + virtual void GetAICommand(); + //ulong LastHealed; +}; + +#endif diff --git a/Main/Include/iconf.h b/Main/Include/iconf.h new file mode 100644 index 0000000..3369d67 --- /dev/null +++ b/Main/Include/iconf.h @@ -0,0 +1,86 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __ICONF_H__ +#define __ICONF_H__ + +#include "config.h" +#include "v2.h" + +class ivanconfig +{ + public: + static cfestring& GetDefaultName() { return DefaultName.Value; } + static cfestring& GetDefaultPetName() { return DefaultPetName.Value; } + static long GetAutoSaveInterval() { return AutoSaveInterval.Value; } + static long GetContrast() { return Contrast.Value; } + static truth GetWarnAboutDanger() { return WarnAboutDanger.Value; } + static truth GetAutoDropLeftOvers() { return AutoDropLeftOvers.Value; } + static truth GetLookZoom() { return LookZoom.Value; } + static truth GetUseAlternativeKeys() { return UseAlternativeKeys.Value; } + static truth GetBeNice() { return BeNice.Value; } +#ifndef __DJGPP__ + static truth GetFullScreenMode() { return FullScreenMode.Value; } + static void SwitchModeHandler(); +#else + static truth GetFullScreenMode() { return true; } +#endif + static long ApplyContrastTo(long); + static void Save() { configsystem::Save(); } + static void Load() { configsystem::Load(); } + static void CalculateContrastLuminance(); + static col24 GetContrastLuminance() { return ContrastLuminance; } + static void Initialize(); + static void Show(); + private: + static v2 GetQuestionPos(); + static void AutoSaveIntervalDisplayer(const numberoption*, festring&); + static void ContrastDisplayer(const numberoption*, festring&); + static truth DefaultNameChangeInterface(stringoption*); + static truth DefaultPetNameChangeInterface(stringoption*); + static truth AutoSaveIntervalChangeInterface(numberoption*); + static truth ContrastChangeInterface(numberoption*); + static void AutoSaveIntervalChanger(numberoption*, long); + static void ContrastChanger(numberoption*, long); +#ifndef __DJGPP__ + static void FullScreenModeChanger(truthoption*, truth); +#endif + static void ContrastHandler(long); + static void BackGroundDrawer(); + static stringoption DefaultName; + static stringoption DefaultPetName; + static numberoption AutoSaveInterval; + static scrollbaroption Contrast; + static truthoption WarnAboutDanger; + static truthoption AutoDropLeftOvers; + static truthoption LookZoom; + static truthoption UseAlternativeKeys; + static truthoption BeNice; +#ifndef __DJGPP__ + static truthoption FullScreenMode; +#endif + static col24 ContrastLuminance; +}; + +inline long ivanconfig::ApplyContrastTo(long L) +{ + long C = Contrast.Value; + + if(C == 100) + return L; + else + return MakeRGB24(41 * GetRed24(L) * C >> 12, + 41 * GetGreen24(L) * C >> 12, + 41 * GetBlue24(L) * C >> 12); +} + +#endif diff --git a/Main/Include/id.h b/Main/Include/id.h new file mode 100644 index 0000000..0e5d385 --- /dev/null +++ b/Main/Include/id.h @@ -0,0 +1,49 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __ID_H__ +#define __ID_H__ + +#include "typedef.h" + +class festring; + +#define CHAR_NAME(Case) GetName(Case).CStr() +#define CHAR_DESCRIPTION(Case) GetDescription(Case).CStr() + +class id +{ + public: + virtual void AddName(festring&, int, int) const; + virtual festring GetName(int, int) const; + virtual void AddName(festring&, int) const; + virtual festring GetName(int) const; + cchar* GetArticle() const { return UsesLongArticle() ? "an" : "a"; } + protected: + virtual cfestring& GetNameSingular() const = 0; + virtual void AddNameSingular(festring&, truth) const; + virtual cfestring& GetNamePlural() const = 0; + virtual truth UsesLongArticle() const = 0; + virtual truth AddRustLevelDescription(festring&, truth) const { return false; } + virtual truth AddStateDescription(festring&, truth) const { return false; } + virtual truth AddAdjective(festring&, truth) const; + virtual cfestring& GetAdjective() const = 0; + virtual truth UsesLongAdjectiveArticle() const = 0; + virtual truth AddMaterialDescription(festring&, truth) const { return false; } + virtual cfestring& GetPostFix() const = 0; + virtual void AddPostFix(festring&, int) const; + virtual int GetArticleMode() const { return 0; } + virtual truth ShowMaterial() const { return false; } + virtual truth AddActiveAdjective(festring&, truth) const; +}; + +#endif diff --git a/Main/Include/igraph.h b/Main/Include/igraph.h new file mode 100644 index 0000000..0cec4cc --- /dev/null +++ b/Main/Include/igraph.h @@ -0,0 +1,147 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __IGRAPH_H__ +#define __IGRAPH_H__ + +#include + +#include "ivandef.h" +#include "femath.h" + +class bitmap; +class rawbitmap; +class outputfile; +class inputfile; +class festring; + +/* memcmp doesn't like alignment of structure members */ + +#ifdef VC +#pragma pack(1) +#endif + +struct graphicid +{ + graphicid() { } + bool operator<(const graphicid&) const; + ushort BitmapPosX NO_ALIGNMENT; + ushort BitmapPosY NO_ALIGNMENT; + packcol16 Color[4] NO_ALIGNMENT; + uchar Frame NO_ALIGNMENT; + uchar FileIndex NO_ALIGNMENT; + ushort SpecialFlags NO_ALIGNMENT; + packalpha Alpha[4] NO_ALIGNMENT; + packalpha BaseAlpha NO_ALIGNMENT; + uchar SparkleFrame NO_ALIGNMENT; + uchar SparklePosX NO_ALIGNMENT; + uchar SparklePosY NO_ALIGNMENT; + packcol16 OutlineColor NO_ALIGNMENT; + packalpha OutlineAlpha NO_ALIGNMENT; + uchar FlyAmount NO_ALIGNMENT; + v2 Position NO_ALIGNMENT; + uchar RustData[4] NO_ALIGNMENT; + ushort Seed NO_ALIGNMENT; + uchar WobbleData NO_ALIGNMENT; +}; + +#ifdef VC +#pragma pack() +#endif + +inline bool graphicid::operator<(const graphicid& GI) const +{ + return memcmp(this, &GI, sizeof(graphicid)) < 0; +} + +outputfile& operator<<(outputfile&, const graphicid&); +inputfile& operator>>(inputfile&, graphicid&); + +struct tile +{ + tile() { } + tile(bitmap* Bitmap) : Bitmap(Bitmap), Users(1) { } + bitmap* Bitmap; + long Users; +}; + +typedef std::map tilemap; + +struct graphicdata +{ + graphicdata() : AnimationFrames(0) { } + ~graphicdata(); + void Save(outputfile&) const; + void Load(inputfile&); + void Retire(); + int AnimationFrames; + bitmap** Picture; + tilemap::iterator* GraphicIterator; +}; + +outputfile& operator<<(outputfile&, const graphicdata&); +inputfile& operator>>(inputfile&, graphicdata&); + +class igraph +{ + public: + static void Init(); + static void DeInit(); + static cbitmap* GetWTerrainGraphic() { return Graphic[GR_WTERRAIN]; } + static cbitmap* GetFOWGraphic() { return Graphic[GR_FOW]; } + static const rawbitmap* GetCursorRawGraphic() { return RawGraphic[GR_CURSOR]; } + static cbitmap* GetSymbolGraphic() { return Graphic[GR_SYMBOL]; } + static bitmap* GetTileBuffer() { return TileBuffer; } + static void DrawCursor(v2, int, int = 0); + static tilemap::iterator AddUser(const graphicid&); + static void RemoveUser(tilemap::iterator); + static const rawbitmap* GetHumanoidRawGraphic() { return RawGraphic[GR_HUMANOID]; } + static const rawbitmap* GetCharacterRawGraphic() { return RawGraphic[GR_CHARACTER]; } + static const rawbitmap* GetEffectRawGraphic() { return RawGraphic[GR_EFFECT]; } + static const rawbitmap* GetRawGraphic(int I) { return RawGraphic[I]; } + static cint* GetBodyBitmapValidityMap(int); + static bitmap* GetFlagBuffer() { return FlagBuffer; } + static cbitmap* GetMenuGraphic() { return Menu; } + static void LoadMenu(); + static void UnLoadMenu(); + static bitmap* GetSilhouetteCache(int I1, int I2, int I3) { return SilhouetteCache[I1][I2][I3]; } + static cbitmap* GetBackGround() { return BackGround; } + static void BlitBackGround(v2, v2); + static void CreateBackGround(int); + static bitmap* GenerateScarBitmap(int, int, int); + static cbitmap* GetSmileyGraphic() { return Graphic[GR_SMILEY]; } + private: + static void EditBodyPartTile(rawbitmap*, rawbitmap*, v2, int); + static v2 RotateTile(rawbitmap*, rawbitmap*, v2, v2, int); + static void CreateBodyBitmapValidityMaps(); + static void CreateSilhouetteCaches(); + static col16 GetBackGroundColor(int); + static rawbitmap* RawGraphic[RAW_TYPES]; + static bitmap* Graphic[GRAPHIC_TYPES]; + static bitmap* TileBuffer; + static cchar* RawGraphicFileName[]; + static cchar* GraphicFileName[]; + static tilemap TileMap; + static uchar RollBuffer[256]; + static bitmap* FlagBuffer; + static int** BodyBitmapValidityMap; + static bitmap* Menu; + static bitmap* SilhouetteCache[HUMANOID_BODYPARTS][CONDITION_COLORS][SILHOUETTE_TYPES]; + static rawbitmap* ColorizeBuffer[2]; + static bitmap* Cursor[CURSOR_TYPES]; + static bitmap* BigCursor[CURSOR_TYPES]; + static col16 CursorColor[CURSOR_TYPES]; + static bitmap* BackGround; + static int CurrentColorType; +}; + +#endif diff --git a/Main/Include/iloops.h b/Main/Include/iloops.h new file mode 100644 index 0000000..902c8a5 --- /dev/null +++ b/Main/Include/iloops.h @@ -0,0 +1,122 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __ILOOPS_H__ +#define __ILOOPS_H__ + +#include "feloops.h" +#include "char.h" + +struct combinebodypartpredicates : public combinepredicates +{ + typedef combinepredicates base; + truth operator()(ccharacter* C, base::routine F, truth OrBit) const + { + return base::operator()(C, &character::GetBodyPart, F, C->GetBodyParts(), OrBit); + } +}; + +template +struct combinebodypartpredicateswithparam : public combinepredicateswithparam +{ + typedef combinepredicateswithparam base; + truth operator()(ccharacter* C, typename base::routine F, param P, truth OrBit) const + { + return base::operator()(C, &character::GetBodyPart, F, P, C->GetBodyParts(), OrBit); + } +}; + +struct combineequipmentpredicates : public combinepredicates +{ + typedef combinepredicates base; + truth operator()(ccharacter* C, base::routine F, truth OrBit) const + { + return base::operator()(C, &character::GetEquipment, F, C->GetEquipments(), OrBit); + } +}; + +template +struct combineequipmentpredicateswithparam : public combinepredicateswithparam +{ + typedef combinepredicateswithparam base; + truth operator()(ccharacter* C, typename base::routine F, param P, truth OrBit) const + { + return base::operator()(C, &character::GetEquipment, F, P, C->GetEquipments(), OrBit); + } +}; + +struct doforbodyparts : public doforelements +{ + typedef doforelements base; + void operator()(ccharacter* C, base::routine F) const + { + base::operator()(C, &character::GetBodyPart, F, C->GetBodyParts()); + } +}; + +template +struct doforbodypartswithparam : public doforelementswithparam +{ + typedef doforelementswithparam base; + void operator()(ccharacter* C, typename base::routine F, param P) const + { + base::operator()(C, &character::GetBodyPart, F, P, C->GetBodyParts()); + } +}; + +struct doforequipments : public doforelements +{ + typedef doforelements base; + void operator()(ccharacter* C, base::routine F) const + { + base::operator()(C, &character::GetEquipment, F, C->GetEquipments()); + } + void operator()(ccharacter* C, void (item::*F)() const) const + { + base::operator()(C, &character::GetEquipment, base::routine(F), C->GetEquipments()); + } +}; + +template +struct doforequipmentswithparam : public doforelementswithparam +{ + typedef doforelementswithparam base; + void operator()(ccharacter* C, typename base::routine F, param P) const + { + base::operator()(C, &character::GetEquipment, F, P, C->GetEquipments()); + } + void operator()(ccharacter* C, void (item::*F)(param) const, param P) const + { + base::operator()(C, &character::GetEquipment, (typename base::routine)(F), P, C->GetEquipments()); + } +}; + +struct sumbodypartproperties : public sumproperties +{ + typedef sumproperties base; + int operator()(ccharacter* C, base::routine F) const + { + return base::operator()(C, &character::GetBodyPart, F, C->GetBodyParts()); + } +}; + +template +struct findequipment : public findelement +{ + typedef findelement base; + item* operator()(ccharacter* C, typename base::routine F, param P) const + { + return base::operator()(C, &character::GetEquipment, F, P, C->GetEquipments()); + } +}; + +#endif diff --git a/Main/Include/item.h b/Main/Include/item.h new file mode 100644 index 0000000..125575b --- /dev/null +++ b/Main/Include/item.h @@ -0,0 +1,631 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __ITEM_H__ +#define __ITEM_H__ + +#include + +#include "object.h" +#include "lsquare.h" +#include "slot.h" +#include "lock.h" + +class felist; +class head; +class itemprototype; +template class databasecreator; + +typedef std::vector itemvector; +typedef std::vector fluidvector; +typedef truth (rawbitmap::*pixelpredicate)(v2) const; +typedef truth (material::*materialpredicate)() const; +typedef truth (item::*sorter)(ccharacter*) const; +typedef item* (*itemspawner)(int, int); +typedef item* (*itemcloner)(citem*); + +extern materialpredicate TrueMaterialPredicate; + +struct sortdata +{ + sortdata(itemvector& AllItems, ccharacter* Character, truth Recurse, sorter Sorter) + : AllItems(AllItems), + Character(Character), + Recurse(Recurse), + Sorter(Sorter) { } + itemvector& AllItems; + ccharacter* Character; + truth Recurse; + sorter Sorter; +}; + +struct idholder +{ + idholder(ulong ID) : ID(ID) { } + idholder* Next; + ulong ID; +}; + +outputfile& operator<<(outputfile&, const idholder*); +inputfile& operator>>(inputfile&, idholder*&); + +class itemlock +{ + public: + typedef itemprototype prototype; + itemlock() : Locked(false) { } + void Save(outputfile&) const; + void Load(inputfile&); + virtual truth TryKey(item*, character*); + virtual int GetVirtualConfig() const = 0; + virtual void SetVirtualConfig(int, int = 0) = 0; + virtual const prototype* GetVirtualProtoType() const = 0; + virtual festring GetVirtualDescription(int) const = 0; + protected: + virtual void PostConstruct(); + truth Locked; +}; + +typedef lockable lockableitem; + +struct itemdatabase : public databasebase +{ + typedef itemprototype prototype; + void InitDefaults(const prototype*, int); + truth AllowRandomInstantiation() const; + void PostProcess() { } + const prototype* ProtoType; + /* Maintained by configcontainer */ + long PartialPossibilitySum; + long PartialCategoryPossibilitySum; + int Possibility; + int WeaponCategory; + truth IsDestroyable; + truth CanBeWished; + truth IsMaterialChangeable; + truth IsPolymorphSpawnable; + truth IsAutoInitializable; + truth IsAbstract; + truth IsPolymorphable; + truth CanBeGeneratedInContainer; + truth IsTwoHanded; + truth CreateDivineConfigurations; + truth CanBeCloned; + truth CanBeMirrored; + truth AffectsArmStrength; + truth AffectsLegStrength; + truth AffectsDexterity; + truth AffectsAgility; + truth AffectsEndurance; + truth AffectsPerception; + truth AffectsIntelligence; + truth AffectsWisdom; + truth AffectsWillPower; + truth AffectsCharisma; + truth AffectsMana; + truth PriceIsProportionalToEnchantment; + truth CanBeUsedBySmith; + truth AffectsCarryingCapacity; + truth HandleInPairs; + truth CanBeEnchanted; + truth IsQuestItem; + truth IsGoodWithPlants; + truth CreateLockConfigurations; + truth CanBePickedUp; + truth HasSecondaryMaterial; + truth AllowEquip; + truth IsValuable; + truth HasNormalPictureDirection; + truth IsKamikazeWeapon; + truth FlexibilityIsEssential; + truth CanBeBroken; + truth CanBePiled; + long Category; + int FireResistance; + int PoisonResistance; + int ElectricityResistance; + int AcidResistance; + int StrengthModifier; + int FormModifier; + int DefaultSize; + long DefaultMainVolume; + long DefaultSecondaryVolume; + v2 BitmapPos; + long Price; + col24 BaseEmitation; + truth UsesLongArticle; + festring Adjective; + truth UsesLongAdjectiveArticle; + festring NameSingular; + festring NamePlural; + festring PostFix; + int ArticleMode; + fearray MainMaterialConfig; + fearray SecondaryMaterialConfig; + fearray MaterialConfigChances; + long MaterialConfigChanceSum; + fearray Alias; + int OKVisualEffects; + int ForcedVisualEffects; + int Roundness; + long GearStates; + int BeamRange; + v2 WallBitmapPos; + festring FlexibleNameSingular; + int MinCharges; + int MaxCharges; + long StorageVolume; + int MaxGeneratedContainedItems; + int BaseEnchantment; + int InElasticityPenaltyModifier; + int DamageDivider; + col16 BeamColor; + int BeamEffect; + int BeamStyle; + int WearWisdomLimit; + int AttachedGod; + int BreakEffectRangeSquare; + v2 WieldedBitmapPos; + int CoverPercentile; + v2 TorsoArmorBitmapPos; + v2 ArmArmorBitmapPos; + v2 AthleteArmArmorBitmapPos; + v2 LegArmorBitmapPos; + v2 HelmetBitmapPos; + v2 CloakBitmapPos; + v2 BeltBitmapPos; + v2 GauntletBitmapPos; + v2 BootBitmapPos; + int ReadDifficulty; + int EnchantmentMinusChance; + int EnchantmentPlusChance; + int TeleportPriority; + int DamageFlags; + festring BreakMsg; + truth IsSadistWeapon; + truth IsThrowingWeapon; + long ThrowItemTypes; +}; + +class itemprototype +{ + public: + friend class databasecreator; + itemprototype(const itemprototype*, itemspawner, itemcloner, cchar*); + item* Spawn(int Config = 0, int SpecialFlags = 0) const { return Spawner(Config, SpecialFlags); } + item* SpawnAndLoad(inputfile&) const; + item* Clone(citem* Item) const { return Cloner(Item); } + cchar* GetClassID() const { return ClassID; } + int GetIndex() const { return Index; } + const itemprototype* GetBase() const { return Base; } + int CreateSpecialConfigurations(itemdatabase**, int, int); + const itemdatabase* ChooseBaseForConfig(itemdatabase**, int, int); + const itemdatabase*const* GetConfigData() const { return ConfigData; } + int GetConfigSize() const { return ConfigSize; } + private: + int Index; + const itemprototype* Base; + itemdatabase** ConfigData; + itemdatabase** ConfigTable[CONFIG_TABLE_SIZE]; + int ConfigSize; + itemspawner Spawner; + itemcloner Cloner; + cchar* ClassID; +}; + +class item : public object +{ + public: + friend class databasecreator; + typedef itemprototype prototype; + typedef itemdatabase database; + item(); + item(citem&); + virtual ~item(); + virtual double GetWeaponStrength() const; + virtual truth Open(character*); + truth Consume(character*, long); + virtual truth IsHeadOfElpuri() const { return false; } + virtual truth IsPetrussNut() const { return false; } + virtual truth IsGoldenEagleShirt() const { return false; } + virtual truth IsKleinBottle() const { return false; } + virtual truth CanBeRead(character*) const { return false; } + virtual truth Read(character*); + virtual void FinishReading(character*) { } + virtual truth HitEffect(character*, character*, v2, int, int, truth) { return false; } + virtual void DipInto(liquid*, character*) { } + virtual liquid* CreateDipLiquid() { return 0; } + virtual item* BetterVersion() const { return 0; } + virtual int GetOfferValue(int) const; + virtual void Fly(character*, int, int); + int HitCharacter(character*, character*, int, double, int); + virtual truth DogWillCatchAndConsume(ccharacter*) const { return false; } + virtual truth Apply(character*); + virtual truth Zap(character*, v2, int) { return false; } + virtual truth Polymorph(character*, stack*); + virtual truth CheckPickUpEffect(character*) { return true; } + virtual void StepOnEffect(character*) { } + virtual truth IsTheAvatar() const { return false; } + virtual void SignalSquarePositionChange(int); + virtual truth CanBeEatenByAI(ccharacter*) const; + virtual truth IsExplosive() const { return false; } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void ChargeFully(character*) { } + void SetSize(int Value) { Size = Value; } + virtual int GetSize() const { return Size; } + ulong GetID() const { return ID; } + virtual void TeleportRandomly(); + virtual int GetStrengthValue() const; + slot* GetSlot(int I = 0) const { return Slot[I]; } + void SetMainSlot(slot* What) { Slot[0] = What; } + void PlaceToSlot(slot* Slot) { Slot->PutInItem(this); } + virtual void RemoveFromSlot(); + void MoveTo(stack*); + truth IsMainSlot(const slot* What) const { return Slot[0] == What; } + static cchar* GetItemCategoryName(long); + virtual truth IsConsumable() const { return true; } + truth IsEatable(ccharacter*) const; + truth IsDrinkable(ccharacter*) const; + virtual truth IsOpenable(ccharacter*) const { return false; } + virtual truth IsReadable(ccharacter*) const { return false; } + virtual truth IsDippable(ccharacter*) const { return false; } + virtual truth IsDipDestination(ccharacter*) const { return false; } + virtual truth IsAppliable(ccharacter*) const { return false; } + virtual truth IsZappable(ccharacter*) const { return false; } + virtual truth IsChargeable(ccharacter*) const { return false; } + virtual truth IsHelmet(ccharacter*) const { return false; } + virtual truth IsAmulet(ccharacter*) const { return false; } + virtual truth IsCloak(ccharacter*) const { return false; } + virtual truth IsBodyArmor(ccharacter*) const { return false; } + virtual truth IsRing(ccharacter*) const { return false; } + virtual truth IsGauntlet(ccharacter*) const { return false; } + virtual truth IsBelt(ccharacter*) const { return false; } + virtual truth IsBoot(ccharacter*) const { return false; } + virtual truth IsShield(ccharacter*) const { return false; } + virtual truth IsWeapon(ccharacter*) const { return false; } + virtual truth IsArmor(ccharacter*) const { return false; } + virtual truth IsEnchantable(ccharacter*) const { return CanBeEnchanted(); } + virtual truth IsRepairable(ccharacter*) const { return IsBroken() || IsRusted(); } + virtual truth IsDecosAdShirt(ccharacter*) const { return false; } + virtual truth IsLuxuryItem(ccharacter*) const { return false; } + virtual truth MaterialIsChangeable(ccharacter*) const { return true; } + virtual truth MaterialChangeableIs(ccharacter*) const { return IsMaterialChangeable(); } + virtual truth IsBeverage(ccharacter*) const; + virtual truth CanBeHardened(ccharacter*) const; + virtual truth HasLock(ccharacter*) const { return false; } + virtual truth IsOnGround() const; + int GetResistance(int) const; + virtual void Be(); + int GetType() const { return GetProtoType()->GetIndex(); } + virtual truth ReceiveDamage(character*, int, int, int = YOURSELF); + virtual truth RaiseTheDead(character*) { return false; } + virtual int GetBodyPartIndex() const { return 0xFF; } + const database* GetDataBase() const { return DataBase; } + virtual truth CanOpenLockType(int) const { return false; } + virtual truth IsWhip() const { return false; } + DATA_BASE_VALUE(const prototype*, ProtoType); + DATA_BASE_VALUE(int, Config); + virtual DATA_BASE_TRUTH_WITH_PARAMETER(IsDestroyable, ccharacter*); + DATA_BASE_TRUTH(IsMaterialChangeable); + DATA_BASE_VALUE(int, WeaponCategory); + DATA_BASE_TRUTH(IsAutoInitializable); + DATA_BASE_VALUE(long, Category); + DATA_BASE_VALUE(int, FireResistance); + DATA_BASE_VALUE(int, PoisonResistance); + DATA_BASE_VALUE(int, ElectricityResistance); + DATA_BASE_VALUE(int, AcidResistance); + DATA_BASE_VALUE(int, StrengthModifier); + virtual DATA_BASE_VALUE(int, FormModifier); + DATA_BASE_VALUE(int, DefaultSize); + DATA_BASE_VALUE(long, DefaultMainVolume); + DATA_BASE_VALUE(long, DefaultSecondaryVolume); + virtual DATA_BASE_VALUE_WITH_PARAMETER(v2, BitmapPos, int); + virtual DATA_BASE_VALUE(long, Price); + virtual DATA_BASE_VALUE(col24, BaseEmitation); + virtual DATA_BASE_TRUTH(UsesLongArticle); + virtual DATA_BASE_VALUE(cfestring&, Adjective); + virtual DATA_BASE_TRUTH(UsesLongAdjectiveArticle); + virtual DATA_BASE_VALUE(cfestring&, NameSingular); + virtual DATA_BASE_VALUE(cfestring&, NamePlural); + virtual DATA_BASE_VALUE(cfestring&, PostFix); + virtual DATA_BASE_VALUE(int, ArticleMode); + DATA_BASE_VALUE(const fearray&, MainMaterialConfig); + DATA_BASE_VALUE(const fearray&, SecondaryMaterialConfig); + virtual DATA_BASE_VALUE(const fearray&, MaterialConfigChances); + virtual DATA_BASE_VALUE(long, MaterialConfigChanceSum); + DATA_BASE_TRUTH(IsPolymorphable); + virtual DATA_BASE_VALUE(int, OKVisualEffects); + DATA_BASE_TRUTH(CanBeGeneratedInContainer); + virtual DATA_BASE_VALUE(int, ForcedVisualEffects); + DATA_BASE_VALUE(int, Roundness); + DATA_BASE_VALUE(long, GearStates); + DATA_BASE_TRUTH(IsTwoHanded); + DATA_BASE_TRUTH(CanBeBroken); + DATA_BASE_VALUE_WITH_PARAMETER(v2, WallBitmapPos, int); + DATA_BASE_VALUE(cfestring&, FlexibleNameSingular); + DATA_BASE_TRUTH(CanBePiled); + DATA_BASE_TRUTH(AffectsArmStrength); + DATA_BASE_TRUTH(AffectsLegStrength); + DATA_BASE_TRUTH(AffectsDexterity); + DATA_BASE_TRUTH(AffectsAgility); + DATA_BASE_TRUTH(AffectsEndurance); + DATA_BASE_TRUTH(AffectsPerception); + DATA_BASE_TRUTH(AffectsIntelligence); + DATA_BASE_TRUTH(AffectsWisdom); + DATA_BASE_TRUTH(AffectsWillPower); + DATA_BASE_TRUTH(AffectsCharisma); + DATA_BASE_TRUTH(AffectsMana); + DATA_BASE_TRUTH(AffectsCarryingCapacity); + DATA_BASE_VALUE(int, BaseEnchantment); + DATA_BASE_TRUTH(PriceIsProportionalToEnchantment); + DATA_BASE_VALUE(int, MaxCharges); + DATA_BASE_VALUE(int, MinCharges); + DATA_BASE_VALUE(int, InElasticityPenaltyModifier); + DATA_BASE_VALUE(long, StorageVolume); + DATA_BASE_VALUE(int, MaxGeneratedContainedItems); + virtual DATA_BASE_TRUTH(CanBeCloned); + virtual DATA_BASE_TRUTH(CanBeMirrored); + DATA_BASE_VALUE(int, BeamRange); + DATA_BASE_TRUTH(CanBeUsedBySmith); + DATA_BASE_VALUE(int, DamageDivider); + DATA_BASE_TRUTH(HandleInPairs); + DATA_BASE_TRUTH(CanBeEnchanted); + DATA_BASE_VALUE(long, BeamColor); + DATA_BASE_VALUE(int, BeamEffect); + DATA_BASE_VALUE(int, BeamStyle); + DATA_BASE_VALUE(int, WearWisdomLimit); + DATA_BASE_VALUE(int, BreakEffectRangeSquare); + virtual DATA_BASE_VALUE_WITH_PARAMETER(v2, WieldedBitmapPos, int); + DATA_BASE_TRUTH(IsQuestItem); + DATA_BASE_TRUTH(IsGoodWithPlants); + DATA_BASE_TRUTH(CanBePickedUp); + DATA_BASE_VALUE(int, CoverPercentile); + DATA_BASE_VALUE_WITH_PARAMETER(v2, TorsoArmorBitmapPos, int); + DATA_BASE_VALUE_WITH_PARAMETER(v2, ArmArmorBitmapPos, int); + DATA_BASE_VALUE_WITH_PARAMETER(v2, AthleteArmArmorBitmapPos, int); + DATA_BASE_VALUE_WITH_PARAMETER(v2, LegArmorBitmapPos, int); + DATA_BASE_VALUE_WITH_PARAMETER(v2, HelmetBitmapPos, int); + DATA_BASE_VALUE_WITH_PARAMETER(v2, CloakBitmapPos, int); + DATA_BASE_VALUE_WITH_PARAMETER(v2, BeltBitmapPos, int); + DATA_BASE_VALUE_WITH_PARAMETER(v2, GauntletBitmapPos, int); + DATA_BASE_VALUE_WITH_PARAMETER(v2, BootBitmapPos, int); + DATA_BASE_TRUTH(AllowEquip); + DATA_BASE_VALUE(int, ReadDifficulty); + DATA_BASE_VALUE(int, EnchantmentMinusChance); + DATA_BASE_VALUE(int, EnchantmentPlusChance); + virtual DATA_BASE_VALUE(int, TeleportPriority); + DATA_BASE_TRUTH(HasNormalPictureDirection); + DATA_BASE_VALUE(int, DamageFlags); + DATA_BASE_TRUTH(FlexibilityIsEssential); + DATA_BASE_VALUE(cfestring&, BreakMsg); + DATA_BASE_TRUTH(IsSadistWeapon); + DATA_BASE_TRUTH(IsThrowingWeapon); + truth CanBeSoldInLibrary(character* Librarian) const { return CanBeRead(Librarian); } + virtual truth TryKey(item*, character*) { return false; } + long GetBlockModifier() const; + truth IsSimiliarTo(item*) const; + virtual truth IsPickable(character*) const { return true; } + truth CanBeSeenByPlayer() const; + virtual truth CanBeSeenBy(ccharacter*) const; + festring GetDescription(int) const; + virtual square* GetSquareUnderEntity(int = 0) const; + virtual square* GetSquareUnder(int = 0) const; + virtual lsquare* GetLSquareUnder(int = 0) const; + level* GetLevel() const { return static_cast(Slot[0]->GetSquareUnder()->GetArea()); } + area* GetArea() const { return Slot[0]->GetSquareUnder()->GetArea(); } + v2 GetPos(int I = 0) const { return Slot[I]->GetSquareUnder()->GetPos(); } + square* GetNearSquare(v2 Pos) const { return Slot[0]->GetSquareUnder()->GetArea()->GetSquare(Pos); } + square* GetNearSquare(int x, int y) const { return Slot[0]->GetSquareUnder()->GetArea()->GetSquare(x, y); } + lsquare* GetNearLSquare(v2 Pos) const { return static_cast(Slot[0]->GetSquareUnder()->GetArea()->GetSquare(Pos)); } + lsquare* GetNearLSquare(int x, int y) const { return static_cast(Slot[0]->GetSquareUnder()->GetArea()->GetSquare(x, y)); } + virtual void SignalVolumeAndWeightChange(); + virtual void CalculateVolumeAndWeight(); + long GetVolume() const { return Volume; } + long GetWeight() const { return Weight; } + virtual void SignalEmitationIncrease(col24); + virtual void SignalEmitationDecrease(col24); + void CalculateAll(); + virtual void DropEquipment(stack* = 0) { } + virtual truth IsDangerous(ccharacter*) const { return false; } + virtual truth NeedDangerSymbol() const { return false; } + void WeaponSkillHit(int); + virtual void SetTeam(int) { } + void SpecialGenerationHandler(); + item* Duplicate(ulong = 0); + virtual void SetIsActive(truth) { } + double GetBaseDamage() const; + int GetBaseMinDamage() const; + int GetBaseMaxDamage() const; + int GetBaseToHitValue() const; + int GetBaseBlockValue() const; + virtual void AddInventoryEntry(ccharacter*, festring&, int, truth) const; + long GetNutritionValue() const; + virtual void SignalSpoil(material*); + virtual truth AllowSpoil() const; + item* DuplicateToStack(stack*, ulong = 0); + virtual truth CanBePiledWith(citem*, ccharacter*) const; + virtual long GetTotalExplosivePower() const { return 0; } + virtual void Break(character*, int = YOURSELF); + virtual void SetEnchantment(int) { } + virtual void EditEnchantment(int) { } + virtual void SignalEnchantmentChange(); + virtual double GetTHVBonus() const { return 0.; } + virtual double GetDamageBonus() const { return 0.; } + virtual void DrawContents(ccharacter*) { } + virtual truth IsBroken() const; + virtual truth IsFood() const; + virtual int GetEnchantment() const { return 0; } + long GetEnchantedPrice(int) const; + virtual item* Fix(); + int GetStrengthRequirement() const; + virtual int GetInElasticityPenalty(int) const { return 0; } + virtual truth IsFixableBySmith(ccharacter*) const { return false; } + virtual truth IsFixableByTailor(ccharacter*) const { return false; } + virtual long GetFixPrice() const; + virtual void DonateSlotTo(item*); + virtual int GetSpoilLevel() const; + virtual void SignalSpoilLevelChange(material*); + void ResetSpoiling(); + virtual void SetItemsInside(const fearray >&, int) { } + virtual int GetCarryingBonus() const { return 0; } + virtual truth IsBanana() const { return false; } + virtual truth IsMangoSeedling() const { return false; } + virtual truth IsEncryptedScroll() const { return false; } + virtual truth IsMondedrPass() const { return false; } + virtual truth IsRingOfThieves() const { return false; } + cchar* GetStrengthValueDescription() const; + cchar* GetBaseToHitValueDescription() const; + cchar* GetBaseBlockValueDescription() const; + virtual truth IsInCorrectSlot(int) const; + truth IsInCorrectSlot() const; + int GetEquipmentIndex() const; + room* GetRoom(int I = 0) const { return GetLSquareUnder(I)->GetRoom(); } + virtual truth HasBetterVersion() const { return false; } + virtual void SortAllItems(const sortdata&) const; + virtual truth AllowAlphaEverywhere() const { return false; } + virtual int GetAttachedGod() const; + virtual long GetTruePrice() const; + virtual void Search(ccharacter*, int) { } + virtual head* Behead() { return 0; } + virtual truth IsGorovitsFamilyRelic() const { return false; } + virtual truth EffectIsGood() const { return false; } + DATA_BASE_VALUE(long, ThrowItemTypes) +#ifdef WIZARD + virtual void AddAttackInfo(felist&) const; + void AddMiscellaneousInfo(felist&) const; +#endif + virtual void PreProcessForBone(); + virtual void PostProcessForBone(); + virtual void FinalProcessForBone() { } + virtual truth SuckSoul(character*, character* = 0) { return false; } + void SetConfig(int, int = 0); + god* GetMasterGod() const; + idholder* GetCloneMotherID() const { return CloneMotherID; } + virtual void SignalStackAdd(stackslot*, void (stack::*)(item*, truth)); + virtual int GetSquareIndex(v2) const { return 0; } + virtual void Draw(blitdata&) const; + v2 GetLargeBitmapPos(v2, int) const; + void LargeDraw(blitdata&) const; + virtual truth BunnyWillCatchAndConsume(ccharacter*) const { return false; } + void DonateIDTo(item*); + virtual void SignalRustLevelChange(); + virtual void SendNewDrawAndMemorizedUpdateRequest() const; + virtual void CalculateSquaresUnder() { SquaresUnder = 1; } + int GetSquaresUnder() const { return SquaresUnder; } + virtual void CalculateEmitation(); + void FillFluidVector(fluidvector&, int = 0) const; + virtual void SpillFluid(character*, liquid*, int = 0); + virtual void TryToRust(long); + void RemoveFluid(fluid*); + void AddFluid(liquid*, festring, int, truth); + virtual truth IsAnimated() const; + const rawbitmap* GetRawPicture() const; + void DrawFluidGearPictures(blitdata&, int = 0) const; + void DrawFluidBodyArmorPictures(blitdata&, int) const; + void CheckFluidGearPictures(v2, int, truth); + void DrawFluids(blitdata&) const; + virtual void ReceiveAcid(material*, cfestring&, long); + virtual truth ShowFluids() const { return true; } + void DonateFluidsTo(item*); + void Destroy(character*, int); + virtual truth AllowFluidBe() const { return true; } + virtual truth IsRusted() const; + virtual void RemoveRust(); + virtual truth IsBananaPeel() const { return false; } + void SetSpoilPercentage(int); + virtual pixelpredicate GetFluidPixelAllowedPredicate() const; + void RedistributeFluids(); + virtual material* GetConsumeMaterial(ccharacter*, materialpredicate = TrueMaterialPredicate) const; + virtual material* RemoveMaterial(material*); + virtual void Cannibalize(); + void InitMaterials(material*, truth = true); + void SetMainMaterial(material*, int = 0); + void ChangeMainMaterial(material*, int = 0); + virtual void GenerateMaterials(); + virtual void InitMaterials(const materialscript*, const materialscript*, truth); + int GetSquarePosition() const { return (Flags & SQUARE_POSITION_BITS) >> SQUARE_POSITION_SHIFT; } + virtual truth IsLanternOnWall() const { return false; } + virtual void DestroyBodyPart(stack*) { SendToHell(); } + int GetLifeExpectancy() const { return LifeExpectancy; } + virtual void SetLifeExpectancy(int, int); + int NeedsBe() const { return LifeExpectancy; } + truth IsVeryCloseToDisappearance() const { return LifeExpectancy && LifeExpectancy < 10; } + truth IsVeryCloseToSpoiling() const; + virtual truth IsValuable() const; + virtual truth Necromancy(character*) { return false; } + virtual void CalculateEnchantment() { } + virtual character* GetBodyPartMaster() const { return 0; } + virtual truth AllowFluids() const { return false; } + int GetHinderVisibilityBonus(ccharacter*) const; + virtual DATA_BASE_TRUTH_WITH_PARAMETER(IsKamikazeWeapon, ccharacter*); + virtual void AddTrapName(festring&, int) const; + int GetMainMaterialRustLevel() const; + truth HasID(ulong What) const { return ID == What; } + virtual truth Spoils() const; + int GetMaxSpoilPercentage() const; + truth HasPrice() const; + virtual void Disappear(); + festring GetLocationDescription() const; + truth IsEquipped() const; + festring GetExtendedDescription() const; + virtual ccharacter* FindCarrier() const; + virtual void BlockEffect(character*, character*, item*, int type) { } + virtual bool WillExplodeSoon() const { return false; } + virtual const character* GetWearer() const; + virtual bool SpecialOfferEffect(int) { return false; } + void Haste(); + void Slow(); + void SendMemorizedUpdateRequest() const; + protected: + virtual cchar* GetBreakVerb() const; + virtual long GetMaterialPrice() const; + void LoadDataBaseStats(); + virtual void PostConstruct() { } + void Initialize(int, int); + virtual int GetGraphicsContainerIndex() const; + virtual truth ShowMaterial() const; + virtual truth AllowSparkling() const { return !Fluid; } + virtual truth WeightIsIrrelevant() const { return false; } + virtual const prototype* FindProtoType() const { return &ProtoType; } + virtual truth AddStateDescription(festring&, truth) const; + static const prototype ProtoType; + slot** Slot; + int Size; + ulong ID; + const database* DataBase; + long Volume; + long Weight; + idholder* CloneMotherID; + fluid** Fluid; + int SquaresUnder; + int LifeExpectancy; + ulong ItemFlags; +}; + +#ifdef __FILE_OF_STATIC_ITEM_PROTOTYPE_DEFINITIONS__ +#define ITEM_PROTO(name, base)\ +template<> const itemprototype\ + name##sysbase::ProtoType(&base::ProtoType,\ + (itemspawner)(&name##sysbase::Spawn),\ + (itemcloner)(&name##sysbase::Clone), #name); +#else +#define ITEM_PROTO(name, base) +#endif + +#define ITEM(name, base)\ +class name;\ +typedef sysbase name##sysbase;\ +ITEM_PROTO(name, base)\ +class name : public name##sysbase + +#endif diff --git a/Main/Include/ivandef.h b/Main/Include/ivandef.h new file mode 100644 index 0000000..e1689a0 --- /dev/null +++ b/Main/Include/ivandef.h @@ -0,0 +1,1154 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __IVANDEF_H__ +#define __IVANDEF_H__ + +/* + * Global defines for the project IVAN. + * This file is created to decrease the need of including headers in + * other headers just for the sake of some silly macros, because it + * decreases compilation efficiency and may cause cross-including + * + * List of macros that should be gathered here: + * 1. all numeric defines used in multiple .cpp or .h files + * except those #defined in balance.h and confdef.h + * 2. all inline functions used in multiple .cpp or .h files + * and independent enough (do not require other headers) + * 3. class construction macros used in multiple .h files + */ + +#include "v2.h" + +#define IVAN_VERSION "CLIVAN alpha" /*Official Release 0.9990001*/ + +class item; +class material; +class character; + +typedef const item citem; +typedef const material cmaterial; +typedef const character ccharacter; + +struct databasebase +{ + int Config; + ulong CommonFlags; + ulong NameFlags; +}; + +template +class sysbase : public base +{ + public: + typedef sysbase mybase; + static type* Spawn(int Config = 0, int SpecialFlags = 0) + { + type* T = new type; + T->Initialize(Config, SpecialFlags); + return T; + } + static type* Clone(const type* T) { return new type(*T); } + virtual const prototype* FindProtoType() const { return &ProtoType; } + static const prototype ProtoType; +}; + +template +class simplesysbase : public base +{ + public: + typedef simplesysbase mybase; + static type* Spawn() { return new type; } + virtual const prototype* GetProtoType() const { return &ProtoType; } + static const prototype ProtoType; +}; + +#define SYSTEM_SPECIALIZATIONS(name)\ +name##prototype** name##_ProtoData;\ +valuemap name##_CodeNameMap;\ +int name##_ProtoSize;\ +\ +template<> name##prototype**& protocontainer::GetProtoData()\ +{ return name##_ProtoData; }\ +template<> valuemap& protocontainer::GetCodeNameMap()\ +{ return name##_CodeNameMap; }\ +template<> int& protocontainer::GetSizeRef()\ +{ return name##_ProtoSize; } + +#define EXTENDED_SYSTEM_SPECIALIZATIONS(name)\ +SYSTEM_SPECIALIZATIONS(name)\ +databasecreator::databasemembermap name##_DataBaseMemberMap;\ +template<> databasecreator::databasemembermap&\ +databasecreator::GetDataBaseMemberMap()\ +{ return name##_DataBaseMemberMap; }\ +const name##prototype name::ProtoType + +#define DATA_BASE_VALUE(type, data) type Get##data() const { return DataBase->data; } +#define DATA_BASE_VALUE_WITH_PARAMETER(type, data, param) type Get##data(param) const { return DataBase->data; } +#define DATA_BASE_TRUTH(data) truth data() const { return DataBase->data; } +#define DATA_BASE_TRUTH_WITH_PARAMETER(data, param) truth data(param) const { return DataBase->data; } + +#define HAS_HIT 0 +#define HAS_BLOCKED 1 +#define HAS_DODGED 2 +#define HAS_DIED 3 +#define DID_NO_DAMAGE 4 +#define HAS_FAILED 5 + +#define OVER_FED_LEVEL 175000 +#define BLOATED_LEVEL 150000 +#define SATIATED_LEVEL 100000 +#define NOT_HUNGER_LEVEL 30000 +#define HUNGER_LEVEL 20000 +#define VERY_HUNGER_LEVEL 10000 + +#define OVER_LOADED 0 +#define STRESSED 1 +#define BURDENED 2 +#define UNBURDENED 3 + +#define STARVING 0 +#define VERY_HUNGRY 1 +#define HUNGRY 2 +#define NOT_HUNGRY 3 +#define SATIATED 4 +#define BLOATED 5 +#define OVER_FED 6 + +#define STATES 23 + +#define POLYMORPHED (1 << 0) +#define HASTE (1 << 1) +#define SLOW (1 << 2) +#define POLYMORPH_CONTROL (1 << 3) +#define LIFE_SAVED (1 << 4) +#define LYCANTHROPY (1 << 5) +#define INVISIBLE (1 << 6) +#define INFRA_VISION (1 << 7) +#define ESP (1 << 8) +#define POISONED (1 << 9) +#define TELEPORT (1 << 10) +#define POLYMORPH (1 << 11) +#define TELEPORT_CONTROL (1 << 12) +#define PANIC (1 << 13) +#define CONFUSED (1 << 14) +#define PARASITIZED (1 << 15) +#define SEARCHING (1 << 16) +#define GAS_IMMUNITY (1 << 17) +#define LEVITATION (1 << 18) +#define LEPROSY (1 << 19) +#define HICCUPS (1 << 20) +#define VAMPIRISM (1 << 21) +#define DETECTING (1 << 22) + +#define THROW_ITEM_TYPES 5 + /*ThrowFlags */ +#define THROW_BONE (1 << 0) +#define THROW_POTION (1 << 1) +#define THROW_AXE (1 << 2) +#define THROW_GAS_GRENADE (1 << 3) +#define THROW_WAND (1 << 4) + +#define TORSO 1 +#define HEAD 2 +#define RIGHT_ARM 4 +#define LEFT_ARM 8 +#define ARMS 12 +#define GROIN 16 +#define RIGHT_LEG 32 +#define LEFT_LEG 64 +#define LEGS 96 +#define OTHER 128 +#define ALL 255 + +#define PHYSICAL_DAMAGE 1 +#define SOUND 2 +#define ACID 4 +#define FIRE 8 +#define ELECTRICITY 16 +#define ENERGY 32 +#define POISON 64 +#define DRAIN 128 +#define MUSTARD_GAS_DAMAGE 256 +#define PSI 512 +#define THROW 32768 + + +#define UNDEFINED 0 +#define MALE 1 +#define FEMALE 2 +#define TRANSSEXUAL 3 + +#define ALL_BODYPART_FLAGS 0x7F + +/* The maximum bodyparts a character can have */ + +#define MAX_BODYPARTS 7 +#define HUMANOID_BODYPARTS 7 + +#define TORSO_INDEX 0 +#define HEAD_INDEX 1 +#define RIGHT_ARM_INDEX 2 +#define LEFT_ARM_INDEX 3 +#define GROIN_INDEX 4 +#define RIGHT_LEG_INDEX 5 +#define LEFT_LEG_INDEX 6 + +#define NONE_INDEX MAX_BODYPARTS + +#define DIRECTION_COMMAND_KEYS 8 +#define EXTENDED_DIRECTION_COMMAND_KEYS 9 +#define YOURSELF 8 +#define RANDOM_DIR 9 + +#ifndef LIGHT_BORDER +#define LIGHT_BORDER 80 +#endif + +#define ALPP 0 +#define ALP 1 +#define AL 2 +#define ALM 3 +#define ANP 4 +#define AN 5 +#define ANM 6 +#define ACP 7 +#define AC 8 +#define ACM 9 +#define ACMM 10 + +#define UNARTICLED 0 +#define PLURAL 1 +#define ARTICLE_BIT 2 +#define DEFINITE 2 +#define INDEFINE_BIT 4 +#define INDEFINITE 6 +#define STRIPPED 8 + +#define TRANSPARENT_COLOR 0xF81F // pink + +#define RAW_TYPES 7 + +#define GR_GLTERRAIN 0 +#define GR_OLTERRAIN 1 +#define GR_ITEM 2 +#define GR_CHARACTER 3 +#define GR_HUMANOID 4 +#define GR_EFFECT 5 +#define GR_CURSOR 6 + +#define GRAPHIC_TYPES 4 + +#define GR_WTERRAIN 0 +#define GR_FOW 1 +#define GR_SYMBOL 2 +#define GR_SMILEY 3 + +/* SpecialFlags for graphics system. No one knows what "ST_" means... */ + +#define ST_NORMAL 0 +#define ST_RIGHT_ARM 8 +#define ST_LEFT_ARM 16 +#define ST_GROIN 24 +#define ST_RIGHT_LEG 32 +#define ST_LEFT_LEG 40 +#define ST_OTHER_BODYPART 48 +#define ST_WIELDED 56 +#define ST_CLOAK 64 +#define ST_LIGHTNING 128 +#define ST_DISALLOW_R_COLORS 256 +#define ST_FLAME_1 512 +#define ST_FLAME_2 1024 +#define ST_FLAME_3 2048 +#define ST_FLAME_4 4096 +#define ST_FLAMES (ST_FLAME_1|ST_FLAME_2|ST_FLAME_3|ST_FLAME_4) +#define ST_FLAME_SHIFT 9 + +#define WOBBLE 1 +#define WOBBLE_HORIZONTALLY_BIT 2 +#define WOBBLE_VERTICALLY WOBBLE +#define WOBBLE_HORIZONTALLY (WOBBLE|WOBBLE_HORIZONTALLY_BIT) +#define WOBBLE_SPEED_SHIFT 2 +#define WOBBLE_SPEED_RANGE (3 << WOBBLE_SPEED_SHIFT) +#define WOBBLE_FREQ_SHIFT 4 +#define WOBBLE_FREQ_RANGE (3 << WOBBLE_FREQ_SHIFT) + +cv2 SILHOUETTE_SIZE(48, 64); + +#define ITEM_CATEGORIES 18 + +#define ANY_CATEGORY 0x7FFFFFFF +#define HELMET (1 << 0) +#define AMULET (1 << 1) +#define CLOAK (1 << 2) +#define BODY_ARMOR (1 << 3) +#define WEAPON (1 << 4) +#define SHIELD (1 << 5) +#define RING (1 << 6) +#define GAUNTLET (1 << 7) +#define BELT (1 << 8) +#define BOOT (1 << 9) +#define FOOD (1 << 10) +#define POTION (1 << 11) +#define SCROLL (1 << 12) +#define BOOK (1 << 13) +#define WAND (1 << 14) +#define TOOL (1 << 15) +#define VALUABLE (1 << 16) +#define MISC (1 << 17) + +#define GOOD 1 +#define NEUTRAL 2 +#define EVIL 3 +#define TOPPLED 4 + +/* ConsumeTypes */ + +#define CT_FRUIT 1 +#define CT_MEAT 2 +#define CT_METAL 4 +#define CT_MINERAL 8 +#define CT_LIQUID 16 +#define CT_BONE 32 +#define CT_PROCESSED 64 +#define CT_MISC_ORGANIC 128 +#define CT_PLASTIC 256 +#define CT_GAS 512 + +/* Possible square positions for item. The first four are used for items + on walls */ + +#define LEFT 0 +#define DOWN 1 +#define UP 2 +#define RIGHT 3 +#define CENTER 4 // item on ground + +#define HOSTILE 1 +#define UNCARING 2 +#define FRIEND 4 + +#define MARTIAL_SKILL_CATEGORIES 3 +#define WEAPON_SKILL_CATEGORIES 11 + +#define UNARMED 0 +#define KICK 1 +#define BITE 2 +#define UNCATEGORIZED 3 +#define SMALL_SWORDS 4 +#define LARGE_SWORDS 5 +#define BLUNT_WEAPONS 6 +#define AXES 7 +#define POLE_ARMS 8 +#define WHIPS 9 +#define SHIELDS 10 + +#define LOCKED 1 + +#define EFFECT_NOTHING 0 +#define EFFECT_POISON 1 +#define EFFECT_DARKNESS 2 +#define EFFECT_OMMEL_URINE 3 +#define EFFECT_PEPSI 4 +#define EFFECT_KOBOLD_FLESH 5 +#define EFFECT_HEAL 6 +#define EFFECT_LYCANTHROPY 7 +#define EFFECT_SCHOOL_FOOD 8 +#define EFFECT_ANTIDOTE 9 +#define EFFECT_CONFUSE 10 +#define EFFECT_POLYMORPH 11 +#define EFFECT_ESP 12 +#define EFFECT_SKUNK_SMELL 13 +#define EFFECT_MAGIC_MUSHROOM 14 +#define EFFECT_TRAIN_PERCEPTION 15 +#define EFFECT_HOLY_BANANA 16 +#define EFFECT_EVIL_WONDER_STAFF_VAPOUR 17 +#define EFFECT_GOOD_WONDER_STAFF_VAPOUR 18 +#define EFFECT_PEA_SOUP 19 +#define EFFECT_BLACK_UNICORN_FLESH 20 +#define EFFECT_GRAY_UNICORN_FLESH 21 +#define EFFECT_WHITE_UNICORN_FLESH 22 +#define EFFECT_TELEPORT_CONTROL 23 +#define EFFECT_MUSHROOM 24 +#define EFFECT_OMMEL_CERUMEN 25 +#define EFFECT_OMMEL_SWEAT 26 +#define EFFECT_OMMEL_TEARS 27 +#define EFFECT_OMMEL_SNOT 28 +#define EFFECT_OMMEL_BONE 29 +#define EFFECT_MUSTARD_GAS 30 +#define EFFECT_MUSTARD_GAS_LIQUID 31 +#define EFFECT_PANIC 32 +#define EFFECT_TELEPORT 33 +#define EFFECT_VAMPIRISM 34 +#define EFFECT_DETECTING 35 +#define EFFECT_HOLY_MANGO 36 + +/* CEM = Consume End Message */ + +#define CEM_NOTHING 0 +#define CEM_SCHOOL_FOOD 1 +#define CEM_BONE 2 +#define CEM_FROG_FLESH 3 +#define CEM_OMMEL 4 +#define CEM_PEPSI 5 +#define CEM_KOBOLD_FLESH 6 +#define CEM_HEALING_LIQUID 7 +#define CEM_ANTIDOTE 8 +#define CEM_ESP 9 +#define CEM_HOLY_BANANA 10 +#define CEM_PEA_SOUP 11 +#define CEM_BLACK_UNICORN_FLESH 12 +#define CEM_GRAY_UNICORN_FLESH 13 +#define CEM_WHITE_UNICORN_FLESH 14 +#define CEM_OMMEL_BONE 15 +#define CEM_LIQUID_HORROR 16 +#define CEM_HOLY_MANGO 17 + +/* HM = Hit Message */ + +#define HM_NOTHING 0 +#define HM_SCHOOL_FOOD 1 +#define HM_FROG_FLESH 2 +#define HM_OMMEL 3 +#define HM_PEPSI 4 +#define HM_KOBOLD_FLESH 5 +#define HM_HEALING_LIQUID 6 +#define HM_ANTIDOTE 7 +#define HM_CONFUSE 8 +#define HM_HOLY_BANANA 9 +#define HM_HOLY_MANGO 10 + +#define UNARMED_ATTACK 0 +#define WEAPON_ATTACK 1 +#define KICK_ATTACK 2 +#define BITE_ATTACK 3 +#define THROW_ATTACK 4 + +#define USE_ARMS 1 +#define USE_LEGS 2 +#define USE_HEAD 4 + +#define ATTRIBUTES 11 +#define BASE_ATTRIBUTES 7 + +#define ENDURANCE 0 +#define PERCEPTION 1 +#define INTELLIGENCE 2 +#define WISDOM 3 +#define WILL_POWER 4 +#define CHARISMA 5 +#define MANA 6 + +#define ARM_STRENGTH 7 +#define LEG_STRENGTH 8 +#define DEXTERITY 9 +#define AGILITY 10 + +#define F_ENDURANCE (1 << ENDURANCE) +#define F_PERCEPTION (1 << PERCEPTION) +#define F_INTELLIGENCE (1 << INTELLIGENCE) +#define F_WISDOM (1 << WISDOM) +#define F_WILL_POWER (1 << WILL_POWER) +#define F_CHARISMA (1 << CHARISMA) +#define F_MANA (1 << MANA) + +#define F_ARM_STRENGTH (1 << ARM_STRENGTH) +#define F_LEG_STRENGTH (1 << LEG_STRENGTH) +#define F_DEXTERITY (1 << DEXTERITY) +#define F_AGILITY (1 << AGILITY) + +#define NO 0 +#define YES 1 +#define REQUIRES_ANSWER -1 + +#define DIR_ERROR 0xFF + +#define MAX_EQUIPMENT_SLOTS 13 + +#define HELMET_INDEX 0 +#define AMULET_INDEX 1 +#define CLOAK_INDEX 2 +#define BODY_ARMOR_INDEX 3 +#define BELT_INDEX 4 +#define RIGHT_WIELDED_INDEX 5 +#define LEFT_WIELDED_INDEX 6 +#define RIGHT_RING_INDEX 7 +#define LEFT_RING_INDEX 8 +#define RIGHT_GAUNTLET_INDEX 9 +#define LEFT_GAUNTLET_INDEX 10 +#define RIGHT_BOOT_INDEX 11 +#define LEFT_BOOT_INDEX 12 + +#define WORLD_MAP 255 + +#define DEFAULT_TEAM 0xFF + +/* Hard-coded teams */ + +#define PLAYER_TEAM 0 +#define MONSTER_TEAM 1 +#define ATTNAM_TEAM 2 +#define SUMO_TEAM 3 +#define IVAN_TEAM 6 +#define NEW_ATTNAM_TEAM 7 +#define COLONIST_TEAM 8 +#define TOURIST_GUIDE_TEAM 9 +#define TOURIST_TEAM 10 +#define BETRAYED_TEAM 11 +#define MONDEDR_TEAM 12 +#define KHARAZ_ARAD_TEAM 13 +#define FORESTMAN_TEAM 14 +#define SOLICITUS_TEAM 15 +#define MORBE_TEAM 16 +#define NO_TEAM 0xFFFF + +#define LOAD 1 +#define NO_PIC_UPDATE 2 +#define NO_EQUIPMENT_PIC_UPDATE (NO_PIC_UPDATE << 1) +#define NO_MATERIALS 8 +#define NO_EQUIPMENT 16 +#define NO_SIGNALS 32 + +#define NOT_WALKABLE 1 +#define HAS_CHARACTER 2 +#define IN_ROOM 4 +#define NOT_IN_ROOM 8 +#define ATTACHABLE (16|NOT_IN_ROOM) /* overrides IN_ROOM */ +#define HAS_NO_OTERRAIN 32 + +#define DEFAULT_ATTACHED_AREA 0xFE +#define DEFAULT_ATTACHED_ENTRY 0xFE +#define NO_ENTRY 0 + +#define RANDOM 0 +#define ELPURI_CAVE 1 +#define ATTNAM 2 +#define NEW_ATTNAM 3 +#define UNDER_WATER_TUNNEL 4 +#define MONDEDR 5 +#define DRAGON_TOWER 6 +#define DARK_FOREST 7 +#define MUNTUO 8 +#define XINROCH_TOMB 9 +#define UNDER_WATER_TUNNEL_EXIT 0x80 + +#define VESANA_LEVEL 2 +#define CRYSTAL_LEVEL 3 +#define SPIDER_LEVEL 4 +#define ENNER_BEAST_LEVEL 4 +#define ZOMBIE_LEVEL 5 +#define IVAN_LEVEL 7 +#define DARK_LEVEL 8 +#define OREE_LAIR 12 + +/* stack::DrawContents flags */ + +#define NO_SELECT 1 // only show items +#define NO_MULTI_SELECT 2 // select only one item +#define NO_SPECIAL_INFO 4 // show only name and amount +#define REMEMBER_SELECTED 8 // if DrawContents will be called multiple times, + // remember the selected item +#define NONE_AS_CHOICE 16 // "none" is a choice, for instance when wielding +#define SELECT_PAIR 32 // if NO_MULTI_SELECT is on, selects a pair if + // appropriate + +#define RECTANGLE 1 +#define ROUND_CORNERS 2 + +/* Gods, 0 == none */ + +#define GODS 15 + +#define VALPURUS 1 +#define LEGIFER 2 +#define ATAVUS 3 +#define DULCIS 4 +#define SEGES 5 +#define SOPHOS 6 +#define TERRA 7 +#define SILVA 7 +#define LORICATUS 8 +#define MELLIS 9 +#define CLEPTIA 10 +#define NEFAS 11 +#define SCABIES 12 +#define INFUSCOR 13 +#define CRUENTUS 14 +#define MORTIFER 15 +#define ATHEIST 16 +#define SOLICITU 16 + +#define MAX_PRICE 2147483647L + +#define PERMANENT 0xFFFF + +#define MISSED 0 +#define HIT 1 +#define CATCHED 2 + +#define BEAM_EFFECTS 13 + +#define BEAM_POLYMORPH 0 +#define BEAM_STRIKE 1 +#define BEAM_FIRE_BALL 2 +#define BEAM_TELEPORT 3 +#define BEAM_HASTE 4 +#define BEAM_SLOW 5 +#define BEAM_RESURRECT 6 +#define BEAM_INVISIBILITY 7 +#define BEAM_DUPLICATE 8 +#define BEAM_LIGHTNING 9 +#define BEAM_DOOR_CREATION 10 +#define BEAM_ACID_RAIN 11 +#define BEAM_NECROMANCY 12 + +#define BEAM_STYLES 3 + +#define PARTICLE_BEAM 0 +#define LIGHTNING_BEAM 1 +#define SHIELD_BEAM 2 + +#define RANDOM_COLOR 0x10000 + +/* Entry indices, not actual config defines */ + +#define STAIRS_UP 100 +#define STAIRS_DOWN 200 +#define WAYPOINT_DEEPER 1100 +#define WAYPOINT_SHALLOWER 1200 +#define FOUNTAIN 0xFFFF + +#define NO_LIMIT 0xFFFF + +#define ALL_ITEMS 0xFFFF + +/* StateData flags */ + +#define NO_FLAGS 0 +#define SECRET 1 +#define DUR_TEMPORARY 2 +#define DUR_PERMANENT 4 +#define DUR_FLAGS (2|4) +#define SRC_FOUNTAIN 8 +#define SRC_MUSHROOM 16 +#define SRC_MAGIC_MUSHROOM 32 +#define SRC_CONFUSE_READ 64 +#define SRC_EVIL 128 +#define SRC_GOOD 256 +#define SRC_FLAGS (8|16|32|64|128|256) +#define RANDOMIZABLE (DUR_FLAGS|SRC_FLAGS) + +#define MAP_HIDDEN 0 +#define SHOW_MAP_IN_TRUE_LIGHT 1 +#define SHOW_MAP_IN_UNIFORM_LIGHT 2 + +#define DIM_LUMINANCE 0x6E6E6E + +#define BROKEN 128 +#define WINDOW 1024 + +/* item flags */ + +#define CANNIBALIZED 4 +#define SQUARE_POSITION_BITS (16|32|64) +#define SQUARE_POSITION_SHIFT 4 + +/* bodypart flags */ + +#define UNIQUE 128 +#define BADLY_HURT 256 +#define STUCK 512 +#define BODYPART_SPARKLE_SHIFT 9 + +#define NO_BROKEN 1 +#define IGNORE_BROKEN_PRICE 2 + +#define MAX_SQUARES_UNDER 16 +#define MAX_NEIGHBOUR_SQUARES 20 + +#define N_LOCK_ID 1024 +#define S_LOCK_ID 16384 +#define LOCK_DELTA 1024 + +#define LOCK_BITS 0xFC00 + +#define BROKEN_LOCK S_LOCK_ID + +/* Normal lock types, which can be randomized */ + +#define ROUND_LOCK (N_LOCK_ID + LOCK_DELTA * 1) +#define SQUARE_LOCK (N_LOCK_ID + LOCK_DELTA * 2) +#define TRIANGULAR_LOCK (N_LOCK_ID + LOCK_DELTA * 3) + +/* Special lock types, which must be generated in the script */ + +#define HEXAGONAL_LOCK (S_LOCK_ID + LOCK_DELTA * 1) +#define OCTAGONAL_LOCK (S_LOCK_ID + LOCK_DELTA * 2) +#define HEART_SHAPED_LOCK (S_LOCK_ID + LOCK_DELTA * 3) +#define PENTAGONAL_LOCK (S_LOCK_ID + LOCK_DELTA * 4) + +#define DESERT 1 +#define JUNGLE 2 +#define STEPPE 3 +#define LEAFY_FOREST 4 +#define EVERGREEN_FOREST 5 +#define TUNDRA 6 +#define GLACIER 7 + +#define NO_MOVE 0 +#define WALK 1 +#define SWIM 2 +#define FLY 4 +#define ETHEREAL 8 +#define ANY_MOVE 15 + +#define KEY_UP_INDEX 1 +#define KEY_LEFT_INDEX 3 +#define KEY_RIGHT_INDEX 4 +#define KEY_DOWN_INDEX 6 + +#define NO_ACTION 0 +#define SUCCESS 1 +#define BLOCKED 2 + +#define STACK_SLOT 1 +#define CHARACTER_SLOT 2 +#define GEAR_SLOT 3 + +#define NOT_RUSTED 0 +#define SLIGHTLY_RUSTED 1 +#define RUSTED 2 +#define VERY_RUSTED 3 + +#define HUMAN_BODY_ARMOR_PIXELS 68 + +#define ARMOR_OUTLINE_PRIORITY ((7 << 4) + 7) +#define CLOAK_PRIORITY ((8 << 4) + 7) + +#define BODY_ARMOR_PARTS 6 + +#define SUMO_ROOM_POS v2(25, 35) +#define SUMO_ARENA_POS v2(19, 12) + +#define MAX_RAIN_DROPS 32 + +#define WON 0 +#define LOST 1 +#define DISQUALIFIED 2 + +#define EMITTER_IDENTIFIER_BITS 0xFFFF +#define EMITTER_SQUARE_PART_BITS 0xF000000 +#define EMITTER_SHADOW_BITS 0xF0000000 +#define EMITTER_SQUARE_PART_SHIFT 24 +#define EMITTER_SHADOW_SHIFT 28 + +#define RE_SUN_EMITATED 0x200000 +#define ID_X_COORDINATE 0x400000 +#define ID_BEGIN 0x800000 + +#define FORCE_ADD 0x400000 +#define SECONDARY_SUN_LIGHT 0x800000 + +/* square & lsquare flags */ + +#define ALLOW_EMITATION_CONTINUE 1 +#define FREEZED 2 /* also a stack flag */ +#define INSIDE 4 +#define NEW_DRAW_REQUEST 8 +#define STRONG_BIT 16 +#define STRONG_NEW_DRAW_REQUEST (NEW_DRAW_REQUEST|STRONG_BIT) +#define DESCRIPTION_CHANGE 32 +#define MEMORIZED_UPDATE_REQUEST 128 +#define IN_SQUARE_STACK 256 +#define CHECK_SUN_LIGHT_NEEDED 512 +#define IS_TRANSPARENT 1024 +#define PERFECTLY_QUADRI_HANDLED 2048 + +/* Slows down protosystem::BalancedCreateItem() but makes it produce more + accurate results */ + +#define BALANCED_CREATE_ITEM_ITERATIONS 100 + +#define CONFIG_TABLE_SIZE 256 + +#define SPARKLE_POS_X_ERROR 128 + +#define SKIN_COLOR 1 +#define CAP_COLOR 2 +#define HAIR_COLOR 4 +#define EYE_COLOR 8 +#define TORSO_MAIN_COLOR 16 +#define BELT_COLOR 32 +#define BOOT_COLOR 64 +#define TORSO_SPECIAL_COLOR 128 +#define ARM_MAIN_COLOR 256 +#define GAUNTLET_COLOR 512 +#define ARM_SPECIAL_COLOR 1024 +#define LEG_MAIN_COLOR 2048 +#define LEG_SPECIAL_COLOR 4096 +#define CLOTH_COLOR (CAP_COLOR\ + |TORSO_MAIN_COLOR\ + |ARM_MAIN_COLOR\ + |GAUNTLET_COLOR\ + |LEG_MAIN_COLOR) + +/* contentscript flags */ + +#define IS_LEADER 1 +#define IS_MASTER 2 + +/* stack flags */ + +/* If set, all items are always considered visible, so CanBeSeenBy calls + become unneeded */ + +#define HIDDEN 1 + +/* All costly updates (like emitation's) are avoided if this is set. + Allows much faster removing and adding items, but make sure the stack is + returned to the original state (excluding item order) before switching + this off. Note: also an lsquare flag */ + +#define FREEZED 2 + +/* End stack Flags */ + +#define SUN_BEAM_DIRECTIONS 48 + +/* Square part flags */ + +#define SP_TOP_LEFT 1 +#define SP_TOP_RIGHT 2 +#define SP_BOTTOM_LEFT 4 +#define SP_BOTTOM_RIGHT 8 +#define SP_TOP (SP_TOP_LEFT|SP_TOP_RIGHT) +#define SP_LEFT (SP_TOP_LEFT|SP_BOTTOM_LEFT) +#define SP_RIGHT (SP_TOP_RIGHT|SP_BOTTOM_RIGHT) +#define SP_BOTTOM (SP_BOTTOM_LEFT|SP_BOTTOM_RIGHT) + +#define CONDITION_COLORS 5 + +#define NATURAL_MATERIAL_FORM 0x7FFF + +#define EXP_DIVISOR 2e-8 +#define EXP_MULTIPLIER 5e+7 +#define MIN_EXP 5e+7 +#define MAX_EXP 5e+10 + +#define HAS_BEEN_GENERATED 1 +#define HAS_BEEN_SEEN 2 + +#define DEPENDS_ON_ATTRIBUTES 0xFFFF + +/* Tiredness states */ + +#define FAINTING 0 +#define EXHAUSTED 1 +#define UNTIRED 2 + +#define DEFAULT_GENERATION_DANGER 0.05 +#define ANGEL_GENERATION_DANGER 0.10 + +/* Duplication flags */ + +#define MIRROR_IMAGE 1 +#define IGNORE_PROHIBITIONS 2 +#define CHANGE_TEAM 4 +#define LE_BASE_SHIFT 3 +#define LE_BASE_RANGE 0x7FFF +#define LE_RAND_SHIFT 18 +#define LE_RAND_RANGE 0x3FFF + +/* action flags */ + +#define IN_DND_MODE 1 +#define TERMINATING 2 + +/* fluid flags */ + +#define HAS_BODY_ARMOR_PICTURES 1 +#define FLUID_INSIDE 2 + +#define COMMAND_FLAGS 4 +#define ALL_COMMAND_FLAGS (1|2|4|8) + +#define FOLLOW_LEADER 1 +#define FLEE_FROM_ENEMIES 2 +#define DONT_CHANGE_EQUIPMENT 4 +#define DONT_CONSUME_ANYTHING_VALUABLE 8 + +#define CHAT_MENU_ENTRIES 5 +#define ALL_MANAGEMENT_FLAGS (1|2|4|8|16) + +#define CHANGE_EQUIPMENT 1 +#define TAKE_ITEMS 2 +#define GIVE_ITEMS 4 +#define ISSUE_COMMANDS 8 +#define CHAT_IDLY 16 + +#define NO_PARAMETERS 0xFF + +#define CURSOR_TYPES 4 + +#define DARK_CURSOR 0 +#define RED_CURSOR 1 +#define BLUE_CURSOR 2 +#define YELLOW_CURSOR 3 +#define CURSOR_FLASH 0x2000 +#define CURSOR_TARGET 0x4000 +#define CURSOR_BIG 0x8000 +#define CURSOR_FLAGS (CURSOR_BIG|CURSOR_FLASH|CURSOR_TARGET) + +#define GRAY_FRACTAL 0 +#define RED_FRACTAL 1 +#define GREEN_FRACTAL 2 +#define BLUE_FRACTAL 3 +#define YELLOW_FRACTAL 4 + +#define DAMAGE_TYPES 3 + +#define BLUNT 1 +#define SLASH 2 +#define PIERCE 4 + +#define SILHOUETTE_TYPES 2 + +#define SILHOUETTE_NORMAL 0 +#define SILHOUETTE_INTER_LACED 1 + +#define WARNED 1 +#define HAS_CAUSED_PANIC 2 + +/* MaxHP calculation flags */ + +#define MAY_CHANGE_HPS 1 +#define CHECK_USABILITY 2 + +#define ITEM_TRAP 0x8000 +#define FLUID_TRAP 0x10000 + +/* Death flags */ + +#define FORCE_MSG 1 +#define FORCE_DEATH 2 +#define DISALLOW_CORPSE 4 +#define DISALLOW_MSG 8 +#define IGNORE_UNCONSCIOUSNESS 16 +#define IGNORE_TRAPS 32 +#define FORBID_REINCARNATION 64 + +/* character flags */ + +#define C_PLAYER 4 +#define C_INITIALIZING 8 +#define C_POLYMORPHED 16 +#define C_IN_NO_MSG_MODE 32 +#define C_PICTURE_UPDATES_FORBIDDEN 64 + +/*************************/ +/* Common DataBase flags */ +/*************************/ + +/* CommonFlags */ +#define IS_ABSTRACT 1 +#define HAS_SECONDARY_MATERIAL 2 +#define CREATE_DIVINE_CONFIGURATIONS 4 +#define CAN_BE_WISHED 8 +#define CAN_BE_DESTROYED 16 +#define IS_VALUABLE 32 +#define CAN_BE_MIRRORED 64 + +/* NameFlags */ +#define USE_AN 1 +#define USE_ADJECTIVE_AN 2 +#define NO_ARTICLE 4 // for instance "Petrus's wive number 4" +#define FORCE_THE 8 +#define SHOW_MATERIAL 16 // only works for terrains + +/****************************/ +/* Character DataBase flags */ +/****************************/ + +/* CommonFlags */ +/* NameFlags */ + +/* BodyFlags */ +#define HAS_HEAD +#define HAS_EYES +#define HAS_A_LEG +#define SPILLS_BLOOD +#define SWEATS +#define USES_NUTRITION +#define ALWAYS_USE_MATERIAL_ATTRIBUTES +#define IS_ENORMOUS +#define IS_EXTRA_FRAGILE +#define IS_PLANT +#define IS_ROOTED + +/* AbilityFlags */ +#define CAN_USE_EQUIPMENT +#define CAN_KICK +#define CAN_TALK +#define CAN_READ +#define CAN_OPEN +#define CAN_ZAP +#define CAN_THROW +#define CAN_APPLY +#define CAN_HEAR + +/* CopyrightFlags */ +#define IS_UNIQUE +#define CAN_BE_GENERATED +#define CAN_BE_NAMED + +/* EffectFlags; */ +#define BODY_PARTS_DISAPPEAR_WHEN_SEVERED +#define DESTROYS_WALLS +#define BITE_CAPTURES_BODY_PART + +/* ImmunityFlags */ +#define IMMUNITY_POLYMORPH +#define IMMUNITY_CHARM +#define IMMUNITY_CLONING +#define IMMUNITY_CONFUSE +#define IMMUNITY_LEPROSY +#define IMMUNITY_ITEM_TELEPORT +#define IMMUNITY_STICKINESS +#define IMMUNITY_CHOKING +#define IMMUNITY_UNCONSCIOUSNESS + +/* MiscFlags */ +#define CREATE_GOLEM_MATERIAL_CONFIGURATIONS 2 +#define IGNORE_DANGER 4 +#define AUTOMATICALLY_SEEN 8 +#define WILL_CARRY_ITEMS 16 +#define IS_EXTRA_COWARD 32 + +/***********************/ +/* Item DataBase flags */ +/***********************/ + +/* CommonFlags */ +/* NameFlags */ +/* AttributeAffectFlags */ + +/* GenerationFlags*/ +#define CREATE_LOCK_CONFIGURATIONS 2 +#define CAN_BE_AUTO_INITIALIZED 4 // used only in WMode +#define CAN_BE_GENERATED_IN_CONTAINER 8 +#define CAN_BE_SPAWNED_BY_POLYMORPH 16 + +/* InteractionFlags */ +#define MATERIAL_CAN_BE_CHANGED +#define CAN_BE_POLYMORPHED +#define CAN_BE_CLONED +#define CAN_BE_ENCHANTED +#define CAN_BE_BROKEN +#define AFFECTS_CARRYING_CAPACITY + +/* CategoryFlags */ +#define IS_QUEST_ITEM +#define CAN_BE_USED_BY_SMITH +#define IS_KAMIKAZE_WEAPON +#define IS_TWO_HANDED +#define IS_GOOD_WITH_PLANTS + +/* MiscFlags */ +#define HANDLE_IN_PAIRS +#define PRICE_IS_PROPORTIONAL_TO_ENCHANTMENT +#define FLEXIBILITY_IS_ESSENTIAL +#define HAS_NORMAL_PICTURE_DIRECTION +#define CAN_BE_PILED +#define CAN_BE_PICKED_UP +#define ALLOW_EQUIP + +/**************************/ +/* Terrain DataBase flags */ +/**************************/ + +/* CommonFlags */ +/* NameFlags */ + +/* OLTerrainFlags */ +#define CREATE_LOCK_CONFIGURATIONS 2 +#define CREATE_WINDOW_CONFIGURATIONS 4 +#define IS_UP_LINK +#define IS_WALL +#define USE_BORDER_TILES +#define IS_ALWAYS_TRANSPARENT +#define SHOW_THINGS_UNDER +#define IS_SAFE_TO_CREATE_DOOR + +/***************************/ +/* Material DataBase flags */ +/***************************/ + +/* CommonFlags */ +/* NameFlags (only USE_AN) */ + +/* CategoryFlags */ +#define IS_METAL 1 +#define IS_BLOOD 2 +#define CAN_BE_TAILORED 4 +#define IS_SPARKLING 8 +#define IS_SCARY 16 +#define IS_GOLEM_MATERIAL 32 +#define IS_BEVERAGE 64 + +/* BodyFlags */ +#define IS_ALIVE 1 +#define IS_WARM 2 +#define CAN_HAVE_PARASITE 4 +#define USE_MATERIAL_ATTRIBUTES 8 +#define CAN_REGENERATE 16 + +/* InteractionFlags */ +#define CAN_BURN 1 +#define CAN_EXPLODE 2 +#define CAN_DISSOLVE 4 +#define AFFECT_INSIDE 8 +#define EFFECT_IS_GOOD 16 +#define IS_AFFECTED_BY_MUSTARD_GAS 32 + +/*************************/ +/* End of DataBase flags */ +/*************************/ + +#define TILE_SIZE 16 +cv2 TILE_V2(TILE_SIZE, TILE_SIZE); + +#define SQUARE_INDEX_MASK 0xFFFF +#define ALLOW_ANIMATE 0x10000 +#define ALLOW_ALPHA 0x20000 + +#define TALENTS 4 + +#define TALENT_STRONG 0 +#define TALENT_FAST_N_ACCURATE 1 +#define TALENT_HEALTHY 2 +#define TALENT_CLEVER 3 + +#define BORDER_PARTNER_ANIMATED (16 << 24) + +/* room flags */ + +#define NO_MONSTER_GENERATION 1 + +#define NO_TAMING -1 + +#define SADIST_HIT 1 + +#define EQUIPMENT_DATAS 13 + +#define SPECIAL_CONFIGURATION_GENERATION_LEVELS 2 + +#endif diff --git a/Main/Include/level.h b/Main/Include/level.h new file mode 100644 index 0000000..95a1fed --- /dev/null +++ b/Main/Include/level.h @@ -0,0 +1,280 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __LEVEL_H__ +#define __LEVEL_H__ + +#include +#include +#include + +#include "area.h" +#include "square.h" +#include "ivandef.h" + +class levelscript; +class roomscript; +class squarescript; +class glterrain; +class olterrain; +class dungeon; +class lsquare; +class room; +class item; +class liquid; +class gas; +class level; +class material; +class team; +struct node; +struct emitter; +template struct fearray; + +struct nodepointerstorer +{ + nodepointerstorer(node* Node) : Node(Node) { } + bool operator<(const nodepointerstorer&) const; + node* Node; +}; + +struct spawnresult +{ + ccharacter* Pioneer; + int Seen; +}; + +typedef std::priority_queue nodequeue; +typedef std::vector itemvector; +typedef std::vector charactervector; +typedef std::vector emittervector; +typedef std::vector sunemittervector; +typedef character* (*characterspawner)(int, int); + +struct node +{ + node(int x, int y, lsquare* Square) : Square(Square), Pos(x, y) { } + void CalculateNextNodes(); + lsquare* Square; + node* Last; + v2 Pos; + long Distance; + long Remaining; + long TotalDistanceEstimate; + long Diagonals; + truth InNodeQueue; + truth Processed; + static node*** NodeMap; + static int RequiredWalkability; + static ccharacter* SpecialMover; + static v2 To; + static uchar** WalkabilityMap; + static int XSize, YSize; + static nodequeue* NodeQueue; +}; + +struct explosion +{ + character* Terrorist; + festring DeathMsg; + v2 Pos; + ulong ID; + int Strength; + int RadiusSquare; + int Size; + truth HurtNeutrals; +}; + +struct beamdata +{ + beamdata(character*, cfestring&, int, ulong); + beamdata(character*, cfestring&, v2, col16, int, int, int, ulong); + character* Owner; + festring DeathMsg; + v2 StartPos; + col16 BeamColor; + int BeamEffect; + int Direction; + int Range; + ulong SpecialParameters; +}; + +inline beamdata::beamdata +( + character* Owner, + cfestring& DeathMsg, + int Direction, + ulong SpecialParameters +) : +Owner(Owner), +DeathMsg(DeathMsg), +Direction(Direction), +SpecialParameters(SpecialParameters) +{ } + +inline beamdata::beamdata +( + character* Owner, + cfestring& DeathMsg, + v2 StartPos, + col16 BeamColor, + int BeamEffect, + int Direction, + int Range, + ulong SpecialParameters +) : +Owner(Owner), +DeathMsg(DeathMsg), +StartPos(StartPos), +BeamColor(BeamColor), +BeamEffect(BeamEffect), +Direction(Direction), +Range(Range), +SpecialParameters(SpecialParameters) +{ } + +class level : public area +{ + public: + level(); + virtual ~level(); + void Generate(int); + v2 GetRandomSquare(ccharacter* = 0, int = 0, const rect* = 0) const; + void GenerateMonsters(); + lsquare* GetLSquare(v2 Pos) const { return Map[Pos.X][Pos.Y]; } + lsquare* GetLSquare(int x, int y) const { return Map[x][y]; } + void GenerateTunnel(int, int, int, int, truth); + void ExpandPossibleRoute(int, int, int, int, truth); + void ExpandStillPossibleRoute(int, int, int, int, truth); + void Save(outputfile&) const; + void Load(inputfile&); + void FiatLux(); + int GetIdealPopulation() const { return IdealPopulation; } + double GetDifficulty() const { return Difficulty; } + void GenerateNewMonsters(int, truth = true); + void AttachPos(int, int); + void AttachPos(v2 Pos) { AttachPos(Pos.X, Pos.Y); } + void CreateItems(int); + truth MakeRoom(const roomscript*); + void ParticleTrail(v2, v2); + festring GetLevelMessage() { return LevelMessage; } + void SetLevelMessage(cfestring& What) { LevelMessage = What; } + void SetLevelScript(const levelscript* What) { LevelScript = What; } + truth IsOnGround() const; + const levelscript* GetLevelScript() const { return LevelScript; } + int GetLOSModifier() const; + room* GetRoom(int) const; + void SetRoom(int, room*); + void AddRoom(room*); + void Explosion(character*, cfestring&, v2, int, truth = true); + truth CollectCreatures(charactervector&, character*, truth); + void ApplyLSquareScript(const squarescript*); + virtual void Draw(truth) const; + v2 GetEntryPos(ccharacter*, int) const; + void GenerateRectangularRoom(std::vector&, std::vector&, std::vector&, const roomscript*, room*, v2, v2); + void Reveal(); + static void (level::*GetBeam(int))(beamdata&); + void ParticleBeam(beamdata&); + void LightningBeam(beamdata&); + void ShieldBeam(beamdata&); + dungeon* GetDungeon() const { return Dungeon; } + void SetDungeon(dungeon* What) { Dungeon = What; } + int GetIndex() const { return Index; } + void SetIndex(int What) { Index = What; } + truth DrawExplosion(const explosion*) const; + int TriggerExplosions(int); + lsquare*** GetMap() const { return Map; } + v2 GetNearestFreeSquare(ccharacter*, v2, truth = true) const; + v2 FreeSquareSeeker(ccharacter*, v2, v2, int, truth) const; + v2 GetFreeAdjacentSquare(ccharacter*, v2, truth) const; + static void (level::*GetBeamEffectVisualizer(int))(const fearray&, col16) const; + void ParticleVisualizer(const fearray&, col16) const; + void LightningVisualizer(const fearray&, col16) const; + truth PreProcessForBone(); + truth PostProcessForBone(); + void FinalProcessForBone(); + void GenerateDungeon(int); + void GenerateDesert(); + void GenerateJungle(); + void GenerateSteppe(); + void GenerateLeafyForest(); + void GenerateEvergreenForest(); + void GenerateTundra(); + void GenerateGlacier(); + void CreateTunnelNetwork(int, int, int, int, v2); + void SetWalkability(v2 Pos, int What) { WalkabilityMap[Pos.X][Pos.Y] = What; } + node* FindRoute(v2, v2, const std::set&, int, ccharacter* = 0); + void AddToAttachQueue(v2); + void CollectEverything(itemvector&, charactervector&); + void CreateGlobalRain(liquid*, v2); + void CheckSunLight(); + col24 GetSunLightEmitation() const { return SunLightEmitation; } + void InitSquarePartEmitationTicks(); + col24 GetAmbientLuminance() const { return AmbientLuminance; } + void ForceEmitterNoxify(const emittervector&) const; + void ForceEmitterEmitation(const emittervector&, const sunemittervector&, ulong = 0) const; + void UpdateLOS(); + void EnableGlobalRain(); + void DisableGlobalRain(); + void InitLastSeen(); + lsquare** GetSquareStack() const { return SquareStack; } + col24 GetNightAmbientLuminance() const { return NightAmbientLuminance; } + int DetectMaterial(cmaterial*); + void BlurMemory(); + void CalculateLuminances(); + int AddRadiusToSquareStack(v2, long) const; + olterrain* GetRandomFountainWithWater(olterrain*) const; + int GetEnchantmentMinusChance() { return EnchantmentMinusChance; } + int GetEnchantmentPlusChance() { return EnchantmentPlusChance; } + void Amnesia(int); + spawnresult SpawnMonsters(characterspawner, team*, v2, int = 0, int = 1, truth = false); + void AddSpecialCursors(); + void GasExplosion(gas*, lsquare*); + //olterrain* FindWard(const room*) const; + protected: + truth GenerateLanterns(int, int, int) const; + truth GenerateWindows(int, int) const; + void CreateRoomSquare(glterrain*, olterrain*, int, int, int, int) const; + void EmitSunBeams(); + void EmitSunBeam(v2, ulong, int) const; + void ChangeSunLight(); + void EmitSunLight(v2); + lsquare*** Map; + const levelscript* LevelScript; + festring LevelMessage; + std::vector Door; + std::vector Room; + int IdealPopulation; + int MonsterGenerationInterval; + double Difficulty; + dungeon* Dungeon; + int Index; + std::vector ExplosionQueue; + std::vector PlayerHurt; + node*** NodeMap; + uchar** WalkabilityMap; + std::vector AttachQueue; + liquid* GlobalRainLiquid; + v2 GlobalRainSpeed; + col24 SunLightEmitation; + v2 SunLightDirection; + col24 AmbientLuminance; + static ulong NextExplosionID; + lsquare** SquareStack; + col24 NightAmbientLuminance; + int EnchantmentMinusChance; + int EnchantmentPlusChance; +}; + +outputfile& operator<<(outputfile&, const level*); +inputfile& operator>>(inputfile&, level*&); + +#endif diff --git a/Main/Include/lock.h b/Main/Include/lock.h new file mode 100644 index 0000000..a414f00 --- /dev/null +++ b/Main/Include/lock.h @@ -0,0 +1,58 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __LOCK_H__ +#define __LOCK_H__ + +template +class lockable : public base, public lockbase +{ + public: + typedef typename lockbase::prototype prototype; + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth IsOpenable(ccharacter*) const { return true; } + virtual truth HasLock(ccharacter*) const { return true; } + virtual truth IsLocked() const { return lockbase::Locked; } + virtual void SetIsLocked(truth What) { lockbase::Locked = What; } + virtual void Lock() { lockbase::Locked = true; } + virtual int GetVirtualConfig() const { return base::GetConfig(); } + virtual void SetVirtualConfig(int What, int F = 0) { base::SetConfig(What, F); } + virtual const prototype* GetVirtualProtoType() const { return base::GetProtoType(); } + virtual festring GetVirtualDescription(int Case) const { return base::GetDescription(Case); } + virtual truth TryKey(item* K, character* C) { return lockbase::TryKey(K, C); } + protected: + virtual void PostConstruct(); +}; + +template +inline void lockable::Save(outputfile& SaveFile) const +{ + base::Save(SaveFile); + lockbase::Save(SaveFile); +} + +template +inline void lockable::Load(inputfile& SaveFile) +{ + base::Load(SaveFile); + lockbase::Load(SaveFile); +} + +template +inline void lockable::PostConstruct() +{ + lockbase::PostConstruct(); + base::PostConstruct(); +} + +#endif diff --git a/Main/Include/lsquare.h b/Main/Include/lsquare.h new file mode 100644 index 0000000..eedf07c --- /dev/null +++ b/Main/Include/lsquare.h @@ -0,0 +1,328 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __LSQUARE_H__ +#define __LSQUARE_H__ + +#include "level.h" +#include "dungeon.h" +#include "lterra.h" + +#ifndef LIGHT_BORDER +#define LIGHT_BORDER 80 +#endif + +class stack; +class glterrain; +class olterrain; +class fluid; +class material; +class item; +class smoke; +class gas; +class bodypart; +class liquid; +class rain; +class trap; +struct sortdata; + +typedef std::vector itemvector; +typedef truth (item::*sorter)(ccharacter*) const; + +struct emitter +{ + emitter(ulong ID, col24 Emitation) : ID(ID), Emitation(Emitation) { } + explicit emitter() { } + ulong ID; + col24 Emitation; +}; + +inline ulong MakeEmitterID(v2 Pos) +{ + return Pos.X + (Pos.Y << 8); +} + +inline v2 ExtractPosFromEmitterID(ulong ID) +{ + return v2(ID & 0xFF, (ID >> 8) & 0xFF); +} + +typedef std::vector emittervector; + +outputfile& operator<<(outputfile&, const emitter&); +inputfile& operator>>(inputfile&, emitter&); + +struct eyecontroller +{ + static truth Handler(int, int); + static lsquare*** Map; +}; + +struct pathcontroller +{ + static truth Handler(int, int); + static lsquare*** Map; + static ccharacter* Character; +}; + +struct stackcontroller +{ + static lsquare*** Map; + static lsquare** Stack; + static long StackIndex; + static int LevelXSize, LevelYSize; + static v2 Center; +}; + +struct tickcontroller +{ + static void PrepareShiftedTick(); + static ulong Tick; + static ulong ShiftedTick[4]; + static ulong ShiftedQuadriTick[4]; +}; + +class lsquare : public square +{ + public: + friend class level; + friend struct loscontroller; + friend struct sunbeamcontroller; + friend struct areacontroller; + friend struct emitationcontroller; + friend struct noxifycontroller; + lsquare(level*, v2); + virtual ~lsquare(); + virtual void AddCharacter(character*); + virtual void RemoveCharacter(); + stack* GetStack() const { return Stack; } + void AlterLuminance(ulong, col24); + void Emitate() { Emitate(Emitation); } + void Emitate(col24, ulong = 0); + void Clean(); + truth Open(character*); + truth Close(character*); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + void SignalEmitationIncrease(col24); + void SignalEmitationDecrease(col24); + col24 GetEmitation() const { return Emitation; } + void Noxify() { Noxify(Emitation); } + void Noxify(col24, ulong = 0); + truth NoxifyEmitter(ulong); + cchar* GetEngraved() const { return Engraved; } + truth Engrave(cfestring&); + void UpdateMemorizedDescription(truth = false); + truth BeKicked(character*, item*, bodypart*, double, double, int, int, truth, truth); + int GetDivineMaster() const; + void Draw(blitdata&) const; + void UpdateMemorized(); + truth CanBeDug() const; + virtual gterrain* GetGTerrain() const { return GLTerrain; } + virtual oterrain* GetOTerrain() const { return OLTerrain; } + glterrain* GetGLTerrain() const { return GLTerrain; } + olterrain* GetOLTerrain() const { return OLTerrain; } + void ChangeLTerrain(glterrain*, olterrain*); + level* GetLevel() const { return static_cast(AreaUnder); } + void SetLevelUnder(level* What) { AreaUnder = What; } + void ChangeGLTerrain(glterrain*); + void ChangeOLTerrain(olterrain*); + void SetLTerrain(glterrain*, olterrain*); + void ApplyScript(const squarescript*, room*); + virtual truth CanBeSeenByPlayer(truth = false) const; + virtual truth CanBeSeenFrom(v2, long, truth = false) const ; + void StepOn(character*, lsquare**); + uint GetRoomIndex() const { return RoomIndex; } + void SetRoomIndex(uint What) { RoomIndex = What; } + void ReceiveVomit(character*, liquid*); + room* GetRoom() const { return RoomIndex ? GetLevel()->GetRoom(RoomIndex) : 0; } + void SetTemporaryEmitation(col24); + col24 GetTemporaryEmitation() const { return TemporaryEmitation; } + void ChangeOLTerrainAndUpdateLights(olterrain*); + void DrawParticles(long, truth = true); + v2 DrawLightning(v2, long, int, truth = true); + truth DipInto(item*, character*); + truth TryKey(item*, character*); + void SignalSeen(ulong); + void CalculateLuminance(); + void DrawStaticContents(blitdata&) const; + void DrawMemorized(blitdata&) const; + void DrawMemorizedCharacter(blitdata&) const; + void SendMemorizedUpdateRequest(); + lsquare* GetNeighbourLSquare(int) const; + lsquare* GetNearLSquare(v2 Pos) const { return static_cast(AreaUnder->GetSquare(Pos)); } + truth IsDangerous(ccharacter*) const; + truth IsScary(ccharacter*) const; + stack* GetStackOfAdjacentSquare(int) const; + void KickAnyoneStandingHereAway(); + truth IsDark() const; + void AddItem(item*); + static truth (lsquare::*GetBeamEffect(int))(const beamdata&); + truth Polymorph(const beamdata&); + truth Strike(const beamdata&); + truth FireBall(const beamdata&); + truth Teleport(const beamdata&); + truth Haste(const beamdata&); + truth Slow(const beamdata&); + truth Confuse(const beamdata&); + truth Parasitize(const beamdata&); + truth InstillPanic(const beamdata&); + truth Resurrect(const beamdata&); + truth Invisibility(const beamdata&); + truth Duplicate(const beamdata&); + truth Lightning(const beamdata&); + truth DoorCreation(const beamdata&); + truth AcidRain(const beamdata&); + truth Necromancy(const beamdata&); + int GetLevelIndex() const { return static_cast(AreaUnder)->GetIndex(); } + int GetDungeonIndex() const { return static_cast(AreaUnder)->GetDungeon()->GetIndex(); } + dungeon* GetDungeon() const { return static_cast(AreaUnder)->GetDungeon(); } + truth CheckKick(ccharacter*) const; + void GetHitByExplosion(const explosion*); + int GetSpoiledItems() const; + void SortAllItems(const sortdata&); + truth LowerEnchantment(const beamdata&); + truth SoftenMaterial(const beamdata&); + void RemoveSmoke(smoke*); + void AddSmoke(gas*); + truth IsFlyable() const { return !OLTerrain || (OLTerrain->GetWalkability() & FLY); } + truth IsTransparent() const { return Flags & IS_TRANSPARENT; } + void SignalSmokeAlphaChange(int); + void ShowSmokeMessage() const; + void DisplaySmokeInfo(festring&) const; + truth IsDipDestination() const; + void ReceiveEarthQuakeDamage(); + truth CanBeFeltByPlayer() const; + void PreProcessForBone(); + void PostProcessForBone(double&, int&); + void DisplayEngravedInfo(festring&) const; + truth EngravingsCanBeReadByPlayer(); + truth HasEngravings() const { return truth(Engraved); } + void FinalProcessForBone(); + truth IsFreezed() const { return Flags & FREEZED; } + truth IsDangerousToBreathe(ccharacter*) const; + truth IsScaryToBreathe(ccharacter*) const; + int GetWalkability() const; + int GetTheoreticalWalkability() const { return OLTerrain ? OLTerrain->GetTheoreticalWalkability() & GLTerrain->GetTheoreticalWalkability() : GLTerrain->GetTheoreticalWalkability(); } + virtual int GetSquareWalkability() const { return GetWalkability(); } + void CalculateGroundBorderPartners(); + void RequestForGroundBorderPartnerUpdates(); + void CalculateOverBorderPartners(); + void RequestForOverBorderPartnerUpdates(); + void SpillFluid(character*, liquid*, truth = false, truth = true); + void DisplayFluidInfo(festring&) const; + void RemoveFluid(fluid*); + fluid* AddFluid(liquid*); + void DrawStacks(blitdata&) const; + truth FluidIsVisible() const { return SmokeAlphaSum < 175; } + void RemoveRain(rain*); + void AddRain(liquid*, v2, int, truth); + truth IsInside() const { return Flags & INSIDE; } + void RemoveSunLight(); + void CheckIfIsSecondarySunLightEmitter(); + void CalculateNeighbourLSquares(); + const emittervector& GetEmitter() const { return Emitter; } + void EnableGlobalRain(); + void DisableGlobalRain(); + void Freeze() { Flags |= FREEZED; } + void UnFreeze() { Flags &= ~FREEZED; } + void InitLastSeen(); + void GetSideItemDescription(festring&, truth = false) const; + void AddSunLightEmitter(ulong); + void SendSunLightSignals(); + const sunemittervector& GetSunEmitter() const { return SunEmitter; } + void ZeroReSunEmitatedFlags(); + virtual truth HasBeenSeen() const { return truth(Memorized); } + truth CalculateIsTransparent(); + void CalculateSunLightLuminance(ulong); + void CreateMemorized(); + truth DetectMaterial(cmaterial*) const; + void Reveal(ulong, truth = false); + void DestroyMemorized(); + void SwapMemorized(lsquare*); + truth HasNoBorderPartners() const; + lsquare* GetRandomAdjacentSquare() const; + void SignalPossibleTransparencyChange(); + truth AddTrap(trap*); + void RemoveTrap(trap*); + void DisplayTrapInfo(festring&) const; + void FillTrapVector(std::vector&) const; + void ReceiveTrapDamage(character*, int, int, int = YOURSELF); + truth HasDangerousTraps(ccharacter*) const; + truth HasDangerousFluids(ccharacter*) const; + void AddLocationDescription(festring&) const; + truth VomitingIsDangerous(ccharacter*) const; + bool TeleportAllSmokeAway(); + bool TeleportAllFluidsAway(); + bool TeleportAllTrapsAway(); + void AddSpecialCursors(); + protected: + void ChangeLuminance(col24&, col24); + void RemoveLuminance(col24&); + void CalculateEmitation(); + void UpdateStaticContentCache(col24) const; + mutable struct staticcontentcache + { + staticcontentcache() : Bitmap(0), Luminance(0) { } + bitmap* Bitmap; + col24 Luminance; + } StaticContentCache; + fluid* Fluid; + smoke* Smoke; + rain* Rain; + trap* Trap; + emittervector Emitter; + sunemittervector SunEmitter; + glterrain* GLTerrain; + olterrain* OLTerrain; + stack* Stack; + bitmap* Memorized; + bitmap* FowMemorized; + char* Engraved; + glterrain** GroundBorderPartnerTerrain; + ulong GroundBorderPartnerInfo; + olterrain** OverBorderPartnerTerrain; + ulong OverBorderPartnerInfo; + ulong SquarePartEmitationTick; + ulong SquarePartLastSeen; + col24 Emitation; + int SmokeAlphaSum; + lsquare* NeighbourLSquare[8]; + col24 AmbientLuminance; + col24 SunLightLuminance; + col24 TemporaryEmitation; + col24 SecondarySunLightEmitation; + ushort LastExplosionID; + uchar RoomIndex; +}; + +inline truth lsquare::IsDark() const +{ + col24 Light = Luminance; + + return (!Light + || ((Light & 0xFF0000) < (LIGHT_BORDER << 16) + && (Light & 0x00FF00) < (LIGHT_BORDER << 8) + && (Light & 0x0000FF) < LIGHT_BORDER)); +} + +inline truth eyecontroller::Handler(int x, int y) +{ + return Map[x][y]->IsTransparent(); +} + +inline lsquare* lsquare::GetNeighbourLSquare(int I) const +{ + return I < 8 ? NeighbourLSquare[I] : const_cast(this); +} + +#endif diff --git a/Main/Include/lterra.h b/Main/Include/lterra.h new file mode 100644 index 0000000..21b325c --- /dev/null +++ b/Main/Include/lterra.h @@ -0,0 +1,367 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __LTERRA_H__ +#define __LTERRA_H__ + +#include "object.h" +#include "terra.h" +#include "script.h" + +class glterrain; +class olterrain; +class item; +class level; +class lsquare; +class stack; +class room; +class liquid; +class materialscript; +class glterrainprototype; +class olterrainprototype; +template class databasecreator; +struct blitdata; + +typedef glterrain* (*glterrainspawner)(int, int); +typedef olterrain* (*olterrainspawner)(int, int); + +struct lterraindatabase : public databasebase +{ + void PostProcess() { } + v2 BitmapPos; + truth UsesLongArticle; + festring Adjective; + truth UsesLongAdjectiveArticle; + festring NameSingular; + festring NamePlural; + festring PostFix; + int ArticleMode; + fearray MainMaterialConfig; + fearray SecondaryMaterialConfig; + fearray MaterialConfigChances; + long MaterialConfigChanceSum; + int OKVisualEffects; + col16 MaterialColorB; + col16 MaterialColorC; + col16 MaterialColorD; + festring SitMessage; + int AttachedGod; + int Walkability; + truth IsAbstract; + truth ShowMaterial; + truth HasSecondaryMaterial; + truth UseBorderTiles; + int BorderTilePriority; +}; + +class lterrain : public object +{ + public: + lterrain() : LSquareUnder(0) { } + virtual void Load(inputfile&); + virtual truth Open(character*) { return false; } + virtual truth Close(character*) { return false; } + v2 GetPos() const; + virtual truth CanBeOpened() const { return false; } + virtual truth AcceptsOffers() const { return false; } + virtual truth ReceiveVomit(character*, liquid*) { return false; } + virtual truth CanBeOpenedByAI() { return false; } + virtual truth Polymorph(character*) { return false; } + virtual truth DipInto(item*, character*) { return false; } + virtual truth IsDipDestination() const { return false; } + virtual truth TryKey(item*, character*) { return false; } + truth CanBeSeenByPlayer() const; + truth CanBeSeenBy(character*) const; + virtual cfestring& GetSitMessage() const = 0; + virtual truth SitOn(character*); + virtual square* GetSquareUnderEntity(int = 0) const; + void SetLSquareUnder(lsquare* What) { LSquareUnder = What; } + lsquare* GetLSquareUnder() const { return LSquareUnder; } + level* GetLevel() const; + lsquare* GetNearLSquare(v2) const; + lsquare* GetNearLSquare(int, int) const; + virtual void CalculateAll() { CalculateEmitation(); } + virtual void SignalEmitationIncrease(col24); + virtual void SignalEmitationDecrease(col24); + virtual truth HasKeyHole() const { return CanBeOpened(); } + virtual truth IsOnGround() const { return true; } + room* GetRoom() const; + virtual void SignalRustLevelChange(); + virtual void TryToRust(long); + virtual void ReceiveAcid(material*, long) { } + void InitMaterials(material*, truth = true); + void SetMainMaterial(material*, int = 0); + void ChangeMainMaterial(material*, int = 0); + virtual void GenerateMaterials(); + virtual void InitMaterials(const materialscript*, const materialscript*, truth); + virtual const fearray& GetMainMaterialConfig() const = 0; + virtual void SurviveEffect(character*) { } + virtual void RestoreHP() { } + virtual void AddLocationDescription(festring&) const; + protected: + void Initialize(int, int); + virtual void PostConstruct() { } + virtual void InstallDataBase(int) = 0; + lsquare* LSquareUnder; +}; + +struct glterraindatabase : public lterraindatabase +{ + typedef glterrainprototype prototype; + void InitDefaults(const prototype*, int); + truth AllowRandomInstantiation() const { return true; } + const prototype* ProtoType; +}; + +class glterrainprototype +{ + public: + friend class databasecreator; + glterrainprototype(const glterrainprototype*, glterrainspawner, cchar*); + glterrain* Spawn(int Config = 0, int SpecialFlags = 0) const { return Spawner(Config, SpecialFlags); } + glterrain* SpawnAndLoad(inputfile&) const; + cchar* GetClassID() const { return ClassID; } + int GetIndex() const { return Index; } + const glterrainprototype* GetBase() const { return Base; } + int CreateSpecialConfigurations(glterraindatabase**, int Configs, int) { return Configs; } + const glterraindatabase* ChooseBaseForConfig(glterraindatabase** TempConfig, int, int) { return *TempConfig; } + const glterraindatabase*const* GetConfigData() const { return ConfigData; } + int GetConfigSize() const { return ConfigSize; } + private: + int Index; + const glterrainprototype* Base; + glterraindatabase** ConfigData; + glterraindatabase** ConfigTable[CONFIG_TABLE_SIZE]; + int ConfigSize; + glterrainspawner Spawner; + cchar* ClassID; +}; + +class glterrain : public lterrain, public gterrain +{ + public: + friend class databasecreator; + typedef glterrainprototype prototype; + typedef glterraindatabase database; + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual int GetEntryDifficulty() const { return 1; } + int GetType() const { return GetProtoType()->GetIndex(); } + const database* GetDataBase() const { return DataBase; } + DATA_BASE_VALUE(const prototype*, ProtoType); + DATA_BASE_VALUE(int, Config); + DATA_BASE_TRUTH(UsesLongArticle); + DATA_BASE_VALUE(cfestring&, Adjective); + DATA_BASE_TRUTH(UsesLongAdjectiveArticle); + DATA_BASE_VALUE(cfestring&, NameSingular); + DATA_BASE_VALUE(cfestring&, NamePlural); + DATA_BASE_VALUE(cfestring&, PostFix); + DATA_BASE_VALUE(int, ArticleMode); + virtual DATA_BASE_VALUE(const fearray&, MainMaterialConfig); + DATA_BASE_VALUE(const fearray&, SecondaryMaterialConfig); + virtual DATA_BASE_VALUE(const fearray&, MaterialConfigChances); + virtual DATA_BASE_VALUE(long, MaterialConfigChanceSum); + DATA_BASE_VALUE(int, OKVisualEffects); + virtual DATA_BASE_VALUE_WITH_PARAMETER(col16, MaterialColorB, int); + virtual DATA_BASE_VALUE_WITH_PARAMETER(col16, MaterialColorC, int); + virtual DATA_BASE_VALUE_WITH_PARAMETER(col16, MaterialColorD, int); + virtual DATA_BASE_VALUE(cfestring&, SitMessage); + DATA_BASE_TRUTH(ShowMaterial); + DATA_BASE_VALUE(int, Walkability); + DATA_BASE_TRUTH(UseBorderTiles); + DATA_BASE_VALUE(int, BorderTilePriority); + virtual int GetAttachedGod() const; + virtual int GetTheoreticalWalkability() const { return DataBase->Walkability; } + void Draw(blitdata&) const; + virtual truth IsAnimated() const; + protected: + virtual void InstallDataBase(int); + virtual int GetGraphicsContainerIndex() const; + virtual const prototype* FindProtoType() const { return &ProtoType; } + virtual v2 GetBitmapPos(int) const; + v2 GetBorderBitmapPos(v2, int) const; + virtual void ModifyAnimationFrames(int&) const; + static const prototype ProtoType; + const database* DataBase; +}; + +struct olterraindatabase : public lterraindatabase +{ + typedef olterrainprototype prototype; + void InitDefaults(const prototype*, int); + truth AllowRandomInstantiation() const; + const prototype* ProtoType; + festring DigMessage; + int RestModifier; + festring RestMessage; + long StorageVolume; + int HPModifier; + v2 OpenBitmapPos; + v2 WindowBitmapPos; + fearray > LeftOverItems; + truth CreateDivineConfigurations; + truth CanBeDestroyed; + truth IsUpLink; + truth CreateLockConfigurations; + truth IsAlwaysTransparent; + truth ShowThingsUnder; + truth IsWall; + truth CreateWindowConfigurations; + truth IsSafeToCreateDoor; +}; + +class olterrainprototype +{ + public: + friend class databasecreator; + olterrainprototype(const olterrainprototype*, olterrainspawner, cchar*); + olterrain* Spawn(int Config = 0, int SpecialFlags = 0) const { return Spawner(Config, SpecialFlags); } + olterrain* SpawnAndLoad(inputfile&) const; + cchar* GetClassID() const { return ClassID; } + int GetIndex() const { return Index; } + const olterrainprototype* GetBase() const { return Base; } + int CreateSpecialConfigurations(olterraindatabase**, int, int); + const olterraindatabase* ChooseBaseForConfig(olterraindatabase** TempConfig, int, int) { return *TempConfig; } + const olterraindatabase*const* GetConfigData() const { return ConfigData; } + int GetConfigSize() const { return ConfigSize; } + private: + int Index; + const olterrainprototype* Base; + olterraindatabase** ConfigData; + olterraindatabase** ConfigTable[CONFIG_TABLE_SIZE]; + int ConfigSize; + olterrainspawner Spawner; + cchar* ClassID; +}; + +class olterrain : public lterrain, public oterrain +{ + public: + friend class databasecreator; + typedef olterrainprototype prototype; + typedef olterraindatabase database; + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth Enter(truth) const; + virtual void BeKicked(character*, int, int); + virtual truth IsDoor() const { return false; } + virtual truth HasEatEffect() const { return false; } + virtual truth HasDrinkEffect() const { return false; } + virtual truth Eat(character*) { return false; } + virtual truth Drink(character*) { return false; } + virtual void Lock() { } + virtual truth IsLocked() const { return false; } + virtual void CreateBoobyTrap() { } + virtual void HasBeenHitByItem(character*, item*, int) { } + virtual void Break(); + int GetHP() const { return HP; } + void EditHP(int What) { HP += What; } + virtual void ReceiveDamage(character*, int, int); + int GetType() const { return GetProtoType()->GetIndex(); } + const database* GetDataBase() const { return DataBase; } + void ShowRestMessage(character*) const; + DATA_BASE_VALUE(const prototype*, ProtoType); + DATA_BASE_VALUE(int, Config); + DATA_BASE_TRUTH(UsesLongArticle); + DATA_BASE_VALUE(cfestring&, Adjective); + DATA_BASE_TRUTH(UsesLongAdjectiveArticle); + DATA_BASE_VALUE(cfestring&, NameSingular); + DATA_BASE_VALUE(cfestring&, NamePlural); + DATA_BASE_VALUE(cfestring&, PostFix); + DATA_BASE_VALUE(int, ArticleMode); + virtual DATA_BASE_VALUE(const fearray&, MainMaterialConfig); + DATA_BASE_VALUE(const fearray&, SecondaryMaterialConfig); + virtual DATA_BASE_VALUE(const fearray&, MaterialConfigChances); + virtual DATA_BASE_VALUE(long, MaterialConfigChanceSum); + DATA_BASE_VALUE(int, OKVisualEffects); + virtual DATA_BASE_VALUE_WITH_PARAMETER(col16, MaterialColorB, int); + virtual DATA_BASE_VALUE_WITH_PARAMETER(col16, MaterialColorC, int); + virtual DATA_BASE_VALUE_WITH_PARAMETER(col16, MaterialColorD, int); + virtual DATA_BASE_VALUE(cfestring&, SitMessage); + DATA_BASE_TRUTH(ShowMaterial); + DATA_BASE_VALUE(cfestring&, DigMessage); + truth CanBeDestroyed() const; + DATA_BASE_VALUE(int, RestModifier); + DATA_BASE_VALUE(cfestring&, RestMessage); + DATA_BASE_TRUTH(IsUpLink); + DATA_BASE_VALUE(long, StorageVolume); + DATA_BASE_VALUE(int, HPModifier); + DATA_BASE_TRUTH(IsSafeToCreateDoor); + DATA_BASE_VALUE_WITH_PARAMETER(v2, OpenBitmapPos, int); + DATA_BASE_TRUTH(UseBorderTiles); + DATA_BASE_VALUE(int, BorderTilePriority); + virtual DATA_BASE_VALUE(int, Walkability); + DATA_BASE_TRUTH(IsAlwaysTransparent); + DATA_BASE_TRUTH(CreateWindowConfigurations); + DATA_BASE_VALUE(v2, WindowBitmapPos); + DATA_BASE_VALUE(const fearray >&, LeftOverItems); + DATA_BASE_TRUTH(IsWall); + virtual void SetAttachedArea(int) { } + virtual void SetAttachedEntry(int) { } + virtual void SetText(cfestring&) { } + virtual festring GetText() const; + virtual void SetItemsInside(const fearray >&, int) { } + int GetStrengthValue() const; + virtual void SignalVolumeAndWeightChange() { HP = CalculateMaxHP(); } + int CalculateMaxHP(); + virtual int GetAttachedGod() const; + void SetConfig(int, int = 0); + god* GetMasterGod() const; + virtual truth IsTransparent() const; + virtual void Draw(blitdata&) const; + virtual int GetTheoreticalWalkability() const { return DataBase->Walkability; } + virtual void BeDestroyed() { Break(); } + virtual void ReceiveAcid(material*, long); + virtual void SignalRustLevelChange(); + virtual truth IsFountainWithWater() const { return false; } + truth ShowThingsUnder() const; + truth WillBeDestroyedBy(ccharacter*) const; + virtual void PreProcessForBone() { } + virtual void PostProcessForBone() { } + virtual void FinalProcessForBone() { } + virtual void RestoreHP() { HP = CalculateMaxHP(); } + virtual truth IsAnimated() const; + virtual truth VomitingIsDangerous(ccharacter*) const { return false; } + virtual void AddSpecialCursors() { } + virtual truth IsWard() const { return false; } + protected: + virtual v2 GetBitmapPos(int) const; + v2 GetBorderBitmapPos(v2, int) const; + virtual void ModifyAnimationFrames(int&) const; + virtual void InstallDataBase(int); + virtual int GetGraphicsContainerIndex() const; + virtual const prototype* FindProtoType() const { return &ProtoType; } + static const prototype ProtoType; + const database* DataBase; + int HP; +}; + +#ifdef __FILE_OF_STATIC_LTERRAIN_PROTOTYPE_DEFINITIONS__ +#define LTERRAIN_PROTO(name, base, protobase)\ +template<> const protobase##prototype\ + name##sysbase::ProtoType(&base::ProtoType,\ + (protobase##spawner)(&name##sysbase::Spawn),\ + #name); +#else +#define LTERRAIN_PROTO(name, base, protobase) +#endif + +#define LTERRAIN(name, base, protobase)\ +class name;\ +typedef sysbase name##sysbase;\ +LTERRAIN_PROTO(name, base, protobase)\ +class name : public name##sysbase + +#define GLTERRAIN(name, base) LTERRAIN(name, base, glterrain) +#define OLTERRAIN(name, base) LTERRAIN(name, base, olterrain) + +#endif diff --git a/Main/Include/lterras.h b/Main/Include/lterras.h new file mode 100644 index 0000000..e231de1 --- /dev/null +++ b/Main/Include/lterras.h @@ -0,0 +1,277 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __LTERRAS_H__ +#define __LTERRAS_H__ + +#include "lterra.h" + +class ghost; + +OLTERRAIN(wall, olterrain) +{ +}; + +OLTERRAIN(decoration, olterrain) +{ +}; + +GLTERRAIN(solidterrain, glterrain) +{ +}; + +OLTERRAIN(door, olterrain) +{ + public: + virtual truth Open(character*); + virtual truth Close(character*); + virtual truth CanBeOpened() const { return !Opened; } + virtual void BeKicked(character*, int, int); + virtual void SetIsOpened(truth What) { Opened = What; } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth IsDoor() const { return true; } + virtual void SetIsLocked(truth What) { Locked = What; } + virtual truth IsLocked() const { return Locked; } + virtual truth CanBeOpenedByAI(); + virtual void ReceiveDamage(character*, int, int); + virtual void CreateBoobyTrap(); + virtual void ActivateBoobyTrap(); + virtual truth TryKey(item*, character*); + virtual void SetParameters(int); + virtual void Lock() { SetIsLocked(true); } + virtual void HasBeenHitByItem(character*, item*, int); + virtual truth IsTransparent() const; + virtual int GetWalkability() const; + virtual int GetTheoreticalWalkability() const; + virtual void BeDestroyed(); + protected: + virtual void PostConstruct(); + virtual truth AddAdjective(festring&, truth) const; + virtual void Break(); + virtual v2 GetBitmapPos(int) const; + virtual void MakeWalkable(); + virtual void MakeNotWalkable(); + virtual int GetBoobyTrap() { return BoobyTrap; } + virtual void SetBoobyTrap(int What) { BoobyTrap = What; } + truth Opened; + truth Locked; + int BoobyTrap; +}; + +OLTERRAIN(stairs, olterrain) +{ + public: + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth Enter(truth) const; + virtual void StepOn(character*); + virtual void SetAttachedArea(int What) { AttachedArea = What; } + virtual void SetAttachedEntry(int What) { AttachedEntry = What; } + int GetAttachedArea() const { return AttachedArea; } + int GetAttachedEntry() const { return AttachedEntry; } + virtual void AddSpecialCursors(); + protected: + virtual void PostConstruct(); + int AttachedArea; + int AttachedEntry; +}; + +OLTERRAIN(portal, stairs) +{ + protected: + virtual int GetClassAnimationFrames() const { return 32; } + virtual v2 GetBitmapPos(int) const; +}; + +OLTERRAIN(altar, olterrain) +{ + public: + virtual truth AcceptsOffers() const { return true; } + virtual void StepOn(character*); + virtual void BeKicked(character*, int, int); + virtual truth ReceiveVomit(character*, liquid*); + virtual truth Polymorph(character*); + virtual truth SitOn(character*); + virtual void Draw(blitdata&) const; + virtual truth VomitingIsDangerous(ccharacter*) const; +}; + +OLTERRAIN(throne, decoration) +{ + public: + virtual truth SitOn(character*); +}; + +OLTERRAIN(olterraincontainer, olterrain) +{ + public: + olterraincontainer(); + virtual ~olterraincontainer(); + virtual truth Open(character*); + virtual truth CanBeOpened() const { return true; } + virtual stack* GetContained() const { return Contained; } + virtual void Load(inputfile&); + virtual void Save(outputfile&) const; + virtual void SetItemsInside(const fearray >&, int); + virtual void Break(); + virtual truth AllowContentEmitation() const { return false; } + virtual void PreProcessForBone(); + virtual void PostProcessForBone(); + virtual void FinalProcessForBone(); + protected: + stack* Contained; +}; + +OLTERRAIN(fountain, olterrain) +{ + public: + virtual ~fountain(); + virtual truth SitOn(character*); + virtual truth Drink(character*); + virtual truth HasDrinkEffect() const { return true; } + virtual void DryOut(); + virtual truth DipInto(item*, character*); + virtual truth IsDipDestination() const; + virtual material* GetSecondaryMaterial() const { return SecondaryMaterial; } + virtual void SetSecondaryMaterial(material*, int = 0); + virtual void ChangeSecondaryMaterial(material*, int = 0); + void InitMaterials(material*, material*, truth = true); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual int GetMaterials() const { return 2; } + virtual material* GetMaterial(int) const; + virtual void InitMaterials(const materialscript*, const materialscript*, truth); + virtual truth IsFountainWithWater() const; + virtual int GetSparkleFlags() const; + protected: + virtual void GenerateMaterials(); + virtual col16 GetMaterialColorB(int) const; + virtual void AddPostFix(festring& String, int) const { AddContainerPostFix(String); } + virtual truth AddAdjective(festring&, truth) const; + virtual v2 GetBitmapPos(int) const; + material* SecondaryMaterial; +}; + +OLTERRAIN(brokendoor, door) +{ + public: + virtual void BeKicked(character*, int, int); + virtual void ReceiveDamage(character*, int, int); + virtual void HasBeenHitByItem(character*, item*, int); + virtual void Break() { olterrain::Break(); } +}; + +GLTERRAIN(liquidterrain, glterrain) +{ + public: + virtual cchar* SurviveMessage() const; + virtual cchar* MonsterSurviveMessage() const; + virtual cchar* DeathMessage() const; + virtual cchar* MonsterDeathVerb() const; + virtual cchar* ScoreEntry() const; + virtual truth IsFatalToStay() const { return true; } + virtual truth DipInto(item*, character*); + virtual truth IsDipDestination() const { return true; } + virtual void SurviveEffect(character*); + virtual void AddLocationDescription(festring&) const; + protected: + virtual void AddPostFix(festring& String, int) const { AddLumpyPostFix(String); } + virtual int GetClassAnimationFrames() const { return 32; } + virtual v2 GetBitmapPos(int) const; +}; + +OLTERRAIN(boulder, olterrain) +{ +}; + +OLTERRAIN(sign, olterrain) +{ + public: + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void SetText(cfestring& What) { Text = What; } + virtual festring GetText() const; + virtual void AddPostFix(festring&, int) const; + virtual void StepOn(character*); + protected: + festring Text; +}; + +OLTERRAIN(ward, olterrain) +{ + public: + virtual truth IsWard() const { return true; } +}; + +OLTERRAIN(earth, olterrain) +{ + public: + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + protected: + virtual void PostConstruct(); + virtual v2 GetBitmapPos(int) const; + int PictureIndex; +}; + +OLTERRAIN(monsterportal, olterrain) +{ + public: + monsterportal(); + protected: + virtual int GetClassAnimationFrames() const { return 32; } + virtual v2 GetBitmapPos(int) const; +}; + +OLTERRAIN(coffin, olterraincontainer) +{ + public: + coffin(); + virtual ~coffin(); + virtual truth Open(character*); + virtual truth CanBeOpened() const { return true; } + virtual stack* GetContained() const { return Contained; } + virtual void Load(inputfile&); + virtual void Save(outputfile&) const; + virtual void SetItemsInside(const fearray >&, int); + virtual void Break(); + virtual truth AllowContentEmitation() const { return false; } + virtual void PreProcessForBone(); + virtual void PostProcessForBone(); + virtual void FinalProcessForBone(); + protected: + stack* Contained; + virtual void GenerateGhost(lsquare*); +}; + +OLTERRAIN(barwall, olterrain) +{ + public: + void Break(); +}; + +OLTERRAIN(ironmaiden, olterrain) +{ + public: + ironmaiden() : Opened(false) {} + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth Open(character*); + virtual truth CanBeOpened() const { return !Opened; } + virtual truth Close(character*); + protected: + virtual v2 GetBitmapPos(int) const; + truth Opened; +}; + +#endif diff --git a/Main/Include/materia.h b/Main/Include/materia.h new file mode 100644 index 0000000..b5d568c --- /dev/null +++ b/Main/Include/materia.h @@ -0,0 +1,255 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __MATERIA_H__ +#define __MATERIA_H__ + +#include "script.h" +#include "ivandef.h" + +#define MAKE_MATERIAL material::MakeMaterial + +class entity; +class bodypart; +class materialprototype; +template class databasecreator; + +typedef material* (*materialspawner)(int, long, truth); +typedef material* (*materialcloner)(cmaterial*); + +struct materialdatabase : public databasebase +{ + typedef materialprototype prototype; + void InitDefaults(const prototype*, int); + void PostProcess() { } + const prototype* ProtoType; + ulong CategoryFlags; + ulong BodyFlags; + ulong InteractionFlags; + int StrengthValue; + int ConsumeType; + int Density; + int Color; + int RainColor; + long PriceModifier; + col24 Emitation; + int NutritionValue; + festring NameStem; + festring AdjectiveStem; + int Effect; + int ConsumeEndMessage; + int HitMessage; + long ExplosivePower; + alpha Alpha; + int Flexibility; + int SpoilModifier; + int EffectStrength; + int DigProductMaterial; + int ConsumeWisdomLimit; + int AttachedGod; + festring BreatheMessage; + int StepInWisdomLimit; + int RustModifier; + int Acidicity; + contentscript NaturalForm; + int HardenedMaterial; + int SoftenedMaterial; + int IntelligenceRequirement; + int Stickiness; + truth DisablesPanicWhenConsumed; +}; + +class materialprototype +{ + public: + friend class databasecreator; + materialprototype(const materialprototype*, materialspawner, materialcloner, cchar*); + material* Spawn(int Config, long Volume = 0) const { return Spawner(Config, Volume, false); } + material* SpawnAndLoad(inputfile&) const; + material* Clone(cmaterial* Material) const { return Cloner(Material); } + cchar* GetClassID() const { return ClassID; } + int GetIndex() const { return Index; } + const materialprototype* GetBase() const { return Base; } + int CreateSpecialConfigurations(materialdatabase**, int Configs, int) { return Configs; } + const materialdatabase* ChooseBaseForConfig(materialdatabase** TempConfig, int, int) { return *TempConfig; } + const materialdatabase*const* GetConfigData() const { return ConfigData; } + int GetConfigSize() const { return ConfigSize; } + private: + int Index; + const materialprototype* Base; + materialdatabase** ConfigData; + materialdatabase** ConfigTable[CONFIG_TABLE_SIZE]; + int ConfigSize; + materialspawner Spawner; + materialcloner Cloner; + cchar* ClassID; +}; + +class material +{ + public: + friend class databasecreator; + typedef materialprototype prototype; + typedef materialdatabase database; + material(int NewConfig, long InitVolume = 0, truth Load = false) : MotherEntity(0) { Initialize(NewConfig, InitVolume, Load); } + material() : MotherEntity(0) { } + virtual ~material() { } + void AddName(festring&, truth = false, truth = true) const; + festring GetName(truth = false, truth = true) const; + material* TakeDipVolumeAway(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + truth Effect(character*, int, long); + virtual material* EatEffect(character*, long); + truth HitEffect(character*, bodypart*); + virtual col16 GetSkinColor() const { return GetColor(); } + virtual void SetSkinColor(int) { } + long GetRawPrice() const; + truth CanBeDug(material* ShovelMaterial) const; + virtual truth HasBe() const { return false; } + virtual void Be(ulong) { } + int GetType() const { return GetProtoType()->GetIndex(); } + virtual void AddConsumeEndMessage(character*) const; + DATA_BASE_VALUE(const prototype*, ProtoType); + DATA_BASE_VALUE(int, Config); + DATA_BASE_VALUE(ulong, CommonFlags); + DATA_BASE_VALUE(ulong, NameFlags); + DATA_BASE_VALUE(ulong, CategoryFlags); + DATA_BASE_VALUE(ulong, BodyFlags); + DATA_BASE_VALUE(ulong, InteractionFlags); + virtual DATA_BASE_VALUE(int, StrengthValue); + DATA_BASE_VALUE(int, ConsumeType); + DATA_BASE_VALUE(int, Density); + DATA_BASE_VALUE(int, Color); + DATA_BASE_VALUE(int, RainColor); + DATA_BASE_VALUE(long, PriceModifier); + DATA_BASE_VALUE(col24, Emitation); + DATA_BASE_VALUE(int, NutritionValue); + DATA_BASE_VALUE(cfestring&, NameStem); + DATA_BASE_VALUE(cfestring&, AdjectiveStem); + DATA_BASE_VALUE(int, Effect); + DATA_BASE_VALUE(int, ConsumeEndMessage); + DATA_BASE_VALUE(int, HitMessage); + DATA_BASE_VALUE(long, ExplosivePower); + DATA_BASE_VALUE(alpha, Alpha); + DATA_BASE_VALUE(int, Flexibility); + DATA_BASE_VALUE(int, SpoilModifier); + DATA_BASE_VALUE(int, EffectStrength); + DATA_BASE_VALUE(int, DigProductMaterial); + DATA_BASE_VALUE(int, ConsumeWisdomLimit); + DATA_BASE_VALUE(int, AttachedGod); + DATA_BASE_VALUE(int, RustModifier); + DATA_BASE_VALUE(int, Acidicity); + DATA_BASE_VALUE(const contentscript&, NaturalForm); + DATA_BASE_VALUE(int, IntelligenceRequirement); + DATA_BASE_VALUE(int, Stickiness); + const database* GetDataBase() const { return DataBase; } + material* SpawnMore() const { return GetProtoType()->Spawn(GetConfig(), Volume); } + material* SpawnMore(long Volume) const { return GetProtoType()->Spawn(GetConfig(), Volume); } + long GetTotalExplosivePower() const; + static material* MakeMaterial(int, long = 0); + virtual truth IsFlesh() const { return false; } + virtual truth IsLiquid() const { return false; } + virtual cchar* GetConsumeVerb() const; + entity* GetMotherEntity() const { return MotherEntity; } + void SetMotherEntity(entity* What) { MotherEntity = What; } + truth IsSameAs(cmaterial* What) const { return What->GetConfig() == GetConfig(); } + truth IsTransparent() const { return GetAlpha() != 255; } + virtual long GetTotalNutritionValue() const; + virtual truth IsVeryCloseToSpoiling() const { return false; } + virtual void AddWetness(long) { } + virtual int GetSpoilLevel() const { return 0; } + virtual void ResetSpoiling() { } + truth CanBeEatenByAI(ccharacter*) const; + virtual void SetSpoilCounter(int) { } + DATA_BASE_VALUE(cfestring&, BreatheMessage); + truth BreatheEffect(character*); + virtual truth SkinColorIsSparkling() const { return IsSparkling(); } + virtual void SetSkinColorIsSparkling(truth) { } + DATA_BASE_VALUE(int, StepInWisdomLimit); + virtual void SetRustLevel(int) { } + virtual int GetRustLevel() const { return NOT_RUSTED; } + virtual int GetRustData() const { return NOT_RUSTED; } + virtual truth TryToRust(long, long = 0) { return false; } + static const database* GetDataBase(int); + virtual truth CanSpoil() const { return false; } + truth IsSolid() const { return !IsLiquid(); } + /* A dummy materialpredicate */ + truth True() const { return true; } + void FinishConsuming(character*); + long GetVolume() const { return Volume; } + long GetWeight() const + { + return Volume ? long(double(Volume) * GetDensity() / 1000) : 0; + } + void EditVolume(long What) { SetVolume(Volume + What); } + void SetVolume(long); + void SetVolumeNoSignals(long What) { Volume = What; } + virtual truth IsPowder() const { return false; } + static item* CreateNaturalForm(int, long); + item* CreateNaturalForm(long) const; + virtual truth IsInfectedByLeprosy() const { return false; } + virtual void SetIsInfectedByLeprosy(truth) { } + virtual truth AddRustLevelDescription(festring&, truth) const { return false; } + int GetHardenedMaterial(citem*) const; + int GetSoftenedMaterial(citem*) const; + int GetHardenModifier(citem*) const; + virtual int GetSpoilPercentage() const { return 0; } + virtual truth Spoils() const { return false; } + virtual truth IsExplosive() const; + virtual truth IsSparkling() const; + material* Duplicate() const { return DataBase->ProtoType->Clone(this); } + truth IsStuckTo(ccharacter*) const; + DATA_BASE_TRUTH(DisablesPanicWhenConsumed); + protected: + virtual void PostConstruct() { } + void Initialize(int, long, truth); + virtual const prototype* FindProtoType() const { return &ProtoType; } + static const prototype ProtoType; + const database* DataBase; + entity* MotherEntity; + long Volume; +}; + +template +class materialsysbase : public base +{ + public: + typedef materialsysbase mybase; + static type* Spawn(int Config = 0, long Volume = 0, truth Load = false) + { + type* M = new type; + M->Initialize(Config, Volume, Load); + return M; + } + static material* Clone(const type* T) { return new type(*T); } + virtual const materialprototype* FindProtoType() const { return &ProtoType; } + static const materialprototype ProtoType; +}; + +#ifdef __FILE_OF_STATIC_MATERIAL_PROTOTYPE_DEFINITIONS__ +#define MATERIAL_PROTO(name, base)\ +template<> const materialprototype\ + name##sysbase::ProtoType(&base::ProtoType,\ + (materialspawner)(&name##sysbase::Spawn),\ + (materialcloner)(&name##sysbase::Clone), #name); +#else +#define MATERIAL_PROTO(name, base) +#endif + +#define MATERIAL(name, base)\ +class name;\ +typedef materialsysbase name##sysbase;\ +MATERIAL_PROTO(name, base)\ +class name : public name##sysbase + +#endif diff --git a/Main/Include/materias.h b/Main/Include/materias.h new file mode 100644 index 0000000..57e1624 --- /dev/null +++ b/Main/Include/materias.h @@ -0,0 +1,115 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __MATERIAS_H__ +#define __MATERIAS_H__ + +#include "materia.h" + +class lterrain; + +MATERIAL(solid, material) +{ +}; + +MATERIAL(organic, solid) +{ + public: + virtual void Be(ulong); + virtual truth HasBe() const { return true; } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth IsVeryCloseToSpoiling() const { return SpoilLevel == 8; } + virtual int GetSpoilLevel() const { return SpoilLevel; } + virtual void ResetSpoiling(); + virtual material* EatEffect(character*, long); + virtual void AddConsumeEndMessage(character*) const; + virtual void SetSpoilCounter(int); + virtual truth CanSpoil() const { return true; } + virtual int GetSpoilPercentage() const; + virtual truth Spoils() const { return true; } + protected: + virtual void PostConstruct(); + ushort SpoilCounter; + uchar SpoilCheckCounter; + uchar SpoilLevel; +}; + +MATERIAL(gas, material) +{ +}; + +MATERIAL(liquid, material) +{ + public: + virtual cchar* GetConsumeVerb() const; + virtual truth IsLiquid() const { return true; } + void TouchEffect(item*, cfestring&); + void TouchEffect(character*, int); + void TouchEffect(lterrain*); + liquid* SpawnMoreLiquid(long Volume) const { return static_cast(SpawnMore(Volume)); } +}; + +MATERIAL(flesh, organic) +{ + public: + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual col16 GetSkinColor() const { return SkinColor; } + virtual void SetSkinColor(int What) { SkinColor = What; } + virtual truth SkinColorIsSparkling() const { return SkinColorSparkling; } + virtual void SetSkinColorIsSparkling(truth What) { SkinColorSparkling = What; } + virtual truth IsFlesh() const { return true; } + virtual void SetIsInfectedByLeprosy(truth What) { InfectedByLeprosy = What; } + virtual truth IsInfectedByLeprosy() const { return InfectedByLeprosy; } + protected: + virtual void PostConstruct(); + col16 SkinColor; + truth SkinColorSparkling; + truth InfectedByLeprosy; +}; + +MATERIAL(powder, liquid) +{ + public: + powder() : Wetness(0) { } + virtual truth IsPowder() const { return true; } + virtual truth IsExplosive() const; + virtual void AddWetness(long What) { Wetness += What; } + virtual void Be(ulong); + virtual truth HasBe() const { return true; } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + protected: + long Wetness; +}; + +/* Materials that can rust */ + +MATERIAL(ironalloy, solid) +{ + public: + ironalloy() : RustData(NOT_RUSTED) { } + virtual void SetRustLevel(int); + virtual int GetStrengthValue() const; + virtual int GetRustLevel() const { return RustData & 3; } + virtual truth IsSparkling() const; + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual int GetRustData() const { return RustData; } + virtual truth TryToRust(long, long = 0); + virtual truth AddRustLevelDescription(festring&, truth) const; + protected: + int RustData; +}; + +#endif diff --git a/Main/Include/message.h b/Main/Include/message.h new file mode 100644 index 0000000..6ab97ff --- /dev/null +++ b/Main/Include/message.h @@ -0,0 +1,57 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __MESSAGE_H__ +#define __MESSAGE_H__ + +#include "v2.h" + +#define ADD_MESSAGE msgsystem::AddMessage + +class felist; +class outputfile; +class inputfile; +class bitmap; +class festring; + +class msgsystem +{ + public: + static void LIKE_PRINTF(1, 2) AddMessage(cchar*, ...); + static void Draw(); + static void DrawMessageHistory(); + static void Format(); + static void Save(outputfile&); + static void Load(inputfile&); + static void ScrollDown(); + static void ScrollUp(); + static void EnableMessages() { Enabled = true; } + static void DisableMessages() { Enabled = false; } + static void EnterBigMessageMode() { BigMessageMode = true; } + static void LeaveBigMessageMode(); + static void Init(); + static void InitMessagesSinceLastKeyScan(); + static void ThyMessagesAreNowOld(); + private: + static felist MessageHistory; + static festring LastMessage; + static festring BigMessage; + static int Times; + static v2 Begin, End; + static truth Enabled; + static truth BigMessageMode; + static truth MessagesChanged; + static bitmap* QuickDrawCache; + static int LastMessageLines; +}; + +#endif diff --git a/Main/Include/miscitem.h b/Main/Include/miscitem.h new file mode 100644 index 0000000..1a80a13 --- /dev/null +++ b/Main/Include/miscitem.h @@ -0,0 +1,799 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __MISCITEM_H__ +#define __MISCITEM_H__ + +#include "item.h" +#include "game.h" /// check +#include "trap.h" +#include "wterras.h" + +ITEM(materialcontainer, item) +{ + public: + materialcontainer() { } + materialcontainer(const materialcontainer&); + virtual ~materialcontainer(); + virtual material* GetSecondaryMaterial() const { return SecondaryMaterial; } + virtual void SetSecondaryMaterial(material*, int = 0); + virtual void ChangeSecondaryMaterial(material*, int = 0); + void InitMaterials(material*, material*, truth = true); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual int GetMaterials() const { return 2; } + virtual void SignalSpoil(material*); + virtual truth CanBePiledWith(citem*, ccharacter*) const; + virtual void Be(); + virtual int GetSpoilLevel() const; + virtual material* GetMaterial(int) const; + virtual int GetAttachedGod() const; + virtual material* GetConsumeMaterial(ccharacter*, materialpredicate = TrueMaterialPredicate) const; + virtual material* RemoveMaterial(material*); + material* RemoveMainMaterial(); + virtual material* RemoveSecondaryMaterial(); + virtual void CalculateEmitation(); + virtual void InitMaterials(const materialscript*, const materialscript*, truth); + virtual int GetSparkleFlags() const; + protected: + virtual long GetMaterialPrice() const; + virtual truth CalculateHasBe() const; + virtual void GenerateMaterials(); + virtual col16 GetMaterialColorB(int) const; + virtual alpha GetAlphaB(int) const; + virtual int GetRustDataB() const; + material* SecondaryMaterial; +}; + +ITEM(banana, materialcontainer) +{ + public: + banana() : TimesUsed(0), Charges(6), Jammed(false) { } + virtual truth Zap(character*, v2, int); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void ChargeFully(character*) { TimesUsed = 0; } + virtual truth IsZappable(ccharacter*) const { return true; } + virtual truth IsChargeable(ccharacter*) const { return true; } + virtual void SignalSpoil(material*); + virtual truth IsBanana() const { return true; } + virtual material* RemoveSecondaryMaterial(); + protected: + int TimesUsed; + int Charges; + truth Jammed; +}; + +ITEM(holybanana, banana) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); + virtual truth Zap(character*, v2, int); + virtual void Be() { } + virtual int GetSpecialFlags() const; + virtual void AddInventoryEntry(ccharacter*, festring&, int, truth) const; + virtual truth ReceiveDamage(character*, int, int, int); +}; + +ITEM(mango, item) +{ +}; + +ITEM(holymango, item) +{ + public: + virtual truth HitEffect(character*, character*, v2, int, int, truth); + ////virtual truth Zap(character*, v2, int); + ////virtual void Be() { } + virtual int GetSpecialFlags() const; + virtual void AddInventoryEntry(ccharacter*, festring&, int, truth) const; + virtual truth ReceiveDamage(character*, int, int, int); + virtual truth IsMango() const { return true; } +}; + +ITEM(lantern, item) +{ + public: + virtual void SignalSquarePositionChange(int); + virtual truth AllowAlphaEverywhere() const { return true; } + virtual int GetSpecialFlags() const; + virtual truth IsLanternOnWall() const { return GetSquarePosition() != CENTER; } + protected: + virtual int GetClassAnimationFrames() const { return !IsBroken() ? 32 : 1; } + virtual col16 GetMaterialColorA(int) const; + virtual col16 GetMaterialColorB(int) const; + virtual col16 GetMaterialColorC(int) const; + virtual col16 GetMaterialColorD(int) const; + virtual alpha GetAlphaA(int) const { return 255; } + virtual alpha GetAlphaB(int) const; + virtual alpha GetAlphaC(int) const; + virtual alpha GetAlphaD(int) const; + virtual v2 GetBitmapPos(int) const; +}; + +/*ITEM(christmaslight, lantern) +{ + public: + virtual void CalculateEmitation() const; + protected: + virtual col16 GetMaterialColorA(int) const; + virtual col16 GetMaterialColorB(int) const; + virtual col16 GetMaterialColorC(int) const; + virtual col16 GetMaterialColorD(int) const; + + virtual alpha GetAlphaA(int) const { return 255; } + virtual alpha GetAlphaB(int) const; + virtual alpha GetAlphaC(int) const; + virtual alpha GetAlphaD(int) const; +};*/ + +/*ITEM(theredlantern, lantern) +{ + public: + virtual truth Zap(character*, v2, int); + virtual truth IsZappable(const character*) const { return true; } +}*/ + +ITEM(can, materialcontainer) +{ + public: + virtual item* BetterVersion() const; + virtual void DipInto(liquid*, character*); + virtual truth IsDippable(ccharacter*) const { return !SecondaryMaterial; } + virtual truth IsDipDestination(ccharacter*) const; + virtual liquid* CreateDipLiquid(); + virtual truth AllowSpoil() const { return false; } // temporary + virtual truth Spoils() const { return false; } // temporary + virtual truth HasBetterVersion() const { return !SecondaryMaterial; } + protected: + virtual void AddPostFix(festring& String, int) const { AddContainerPostFix(String); } + virtual truth AddAdjective(festring&, truth) const; + virtual v2 GetBitmapPos(int) const; +}; + +ITEM(lump, item) +{ + protected: + virtual void AddPostFix(festring& String, int) const { AddLumpyPostFix(String); } + virtual truth ShowMaterial() const { return false; } + virtual truth WeightIsIrrelevant() const { return true; } +}; + +ITEM(potion, materialcontainer) +{ + public: + virtual item* BetterVersion() const; + virtual void DipInto(liquid*, character*); + virtual liquid* CreateDipLiquid(); + virtual truth IsDippable(ccharacter*) const { return !SecondaryMaterial; } + virtual void Break(character*, int); + virtual truth IsDipDestination(ccharacter*) const; + virtual truth IsExplosive() const; + virtual truth ReceiveDamage(character*, int, int, int); + virtual truth HasBetterVersion() const { return !SecondaryMaterial; } + virtual truth EffectIsGood() const; + virtual truth IsKamikazeWeapon(ccharacter*) const { return IsExplosive(); } + protected: + virtual void AddPostFix(festring& String, int) const { AddContainerPostFix(String); } + virtual truth AddAdjective(festring&, truth) const; +}; + +ITEM(kleinbottle, materialcontainer) +{ + public: + //virtual item* BetterVersion() const; // better version is a filled version of the klein bottle + virtual void DipInto(liquid*, character*); + virtual liquid* CreateDipLiquid(); + virtual truth IsDippable(ccharacter*) const { return !SecondaryMaterial; } + virtual material* GetConsumeMaterial(ccharacter*, materialpredicate = TrueMaterialPredicate) const; + //virtual void Break(character*, int); // cannot be broken, remove this + //virtual truth IsDipDestination(ccharacter*) const; // cannot be dipped into + //virtual truth IsExplosive() const; // cannot explode contents since the contents resides in another dimension + //virtual truth ReceiveDamage(character*, int, int, int); // should not recieve damage + //virtual truth HasBetterVersion() const { return !SecondaryMaterial; } // cannot be filled by mellis + //virtual truth EffectIsGood() const; + //virtual truth IsKamikazeWeapon(ccharacter*) const { return IsExplosive(); } // no explode, so false + virtual truth IsKleinBottle() const { return true; } + protected: + virtual void AddPostFix(festring& String, int) const { AddContainerPostFix(String); } + virtual truth AddAdjective(festring&, truth) const; +}; + +ITEM(cauldron, materialcontainer) +{ + public: + virtual item* BetterVersion() const; + virtual void DipInto(liquid*, character*); + virtual liquid* CreateDipLiquid(); + virtual truth IsDippable(ccharacter*) const { return !SecondaryMaterial; } + //virtual void Break(character*, int); + virtual truth IsDipDestination(ccharacter*) const; + virtual truth IsExplosive() const; + //virtual truth ReceiveDamage(character*, int, int, int);// can't break ... unless it turns into a lump of copper?? hmmm. Maybe can have a cracked cauldron. + virtual truth HasBetterVersion() const { return !SecondaryMaterial; } + virtual truth EffectIsGood() const; + virtual truth IsKamikazeWeapon(ccharacter*) const { return IsExplosive(); } + protected: + virtual void AddPostFix(festring& String, int) const { AddContainerPostFix(String); } + virtual truth AddAdjective(festring&, truth) const; +}; + +ITEM(bananapeels, item) +{ + public: + virtual item* BetterVersion() const; + virtual truth HasBetterVersion() const { return true; } + virtual void StepOnEffect(character*); + virtual truth IsBananaPeel() const { return true; } + virtual truth IsDangerous(ccharacter*) const; + virtual truth RaiseTheDead(character*); +}; + +ITEM(brokenbottle, item) +{ + public: + virtual truth IsBroken() const { return true; } + virtual item* BetterVersion() const; + virtual truth HasBetterVersion() const { return true; } + virtual void StepOnEffect(character*); + virtual item* Fix(); + virtual truth IsDangerous(ccharacter*) const; +}; + +ITEM(scroll, item) +{ + public: + virtual truth CanBeRead(character*) const; + virtual truth IsReadable(ccharacter*) const { return true; } + virtual truth ReceiveDamage(character*, int, int, int); +}; + +ITEM(scrollofteleportation, scroll) +{ + public: + virtual void FinishReading(character*); +}; + +ITEM(scrolloffireballs, scroll) +{ + public: + virtual void FinishReading(character*); + virtual truth IsExplosive() const { return true; } +}; + +ITEM(scrollofcharging, scroll) +{ + public: + virtual void FinishReading(character*); +}; + +ITEM(locationmap, item) +{ + public: + virtual truth CanBeRead(character*) const; + virtual truth IsReadable(ccharacter*) const { return true; } + virtual truth ReceiveDamage(character*, int, int, int); +}; + +ITEM(maptotombofxinroch, locationmap) +{ + public: + virtual void FinishReading(character*); +}; + +ITEM(nut, item) +{ +}; + +ITEM(leftnutofpetrus, nut) +{ + public: + virtual void Be() { } + virtual truth IsPetrussNut() const { return true; } + virtual truth IsConsumable() const { return false; } +}; + +ITEM(bone, item) +{ + public: + virtual truth DogWillCatchAndConsume(ccharacter*) const; +}; + +ITEM(loaf, item) +{ + protected: + virtual void AddPostFix(festring& String, int) const { AddLumpyPostFix(String); } + virtual truth ShowMaterial() const { return false; } +}; + +ITEM(scrollofwishing, scroll) +{ + public: + virtual void FinishReading(character*); +}; + +ITEM(copyofleftnutofpetrus, nut) +{ +}; + +ITEM(wand, item) +{ + public: + virtual truth Apply(character*); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void ChargeFully(character*) { TimesUsed = 0; } + virtual truth IsAppliable(ccharacter*) const { return true; } + virtual truth IsZappable(ccharacter*) const { return true; } + virtual truth IsChargeable(ccharacter*) const { return true; } + virtual truth ReceiveDamage(character*, int, int, int); + virtual truth Zap(character*, v2, int); + virtual void AddInventoryEntry(ccharacter*, festring&, int, truth) const; + virtual long GetPrice() const; + virtual truth IsExplosive() const { return true; } + protected: + virtual void PostConstruct(); + void BreakEffect(character*, cfestring&); + ulong GetSpecialParameters() const; + int Charges; + int TimesUsed; +}; + +ITEM(scrollofchangematerial, scroll) +{ + public: + virtual void FinishReading(character*); +}; + +ITEM(avatarofvalpurus, item) +{ + public: + virtual void Be() { } + virtual truth IsTheAvatar() const { return true; } + virtual truth IsConsumable() const { return false; } +}; + +ITEM(kiwi, item) +{ +}; + +ITEM(pineapple, item) +{ +}; + +ITEM(palmbranch, item) +{ + public: + virtual truth IsShield(ccharacter*) const { return true; } +}; + +ITEM(backpack, materialcontainer) +{ + public: + virtual truth Apply(character*); + virtual truth IsAppliable(ccharacter*) const { return true; } + virtual truth ReceiveDamage(character*, int, int, int); + virtual truth IsExplosive() const; + virtual long GetTotalExplosivePower() const; + virtual void SpillFluid(character*, liquid*, int = 0); + protected: + virtual void AddPostFix(festring& String, int) const { AddContainerPostFix(String); } +}; + +ITEM(holybook, item) +{ + public: + virtual truth CanBeRead(character*) const; + virtual truth IsReadable(ccharacter*) const { return true; } + virtual truth ReceiveDamage(character*, int, int, int); + virtual void FinishReading(character*); + protected: + virtual col16 GetMaterialColorA(int) const; + virtual truth ShowMaterial() const { return false; } +}; + +ITEM(fiftymillionroubles, item) +{ +}; + +ITEM(oillamp, item) +{ + public: + oillamp(); + oillamp(const oillamp&); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth GetInhabitedByGenie() const { return InhabitedByGenie; } + virtual void SetInhabitedByGenie(truth What) { InhabitedByGenie = What; } + virtual truth Apply(character*); + virtual truth IsAppliable(ccharacter*) const { return true; } + protected: + truth InhabitedByGenie; +}; + +ITEM(stone, item) +{ + public: + virtual long GetTruePrice() const; + virtual truth IsLuxuryItem(ccharacter*) const { return GetTruePrice() > 0; } + protected: + virtual truth WeightIsIrrelevant() const { return true; } +}; + +ITEM(solstone, stone) +{ + public: + virtual truth AllowAlphaEverywhere() const { return true; } + protected: + virtual int GetClassAnimationFrames() const; + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; +}; + +ITEM(scrolloftaming, scroll) +{ + public: + virtual void FinishReading(character*); +}; + +ITEM(mine, itemtrap) +{ + public: + virtual void StepOnEffect(character*); + virtual truth ReceiveDamage(character*, int, int, int); + virtual truth Apply(character* User); + virtual truth IsDangerous(ccharacter* Stepper) const { return WillExplode(Stepper); } + virtual truth WillExplode(ccharacter*) const; + virtual truth CheckPickUpEffect(character*); + protected: + virtual truth AddAdjective(festring&, truth) const; +}; + +ITEM(key, item) +{ + public: + virtual truth Apply(character*); + virtual truth IsAppliable(ccharacter*) const { return true; } + virtual truth CanOpenDoors() const { return true; } + virtual truth CanOpenLockType(int AnotherLockType) const { return GetConfig() == AnotherLockType; } +}; + +ITEM(headofelpuri, item) // can't wear equipment, so not "head" +{ + public: + virtual truth IsHeadOfElpuri() const { return true; } + virtual truth IsConsumable() const { return false; } + virtual void Be() { } +}; + +ITEM(whistle, item) +{ + public: + virtual truth Apply(character*); + virtual truth IsAppliable(ccharacter*) const { return true; } + virtual void BlowEffect(character*); + protected: + virtual col16 GetMaterialColorB(int) const; +}; + +ITEM(magicalwhistle, whistle) +{ + public: + magicalwhistle() : LastUsed(0) { } + virtual void BlowEffect(character*); + virtual void Load(inputfile&); + virtual void Save(outputfile&) const; + virtual void FinalProcessForBone(); + protected: + ulong LastUsed; +}; + +ITEM(itemcontainer, lockableitem) +{ + public: + itemcontainer(); + itemcontainer(const itemcontainer&); + virtual ~itemcontainer(); + virtual truth Open(character*); + virtual void Load(inputfile&); + virtual void Save(outputfile&) const; + virtual truth Polymorph(character*, stack*); + virtual void CalculateVolumeAndWeight(); + virtual truth ContentsCanBeSeenBy(ccharacter*) const; + virtual long GetTruePrice() const; + virtual truth ReceiveDamage(character*, int, int, int); + virtual void DrawContents(ccharacter*); + virtual truth Apply(character* Applier) { return Open(Applier); } + virtual truth IsAppliable(ccharacter*) const { return true; } + virtual void SetItemsInside(const fearray >&, int); + virtual truth AllowContentEmitation() const { return false; } + virtual truth IsDestroyable(ccharacter*) const; + virtual int GetOfferValue(int) const; + virtual void SortAllItems(const sortdata&) const; + virtual void PreProcessForBone(); + virtual void PostProcessForBone(); + virtual void FinalProcessForBone(); + virtual material* RemoveMaterial(material*); + virtual void SetLifeExpectancy(int, int); + virtual void CalculateEnchantment(); + virtual int GetTeleportPriority() const; + virtual void SetParameters(int); + virtual void Disappear(); + virtual stack* GetContained() const { return Contained; } + protected: + virtual col16 GetMaterialColorB(int) const; + virtual void PostConstruct(); + stack* Contained; +}; + +ITEM(beartrap, itemtrap) +{ + public: + beartrap(); + beartrap(const beartrap&); + virtual ~beartrap(); + virtual void Load(inputfile&); + virtual void Save(outputfile&) const; + virtual void StepOnEffect(character*); + virtual truth CheckPickUpEffect(character*); + virtual truth IsPickable(character*) const; + virtual truth Apply(character*); + virtual v2 GetBitmapPos(int) const; + virtual truth IsDangerous(ccharacter*) const { return Active; } + virtual truth ReceiveDamage(character*, int, int, int); + virtual truth NeedDangerSymbol() const { return IsActive(); } + virtual void Fly(character*, int, int); + virtual ulong GetTrapID() const { return TrapData.TrapID; } + virtual ulong GetVictimID() const { return TrapData.VictimID; } + virtual void UnStick() { TrapData.VictimID = 0; } + virtual void UnStick(int I) { TrapData.BodyParts &= ~(1 << I); } + virtual truth TryToUnStick(character*, v2); + virtual void RemoveFromSlot(); + virtual int GetTrapType() const { return GetType() | ITEM_TRAP; } + virtual void PreProcessForBone(); + virtual void PostProcessForBone(); + virtual void DonateSlotTo(item*); + protected: + virtual truth AddAdjective(festring&, truth) const; + truth IsStuck() const { return TrapData.VictimID; } + int GetBaseTrapDamage() const; + trapdata TrapData; +}; + +ITEM(stethoscope, item) +{ + public: + virtual truth Apply(character*); + virtual truth IsAppliable(ccharacter*) const { return true; }; +}; + +ITEM(scrollofenchantweapon, scroll) +{ + public: + virtual void FinishReading(character*); +}; + +ITEM(scrollofenchantarmor, scroll) +{ + public: + virtual void FinishReading(character*); +}; + +ITEM(skull, item) +{ +}; + +ITEM(scrollofrepair, scroll) +{ + public: + virtual void FinishReading(character*); +}; + +ITEM(encryptedscroll, scroll) +{ + public: + virtual void Be() { } + virtual truth Read(character*); + virtual truth ReceiveDamage(character*, int, int, int) { return false; } + virtual truth IsEncryptedScroll() const { return true; } +}; + +ITEM(mondedrpass, scroll) +{ + public: + virtual void FinishReading(character*); + virtual truth IsMondedrPass() const { return true; } +}; + +ITEM(horn, item) +{ + public: + horn() : LastUsed(0) { } + virtual void Load(inputfile&); + virtual void Save(outputfile&) const; + virtual truth Apply(character*); + virtual truth IsAppliable(ccharacter*) const { return true; } + virtual void FinalProcessForBone(); + protected: + ulong LastUsed; +}; + +ITEM(carrot, item) +{ + public: + virtual truth BunnyWillCatchAndConsume(ccharacter*) const; + protected: + virtual col16 GetMaterialColorB(int) const; +}; + +ITEM(charmlyre, item) +{ + public: + charmlyre(); + virtual truth Apply(character*); + virtual truth IsAppliable(ccharacter*) const { return true; } + virtual void Load(inputfile&); + virtual void Save(outputfile&) const; + virtual void FinalProcessForBone(); + protected: + virtual col16 GetMaterialColorB(int) const; + ulong LastUsed; +}; + +ITEM(scrollofdetectmaterial, scroll) +{ + public: + virtual void FinishReading(character*); +}; + +ITEM(stick, item) +{ + protected: + virtual void AddPostFix(festring& String, int) const { AddLumpyPostFix(String); } + virtual truth ShowMaterial() const { return false; } + virtual truth WeightIsIrrelevant() const { return true; } +}; + +ITEM(scrollofhardenmaterial, scroll) +{ + public: + virtual void FinishReading(character*); +}; + +ITEM(scrollofgolemcreation, scroll) +{ + public: + virtual void FinishReading(character*); +}; + + +ITEM(gasgrenade, materialcontainer) +{ + protected: + virtual void AddPostFix(festring& String, int) const { AddContainerPostFix(String); } + truth ReceiveDamage(character* Damage, int Damage, int Type, int); +}; + +ITEM(holyhandgrenade, item) +{ + public: + virtual truth Apply(character*); + virtual truth IsAppliable(ccharacter*) const { return true; } + virtual truth CalculateHasBe() const; + virtual void Explode(); + virtual void Be(); + virtual v2 GetBitmapPos(int) const; + virtual int GetClassAnimationFrames() const; + virtual alpha GetOutlineAlpha(int) const; + virtual col16 GetOutlineColor(int) const; + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void PreProcessForBone(); + virtual void PostConstruct(); + virtual truth AllowAlphaEverywhere() const { return true; } + virtual col16 GetMaterialColorB(int) const; + virtual bool WillExplodeSoon() const; + protected: + ulong PinPulledTick; + int Count; + ulong PinPullerID; +}; + +ITEM(mangoseedling, item) +{ + public: + //mangoseedling(); + //mangoseedling(const mangoseedling&); + // virtual truth Apply(character*); + //virtual truth IsAppliable(ccharacter*) const { return true; } + virtual truth IsMangoSeedling() const { return true; } + //virtual truth CalculateHasBe() const; + //virtual void Be(); + //virtual v2 GetBitmapPos(int) const; + //virtual int GetClassAnimationFrames() const; + //virtual alpha GetOutlineAlpha(int) const; + //virtual col16 GetOutlineColor(int) const; + //virtual void Save(outputfile&) const; + //virtual void Load(inputfile&); + //virtual void PreProcessForBone(); + //virtual void PostConstruct(); + //virtual truth AllowAlphaEverywhere() const { return true; } + //virtual col16 GetMaterialColorB(int) const; + virtual truth AllowAlphaEverywhere() const { return true; } + protected: + virtual int GetClassAnimationFrames() const { return 32; } + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; +}; + +ITEM(pantheonbook, holybook) +{ + public: + virtual void FinishReading(character*); + protected: + virtual col16 GetMaterialColorA(int) const; +}; + +ITEM(celestialmonograph, holybook) +{ + public: + virtual void FinishReading(character*); + protected: + virtual col16 GetMaterialColorA(int) const; +}; + +ITEM(constitution, holybook) +{ + public: + virtual void FinishReading(character*); + protected: + virtual col16 GetMaterialColorA(int) const; +}; + +ITEM(gorovitscopyoflenin, item) +{ + protected: + virtual col16 GetMaterialColorB(int) const; +}; + +ITEM(firstbornchild, item) +{ + public: + virtual bool SpecialOfferEffect(int); + virtual truth AllowSpoil() const { return false; } // temporary + virtual truth Spoils() const { return false; } // temporary + protected: + virtual col16 GetMaterialColorB(int) const; +}; + +ITEM(ullrbone, item) +{ + public: + ullrbone() : TimesUsed(0), Charges(12) { } + virtual truth Zap(character*, v2, int); + virtual void ChargeFully(character*) { TimesUsed = 0; } + virtual truth IsZappable(const character*) const { return true; } + virtual truth IsChargeable(const character*) const { return true; } + virtual truth HitEffect(character*, character*, v2, int, int, truth); + virtual void Be() { } + virtual void AddInventoryEntry(const character*, festring&, int, truth) const; + virtual truth ReceiveDamage(character*, int, int, int); + virtual truth AllowAlphaEverywhere() const { return true; } + protected: + int TimesUsed; + int Charges; + virtual int GetClassAnimationFrames() const { return 32; } + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const; +}; + +#endif diff --git a/Main/Include/nonhuman.h b/Main/Include/nonhuman.h new file mode 100644 index 0000000..97737b6 --- /dev/null +++ b/Main/Include/nonhuman.h @@ -0,0 +1,606 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __NONHUMAN_H__ +#define __NONHUMAN_H__ + +#include "char.h" + +CHARACTER(nonhumanoid, character) +{ + public: + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + void CalculateUnarmedDamage(); + void CalculateKickDamage(); + void CalculateBiteDamage(); + void CalculateUnarmedToHitValue(); + void CalculateKickToHitValue(); + void CalculateBiteToHitValue(); + void CalculateUnarmedAPCost(); + void CalculateKickAPCost(); + void CalculateBiteAPCost(); + double GetUnarmedDamage() const { return UnarmedDamage; } + int GetUnarmedMinDamage() const; + int GetUnarmedMaxDamage() const; + double GetKickDamage() const { return KickDamage; } + int GetKickMinDamage() const; + int GetKickMaxDamage() const; + double GetBiteDamage() const { return BiteDamage; } + int GetBiteMinDamage() const; + int GetBiteMaxDamage() const; + double GetUnarmedToHitValue() const { return UnarmedToHitValue; } + double GetKickToHitValue() const { return KickToHitValue; } + double GetBiteToHitValue() const { return BiteToHitValue; } + long GetUnarmedAPCost() const { return UnarmedAPCost; } + long GetKickAPCost() const { return KickAPCost; } + long GetBiteAPCost() const { return BiteAPCost; } + virtual void Kick(lsquare*, int, truth = false); + virtual truth Hit(character*, v2, int, int = 0); + virtual void UnarmedHit(character*, v2, int, truth = false); + virtual void InitSpecialAttributes(); + virtual double GetTimeToKill(ccharacter*, truth) const; + virtual int GetAttribute(int, truth = true) const; + virtual truth EditAttribute(int, int); + virtual void EditExperience(int, double, double); + virtual int DrawStats(truth) const; + virtual void Bite(character*, v2, int, truth = false); + virtual int GetCarryingStrength() const; + virtual void CalculateBattleInfo(); + void CalculateUnarmedAttackInfo(); + void CalculateKickAttackInfo(); + void CalculateBiteAttackInfo(); + virtual truth UseMaterialAttributes() const; + virtual void AddSpecialStethoscopeInfo(felist&) const; + virtual truth EditAllAttributes(int); + virtual void AddAttributeInfo(festring&) const; + virtual void AddAttackInfo(felist&) const; + protected: + double StrengthExperience; + double AgilityExperience; + double UnarmedDamage; + double KickDamage; + double BiteDamage; + double UnarmedToHitValue; + double KickToHitValue; + double BiteToHitValue; + long UnarmedAPCost; + long KickAPCost; + long BiteAPCost; +}; + +CHARACTER(frog, nonhumanoid) +{ + public: + virtual truth MoveRandomly() { return MoveRandomlyInRoom(); } +}; + +CHARACTER(billswill, nonhumanoid) +{ + protected: + virtual int GetBodyPartWobbleData(int) const; + virtual cchar* FirstPersonBiteVerb() const; + virtual cchar* FirstPersonCriticalBiteVerb() const; + virtual cchar* ThirdPersonBiteVerb() const; + virtual cchar* ThirdPersonCriticalBiteVerb() const; + virtual truth AttackIsBlockable(int) const { return false; } + virtual truth AttackMayDamageArmor() const { return false; } +}; + +CHARACTER(mommo, nonhumanoid) +{ + protected: + virtual int GetBodyPartWobbleData(int) const; + virtual truth CanVomit() const { return true; } + virtual void CreateCorpse(lsquare*); + virtual truth Hit(character*, v2, int, int = 0); + virtual void GetAICommand(); +}; + +CHARACTER(canine, nonhumanoid) +{ +}; + +CHARACTER(wolf, canine) +{ + protected: + virtual col16 GetSkinColor() const; +}; + +CHARACTER(dog, canine) +{ + public: + virtual truth Catches(item*); + virtual void BeTalkedTo(); + protected: + virtual bodypart* MakeBodyPart(int) const; + virtual void GetAICommand(); +}; + +CHARACTER(firefox, canine) +{ + protected: + virtual truth SpecialBiteEffect(character*, v2, int, int, truth); + virtual int GetSpecialBodyPartFlags(int) const; +}; + +CHARACTER(spider, nonhumanoid) +{ + protected: + virtual truth SpecialBiteEffect(character*, v2, int, int, truth); + virtual void GetAICommand(); + virtual bodypart* MakeBodyPart(int) const; +}; + +CHARACTER(jackal, nonhumanoid) +{ +}; + +CHARACTER(ass, nonhumanoid) +{ +}; + +CHARACTER(bear, nonhumanoid) +{ +}; + +CHARACTER(okapi, nonhumanoid) +{ +}; + +CHARACTER(dolphin, nonhumanoid) +{ + protected: + virtual int GetSpecialBodyPartFlags(int) const; + virtual void SpecialTurnHandler() { UpdatePictures(); } +}; + +CHARACTER(bat, nonhumanoid) +{ + protected: + virtual bodypart* MakeBodyPart(int) const; +}; + +CHARACTER(fruitbat, nonhumanoid) +{ + public: + virtual void GetAICommand(); + virtual truth IsRetreating() const; + protected: + virtual bodypart* MakeBodyPart(int) const; +}; + +CHARACTER(largecat, nonhumanoid) +{ + public: + largecat() : Lives(7) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth SpecialSaveLife(); + protected: + int Lives; +}; + +CHARACTER(largerat, nonhumanoid) +{ +}; + +CHARACTER(mouse, nonhumanoid) +{ +}; + +CHARACTER(pig, nonhumanoid) +{ +}; + +CHARACTER(ox, nonhumanoid) +{ +}; + +CHARACTER(mammoth, nonhumanoid) +{ +}; + +CHARACTER(unicorn, nonhumanoid) +{ + public: + virtual int TakeHit(character*, item*, bodypart*, v2, double, double, int, int, int, truth, truth); + virtual truth SpecialEnemySightedReaction(character*); + protected: + void MonsterTeleport(cchar*); +}; + +CHARACTER(lion, nonhumanoid) +{ +}; + +CHARACTER(carnivorousplant, nonhumanoid) +{ + protected: + virtual col16 GetTorsoSpecialColor() const; + virtual void GetAICommand(); + virtual void CreateCorpse(lsquare*); +}; + +CHARACTER(noxiousorchid, nonhumanoid) +{ + public: + virtual truth Hit(character*, v2, int, int = 0); + protected: + virtual col16 GetTorsoSpecialColor() const; + virtual void GetAICommand(); + virtual void CreateCorpse(lsquare*); + virtual void PostConstruct(); +}; + +CHARACTER(buffalo, nonhumanoid) +{ +}; + +CHARACTER(snake, nonhumanoid) +{ + protected: + virtual truth SpecialBiteEffect(character*, v2, int, int, truth); +}; + +CHARACTER(ostrich, nonhumanoid) +{ + public: + ostrich() : HasDroppedBananas(false) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + protected: + virtual truth HandleCharacterBlockingTheWay(character*, v2, int); + virtual void GetAICommand(); + truth HasDroppedBananas; +}; + +CHARACTER(chameleon, nonhumanoid) +{ + public: + virtual int TakeHit(character*, item*, bodypart*, v2, double, double, int, int, int, truth, truth); + virtual truth SpecialEnemySightedReaction(character*); + protected: + virtual col16 GetSkinColor() const; + virtual void SpecialTurnHandler() { UpdatePictures(); } +}; + +CHARACTER(floatingeye, nonhumanoid) +{ + public: + floatingeye() : NextWayPoint(0) { } + virtual truth Hit(character*, v2, int, int = 0); + virtual int TakeHit(character*, item*, bodypart*, v2, double, double, int, int, int, truth, truth); + virtual void SetWayPoints(const fearray&); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth IsSpy() const { return true; } + virtual truth CanAttack() const { return false; } + protected: + virtual void GetAICommand(); + std::vector WayPoints; + uint NextWayPoint; +}; + +CHARACTER(eddy, nonhumanoid) +{ + public: + virtual truth Hit(character*, v2, int, int = 0); + protected: + virtual int GetBodyPartWobbleData(int) const; + virtual bodypart* MakeBodyPart(int) const; + virtual void GetAICommand(); +}; + +CHARACTER(mushroom, nonhumanoid) +{ + public: + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + void SetSpecies(int); + int GetSpecies() const { return Species; } + virtual truth IsMushroom() const { return true; } + protected: + virtual void PostConstruct(); + virtual void GetAICommand(); + virtual col16 GetTorsoMainColor() const { return Species; } + int Species; +}; + +CHARACTER(magicmushroom, mushroom) +{ + protected: + virtual bodypart* MakeBodyPart(int) const; + virtual void GetAICommand(); +}; + +CHARACTER(ghost, nonhumanoid) +{ + public: + ghost() : Active(true) { } + virtual void AddName(festring&, int) const; + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + void SetOwnerSoul(cfestring& What) { OwnerSoul = What; } + virtual truth IsNameable() const { return OwnerSoul.IsEmpty(); } + virtual truth RaiseTheDead(character*); + virtual int ReceiveBodyPartDamage(character*, int, int, int, int = 8, truth = false, truth = false, truth = true, truth = false); + virtual truth SpecialEnemySightedReaction(character*); + void SetIsActive(truth What) { Active = What; } + virtual truth IsPolymorphable() const { return MaxHP < 100; } + protected: + virtual int GetBodyPartWobbleData(int) const; + virtual cchar* FirstPersonBiteVerb() const; + virtual cchar* FirstPersonCriticalBiteVerb() const; + virtual cchar* ThirdPersonBiteVerb() const; + virtual cchar* ThirdPersonCriticalBiteVerb() const; + virtual truth AttackIsBlockable(int) const { return false; } + virtual truth AttackMayDamageArmor() const { return false; } + virtual void GetAICommand(); + festring OwnerSoul; + truth Active; +}; + +CHARACTER(twoheadedmoose, nonhumanoid) +{ + public: + virtual truth Hit(character*, v2, int, int = 0); +}; + +CHARACTER(magpie, nonhumanoid) +{ + public: + virtual void GetAICommand(); + virtual truth IsRetreating() const; + protected: + virtual cchar* FirstPersonBiteVerb() const; + virtual cchar* FirstPersonCriticalBiteVerb() const; + virtual cchar* ThirdPersonBiteVerb() const; + virtual cchar* ThirdPersonCriticalBiteVerb() const; +}; + +CHARACTER(thunderbird, nonhumanoid) +{ + protected: + virtual cchar* FirstPersonBiteVerb() const; + virtual cchar* FirstPersonCriticalBiteVerb() const; + virtual cchar* ThirdPersonBiteVerb() const; + virtual cchar* ThirdPersonCriticalBiteVerb() const; + virtual int GetSpecialBodyPartFlags(int) const; + virtual truth SpecialBiteEffect(character*, v2, int, int, truth); +}; + +CHARACTER(skunk, nonhumanoid) +{ + public: + virtual void GetAICommand(); +}; + +CHARACTER(invisiblestalker, nonhumanoid) +{ +}; + +CHARACTER(largecreature, nonhumanoid) +{ + public: + virtual void CalculateSquaresUnder() { SquaresUnder = 4; } + virtual int GetSquareIndex(v2) const; + virtual int GetNeighbourSquares() const { return 12; } + virtual int GetExtendedNeighbourSquares() const { return 16; } + virtual square* GetNeighbourSquare(int) const; + virtual lsquare* GetNeighbourLSquare(int) const; + virtual wsquare* GetNeighbourWSquare(int) const; + virtual int CalculateNewSquaresUnder(lsquare**, v2) const; + virtual truth IsFreeForMe(square*) const; + virtual truth CanMoveOn(const lsquare*) const; + virtual truth CanMoveOn(const square*) const; + virtual void PutTo(v2); + virtual void Remove(); + virtual truth CreateRoute(); + virtual truth CanTheoreticallyMoveOn(const lsquare*) const; + virtual int GetFlySymbolSquareIndex() const { return 1; } + virtual int GetSwimmingSymbolSquareIndex() const { return 3; } + virtual int GetUnconsciousSymbolSquareIndex() const { return 2; } + virtual truth PlaceIsIllegal(v2, v2) const; + truth PartCanMoveOn(const lsquare*) const; + protected: + virtual bodypart* MakeBodyPart(int) const; + virtual void CreateCorpse(lsquare*); + virtual void LoadSquaresUnder(); +}; + +CHARACTER(elpuri, largecreature) +{ + public: + elpuri() : Active(false) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth Hit(character*, v2, int, int = 0); + virtual int ReceiveBodyPartDamage(character*, int, int, int, int = 8, truth = false, truth = false, truth = true, truth = false); + virtual truth SpecialEnemySightedReaction(character*); + virtual truth MustBeRemovedFromBone() const; + virtual truth TryToRiseFromTheDead(); + protected: + virtual void GetAICommand(); + virtual void CreateCorpse(lsquare*); + truth Active; +}; + +CHARACTER(genetrixvesana, largecreature) +{ + public: + genetrixvesana() : TurnsExisted(0) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void FinalProcessForBone(); + protected: + virtual void GetAICommand(); + virtual void CreateCorpse(lsquare*); + virtual truth MustBeRemovedFromBone() const; + long TurnsExisted; +}; + +CHARACTER(menatrixfusanga, largecreature) +{ + public: + void SetSpecies(int); + int GetSpecies() const { return Species; } + menatrixfusanga() : TurnsExisted(0) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void FinalProcessForBone(); + virtual col16 GetSkinColor() const; + virtual bodypart* MakeBodyPart(int) const; + protected: + virtual void GetAICommand(); + virtual void CreateCorpse(lsquare*); + virtual truth MustBeRemovedFromBone() const; + long TurnsExisted; + int Species; +}; + +CHARACTER(genefourxvesana, largecreature) +{ + public: + void SetSpecies(int); + int GetSpecies() const { return Species; } + genefourxvesana() : TurnsExisted(0) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void FinalProcessForBone(); + protected: + virtual void GetAICommand(); + virtual void CreateCorpse(lsquare*); + virtual truth MustBeRemovedFromBone() const; + long TurnsExisted; + int Species; +}; + +CHARACTER(solicitus, largecreature) +{ + public: + //genetrixvesana() : TurnsExisted(0) { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void FinalProcessForBone(); + virtual void BeTalkedTo(); + protected: + virtual void GetAICommand(); + virtual void CreateCorpse(lsquare*); + virtual truth MustBeRemovedFromBone() const; + //long TurnsExisted; +}; + +CHARACTER(hedgehog, nonhumanoid) +{ + public: + virtual void SpecialBodyDefenceEffect(character*, bodypart*, int); +}; + +CHARACTER(bunny, nonhumanoid) +{ + public: + virtual truth CheckIfSatiated() { return GetNP() > BLOATED_LEVEL; } + virtual void SignalNaturalGeneration(); + virtual truth IsBunny() const { return true; } + virtual truth Catches(item*); + protected: + truth CheckForMatePartner(); + virtual void GetAICommand(); +}; + +CHARACTER(vladimir, largecreature) +{ + public: + virtual truth MustBeRemovedFromBone() const; + virtual col16 GetSkinColor() const; + virtual void SpecialTurnHandler() { UpdatePictures(); } +}; + +CHARACTER(haastseagle, largecreature) +{ + protected: + virtual cchar* FirstPersonBiteVerb() const; + virtual cchar* FirstPersonCriticalBiteVerb() const; + virtual cchar* ThirdPersonBiteVerb() const; + virtual cchar* ThirdPersonCriticalBiteVerb() const; +}; + +CHARACTER(anvitas, largecreature) +{ + public: + virtual void SpecialBodyDefenceEffect(character*, bodypart*, int); +}; + +CHARACTER(hattifattener, nonhumanoid) +{ + public: + truth Hit(character*, v2, int, int = 0) { return false; } + protected: + virtual int GetBodyPartWobbleData(int) const; + virtual int GetSpecialBodyPartFlags(int) const; + virtual void GetAICommand(); + virtual void CreateCorpse(lsquare*); +}; + +CHARACTER(blinkdog, dog) +{ + public: + blinkdog(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual int TakeHit(character*, item*, bodypart*, v2, double, double, int, int, int, truth, truth); + virtual truth SpecialEnemySightedReaction(character*); + protected: + virtual bodypart* MakeBodyPart(int) const; + void MonsterTeleport(cchar*); + truth SummonFriend(); + int SummonModifier; +}; + +CHARACTER(mysticfrog, frog) +{ + public: + virtual void GetAICommand(); + protected: + virtual int GetBodyPartWobbleData(int) const; + virtual bodypart* MakeBodyPart(int) const; + int GetSpellAPCost() const { return 1500; } +}; + +CHARACTER(lobhse, largecreature) +{ + protected: + virtual truth SpecialBiteEffect(character*, v2, int, int, truth); + virtual void GetAICommand(); + virtual void CreateCorpse(lsquare*); + virtual truth MustBeRemovedFromBone() const; + virtual bodypart* MakeBodyPart(int) const; +}; + +CHARACTER(mindworm, nonhumanoid) +{ + protected: + virtual void GetAICommand(); + virtual void TryToImplantLarvae(character*); + virtual void PsiAttack(character*); +}; + +CHARACTER(bluedragon, nonhumanoid) +{ +}; + +CHARACTER(reddragon, nonhumanoid) +{ +}; + +#endif diff --git a/Main/Include/object.h b/Main/Include/object.h new file mode 100644 index 0000000..e281d27 --- /dev/null +++ b/Main/Include/object.h @@ -0,0 +1,106 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __OBJECT_H__ +#define __OBJECT_H__ + +#include "igraph.h" +#include "entity.h" +#include "id.h" +#include "fearray.h" + +class god; +class object; + +typedef v2 (object::*bposretriever)(int) const; + +class object : public entity, public id +{ + public: + object(); + object(const object&); + virtual ~object(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void UpdatePictures(); + material* GetMainMaterial() const { return MainMaterial; } + virtual material* GetSecondaryMaterial() const { return 0; } + virtual void SetSecondaryMaterial(material*, int = 0) { } + virtual void ChangeSecondaryMaterial(material*, int = 0) { } + virtual int GetMaterials() const { return 1; } + virtual material* GetMaterial(int) const { return MainMaterial; } + cbitmap*const* GetPicture() const; + virtual col24 GetBaseEmitation() const { return 0; } + virtual void SetParameters(int) { } + virtual int GetOKVisualEffects() const { return 0; } + int GetVisualEffects() const { return VisualEffects; } + void SetVisualEffects(int What) { VisualEffects = What; } + virtual int GetForcedVisualEffects() const { return 0; } + int GetAnimationFrames() const { return GraphicData.AnimationFrames; } + virtual truth IsAnimated() const { return GraphicData.AnimationFrames > 1; } + virtual void CalculateEmitation(); + void LoadMaterial(inputfile&, material*&); + virtual const fearray& GetMaterialConfigChances() const = 0; + virtual long GetMaterialConfigChanceSum() const = 0; + virtual void CalculateAll() = 0; + virtual int GetSpoilLevel() const { return 0; } + void CreateWieldedBitmap(graphicid&) const; + virtual int GetSpecialFlags() const; + static void InitSparkleValidityArrays(); + void UpdatePictures(graphicdata&, v2, int, alpha, int, bposretriever) const; + void InitMaterial(material*&, material*, long); + virtual truth DetectMaterial(cmaterial*) const; + virtual int GetSparkleFlags() const; + virtual void SignalMaterialChange() { } + protected: + void CopyMaterial(material* const&, material*&); + void ObjectInitMaterials(material*&, material*, long, material*&, material*, long, truth); + material* SetMaterial(material*&, material*, long, int); + void ChangeMaterial(material*&, material*, long, int); + virtual truth CalculateHasBe() const; + virtual int GetGraphicsContainerIndex() const = 0; + virtual col16 GetMaterialColorA(int) const; + virtual col16 GetMaterialColorB(int) const { return 0; } + virtual col16 GetMaterialColorC(int) const { return 0; } + virtual col16 GetMaterialColorD(int) const { return 0; } + virtual alpha GetMaxAlpha() const { return 255; } + virtual alpha GetAlphaA(int) const; + virtual alpha GetAlphaB(int) const { return 255; } + virtual alpha GetAlphaC(int) const { return 255; } + virtual alpha GetAlphaD(int) const { return 255; } + virtual col16 GetOutlineColor(int) const; + virtual alpha GetOutlineAlpha(int) const { return 255; } + virtual truth AddRustLevelDescription(festring&, truth) const; + virtual truth AddMaterialDescription(festring&, truth) const; + int RandomizeMaterialConfiguration(); + virtual int GetClassAnimationFrames() const { return 1; } + void AddContainerPostFix(festring&) const; + void AddLumpyPostFix(festring&) const; + truth AddEmptyAdjective(festring&, truth) const; + virtual v2 GetBitmapPos(int) const = 0; + void RandomizeVisualEffects(); + virtual void ModifyAnimationFrames(int&) const { } + virtual int GetRustDataA() const; + virtual int GetRustDataB() const { return NOT_RUSTED; } + virtual int GetRustDataC() const { return NOT_RUSTED; } + virtual int GetRustDataD() const { return NOT_RUSTED; } + virtual col16 GetDripColor() const { return 0; } + virtual truth AllowSparkling() const { return true; } + virtual truth AllowRegularColors() const { return true; } + virtual int GetWobbleData() const { return 0; } + truth RandomizeSparklePos(v2&, v2, int&, ulong, int, int) const; + graphicdata GraphicData; + material* MainMaterial; + int VisualEffects; +}; + +#endif diff --git a/Main/Include/pool.h b/Main/Include/pool.h new file mode 100644 index 0000000..e8bbe65 --- /dev/null +++ b/Main/Include/pool.h @@ -0,0 +1,34 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __POOL_H__ +#define __POOL_H__ + +class entity; + +class pool +{ + public: + static void Add(entity*); + static void Remove(entity*); + static void AddToHell(entity*); + static void BurnHell(); + static void Be(); + private: + static entity* FirstEntity; + static entity* LastEntity; + static entity* FirstDoomed; + static entity* LastDoomed; + static entity* CurrentEntity; +}; + +#endif diff --git a/Main/Include/proto.h b/Main/Include/proto.h new file mode 100644 index 0000000..b9f5091 --- /dev/null +++ b/Main/Include/proto.h @@ -0,0 +1,120 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __PROTO_H__ +#define __PROTO_H__ + +#include +#include + +#include "ivandef.h" +#include "save.h" + +class character; +class item; +class material; +class god; +template class databasecreator; +struct itemdatabase; + +typedef std::map valuemap; +typedef std::vector itemvector; +typedef std::vector itemvectorvector; +typedef std::vector charactervector; +typedef std::vector materialvector; + +template class protocontainer +{ + public: + friend class protosystem; + friend class databasecreator; + typedef typename type::prototype prototype; + static int Add(prototype*); + static const prototype* GetProto(int I) { return GetProtoData()[I]; } + static int SearchCodeName(cfestring&); + static cchar* GetMainClassID() { return GetProtoData()[1]->GetClassID(); } + static int GetSize() { return GetSizeRef(); } + private: + static prototype**& GetProtoData(); + static valuemap& GetCodeNameMap(); + static int& GetSizeRef(); +}; + +template +inline int protocontainer::Add(prototype* Proto) +{ + if(!GetSize()) + (GetProtoData() = new prototype*[1024])[GetSizeRef()++] = 0; + + int Index = GetSizeRef()++; + GetProtoData()[Index] = Proto; + std::pair Pair(Proto->GetClassID(), Index); + GetCodeNameMap().insert(Pair); + return Index; +} + +template +inline int protocontainer::SearchCodeName(cfestring& Name) +{ + valuemap::iterator I = GetCodeNameMap().find(Name); + return I != GetCodeNameMap().end() ? I->second : 0; +} + +class protosystem +{ + public: + static character* BalancedCreateMonster(); + static item* BalancedCreateItem(long = 0, long = MAX_PRICE, long = ANY_CATEGORY, int = 0, int = 0, int = 0, truth = false); + static character* CreateMonster(int = 1, int = 999999, int = 0); + static character* CreateMonster(cfestring&, int = 0, truth = true); + static item* CreateItem(cfestring&, truth = true); + static material* CreateMaterial(cfestring&, long = 0, truth = true); + static void CreateEveryNormalEnemy(charactervector&); +#ifdef WIZARD + static void CreateEveryCharacter(charactervector&); + static void CreateEveryItem(itemvectorvector&); + static void CreateEveryMaterial(std::vector&); +#endif + static void Initialize(); + static void InitCharacterDataBaseFlags(); + static void SaveCharacterDataBaseFlags(outputfile&); + static void LoadCharacterDataBaseFlags(inputfile&); + static void CreateEverySeenCharacter(charactervector&); + static void CreateEveryMaterial(std::vector&, const god*, ccharacter*); + private: + static itemdatabase** ItemConfigData; + static int ItemConfigDataSize; + static itemdatabase** ItemCategoryData[ITEM_CATEGORIES]; + static int ItemCategorySize[ITEM_CATEGORIES]; + static long ItemCategoryPossibility[ITEM_CATEGORIES]; + static long TotalItemPossibility; +}; + +template inline outputfile& operator<<(outputfile& SaveFile, const type* Class) +{ + if(Class) + Class->Save(SaveFile); + else + SaveFile << ushort(0); + + return SaveFile; +} + +template inline inputfile& operator>>(inputfile& SaveFile, type*& Class) +{ + int Type = 0; + SaveFile >> (ushort&)Type; + Class = Type ? protocontainer::GetProto(Type)->SpawnAndLoad(SaveFile) : 0; + return SaveFile; +} + +#endif diff --git a/Main/Include/rain.h b/Main/Include/rain.h new file mode 100644 index 0000000..fc3d218 --- /dev/null +++ b/Main/Include/rain.h @@ -0,0 +1,61 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __RAIN_H__ +#define __RAIN_H__ + +#include "lsquare.h" +#include "entity.h" + +class rain : public entity +{ + public: + /* Come To The Dark Side */ + rain* Next; + public: + rain() : entity(HAS_BE), Next(0), Drop(0), Drops(0), OwnLiquid(0) { } + rain(liquid*, lsquare*, v2, int, truth); + ~rain(); + virtual void Be(); + void Save(outputfile&) const; + void Load(inputfile&); + void Draw(blitdata&) const; + truth HasOwnLiquid() const { return OwnLiquid; } + void RandomizeDropPos(int) const; + liquid* GetLiquid() const { return Liquid; } + virtual square* GetSquareUnderEntity(int = 0) const { return LSquareUnder; } + square* GetSquareUnder() const { return LSquareUnder; } + void SetLSquareUnder(lsquare* What) { LSquareUnder = What; } + lsquare* GetLSquareUnder() const { return LSquareUnder; } + virtual truth IsOnGround() const { return true; } + int GetTeam() const { return Team; } + protected: + mutable struct drop + { + packv2 StartPos; + ushort StartTick; + ushort MaxAge; + }* Drop; + liquid* Liquid; + lsquare* LSquareUnder; + v2 Speed; + long SpeedAbs; + mutable int Drops : 8; + int BeCounter : 7; + truth OwnLiquid : 1; + int Team : 8; +}; + +outputfile& operator<<(outputfile&, const rain*); +inputfile& operator>>(inputfile&, rain*&); + +#endif diff --git a/Main/Include/room.h b/Main/Include/room.h new file mode 100644 index 0000000..889f719 --- /dev/null +++ b/Main/Include/room.h @@ -0,0 +1,120 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __ROOM_H__ +#define __ROOM_H__ + +#include "v2.h" + +class room; +class item; +class olterrain; +class lsquare; +class festring; +class outputfile; +class inputfile; +class character; + +typedef room* (*roomspawner)(); + +class roomprototype +{ + public: + roomprototype(roomspawner, cchar*); + room* Spawn() const { return Spawner(); } + room* SpawnAndLoad(inputfile&) const; + cchar* GetClassID() const { return ClassID; } + int GetIndex() const { return Index; } + private: + int Index; + roomspawner Spawner; + cchar* ClassID; +}; + +class room +{ + public: + typedef roomprototype prototype; + room() : LastMasterSearchTick(0), MasterID(0), LastWardSearchTick(0) { } + virtual ~room() { } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void Enter(character*) { } + v2 GetPos() const { return Pos; } + void SetPos(v2 What) { Pos = What; } + v2 GetSize() const { return Size; } + void SetSize(v2 What) { Size = What; } + void SetIndex(int What) { Index = What; } + int GetIndex() const { return Index; } + character* GetMaster() const; + void SetMasterID(ulong What) { MasterID = What; } + virtual truth PickupItem(character*, item*, int) { return true; } + virtual truth DropItem(character*, item*, int) { return true; } + int GetDivineMaster() const { return DivineMaster; } + void SetDivineMaster(int What) { DivineMaster = What; } + virtual void KickSquare(character*, lsquare*) { } + virtual truth ConsumeItem(character*, item*, int) { return true; } + virtual truth AllowDropGifts() const { return true; } + virtual truth Drink(character*) const { return true; } + virtual truth HasDrinkHandler() const { return false; } + virtual truth Dip(character*) const { return true; } + virtual truth HasDipHandler() const { return false; } + virtual void TeleportSquare(character*, lsquare*) { } + virtual const prototype* GetProtoType() const = 0; + int GetType() const { return GetProtoType()->GetIndex(); } + virtual void DestroyTerrain(character*); + virtual truth AllowSpoil(citem*) const { return true; } + virtual truth CheckDestroyTerrain(character*); + virtual int GetGodRelationAdjustment() const { return -50; } + virtual truth AllowKick(ccharacter*, const lsquare*) const { return true; } + truth MasterIsActive() const; + truth CheckKickSquare(ccharacter*, const lsquare*) const; + virtual void HostileAction(character*) const { } + virtual truth AllowAltarPolymorph() const { return true; } + virtual truth AllowFoodSearch() const { return true; } + virtual void ReceiveVomit(character*) { } + virtual truth IsOKToDestroyWalls(ccharacter*) const; + virtual void AddItemEffect(item*) { }; + void FinalProcessForBone(); + void SetFlags(ulong What) { Flags = What; } + truth DontGenerateMonsters() const { return Flags & NO_MONSTER_GENERATION; } + olterrain* GetWard() const; + truth WardIsActive() const; + virtual truth IsOKToTeleportInto() const; + protected: + mutable character* Master; + mutable ulong LastMasterSearchTick; + v2 Pos; + v2 Size; + ulong MasterID; + int Index; + int DivineMaster; + ulong Flags; + mutable olterrain* Ward; + mutable ulong LastWardSearchTick; +}; + +#ifdef __FILE_OF_STATIC_ROOM_PROTOTYPE_DEFINITIONS__ +#define ROOM_PROTO(name)\ +template<> const roomprototype\ + name##sysbase::ProtoType((roomspawner)(&name##sysbase::Spawn), #name); +#else +#define ROOM_PROTO(name) +#endif + +#define ROOM(name, base)\ +class name;\ +typedef simplesysbase name##sysbase;\ +ROOM_PROTO(name)\ +class name : public name##sysbase + +#endif diff --git a/Main/Include/rooms.h b/Main/Include/rooms.h new file mode 100644 index 0000000..b9ee383 --- /dev/null +++ b/Main/Include/rooms.h @@ -0,0 +1,132 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __ROOMS_H__ +#define __ROOMS_H__ + +#include "room.h" + +ROOM(normalroom, room) { }; + +ROOM(shop, room) +{ + public: + virtual void Enter(character*); + virtual truth PickupItem(character*, item*, int); + virtual truth DropItem(character*, item*, int); + virtual void KickSquare(character*, lsquare*); + virtual truth ConsumeItem(character*, item*, int); + virtual truth AllowDropGifts() const { return false; } + virtual void TeleportSquare(character*, lsquare*); + virtual truth AllowSpoil(citem*) const; + virtual truth AllowKick(ccharacter*,const lsquare*) const; + virtual void HostileAction(character*) const; + virtual truth AllowFoodSearch() const { return false; } + virtual void ReceiveVomit(character*); +}; + +ROOM(cathedral, room) +{ + public: + cathedral(); + virtual void Enter(character*); + virtual truth PickupItem(character*, item*, int); + virtual truth DropItem(character*, item*, int); + virtual void KickSquare(character*, lsquare*); + virtual truth ConsumeItem(character*, item*, int); + virtual void SetEntered(truth What) { Entered = What; } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth AllowDropGifts() const { return false; } + virtual truth Drink(character*) const; + virtual truth HasDrinkHandler() const { return true; } + virtual truth Dip(character*) const; + virtual truth HasDipHandler() const { return true; } + virtual void TeleportSquare(character*, lsquare*); + virtual truth AllowSpoil(citem*) const { return false; } + virtual int GetGodRelationAdjustment() const { return -150; } + virtual truth AllowKick(ccharacter*,const lsquare*) const; + virtual void HostileAction(character*) const; + virtual truth AllowAltarPolymorph() const { return false; } + virtual truth AllowFoodSearch() const { return false; } + virtual void AddItemEffect(item*); + character* FindRandomExplosiveReceiver() const; + protected: + truth Entered; +}; + +ROOM(library, room) +{ + public: + virtual void Enter(character*); + virtual truth PickupItem(character*, item*, int); + virtual truth DropItem(character*, item*, int); + virtual void KickSquare(character*, lsquare*); + virtual truth ConsumeItem(character*, item*, int); + virtual truth AllowDropGifts() const { return false; } + virtual void TeleportSquare(character*, lsquare*); + virtual truth AllowKick(ccharacter*, const lsquare*) const; + virtual void HostileAction(character*) const; +}; + +ROOM(bananadroparea, room) +{ + public: + virtual truth PickupItem(character*, item*, int); + virtual truth DropItem(character*, item*, int); + virtual void KickSquare(character*, lsquare*); + virtual truth ConsumeItem(character*, item*, int); + virtual truth AllowDropGifts() const { return false; } + virtual void TeleportSquare(character*, lsquare*); + virtual truth AllowKick(ccharacter*, const lsquare*) const; + virtual void HostileAction(character*) const; +}; + +ROOM(sumoarena, room) +{ + public: + virtual void DestroyTerrain(character*); + virtual void HostileAction(character*) const; + virtual truth CheckDestroyTerrain(character*); +}; + +ROOM(vault, room) +{ + public: + vault(); + virtual void Enter(character*); + virtual truth PickupItem(character*, item*, int); + virtual truth DropItem(character*, item*, int); + virtual void KickSquare(character*, lsquare*); + virtual truth ConsumeItem(character*, item*, int); + virtual void SetEntered(truth What) { Entered = What; } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth AllowDropGifts() const { return false; } + virtual truth Drink(character*) const; + virtual truth HasDrinkHandler() const { return true; } + virtual truth Dip(character*) const; + virtual truth HasDipHandler() const { return true; } + virtual void TeleportSquare(character*, lsquare*); + virtual truth AllowSpoil(citem*) const { return false; } + virtual int GetGodRelationAdjustment() const { return -150; } + virtual truth AllowKick(ccharacter*,const lsquare*) const; + virtual void HostileAction(character*) const; + virtual truth AllowAltarPolymorph() const { return false; } + virtual truth AllowFoodSearch() const { return false; } + virtual void AddItemEffect(item*); + character* FindRandomExplosiveReceiver() const; + protected: + truth Entered; +}; + +#endif diff --git a/Main/Include/script.h b/Main/Include/script.h new file mode 100644 index 0000000..8443011 --- /dev/null +++ b/Main/Include/script.h @@ -0,0 +1,512 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __SCRIPT_H__ +#define __SCRIPT_H__ + +#include +#include + +#include "rect.h" +#include "femath.h" +#include "festring.h" + +#define SCRIPT_MEMBER(type, name)\ + public:\ + const type* Get##name() const { return name##Holder.Member; }\ + protected:\ + scriptmember< type > name##Holder + +#define SCRIPT_MEMBER_WITH_BASE(type, name)\ + public:\ + const type* Get##name() const { return GetMemberOf(name##Holder, Base, &scripttype::Get##name); }\ + protected:\ + scriptmember< type > name##Holder + +#define FAST_SCRIPT_MEMBER(type, name)\ + public:\ + type Get##name() const { return name##Holder.Member; }\ + protected:\ + fastscriptmember< type > name##Holder + +#define SCRIPT_TRUTH(name)\ + public:\ + ctruth* name() const { return name##Holder.Member; }\ + protected:\ + scriptmember name##Holder + +#define SCRIPT_TRUTH_WITH_BASE(name)\ + public:\ + ctruth* name() const { return GetMemberOf(name##Holder, Base, &scripttype::name); }\ + protected:\ + scriptmember name##Holder + +#define FAST_SCRIPT_TRUTH(name)\ + public:\ + truth name() const { return name##Holder.Member; }\ + protected:\ + fastscriptmember name##Holder + +class glterrain; +class olterrain; +class character; +class item; +class material; +class scriptwithbase; +class outputfile; +class inputfile; + +struct scriptmemberbase +{ + virtual ~scriptmemberbase() { } + virtual void ReadFrom(inputfile&) = 0; + virtual void Save(outputfile&) const = 0; + virtual void Load(inputfile&) = 0; + virtual void Replace(scriptmemberbase&) = 0; +}; + +template struct scriptmember : public scriptmemberbase +{ + virtual ~scriptmember() { delete Member; } + scriptmember() : Member(0) { } + scriptmember(const scriptmember& Data) : scriptmemberbase(Data), Member(Data.Member ? new type(*Data.Member) : 0) { } + scriptmember& operator=(const scriptmember&); + virtual void ReadFrom(inputfile&); + virtual void Replace(scriptmemberbase&); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + type* Member; +}; + +template inline const type* GetMemberOf(const scriptmember& Data, const scriptwithbase* Base, const type* (scripttype::*MemberRetriever)() const) +{ + return Data.Member ? Data.Member : Base ? (static_cast(Base)->*MemberRetriever)() : 0; +} + +template inline scriptmember& scriptmember::operator=(const scriptmember& Data) +{ + if(Member) + { + if(Data.Member) + *Member = *Data.Member; + else + { + delete Member; + Member = 0; + } + } + else if(Data.Member) + Member = new type(*Data.Member); + + return *this; +} + +template struct fastscriptmember : public scriptmemberbase +{ + fastscriptmember() { } + fastscriptmember(type Member) : Member(Member) { } + virtual void ReadFrom(inputfile&); + virtual void Replace(scriptmemberbase&); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + type Member; +}; + +class script +{ + public: + typedef std::map datamap; + virtual ~script() { } + virtual void ReadFrom(inputfile&) = 0; + virtual void Save(outputfile& SaveFile) const { SaveDataMap(GetDataMap(), SaveFile); } + virtual void Load(inputfile& SaveFile) { LoadDataMap(GetDataMap(), SaveFile); } + protected: + truth ReadMember(inputfile&, cfestring&); + virtual scriptmemberbase* GetDataFromMap(const datamap&, cchar*); + virtual scriptmemberbase* GetData(cchar* String) { return GetDataFromMap(GetDataMap(), String); } + virtual const datamap& GetDataMap() const = 0; + virtual void SaveDataMap(const datamap&, outputfile&) const; + virtual void LoadDataMap(const datamap&, inputfile&); +}; + +inline void ReadData(script& Type, inputfile& SaveFile) { Type.ReadFrom(SaveFile); } +inline outputfile& operator<<(outputfile& SaveFile, const script& Script) { Script.Save(SaveFile); return SaveFile; } +inline inputfile& operator>>(inputfile& SaveFile, script& Script) { Script.Load(SaveFile); return SaveFile; } + +class scriptwithbase : public script +{ + public: + scriptwithbase() : Base(0) { } + const scriptwithbase* GetBase() const { return Base; } + virtual void SetBase(const scriptwithbase* What) { Base = What; } + protected: + const scriptwithbase* Base; +}; + +class posscript : public script +{ + public: + typedef posscript scripttype; + virtual void ReadFrom(inputfile&); + truth GetRandom() const { return Random; } + static void InitDataMap(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + protected: + virtual const datamap& GetDataMap() const { return DataMap; } + static datamap DataMap; + SCRIPT_MEMBER(rect, Borders); + FAST_SCRIPT_MEMBER(packv2, Vector); + FAST_SCRIPT_MEMBER(uchar, Flags); + truth Random; +}; + +class materialscript : public script +{ + public: + typedef materialscript scripttype; + virtual void ReadFrom(inputfile&); + int GetConfig() const { return Config; } + void SetConfig(int What) { Config = What; } + material* Instantiate() const; + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + static void InitDataMap(); + protected: + virtual const datamap& GetDataMap() const { return DataMap; } + static datamap DataMap; + SCRIPT_MEMBER(interval, Volume); + int Config; +}; + +class basecontentscript : public script +{ + public: + typedef basecontentscript scripttype; + basecontentscript(); + virtual void ReadFrom(inputfile&); + int GetContentType() const { return ContentType; } + truth IsValid() const { return ContentType || Random; } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + static void InitDataMap(); + protected: + virtual const datamap& GetDataMap() const { return DataMap; } + virtual scriptmemberbase* GetData(cchar*); + virtual int SearchCodeName(cfestring&) const = 0; + virtual cchar* GetClassID() const = 0; + static datamap DataMap; + SCRIPT_MEMBER(materialscript, MainMaterial); + SCRIPT_MEMBER(materialscript, SecondaryMaterial); + ushort ContentType : 15; + truth Random : 1; + ushort Config; + FAST_SCRIPT_MEMBER(uchar, Parameters); +}; + +inline truth IsValidScript(const basecontentscript* S) { return S->IsValid(); } + +template class contentscripttemplate : public basecontentscript +{ + protected: + type* BasicInstantiate(int) const; + virtual int SearchCodeName(cfestring&) const; +}; + +template class contentscript; + +template<> +class contentscript : public contentscripttemplate +{ + public: + typedef contentscript scripttype; + contentscript(); + item* InstantiateBasedOnMaterial(int, int = 0) const; + item* Instantiate(int = 0) const; + static void InitDataMap(); + protected: + virtual const datamap& GetDataMap() const { return DataMap; } + virtual cchar* GetClassID() const; + static datamap DataMap; + SCRIPT_MEMBER(fearray >, ItemsInside); + SCRIPT_MEMBER(interval, Times); + SCRIPT_MEMBER(interval, LifeExpectancy); + FAST_SCRIPT_MEMBER(ulong, Category); + FAST_SCRIPT_MEMBER(long, MinPrice); + FAST_SCRIPT_MEMBER(long, MaxPrice); + FAST_SCRIPT_MEMBER(uchar, Team); + FAST_SCRIPT_MEMBER(uchar, SquarePosition); + FAST_SCRIPT_MEMBER(uchar, Chance); + FAST_SCRIPT_MEMBER(uchar, ConfigFlags); + FAST_SCRIPT_MEMBER(uchar, SpoilPercentage); + FAST_SCRIPT_MEMBER(char, Enchantment); + FAST_SCRIPT_TRUTH(IsActive); +}; + +truth IsValidScript(const fearray >*); + +template <> +class contentscript : public contentscripttemplate +{ + public: + typedef contentscript scripttype; + contentscript(); + character* Instantiate(int = 0) const; + static void InitDataMap(); + protected: + virtual const datamap& GetDataMap() const { return DataMap; } + virtual cchar* GetClassID() const; + static datamap DataMap; + SCRIPT_MEMBER(fearray >, Inventory); + SCRIPT_MEMBER(fearray, WayPoint); + FAST_SCRIPT_MEMBER(uchar, Team); + FAST_SCRIPT_MEMBER(uchar, Flags); +}; + +template <> +class contentscript : public contentscripttemplate +{ + public: + typedef contentscript scripttype; + glterrain* Instantiate(int SpecialFlags = 0) const { return contentscripttemplate::BasicInstantiate(SpecialFlags); } + static void InitDataMap(); + protected: + virtual const datamap& GetDataMap() const { return DataMap; } + static datamap DataMap; + virtual cchar* GetClassID() const; + SCRIPT_TRUTH(IsInside); +}; + +template <> +class contentscript : public contentscripttemplate +{ + public: + typedef contentscript scripttype; + contentscript(); + olterrain* Instantiate(int = 0) const; + static void InitDataMap(); + protected: + virtual const datamap& GetDataMap() const { return DataMap; } + static datamap DataMap; + virtual cchar* GetClassID() const; + SCRIPT_MEMBER(fearray >, ItemsInside); + SCRIPT_MEMBER(festring, Text); + FAST_SCRIPT_MEMBER(uchar, VisualEffects); + FAST_SCRIPT_MEMBER(uchar, AttachedArea); + FAST_SCRIPT_MEMBER(uchar, AttachedEntry); +}; + +class squarescript : public script +{ + public: + typedef squarescript scripttype; + squarescript(); + virtual void ReadFrom(inputfile&); + static void InitDataMap(); + protected: + virtual const datamap& GetDataMap() const { return DataMap; } + static datamap DataMap; + SCRIPT_MEMBER(posscript, Position); + SCRIPT_MEMBER(contentscript, Character); + SCRIPT_MEMBER(fearray >, Items); + SCRIPT_MEMBER(contentscript, GTerrain); + SCRIPT_MEMBER(contentscript, OTerrain); + SCRIPT_MEMBER(interval, Times); + FAST_SCRIPT_MEMBER(uchar, EntryIndex); + FAST_SCRIPT_TRUTH(AttachRequired); +}; + +template > class contentmap : public script +{ + public: + typedef contentmap scripttype; + contentmap(); + virtual ~contentmap(); + virtual void ReadFrom(inputfile&); + const contenttype* GetContentScript(int X, int Y) const { return ContentMap[X][Y].second; } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + static void InitDataMap(); + protected: + virtual const datamap& GetDataMap() const { return DataMap; } + static datamap DataMap; + std::pair** ContentMap; + std::map SymbolMap; + SCRIPT_MEMBER(v2, Size); + SCRIPT_MEMBER(v2, Pos); +}; + +typedef contentmap > > itemcontentmap; +typedef contentmap charactercontentmap; +typedef contentmap glterraincontentmap; +typedef contentmap olterraincontentmap; + +class roomscript : public scriptwithbase +{ + public: + typedef roomscript scripttype; + void ReadFrom(inputfile&); + const std::list& GetSquare() const; + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + static void InitDataMap(); + protected: + virtual const datamap& GetDataMap() const { return DataMap; } + static datamap DataMap; + std::list Square; + SCRIPT_MEMBER_WITH_BASE(charactercontentmap, CharacterMap); + SCRIPT_MEMBER_WITH_BASE(itemcontentmap, ItemMap); + SCRIPT_MEMBER_WITH_BASE(glterraincontentmap, GTerrainMap); + SCRIPT_MEMBER_WITH_BASE(olterraincontentmap, OTerrainMap); + SCRIPT_MEMBER_WITH_BASE(squarescript, WallSquare); + SCRIPT_MEMBER_WITH_BASE(squarescript, FloorSquare); + SCRIPT_MEMBER_WITH_BASE(squarescript, DoorSquare); + SCRIPT_MEMBER_WITH_BASE(region, Size); + SCRIPT_MEMBER_WITH_BASE(region, Pos); + SCRIPT_TRUTH_WITH_BASE(AltarPossible); + SCRIPT_TRUTH_WITH_BASE(GenerateDoor); + SCRIPT_TRUTH_WITH_BASE(GenerateTunnel); + SCRIPT_MEMBER_WITH_BASE(int, DivineMaster); + SCRIPT_TRUTH_WITH_BASE(GenerateLanterns); + SCRIPT_MEMBER_WITH_BASE(int, Type); + SCRIPT_TRUTH_WITH_BASE(GenerateFountains); + SCRIPT_TRUTH_WITH_BASE(AllowLockedDoors); + SCRIPT_TRUTH_WITH_BASE(AllowBoobyTrappedDoors); + SCRIPT_MEMBER_WITH_BASE(int, Shape); + SCRIPT_TRUTH_WITH_BASE(IsInside); + SCRIPT_TRUTH_WITH_BASE(GenerateWindows); + SCRIPT_TRUTH_WITH_BASE(UseFillSquareWalls); + SCRIPT_MEMBER_WITH_BASE(ulong, Flags); + SCRIPT_TRUTH_WITH_BASE(GenerateWards); +}; + +class levelscript : public scriptwithbase +{ + public: + typedef levelscript scripttype; + void ReadFrom(inputfile&); + const std::list& GetSquare() const; + const std::list& GetRoom() const; + void Combine(levelscript&); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void SetBase(const scriptwithbase*); + static void InitDataMap(); + protected: + virtual const datamap& GetDataMap() const { return DataMap; } + static datamap DataMap; + std::list Square; + std::list Room; + SCRIPT_MEMBER_WITH_BASE(roomscript, RoomDefault); + SCRIPT_MEMBER_WITH_BASE(squarescript, FillSquare); + SCRIPT_MEMBER_WITH_BASE(squarescript, TunnelSquare); + SCRIPT_MEMBER_WITH_BASE(festring, LevelMessage); + SCRIPT_MEMBER_WITH_BASE(v2, Size); + SCRIPT_MEMBER_WITH_BASE(interval, Items); + SCRIPT_MEMBER_WITH_BASE(interval, Rooms); + SCRIPT_TRUTH_WITH_BASE(GenerateMonsters); + SCRIPT_TRUTH_WITH_BASE(IsOnGround); + SCRIPT_MEMBER_WITH_BASE(int, TeamDefault); + SCRIPT_MEMBER_WITH_BASE(festring, Description); + SCRIPT_MEMBER_WITH_BASE(int, LOSModifier); + SCRIPT_TRUTH_WITH_BASE(IgnoreDefaultSpecialSquares); + SCRIPT_MEMBER_WITH_BASE(int, DifficultyBase); + SCRIPT_MEMBER_WITH_BASE(int, DifficultyDelta); + SCRIPT_MEMBER_WITH_BASE(int, MonsterAmountBase); + SCRIPT_MEMBER_WITH_BASE(int, MonsterAmountDelta); + SCRIPT_MEMBER_WITH_BASE(int, MonsterGenerationIntervalBase); + SCRIPT_MEMBER_WITH_BASE(int, MonsterGenerationIntervalDelta); + SCRIPT_TRUTH_WITH_BASE(AutoReveal); + SCRIPT_MEMBER_WITH_BASE(festring, ShortDescription); + SCRIPT_TRUTH_WITH_BASE(CanGenerateBone); + SCRIPT_MEMBER_WITH_BASE(int, ItemMinPriceBase); + SCRIPT_MEMBER_WITH_BASE(int, ItemMinPriceDelta); + SCRIPT_MEMBER_WITH_BASE(int, Type); + SCRIPT_MEMBER_WITH_BASE(int, EnchantmentMinusChanceBase); + SCRIPT_MEMBER_WITH_BASE(int, EnchantmentMinusChanceDelta); + SCRIPT_MEMBER_WITH_BASE(int, EnchantmentPlusChanceBase); + SCRIPT_MEMBER_WITH_BASE(int, EnchantmentPlusChanceDelta); + SCRIPT_MEMBER_WITH_BASE(int, BackGroundType); + SCRIPT_TRUTH_WITH_BASE(IsCatacomb); + SCRIPT_MEMBER_WITH_BASE(festring, EnterImage); + SCRIPT_MEMBER_WITH_BASE(v2, EnterTextDisplacement); +}; + +class dungeonscript : public script +{ + public: + typedef dungeonscript scripttype; + dungeonscript(); + virtual ~dungeonscript(); + virtual void ReadFrom(inputfile&); + const std::map& GetLevel() const; + void RandomizeLevels(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual const datamap& GetDataMap() const { return DataMap; } + static void InitDataMap(); + protected: + static datamap DataMap; + std::map Level; + std::list > RandomLevel; + SCRIPT_MEMBER(levelscript, LevelDefault); + SCRIPT_MEMBER(int, Levels); + SCRIPT_MEMBER(festring, Description); + SCRIPT_MEMBER(festring, ShortDescription); +}; + +class teamscript : public script +{ + public: + typedef teamscript scripttype; + virtual void ReadFrom(inputfile&); + const std::vector >& GetRelation() const; + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + static void InitDataMap(); + protected: + virtual const datamap& GetDataMap() const { return DataMap; } + static datamap DataMap; + std::vector > Relation; + SCRIPT_MEMBER(int, KillEvilness); +}; + +class gamescript : public script +{ + public: + typedef gamescript scripttype; + virtual void ReadFrom(inputfile&); + const std::list >& GetTeam() const; + const std::map& GetDungeon() const; + void RandomizeLevels(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + static void InitDataMap(); + protected: + virtual const datamap& GetDataMap() const { return DataMap; } + static datamap DataMap; + std::list > Team; + std::map Dungeon; + SCRIPT_MEMBER(int, Dungeons); + SCRIPT_MEMBER(int, Teams); +}; + +outputfile& operator<<(outputfile&, const gamescript*); +inputfile& operator>>(inputfile&, gamescript*&); + +class scriptsystem +{ + public: + static void Initialize(); +}; + +#endif diff --git a/Main/Include/slot.h b/Main/Include/slot.h new file mode 100644 index 0000000..bfdf894 --- /dev/null +++ b/Main/Include/slot.h @@ -0,0 +1,136 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __SLOT_H__ +#define __SLOT_H__ + +#include "typedef.h" + +class item; +class character; +class outputfile; +class inputfile; +class bodypart; +class square; + +class slot +{ + public: + slot() : Item(0) { } + virtual void Empty() = 0; + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + item* GetItem() const { return Item; } + item* operator->() const { return Item; } + item* operator*() const { return Item; } + virtual void AddFriendItem(item*) const = 0; + virtual truth IsOnGround() const { return false; } + virtual void PutInItem(item*) = 0; + virtual square* GetSquareUnder(int = 0) const = 0; + virtual void SignalVolumeAndWeightChange() = 0; + virtual void SignalEmitationIncrease(col24) = 0; + virtual void SignalEmitationDecrease(col24) = 0; + virtual void DonateTo(item*); + virtual truth CanBeSeenBy(ccharacter*) const = 0; + virtual void SignalEnchantmentChange() { } + virtual truth IsVisible() const = 0; + virtual truth IsGearSlot() const { return false; } + virtual ccharacter* FindCarrier() const = 0; + protected: + item* Item; +}; + +class stackslot : public slot +{ + public: + friend class stack; + friend class stackiterator; + stackslot(stack* MotherStack, stackslot* Last) : MotherStack(MotherStack), Last(Last), Next(0) { } + virtual void Empty(); + virtual void AddFriendItem(item*) const; + virtual truth IsOnGround() const; + virtual square* GetSquareUnder(int = 0) const; + virtual void SignalVolumeAndWeightChange(); + virtual void SignalEmitationIncrease(col24); + virtual void SignalEmitationDecrease(col24); + virtual void DonateTo(item*); + virtual truth CanBeSeenBy(ccharacter*) const; + stack* GetMotherStack() const { return MotherStack; } + void SetMotherStack(stack* What) { MotherStack = What; } + virtual truth IsVisible() const; + virtual void PutInItem(item*); + virtual void Load(inputfile&); + virtual ccharacter* FindCarrier() const; + protected: + stack* MotherStack; + stackslot* Last; + stackslot* Next; +}; + +class bodypartslot : public slot +{ + public: + virtual void Empty(); + character* GetMaster() const { return Master; } + void SetMaster(character* What) { Master = What; } + virtual void AddFriendItem(item*) const; + virtual square* GetSquareUnder(int = 0) const; + virtual void SignalVolumeAndWeightChange(); + virtual void SignalEmitationIncrease(col24); + virtual void SignalEmitationDecrease(col24); + virtual void PutInItem(item*); + virtual void Load(inputfile&); + virtual truth CanBeSeenBy(ccharacter*) const; + virtual truth IsVisible() const { return false; } + virtual ccharacter* FindCarrier() const { return Master; } + protected: + character* Master; +}; + +class gearslot : public slot +{ + public: + virtual void Empty(); + bodypart* GetBodyPart() const { return BodyPart; } + void SetBodyPart(bodypart* What) { BodyPart = What; } + virtual void AddFriendItem(item*) const; + void Init(bodypart*, int); + int GetEquipmentIndex() const { return EquipmentIndex; } + void SetEquipmentIndex(int What) { EquipmentIndex = What; } + virtual void PutInItem(item*); + virtual square* GetSquareUnder(int = 0) const; + virtual void SignalVolumeAndWeightChange(); + virtual void SignalEmitationIncrease(col24); + virtual void SignalEmitationDecrease(col24); + virtual truth CanBeSeenBy(ccharacter*) const; + virtual void SignalEnchantmentChange(); + virtual truth IsVisible() const { return false; } + virtual truth IsGearSlot() const { return true; } + virtual ccharacter* FindCarrier() const; + protected: + bodypart* BodyPart; + int EquipmentIndex; +}; + +inline outputfile& operator<<(outputfile& SaveFile, const slot& Slot) +{ + Slot.Save(SaveFile); + return SaveFile; +} + +inline inputfile& operator>>(inputfile& SaveFile, slot& Slot) +{ + Slot.Load(SaveFile); + return SaveFile; +} + +#endif diff --git a/Main/Include/smoke.h b/Main/Include/smoke.h new file mode 100644 index 0000000..ec5d30c --- /dev/null +++ b/Main/Include/smoke.h @@ -0,0 +1,59 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __SMOKE_H__ +#define __SMOKE_H__ + +#include + +#include "entity.h" +#include "v2.h" + +class gas; +class lsquare; +class bitmap; +class inputfile; +class outputfile; + +class smoke : public entity +{ + public: + /* Come To The Dark Side */ + smoke* Next; + public: + smoke(); + smoke(gas*, lsquare*); + virtual ~smoke(); + virtual void Be(); + virtual void Draw(blitdata&) const; + virtual square* GetSquareUnderEntity(int = 0) const; + void SetLSquareUnder(lsquare* What) { LSquareUnder = What; } + lsquare* GetLSquareUnder() const { return LSquareUnder; } + void Save(outputfile&) const; + void Load(inputfile&); + virtual truth IsOnGround() const { return true; } + void AddBreatheMessage() const; + void Merge(gas*); + cmaterial* GetGas() const { return Gas; } + truth IsDangerousToBreathe(ccharacter*) const; + truth IsScaryToBreathe(ccharacter*) const; + protected: + material* Gas; + std::vector Picture; + lsquare* LSquareUnder; + alpha Alpha; +}; + +outputfile& operator<<(outputfile&, const smoke*); +inputfile& operator>>(inputfile&, smoke*&); + +#endif diff --git a/Main/Include/square.h b/Main/Include/square.h new file mode 100644 index 0000000..6f0cbfb --- /dev/null +++ b/Main/Include/square.h @@ -0,0 +1,93 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __SQUARE_H__ +#define __SQUARE_H__ + +#include "festring.h" +#include "ivandef.h" +#include "v2.h" + +class area; +class gterrain; +class oterrain; +class outputfile; +class inputfile; +class character; + +class square +{ + public: + square(area*, v2); + virtual ~square(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void AddCharacter(character*); + virtual void RemoveCharacter(); + virtual character* GetCharacter() const { return Character; } + ulong GetLastSeen() const { return LastSeen; } + v2 GetPos() const { return Pos; } + area* GetArea() const { return AreaUnder; } + virtual gterrain* GetGTerrain() const = 0; + virtual oterrain* GetOTerrain() const = 0; + festring GetMemorizedDescription() { return MemorizedDescription; } + void SetMemorizedDescription(cfestring& What) { MemorizedDescription = What; } + virtual truth CanBeSeenByPlayer(truth = false) const = 0; + virtual truth CanBeSeenFrom(v2, long, truth = false) const = 0; + void SendNewDrawRequest() { Flags |= NEW_DRAW_REQUEST; } + void SendStrongNewDrawRequest() { Flags |= STRONG_NEW_DRAW_REQUEST; } + cchar* SurviveMessage(character*) const; + cchar* MonsterSurviveMessage(character*) const; + cchar* DeathMessage(character*) const; + cchar* MonsterDeathVerb(character*) const; + cchar* ScoreEntry(character*) const; + truth IsFatalToStay() const; + int GetEntryDifficulty() const; + int GetRestModifier() const; + void IncStaticAnimatedEntities() + { + ++StaticAnimatedEntities; + ++AnimatedEntities; + } + void DecStaticAnimatedEntities() + { + if(!StaticAnimatedEntities) + int esko = esko = 2; + + --StaticAnimatedEntities; + --AnimatedEntities; + } + void IncAnimatedEntities() { ++AnimatedEntities; } + void DecAnimatedEntities() { --AnimatedEntities; } + truth CanBeSeenBy(ccharacter*, truth = false) const; + col24 GetLuminance() const { return Luminance; } + square* GetNeighbourSquare(int) const; + square* GetNearSquare(v2) const; + virtual int GetSquareWalkability() const = 0; + void SetCharacter(character* What) { Character = What; } + void AddFlags(ulong What) { Flags |= What; } + void RemoveFlags(ulong What) { Flags &= ~What; } + virtual truth HasBeenSeen() const { return LastSeen; } + virtual void SurviveEffect(character*); + protected: + festring MemorizedDescription; + area* AreaUnder; + character* Character; + v2 Pos; + col24 Luminance; + mutable ulong Flags; + ushort StaticAnimatedEntities; + ushort AnimatedEntities; + mutable uchar LastSeen; +}; + +#endif diff --git a/Main/Include/stack.h b/Main/Include/stack.h new file mode 100644 index 0000000..e6de7f5 --- /dev/null +++ b/Main/Include/stack.h @@ -0,0 +1,173 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __STACK_H__ +#define __STACK_H__ + +#include "lsquare.h" +#include "slot.h" + +class felist; +class entity; + +typedef std::vector itemvector; +typedef std::vector itemvectorvector; + +/* Stack contains an arbitrary number of items in a linked list, which can + be browsed using stackiterators like this: + + for(stackiterator i = Stack->GetBottom(); i.HasItem(); ++i) + { + item* Item = *i; + i->ItemMemberFunction(); + } + + or using a temporary vector: + + itemvector ItemVector; + Stack->FillItemVector(ItemVector); + + for(int c = 0; c < ItemVector.size(); ++c) + { + item* Item = ItemVector[c]; + ItemVector[c]->ItemMemberFunction(); + } + + The former is faster and should be used if items can't be removed nor + added during the loop (at worst, this could cause a crash), otherwise + the latter is necessary. + + Items are added to stack with Stack->AddItem(Item) and removed by + Item->RemoveFromSlot(). A number of Stack->DrawContents() functions + is provided as an easy item-selecting GUI. */ + +class stackiterator +{ + public: + stackiterator(stackslot* Slot) : Slot(Slot) { } + stackiterator& operator++() { Slot = Slot->Next; return *this; } + stackiterator& operator--() { Slot = Slot->Last; return *this; } + truth HasItem() const { return truth(Slot); } + item* operator->() const { return Slot->Item; } + item* operator*() const { return Slot->Item; } + const stackslot& GetSlot() const { return *Slot; } + private: + stackslot* Slot; +}; + +class stack +{ + public: + stack(square*, entity*, ulong = 0); + ~stack(); + void Load(inputfile&); + void Draw(ccharacter*, blitdata&, int) const; + void AddItem(item*, truth = true); + void RemoveItem(stackslot*); + item* GetItem(int) const; + stackiterator GetBottom() const { return stackiterator(Bottom); } + stackiterator GetTop() const { return stackiterator(Top); } + int GetItems() const { return Items; } + int GetSideItems(int) const; + int GetVisibleItems(ccharacter*) const; + int GetNativeVisibleItems(ccharacter*) const; + int GetVisibleSideItems(ccharacter*, int) const; + void SetMotherSquare(square* What) { MotherSquare = What; } + item* DrawContents(ccharacter*, cfestring&, int = 0, sorter = 0) const; + int DrawContents(itemvector&, ccharacter*, cfestring&, int = 0, sorter = 0) const; + int DrawContents(itemvector&, stack*, ccharacter*, cfestring&, cfestring&, cfestring&, cfestring&, col16, int, sorter = 0) const; + v2 GetPos() const; + void Clean(truth = false); + void Save(outputfile&) const; + int SearchItem(item*) const; + square* GetSquareUnder() const; + lsquare* GetLSquareUnder() const { return static_cast(GetSquareUnder()); } + truth SortedItems(ccharacter*, sorter) const; + void BeKicked(character*, int, int); + void Polymorph(character*); + void CheckForStepOnEffect(character*); + lsquare* GetLSquareTrulyUnder(int) const; + void ReceiveDamage(character*, int, int, int = YOURSELF); + void TeleportRandomly(uint = 0xFFFF); + void FillItemVector(itemvector&) const; + truth IsOnGround() const; + truth RaiseTheDead(character*); + truth TryKey(item*, character*); + truth Open(character*); + void SignalVolumeAndWeightChange(); + void CalculateVolumeAndWeight(); + long GetVolume() const { return Volume; } + long GetWeight() const { return Weight; } + long GetWeight(ccharacter*, int) const; + entity* GetMotherEntity() const { return MotherEntity; } + void SetMotherEntity(entity* What) { MotherEntity = What; } + area* GetArea() const { return GetSquareUnder()->GetArea(); } + lsquare* GetNearLSquare(v2 Pos) const { return GetLSquareUnder()->GetLevel()->GetLSquare(Pos); } + col24 GetEmitation() const { return Emitation; } + void SignalEmitationIncrease(int, col24); + void SignalEmitationDecrease(int, col24); + void CalculateEmitation(); + col24 GetSideEmitation(int); + truth CanBeSeenBy(ccharacter*, int) const; + truth IsDangerous(ccharacter*) const; + truth Duplicate(int, ulong = 0); + void MoveItemsTo(stack*); + void MoveItemsTo(slot*); + item* GetBottomItem(ccharacter*, truth) const; + item* GetBottomVisibleItem(ccharacter*) const; + item* GetBottomSideItem(ccharacter*, int, truth) const; + void Pile(itemvectorvector&, ccharacter*, int, sorter = 0) const; + long GetTruePrice() const; + static int GetSelected() { return Selected; } + static void SetSelected(int What) { Selected = What; } + truth TakeSomethingFrom(character*, cfestring&); + truth PutSomethingIn(character*, cfestring&, long, ulong); + truth IsVisible() const { return !(Flags & HIDDEN); } + int GetSpoiledItems() const; + void SortAllItems(const sortdata&) const; + void Search(ccharacter*, int); + truth NeedDangerSymbol(ccharacter*) const; + void PreProcessForBone(); + void PostProcessForBone(); + void FinalProcessForBone(); + void AddElement(item*, truth = false); + void SpillFluid(character*, liquid*, long); + void AddItems(const itemvector&); + void MoveItemsTo(itemvector&, int); + void Freeze() { Flags |= FREEZED; } + void UnFreeze() { Flags &= ~FREEZED; } + void DropSideItems(); + truth DetectMaterial(cmaterial*) const; + void SetLifeExpectancy(int, int); + truth Necromancy(character*); + void CalculateEnchantments(); + ccharacter* FindCarrier() const; + void Haste(); + void Slow(); + private: + void RemoveElement(stackslot*); + void AddContentsToList(felist&, ccharacter*, cfestring&, int, int, sorter) const; + int SearchChosen(itemvector&, ccharacter*, int, int, int, int, sorter = 0) const; + static truth AllowDamage(int, int); + static int Selected; + stackslot* Bottom; + stackslot* Top; + square* MotherSquare; + entity* MotherEntity; + long Volume; + long Weight; + col24 Emitation : 24; + ulong Flags : 8; + int Items; +}; + +#endif diff --git a/Main/Include/team.h b/Main/Include/team.h new file mode 100644 index 0000000..f5d993b --- /dev/null +++ b/Main/Include/team.h @@ -0,0 +1,62 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __TEAM_H__ +#define __TEAM_H__ + +#include +#include +#include + +#include "typedef.h" + +class outputfile; +class inputfile; +class character; + +typedef std::vector charactervector; + +class team +{ + public: + team(); + team(ulong); + void SetRelation(team*, int); + int GetRelation(const team*) const; + void Hostility(team*); + ulong GetID() const { return ID; } + void SetID(ulong What) { ID = What; } + void Save(outputfile&) const; + void Load(inputfile&); + void SetLeader(character* What) { Leader = What; } + character* GetLeader() const { return Leader; } + std::list::iterator Add(character*); + void Remove(std::list::iterator); + const std::list& GetMember() const { return Member; } + int GetKillEvilness() const { return KillEvilness; } + void SetKillEvilness(int What) { KillEvilness = What; } + truth HasEnemy() const; + int GetMembers() const { return Member.size(); } + int GetEnabledMembers() const; + void MoveMembersTo(charactervector&); + private: + character* Leader; + std::map Relation; + std::list Member; + ulong ID; + int KillEvilness; +}; + +outputfile& operator<<(outputfile&, const team*); +inputfile& operator>>(inputfile&, team*&); + +#endif diff --git a/Main/Include/terra.h b/Main/Include/terra.h new file mode 100644 index 0000000..fee6111 --- /dev/null +++ b/Main/Include/terra.h @@ -0,0 +1,49 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __TERRA_H__ +#define __TERRA_H__ + +#include "typedef.h" + +class character; +class festring; + +class terrain +{ + public: + virtual void StepOn(character*) { } + virtual cchar* SurviveMessage() const; + virtual cchar* MonsterSurviveMessage() const; + virtual cchar* DeathMessage() const; + virtual cchar* MonsterDeathVerb() const; + virtual cchar* ScoreEntry() const; + virtual truth IsFatalToStay() const { return false; } + virtual void SurviveEffect(character*) { }; +}; + +class gterrain : public terrain +{ + public: + virtual int GetEntryDifficulty() const = 0; +}; + +class oterrain : public terrain +{ + public: + virtual truth Enter(truth) const = 0; + virtual int GetRestModifier() const { return 1; } + virtual void ShowRestMessage(character*) const { } + virtual int GetWalkability() const = 0; +}; + +#endif diff --git a/Main/Include/trap.h b/Main/Include/trap.h new file mode 100644 index 0000000..db8a27e --- /dev/null +++ b/Main/Include/trap.h @@ -0,0 +1,183 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __TRAP_H__ +#define __TRAP_H__ + +#include + +#include "entity.h" +#include "festring.h" + +class trap; +class lsquare; +class character; +class bitmap; +class outputfile; +class inputfile; +struct blitdata; + +typedef trap* (*trapspawner)(); + +struct trapdata +{ + trapdata(ulong TrapID, ulong VictimID, ulong BodyParts) : Next(0), TrapID(TrapID), VictimID(VictimID), BodyParts(BodyParts) { } + trapdata() : Next(0) { } + trapdata* Next; + ulong TrapID; + ulong VictimID; + ulong BodyParts; +}; + +outputfile& operator<<(outputfile&, const trapdata*); +inputfile& operator>>(inputfile&, trapdata*&); +outputfile& operator<<(outputfile&, const trapdata&); +inputfile& operator>>(inputfile&, trapdata&); + +class itemtrapbase +{ + public: + itemtrapbase() : Active(false) { } + void Save(outputfile&) const; + void Load(inputfile&); + void SetIsActive(truth); + truth CanBeSeenBy(ccharacter*) const; + void Search(ccharacter*, int); + void FinalProcessForBone(); + void TeleportRandomly(); + virtual void SendNewDrawAndMemorizedUpdateRequest() const = 0; + virtual festring GetName(int) const = 0; + virtual void UpdatePictures() = 0; + protected: + truth Active; + int Team; + std::set DiscoveredByTeam; +}; + +template +class itemtrap : public base, public itemtrapbase +{ + public: + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual truth IsActive() const { return Active; } + virtual truth IsAppliable(ccharacter*) const { return true; } + virtual truth NeedDangerSymbol() const { return IsActive(); } + virtual int GetTeam() const { return Team; } + virtual void SetTeam(int What) { Team = What; } + virtual void FinalProcessForBone(); + virtual void TeleportRandomly(); + virtual truth CanBeSeenBy(ccharacter*) const; + virtual void Search(ccharacter* Char, int Perception) + { + itemtrapbase::Search(Char, Perception); + } + virtual void SetIsActive(truth What) { itemtrapbase::SetIsActive(What); } + virtual void SendNewDrawAndMemorizedUpdateRequest() const + { + base::SendNewDrawAndMemorizedUpdateRequest(); + } + virtual festring GetName(int Case) const { return base::GetName(Case); } + virtual void UpdatePictures() { base::UpdatePictures(); } +}; + +template +inline void itemtrap::Load(inputfile& SaveFile) +{ + base::Load(SaveFile); + itemtrapbase::Load(SaveFile); +} + +template +inline void itemtrap::Save(outputfile& SaveFile) const +{ + base::Save(SaveFile); + itemtrapbase::Save(SaveFile); +} + +template +inline void itemtrap::FinalProcessForBone() +{ + base::FinalProcessForBone(); + itemtrapbase::FinalProcessForBone(); +} + +template +inline void itemtrap::TeleportRandomly() +{ + itemtrapbase::TeleportRandomly(); + base::TeleportRandomly(); +} + +template +inline truth itemtrap::CanBeSeenBy(ccharacter* Viewer) const +{ + return itemtrapbase::CanBeSeenBy(Viewer) && base::CanBeSeenBy(Viewer); +} + +class trapprototype +{ + public: + trapprototype(trapspawner truth, cchar*); + trap* SpawnAndLoad(inputfile&) const; + cchar* GetClassID() const { return ClassID; } + int GetIndex() const { return Index; } + private: + int Index; + trapspawner Spawner; + cchar* ClassID; +}; + +class trap : public entity +{ + public: + /* Come To The Dark Side */ + trap* Next; + public: + typedef trapprototype prototype; + trap(); + virtual ~trap(); + virtual square* GetSquareUnderEntity(int = 0) const; + void SetLSquareUnder(lsquare* What) { LSquareUnder = What; } + lsquare* GetLSquareUnder() const { return LSquareUnder; } + virtual truth IsOnGround() const { return true; } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + int GetType() const { return GetProtoType()->GetIndex(); } + virtual void AddDescription(festring&) const = 0; + virtual const prototype* GetProtoType() const = 0; + virtual void StepOnEffect(character*) = 0; + virtual void Draw(blitdata&) const = 0; + virtual void ReceiveDamage(character*, int, int, int) { } + virtual truth IsDangerous(ccharacter*) const { return false; } + virtual void PreProcessForBone() { } + virtual void PostProcessForBone() { } + virtual void Untrap() = 0; + protected: + lsquare* LSquareUnder; +}; + +#ifdef __FILE_OF_STATIC_TRAP_PROTOTYPE_DEFINITIONS__ +#define TRAP_PROTO(name)\ +template<> const trapprototype\ + name##sysbase::ProtoType((trapspawner)(&name##sysbase::Spawn), #name); +#else +#define TRAP_PROTO(name) +#endif + +#define TRAP(name, base)\ +class name;\ +typedef simplesysbase name##sysbase;\ +TRAP_PROTO(name)\ +class name : public name##sysbase + +#endif diff --git a/Main/Include/traps.h b/Main/Include/traps.h new file mode 100644 index 0000000..f655fe3 --- /dev/null +++ b/Main/Include/traps.h @@ -0,0 +1,53 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __TRAPS_H__ +#define __TRAPS_H__ + +#include "trap.h" + +class bitmap; + +TRAP(web, trap) +{ + public: + web(); + virtual ~web(); + virtual void AddDescription(festring&) const; + virtual truth TryToUnStick(character*, v2); + virtual int GetTrapBaseModifier() const { return Strength; } + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual void StepOnEffect(character*); + virtual int GetTrapType() const { return GetType(); } + virtual ulong GetTrapID() const { return TrapData.TrapID; } + virtual ulong GetVictimID() const { return TrapData.VictimID; } + virtual void AddTrapName(festring&, int) const; + virtual void UnStick() { TrapData.VictimID = 0; } + virtual void UnStick(int I) { TrapData.BodyParts &= ~(1 << I); } + void SetStrength(int What) { Strength = What; } + virtual void Draw(blitdata&) const; + truth IsStuckToBodyPart(int) const; + virtual void ReceiveDamage(character*, int, int, int); + virtual void Destroy(); + virtual truth IsDangerous(ccharacter* Char) const { return CanBeSeenBy(Char); } + virtual truth CanBeSeenBy(ccharacter*) const; + virtual void PreProcessForBone(); + virtual void PostProcessForBone(); + virtual void Untrap(); + protected: + trapdata TrapData; + bitmap* Picture; + int Strength; /* must be more than 0 */ +}; + +#endif diff --git a/Main/Include/worldmap.h b/Main/Include/worldmap.h new file mode 100644 index 0000000..b368455 --- /dev/null +++ b/Main/Include/worldmap.h @@ -0,0 +1,72 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __WORLDMAP_H__ +#define __WORLDMAP_H__ + +#include + +#include "area.h" + +class wsquare; +class continent; + +typedef std::vector charactervector; + +class worldmap : public area +{ + public: + worldmap(int, int); + worldmap(); + virtual ~worldmap(); + void Generate(); + void Save(outputfile&) const; + void Load(inputfile&); + wsquare* GetWSquare(v2 Pos) const { return Map[Pos.X][Pos.Y]; } + wsquare* GetWSquare(int x, int y) const { return Map[x][y]; } + void GenerateClimate(); + int WhatTerrainIsMostCommonAroundCurrentTerritorySquareIncludingTheSquareItself(int, int); + void CalculateContinents(); + void SmoothAltitude(); + void SmoothClimate(); + void RandomizeAltitude(); + continent* GetContinentUnder(v2) const; + continent* GetContinent(int) const; + void RemoveEmptyContinents(); + int GetAltitude(v2); + charactervector& GetPlayerGroup(); + character* GetPlayerGroupMember(int); + virtual void Draw(truth) const; + void CalculateLuminances(); + void CalculateNeighbourBitmapPoses(); + wsquare* GetNeighbourWSquare(v2, int) const; + v2 GetEntryPos(ccharacter*, int) const; + void RevealEnvironment(v2, int); + void SafeSmooth(int, int); + void FastSmooth(int, int); + wsquare*** GetMap() const { return Map; } + void UpdateLOS(); + protected: + wsquare*** Map; + std::vector Continent; + uchar** TypeBuffer; + uchar** OldTypeBuffer; + short** AltitudeBuffer; + short** OldAltitudeBuffer; + uchar** ContinentBuffer; + charactervector PlayerGroup; +}; + +outputfile& operator<<(outputfile&, const worldmap*); +inputfile& operator>>(inputfile&, worldmap*&); + +#endif diff --git a/Main/Include/wskill.h b/Main/Include/wskill.h new file mode 100644 index 0000000..4afc505 --- /dev/null +++ b/Main/Include/wskill.h @@ -0,0 +1,102 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __WSKILL_H__ +#define __WSKILL_H__ + +#include "ivandef.h" + +class outputfile; +class inputfile; + +class weaponskill +{ + public: + weaponskill() : Level(0), Hits(0), HitCounter(0) { } + int GetLevel() const { return Level; } + int GetHits() const { return Hits; } + truth Tick(); + truth AddHit(int); + truth SubHit(int); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + virtual int GetLevelMap(int) const = 0; + virtual ulong GetUnuseTickMap(int) const = 0; + virtual int GetUnusePenaltyMap(int) const = 0; + protected: + int Level; + int Hits; + uint HitCounter; +}; + +class cweaponskill : public weaponskill +{ + public: + virtual int GetLevelMap(int) const; + virtual ulong GetUnuseTickMap(int) const; + virtual int GetUnusePenaltyMap(int) const; + cchar* GetName(int) const; + int GetBonus() const { return 1000 + 50 * Level; } + void AddLevelUpMessage(int) const; + void AddLevelDownMessage(int) const; +}; + +inline outputfile& operator<<(outputfile& SaveFile, const cweaponskill& WeaponSkill) +{ + WeaponSkill.Save(SaveFile); + return SaveFile; +} + +inline inputfile& operator>>(inputfile& SaveFile, cweaponskill& WeaponSkill) +{ + WeaponSkill.Load(SaveFile); + return SaveFile; +} + +class sweaponskill : public weaponskill +{ + public: + sweaponskill() : ID(0), Weight(0), Config(0) { } + sweaponskill(citem*); + virtual int GetLevelMap(int) const; + virtual ulong GetUnuseTickMap(int) const; + virtual int GetUnusePenaltyMap(int) const; + int GetBonus() const { return Level ? 1150 + 25 * (Level - 1) : 1000; } + void AddLevelUpMessage(cchar*) const; + void AddLevelDownMessage(cchar*) const; + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + truth IsSkillOf(citem*) const; + truth IsSkillOfCloneMother(citem*, ulong) const; + void SetID(ulong What) { ID = What; } + ulong GetID() const { return ID; } + void PreProcessForBone() { ID = -ID; } + private: + ulong ID; + long Weight; + int Config; +}; + +inline outputfile& operator<<(outputfile& SaveFile, const sweaponskill* WeaponSkill) +{ + WeaponSkill->Save(SaveFile); + return SaveFile; +} + +inline inputfile& operator>>(inputfile& SaveFile, sweaponskill*& WeaponSkill) +{ + WeaponSkill = new sweaponskill; + WeaponSkill->Load(SaveFile); + return SaveFile; +} + +#endif diff --git a/Main/Include/wsquare.h b/Main/Include/wsquare.h new file mode 100644 index 0000000..2c345cc --- /dev/null +++ b/Main/Include/wsquare.h @@ -0,0 +1,57 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __WSQUARE_H__ +#define __WSQUARE_H__ + +#include "square.h" +#include "worldmap.h" + +class gwterrain; +class owterrain; +struct blitdata; + +class wsquare : public square +{ + public: + friend class worldmap; + wsquare(worldmap*, v2); + virtual ~wsquare(); + virtual void Save(outputfile&) const; + virtual void Load(inputfile&); + void Draw(blitdata&); + void SetGWTerrain(gwterrain*); + void SetOWTerrain(owterrain*); + gwterrain* GetGWTerrain() const { return GWTerrain; } + owterrain* GetOWTerrain() const { return OWTerrain; } + void ChangeWTerrain(gwterrain*, owterrain*); + worldmap* GetWorldMap() const { return static_cast(AreaUnder); } + void SetWorldMapUnder(worldmap* What) { AreaUnder = What; } + void UpdateMemorizedDescription(truth = false); + virtual gterrain* GetGTerrain() const; + virtual oterrain* GetOTerrain() const; + void ChangeGWTerrain(gwterrain*); + void ChangeOWTerrain(owterrain*); + void SetWTerrain(gwterrain*, owterrain*); + truth SignalSeen(); + void CalculateLuminance(); + wsquare* GetNeighbourWSquare(int I) const { return static_cast(AreaUnder)->GetNeighbourWSquare(Pos, I); } + int GetWalkability() const; + virtual int GetSquareWalkability() const { return GetWalkability(); } + virtual truth CanBeSeenByPlayer(truth = false) const; + virtual truth CanBeSeenFrom(v2, long, truth = false) const; + protected: + gwterrain* GWTerrain; + owterrain* OWTerrain; +}; + +#endif diff --git a/Main/Include/wterra.h b/Main/Include/wterra.h new file mode 100644 index 0000000..030d5b6 --- /dev/null +++ b/Main/Include/wterra.h @@ -0,0 +1,122 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __WTERRA_H__ +#define __WTERRA_H__ + +#include "terra.h" +#include "wsquare.h" + +struct blitdata; + +typedef gwterrain* (*gwterrainspawner)(); +typedef owterrain* (*owterrainspawner)(); + +class wterrain +{ + public: + wterrain() : WSquareUnder(0), AnimationFrames(1) { } + virtual ~wterrain() { } + virtual void Load(inputfile&); + v2 GetPos() const { return WSquareUnder->GetPos(); } + void SetWSquareUnder(wsquare* What) { WSquareUnder = What; } + worldmap* GetWorldMap() const { return WSquareUnder->GetWorldMap(); } + void AddName(festring&, int) const; + festring GetName(int) const; + truth IsAnimated() const { return AnimationFrames > 1; } + void SetAnimationFrames(int What) { AnimationFrames = What; } + virtual cchar* GetNameStem() const = 0; + protected: + virtual truth UsesLongArticle() const { return false; } + virtual v2 GetBitmapPos(int) const = 0; + wsquare* WSquareUnder; + int AnimationFrames; +}; + +class gwterrainprototype +{ + public: + gwterrainprototype(gwterrainspawner, cchar*); + gwterrain* Spawn() const { return Spawner(); } + gwterrain* SpawnAndLoad(inputfile&) const; + cchar* GetClassID() const { return ClassID; } + int GetIndex() const { return Index; } + private: + int Index; + gwterrainspawner Spawner; + cchar* ClassID; +}; + +class gwterrain : public wterrain, public gterrain +{ + public: + typedef gwterrainprototype prototype; + virtual void Save(outputfile&) const; + void Draw(blitdata&) const; + virtual int GetPriority() const = 0; + virtual int GetEntryDifficulty() const { return 10; } + virtual const prototype* GetProtoType() const = 0; + int GetType() const { return GetProtoType()->GetIndex(); } + void CalculateNeighbourBitmapPoses(); + virtual int GetWalkability() const; + protected: + std::pair Neighbour[8]; +}; + +class owterrainprototype +{ + public: + owterrainprototype(owterrainspawner, cchar*); + owterrain* Spawn() const { return Spawner(); } + owterrain* SpawnAndLoad(inputfile&) const; + cchar* GetClassID() const { return ClassID; } + int GetIndex() const { return Index; } + private: + int Index; + owterrainspawner Spawner; + cchar* ClassID; +}; + +class owterrain : public wterrain, public oterrain +{ + public: + typedef owterrainprototype prototype; + virtual void Save(outputfile&) const; + void Draw(blitdata&) const; + virtual const prototype* GetProtoType() const = 0; + int GetType() const { return GetProtoType()->GetIndex(); } + virtual int GetAttachedDungeon() const { return 0; } + virtual int GetAttachedArea() const { return 0; } + virtual int GetAttachedEntry() const; + virtual truth Enter(truth) const; + virtual int GetWalkability() const; +}; + +#ifdef __FILE_OF_STATIC_WTERRAIN_PROTOTYPE_DEFINITIONS__ +#define WTERRAIN_PROTO(name, protobase)\ +template<> const protobase##prototype\ + name##sysbase::ProtoType((protobase##spawner)(&name##sysbase::Spawn),\ + #name); +#else +#define WTERRAIN_PROTO(name, protobase) +#endif + +#define WTERRAIN(name, base, protobase)\ +class name;\ +typedef simplesysbase name##sysbase;\ +WTERRAIN_PROTO(name, protobase)\ +class name : public name##sysbase + +#define GWTERRAIN(name, base) WTERRAIN(name, base, gwterrain) +#define OWTERRAIN(name, base) WTERRAIN(name, base, owterrain) + +#endif diff --git a/Main/Include/wterras.h b/Main/Include/wterras.h new file mode 100644 index 0000000..5f11f8e --- /dev/null +++ b/Main/Include/wterras.h @@ -0,0 +1,174 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#ifndef __WTERRAS_H__ +#define __WTERRAS_H__ + +#include "wterra.h" + +GWTERRAIN(ocean, gwterrain) +{ + public: + ocean() { SetAnimationFrames(32); } + virtual cchar* GetNameStem() const; + virtual truth UsesLongArticle() const { return true; } + virtual v2 GetBitmapPos(int) const; + virtual int GetPriority() const { return 10; } + virtual cchar* SurviveMessage() const; + virtual cchar* MonsterSurviveMessage() const; + virtual cchar* DeathMessage() const; + virtual cchar* MonsterDeathVerb() const; + virtual cchar* ScoreEntry() const; + virtual truth IsFatalToStay() const { return true; } + virtual int GetWalkability() const; +}; + +GWTERRAIN(glacier, gwterrain) +{ + public: + virtual cchar* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual int GetPriority() const { return 90; } +}; + +GWTERRAIN(desert, gwterrain) +{ + public: + virtual cchar* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual int GetPriority() const { return 20; } +}; + +GWTERRAIN(snow, gwterrain) +{ + public: + virtual cchar* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual int GetPriority() const { return 80; } +}; + +GWTERRAIN(jungle, gwterrain) +{ + public: + virtual cchar* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual int GetPriority() const { return 50; } +}; + +GWTERRAIN(leafyforest, gwterrain) +{ + virtual cchar* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual int GetPriority() const { return 60; } +}; + +GWTERRAIN(evergreenforest, gwterrain) +{ + public: + virtual cchar* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual truth UsesLongArticle() const { return true; } + virtual int GetPriority() const { return 70; } +}; + +GWTERRAIN(steppe, gwterrain) +{ + public: + virtual cchar* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual int GetPriority() const { return 30; } +}; + +OWTERRAIN(attnam, owterrain) +{ + public: + virtual cchar* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual int GetAttachedDungeon() const; +}; + +OWTERRAIN(elpuricave, owterrain) +{ + public: + virtual cchar* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual int GetAttachedDungeon() const; +}; + +OWTERRAIN(newattnam, owterrain) +{ + public: + virtual cchar* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual int GetAttachedDungeon() const; +}; + +OWTERRAIN(mondedr, owterrain) +{ + public: + virtual cchar* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual int GetAttachedDungeon() const; +}; + +OWTERRAIN(dragontower, owterrain) +{ + public: + virtual cchar* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual int GetAttachedDungeon() const; +}; + +OWTERRAIN(underwatertunnel, owterrain) +{ + public: + virtual cchar* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual int GetAttachedDungeon() const; + virtual truth UsesLongArticle() const { return true; } +}; + +OWTERRAIN(underwatertunnelexit, owterrain) +{ + public: + virtual cchar* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual int GetAttachedDungeon() const; + virtual truth UsesLongArticle() const { return true; } + virtual int GetAttachedArea() const { return 2; } +}; + +OWTERRAIN(darkforest, owterrain) +{ + public: + virtual const char* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual int GetAttachedDungeon() const; +}; + +OWTERRAIN(muntuo, owterrain) +{ + public: + virtual const char* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual int GetAttachedDungeon() const; +}; + +OWTERRAIN(xinrochtomb, owterrain) +{ + public: + virtual cchar* GetNameStem() const; + virtual v2 GetBitmapPos(int) const; + virtual int GetAttachedDungeon() const; +}; + +#endif diff --git a/Main/Makefile.am b/Main/Makefile.am new file mode 100644 index 0000000..c47ba73 --- /dev/null +++ b/Main/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = Include Source Resource diff --git a/Main/Resource/CVS/Entries b/Main/Resource/CVS/Entries new file mode 100644 index 0000000..0ab288a --- /dev/null +++ b/Main/Resource/CVS/Entries @@ -0,0 +1,5 @@ +/Ivan.ico/1.1/Sun Jan 12 19:25:46 2003/-kb/ +/Ivan.rc/1.1/Sun Jan 12 19:25:46 2003// +/Makefile.am/1.4/Sun Jan 12 19:25:47 2003// +/resource.h/1.4/Sun Jan 12 19:25:48 2003// +D diff --git a/Main/Resource/CVS/Repository b/Main/Resource/CVS/Repository new file mode 100644 index 0000000..0828e85 --- /dev/null +++ b/Main/Resource/CVS/Repository @@ -0,0 +1 @@ +ivan/Main/Resource diff --git a/Main/Resource/CVS/Root b/Main/Resource/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/Main/Resource/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/Main/Resource/Ivan.ico b/Main/Resource/Ivan.ico new file mode 100644 index 0000000..d950604 Binary files /dev/null and b/Main/Resource/Ivan.ico differ diff --git a/Main/Resource/Ivan.rc b/Main/Resource/Ivan.rc new file mode 100644 index 0000000..966824c --- /dev/null +++ b/Main/Resource/Ivan.rc @@ -0,0 +1,72 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Finnish resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FIN) +#ifdef _WIN32 +LANGUAGE LANG_FINNISH, SUBLANG_DEFAULT +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON ICON DISCARDABLE "Ivan.ico" +#endif // Finnish resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Main/Resource/Makefile.am b/Main/Resource/Makefile.am new file mode 100644 index 0000000..444200c --- /dev/null +++ b/Main/Resource/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = Ivan.ico Ivan.rc resource.h diff --git a/Main/Resource/resource.h b/Main/Resource/resource.h new file mode 100644 index 0000000..12604f9 --- /dev/null +++ b/Main/Resource/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by Ivan.rc +// +#define IDI_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Main/Source/CVS/Entries b/Main/Source/CVS/Entries new file mode 100644 index 0000000..75164a5 --- /dev/null +++ b/Main/Source/CVS/Entries @@ -0,0 +1,66 @@ +/Makefile.am/1.20/Thu Aug 19 20:10:05 2004// +/action.cpp/1.13/Tue May 10 20:33:03 2005// +/actions.cpp/1.33/Tue May 10 20:33:03 2005// +/actset.cpp/1.9/Wed Sep 29 21:54:35 2004// +/area.cpp/1.72/Tue May 10 20:33:03 2005// +/areaset.cpp/1.7/Wed Sep 29 21:54:35 2004// +/bodypart.cpp/1.98/Mon Aug 21 18:58:56 2006// +/char.cpp/1.259/Sun Sep 3 18:02:55 2006// +/charset.cpp/1.19/Wed Sep 29 21:54:50 2004// +/charsset.cpp/1.15/Wed Sep 29 21:54:50 2004// +/command.cpp/1.96/Thu Aug 17 09:20:21 2006// +/cont.cpp/1.24/Wed Sep 29 21:54:50 2004// +/coreset.cpp/1.6/Wed Sep 29 21:54:50 2004// +/database.cpp/1.132/Tue Aug 22 12:18:40 2006// +/dataset.cpp/1.7/Wed Sep 29 21:54:50 2004// +/dungeon.cpp/1.74/Sun Sep 3 18:02:56 2006// +/entity.cpp/1.21/Wed Sep 29 21:54:50 2004// +/fluid.cpp/1.45/Tue May 10 20:33:05 2005// +/game.cpp/1.414/Sun Sep 3 18:02:56 2006// +/gear.cpp/1.63/Tue Aug 22 19:45:09 2006// +/god.cpp/1.113/Wed Aug 16 10:36:49 2006// +/gods.cpp/1.61/Fri Jul 22 20:25:44 2005// +/godset.cpp/1.10/Wed Sep 29 21:54:51 2004// +/human.cpp/1.143/Thu Aug 17 11:27:20 2006// +/iconf.cpp/1.13/Sun Jul 24 17:13:55 2005// +/id.cpp/1.24/Mon Aug 21 18:58:56 2006// +/igraph.cpp/1.101/Thu Aug 24 06:10:51 2006// +/item.cpp/1.97/Tue Aug 22 12:18:40 2006// +/itemset.cpp/1.16/Wed Sep 29 21:54:52 2004// +/level.cpp/1.233/Thu Aug 17 09:20:21 2006// +/levelset.cpp/1.14/Wed Sep 29 21:54:52 2004// +/lsquare.cpp/1.287/Mon Aug 21 18:58:56 2006// +/lterra.cpp/1.33/Wed Aug 16 10:16:02 2006// +/lterras.cpp/1.80/Thu Aug 24 06:10:51 2006// +/main.cpp/1.83/Wed Sep 29 21:54:52 2004// +/materia.cpp/1.50/Tue Aug 22 12:18:40 2006// +/materias.cpp/1.31/Mon Aug 21 18:58:56 2006// +/materset.cpp/1.9/Wed Sep 29 21:54:52 2004// +/message.cpp/1.64/Tue May 10 20:33:08 2005// +/miscitem.cpp/1.121/Mon Aug 21 18:58:56 2006// +/nonhuman.cpp/1.103/Sun Jul 24 17:13:55 2005// +/object.cpp/1.113/Tue May 10 20:33:09 2005// +/pool.cpp/1.25/Wed Sep 29 21:54:53 2004// +/proto.cpp/1.131/Thu Aug 17 09:20:22 2006// +/rain.cpp/1.12/Wed Sep 29 21:54:53 2004// +/room.cpp/1.20/Tue May 10 20:33:09 2005// +/rooms.cpp/1.32/Thu Aug 24 06:10:51 2006// +/roomset.cpp/1.7/Wed Sep 29 21:54:53 2004// +/script.cpp/1.127/Sun Sep 3 18:02:56 2006// +/slot.cpp/1.46/Sat Jul 23 13:48:58 2005// +/slotset.cpp/1.8/Wed Sep 29 21:54:53 2004// +/smoke.cpp/1.24/Tue May 10 20:33:09 2005// +/square.cpp/1.81/Tue May 10 20:33:09 2005// +/stack.cpp/1.185/Mon Aug 21 18:58:56 2006// +/team.cpp/1.38/Sun Jul 24 15:05:24 2005// +/terra.cpp/1.16/Tue May 10 20:33:09 2005// +/trap.cpp/1.5/Tue May 10 20:33:09 2005// +/traps.cpp/1.23/Tue May 10 20:33:09 2005// +/trapset.cpp/1.7/Tue May 10 20:33:09 2005// +/wmapset.cpp/1.11/Tue May 10 20:33:09 2005// +/worldmap.cpp/1.105/Tue May 10 20:33:09 2005// +/wskill.cpp/1.48/Tue Aug 22 19:45:09 2006// +/wsquare.cpp/1.73/Fri Dec 10 09:09:43 2004// +/wterra.cpp/1.19/Tue May 10 20:33:09 2005// +/wterras.cpp/1.19/Tue May 10 20:33:09 2005// +D diff --git a/Main/Source/CVS/Repository b/Main/Source/CVS/Repository new file mode 100644 index 0000000..37209b9 --- /dev/null +++ b/Main/Source/CVS/Repository @@ -0,0 +1 @@ +ivan/Main/Source diff --git a/Main/Source/CVS/Root b/Main/Source/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/Main/Source/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/Main/Source/Makefile.am b/Main/Source/Makefile.am new file mode 100644 index 0000000..0278c6e --- /dev/null +++ b/Main/Source/Makefile.am @@ -0,0 +1,5 @@ +bin_PROGRAMS = ivan +INCLUDES=-I@top_srcdir@/FeLib/Include -I@top_srcdir@/Main/Include +ivan_SOURCES = actset.cpp areaset.cpp charset.cpp charsset.cpp command.cpp coreset.cpp dataset.cpp dungeon.cpp game.cpp godset.cpp iconf.cpp id.cpp igraph.cpp itemset.cpp levelset.cpp main.cpp materset.cpp message.cpp object.cpp roomset.cpp script.cpp slotset.cpp trapset.cpp wmapset.cpp wskill.cpp +ivan_LDADD = @top_builddir@/FeLib/Source/libFeLib.a +EXTRA_DIST = action.cpp actions.cpp area.cpp bodypart.cpp char.cpp cont.cpp database.cpp entity.cpp fluid.cpp gear.cpp god.cpp gods.cpp human.cpp item.cpp level.cpp lsquare.cpp lterra.cpp lterras.cpp materia.cpp materias.cpp miscitem.cpp nonhuman.cpp pool.cpp proto.cpp rain.cpp room.cpp rooms.cpp slot.cpp smoke.cpp square.cpp stack.cpp team.cpp terra.cpp trap.cpp traps.cpp worldmap.cpp wsquare.cpp wterra.cpp wterras.cpp diff --git a/Main/Source/action.cpp b/Main/Source/action.cpp new file mode 100644 index 0000000..b61a0e2 --- /dev/null +++ b/Main/Source/action.cpp @@ -0,0 +1,38 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through actset.cpp */ + +actionprototype::actionprototype(actionspawner Spawner, cchar* ClassID) : Spawner(Spawner), ClassID(ClassID) { Index = protocontainer::Add(this); } + +void action::Terminate(truth) +{ + GetActor()->SetAction(0); + delete this; +} + +void action::Save(outputfile& SaveFile) const +{ + SaveFile << (ushort)GetType() << Flags; +} + +void action::Load(inputfile& SaveFile) +{ + SaveFile >> Flags; +} + +action* actionprototype::SpawnAndLoad(inputfile& SaveFile) const +{ + action* Action = Spawner(0); + Action->Load(SaveFile); + return Action; +} diff --git a/Main/Source/actions.cpp b/Main/Source/actions.cpp new file mode 100644 index 0000000..391fe68 --- /dev/null +++ b/Main/Source/actions.cpp @@ -0,0 +1,412 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through actset.cpp */ + +cchar* unconsciousness::GetDeathExplanation() const { return " unconscious"; } +cchar* unconsciousness::GetDescription() const { return "unconscious"; } +cchar* consume::GetDescription() const { return Description.CStr(); } +void consume::SetDescription(cfestring& What) { Description = What; } +cchar* rest::GetDescription() const { return "resting"; } +cchar* dig::GetDescription() const { return "digging"; } +cchar* go::GetDescription() const { return "going"; } +cchar* study::GetDescription() const { return "reading"; } + +void unconsciousness::Save(outputfile& SaveFile) const +{ + action::Save(SaveFile); + SaveFile << Counter; +} + +void unconsciousness::Load(inputfile& SaveFile) +{ + action::Load(SaveFile); + SaveFile >> Counter; +} + +void unconsciousness::Handle() +{ + if(!--Counter) + Terminate(true); + else + { + Actor->EditExperience(ARM_STRENGTH, -50, 1 << 2); + Actor->EditExperience(LEG_STRENGTH, -50, 1 << 2); + } +} + +void unconsciousness::Terminate(truth Finished) +{ + if(Flags & TERMINATING) + return; + + Flags |= TERMINATING; + + if(GetActor()->IsPlayer()) + ADD_MESSAGE("You wake up."); + else if(GetActor()->CanBeSeenByPlayer()) + ADD_MESSAGE("%s wakes up.", GetActor()->CHAR_NAME(DEFINITE)); + + action::Terminate(Finished); +} + +void consume::Save(outputfile& SaveFile) const +{ + action::Save(SaveFile); + SaveFile << ConsumingID << Description; +} + +void consume::Load(inputfile& SaveFile) +{ + action::Load(SaveFile); + SaveFile >> ConsumingID >> Description; +} + +void consume::Handle() +{ + item* Consuming = game::SearchItem(ConsumingID); + + if(!Consuming || !Consuming->Exists() || !Actor->IsOver(Consuming)) + { + Terminate(false); + return; + } + + character* Actor = GetActor(); + + if(!InDNDMode() && Actor->GetHungerState() >= BLOATED) + if(Actor->IsPlayer()) + { + ADD_MESSAGE("You have a really hard time getting all this down your throat."); + + if(game::TruthQuestion(CONST_S("Continue ") + GetDescription() + "? [y/N]")) + ActivateInDNDMode(); + else + { + Terminate(false); + return; + } + } + else + { + Terminate(false); + return; + } + + if(!Actor->IsPlayer() && !Consuming->CanBeEatenByAI(Actor)) // item may be spoiled after action was started + { + Terminate(false); + return; + } + + /* Note: if backupped Actor has died of food effect, + Action is deleted automatically, so we mustn't Terminate it */ + + if(Consuming->Consume(Actor, 500) && Actor->GetAction() == this && Actor->IsEnabled()) + Terminate(true); + else if(Actor->GetHungerState() == OVER_FED) + { + Actor->DeActivateVoluntaryAction(CONST_S("You are about to choke on this stuff.")); + Actor->Vomit(Actor->GetPos(), 500 + RAND() % 500); + } +} + +void consume::Terminate(truth Finished) +{ + if(Flags & TERMINATING) + return; + + Flags |= TERMINATING; + item* Consuming = game::SearchItem(ConsumingID); + character* Actor = GetActor(); + + if(Actor->IsPlayer()) + ADD_MESSAGE("You %s %s.", Finished ? "finish" : "stop", Description.CStr()); + else if(Actor->CanBeSeenByPlayer()) + ADD_MESSAGE("%s %s %s.", Actor->CHAR_NAME(DEFINITE), Finished ? "finishes" : "stops", Description.CStr()); + + if(Finished) + { + if(Consuming->Exists() && !game::IsInWilderness() && (!Actor->IsPlayer() || ivanconfig::GetAutoDropLeftOvers())) + { + Consuming->RemoveFromSlot(); + Actor->GetStackUnder()->AddItem(Consuming); + Actor->DexterityAction(2); + } + } + else if(Consuming && Consuming->Exists()) + { + material* ConsumeMaterial = Consuming->GetConsumeMaterial(Actor); + + if(ConsumeMaterial) + ConsumeMaterial->FinishConsuming(Actor); + } + + action::Terminate(Finished); +} + +void rest::Save(outputfile& SaveFile) const +{ + action::Save(SaveFile); + SaveFile << GoalHP << MinToStop; +} + +void rest::Load(inputfile& SaveFile) +{ + action::Load(SaveFile); + SaveFile >> GoalHP >> MinToStop; +} + +void rest::Handle() +{ + if((GoalHP && (GetActor()->GetHP() >= GoalHP + || GetActor()->GetHP() == GetActor()->GetMaxHP() + || !GetActor()->CanHeal())) + || (MinToStop && game::GetTotalMinutes() >= MinToStop)) + Terminate(true); + else + { + GetActor()->EditExperience(DEXTERITY, -25, 1 << 1); + GetActor()->EditExperience(AGILITY, -25, 1 << 1); + } +} + +void rest::Terminate(truth Finished) +{ + if(Flags & TERMINATING) + return; + + Flags |= TERMINATING; + + if(Finished) + { + if(GetActor()->IsPlayer()) + ADD_MESSAGE("You finish resting."); + else if(GetActor()->CanBeSeenByPlayer()) + ADD_MESSAGE("%s finishes resting.", GetActor()->CHAR_NAME(DEFINITE)); + } + else + { + if(GetActor()->IsPlayer()) + ADD_MESSAGE("You stop resting."); + else if(GetActor()->CanBeSeenByPlayer()) + ADD_MESSAGE("%s stops resting.", GetActor()->CHAR_NAME(DEFINITE)); + } + + action::Terminate(Finished); +} + +void dig::Save(outputfile& SaveFile) const +{ + action::Save(SaveFile); + SaveFile << SquareDug << MoveDigger << RightBackupID << LeftBackupID; +} + +void dig::Load(inputfile& SaveFile) +{ + action::Load(SaveFile); + SaveFile >> SquareDug >> MoveDigger >> RightBackupID >> LeftBackupID; +} + +void dig::Handle() +{ + character* Actor = GetActor(); + item* Digger = Actor->GetMainWielded(); + + if(!Digger) + { + Terminate(false); + return; + } + + lsquare* Square = Actor->GetNearLSquare(SquareDug); + olterrain* Terrain = Square->GetOLTerrain(); + + if(!Terrain || !Terrain->CanBeDestroyed() || !Terrain->GetMainMaterial()->CanBeDug(Digger->GetMainMaterial())) + { + Terminate(false); + return; + } + + int Damage = Actor->GetAttribute(ARM_STRENGTH) * Digger->GetMainMaterial()->GetStrengthValue() / 500; + Terrain->EditHP(-Max(Damage, 1)); + Actor->EditExperience(ARM_STRENGTH, 200, 1 << 5); + Actor->EditAP(-200000 / APBonus(Actor->GetAttribute(DEXTERITY))); + Actor->EditNP(-500); + + if(Terrain->GetHP() <= 0) + { + if(Square->CanBeSeenByPlayer()) + ADD_MESSAGE("%s", Terrain->GetDigMessage().CStr()); + + Terrain->Break(); + + /* If the door was boobytrapped etc. and the character is dead, Action has already been deleted */ + + if(!Actor->IsEnabled()) + return; + + if(MoveDigger && Actor->GetMainWielded()) + Actor->GetMainWielded()->MoveTo(Actor->GetStack()); + + item* RightBackup = game::SearchItem(RightBackupID); + + if(RightBackup && RightBackup->Exists() && Actor->IsOver(RightBackup)) + { + RightBackup->RemoveFromSlot(); + Actor->SetRightWielded(RightBackup); + } + + item* LeftBackup = game::SearchItem(LeftBackupID); + + if(LeftBackup && LeftBackup->Exists() && Actor->IsOver(LeftBackup)) + { + LeftBackup->RemoveFromSlot(); + Actor->SetLeftWielded(LeftBackup); + } + + Terminate(true); + } + else + game::DrawEverything(); +} + +void dig::Terminate(truth Finished) +{ + if(Flags & TERMINATING) + return; + + Flags |= TERMINATING; + + if(!Finished) + { + if(GetActor()->IsPlayer()) + ADD_MESSAGE("You stop digging."); + else if(GetActor()->CanBeSeenByPlayer()) + ADD_MESSAGE("%s stops digging.", GetActor()->CHAR_NAME(DEFINITE)); + } + + action::Terminate(Finished); +} + +void go::Save(outputfile& SaveFile) const +{ + action::Save(SaveFile); + SaveFile << Direction << WalkingInOpen; +} + +void go::Load(inputfile& SaveFile) +{ + action::Load(SaveFile); + SaveFile >> Direction >> WalkingInOpen; +} + +void go::Handle() +{ + GetActor()->EditAP(GetActor()->GetStateAPGain(100)); // gum solution + GetActor()->GoOn(this); +} + +void study::Handle() +{ + item* Literature = game::SearchItem(LiteratureID); + + if(!Literature || !Literature->Exists() || !Actor->IsOver(Literature)) + { + Terminate(false); + return; + } + + if(GetActor()->GetLSquareUnder()->IsDark() && !game::GetSeeWholeMapCheatMode()) + { + ADD_MESSAGE("It is too dark to read now."); + Terminate(false); + return; + } + + if(game::CompareLightToInt(GetActor()->GetLSquareUnder()->GetLuminance(), 115) < 0) + GetActor()->EditExperience(PERCEPTION, -50, 1 << 1); + + if(!Counter) + { + Terminate(true); + return; + } + + if(GetActor()->GetAttribute(INTELLIGENCE) >= Counter) + Counter = 0; + else + Counter -= GetActor()->GetAttribute(INTELLIGENCE); +} + +void study::Terminate(truth Finished) +{ + if(Flags & TERMINATING) + return; + + Flags |= TERMINATING; + item* Literature = game::SearchItem(LiteratureID); + + if(Finished) + { + if(GetActor()->IsPlayer()) + ADD_MESSAGE("You finish reading %s.", Literature->CHAR_NAME(DEFINITE)); + else if(GetActor()->CanBeSeenByPlayer()) + ADD_MESSAGE("%s finishes reading %s.", GetActor()->CHAR_NAME(DEFINITE), Literature->CHAR_NAME(DEFINITE)); + + character* Actor = GetActor(); + Literature->FinishReading(Actor); + + if(!Actor->IsEnabled()) + return; + } + else if(Literature && Literature->Exists()) + { + if(GetActor()->IsPlayer()) + ADD_MESSAGE("You stop reading %s.", Literature->CHAR_NAME(DEFINITE)); + else if(GetActor()->CanBeSeenByPlayer()) + ADD_MESSAGE("%s stops reading %s.", GetActor()->CHAR_NAME(DEFINITE), Literature->CHAR_NAME(DEFINITE)); + } + else + { + if(GetActor()->IsPlayer()) + ADD_MESSAGE("You stop reading."); + else if(GetActor()->CanBeSeenByPlayer()) + ADD_MESSAGE("%s stops reading.", GetActor()->CHAR_NAME(DEFINITE)); + } + + action::Terminate(Finished); +} + +void study::Save(outputfile& SaveFile) const +{ + action::Save(SaveFile); + SaveFile << Counter << LiteratureID; +} + +void study::Load(inputfile& SaveFile) +{ + action::Load(SaveFile); + SaveFile >> Counter >> LiteratureID; +} + +truth go::TryDisplace() +{ + Terminate(false); + return true; +} + +void unconsciousness::RaiseCounterTo(int What) +{ + if(Counter < What) + Counter = What; +} diff --git a/Main/Source/actset.cpp b/Main/Source/actset.cpp new file mode 100644 index 0000000..be0c416 --- /dev/null +++ b/Main/Source/actset.cpp @@ -0,0 +1,33 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#define __FILE_OF_STATIC_ACTION_PROTOTYPE_DEFINITIONS__ + +#include "proto.h" +#include "action.h" + +SYSTEM_SPECIALIZATIONS(action) + +#include "actions.h" + +#undef __FILE_OF_STATIC_ACTION_PROTOTYPE_DEFINITIONS__ + +#include "message.h" +#include "char.h" +#include "materia.h" +#include "game.h" +#include "save.h" +#include "iconf.h" +#include "stack.h" + +#include "action.cpp" +#include "actions.cpp" diff --git a/Main/Source/area.cpp b/Main/Source/area.cpp new file mode 100644 index 0000000..973bb69 --- /dev/null +++ b/Main/Source/area.cpp @@ -0,0 +1,93 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through areaset.cpp */ + +area::area() { } + +area::area(int InitXSize, int InitYSize) +{ + Initialize(InitXSize, InitYSize); +} + +void area::Initialize(int InitXSize, int InitYSize) +{ + XSize = InitXSize; + YSize = InitYSize; + XSizeTimesYSize = XSize * YSize; + Border = rect(0, 0, XSize - 1, YSize - 1); + Alloc2D(Map, XSize, YSize); + Alloc2D(FlagMap, XSize, YSize); + memset(FlagMap[0], 0, XSizeTimesYSize * sizeof(uchar)); +} + +area::~area() +{ + for(ulong c = 0; c < XSizeTimesYSize; ++c) + delete Map[0][c]; + + delete [] Map; + delete [] FlagMap; +} + +void area::Save(outputfile& SaveFile) const +{ + SaveFile << XSize << YSize << EntryMap; + SaveFile.Write(reinterpret_cast(FlagMap[0]), XSizeTimesYSize * sizeof(uchar)); +} + +void area::Load(inputfile& SaveFile) +{ + game::SetAreaInLoad(this); + SaveFile >> XSize >> YSize; + + if(SaveFile.Eof()) + ABORT("Area file %s corrupted!", SaveFile.GetFileName().CStr()); + + SaveFile >> EntryMap; + XSizeTimesYSize = XSize * YSize; + Border = rect(0, 0, XSize - 1, YSize - 1); + Alloc2D(Map, XSize, YSize); + Alloc2D(FlagMap, XSize, YSize); + SaveFile.Read(reinterpret_cast(FlagMap[0]), XSizeTimesYSize * sizeof(uchar)); +} + +void area::SendNewDrawRequest() +{ + cint XMin = Max(game::GetCamera().X, 0); + cint YMin = Max(game::GetCamera().Y, 0); + cint XMax = Min(XSize, game::GetCamera().X + game::GetScreenXSize()); + cint YMax = Min(YSize, game::GetCamera().Y + game::GetScreenYSize()); + + for(int x = XMin; x < XSize && x < XMax; ++x) + for(int y = YMin; y < YMax; ++y) + Map[x][y]->SendStrongNewDrawRequest(); + + igraph::GetBackGround()->FastBlit(DOUBLE_BUFFER); + DOUBLE_BUFFER->DrawRectangle(14, 30, 17 + (game::GetScreenXSize() << 4), 33 + (game::GetScreenYSize() << 4), DARK_GRAY, true); + DOUBLE_BUFFER->Fill(16, 32, game::GetScreenXSize() << 4, game::GetScreenYSize() << 4, BLACK); +} + +square* area::GetNeighbourSquare(v2 Pos, int I) const +{ + Pos += game::GetMoveVector(I); + + if(Pos.X >= 0 && Pos.Y >= 0 && Pos.X < XSize && Pos.Y < YSize) + return Map[Pos.X][Pos.Y]; + else + return 0; +} + +void area::SetEntryPos(int I, v2 Pos) +{ + EntryMap.insert(std::make_pair(I, Pos)); +} diff --git a/Main/Source/areaset.cpp b/Main/Source/areaset.cpp new file mode 100644 index 0000000..a1eae53 --- /dev/null +++ b/Main/Source/areaset.cpp @@ -0,0 +1,25 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "allocate.h" +#include "char.h" +#include "game.h" +#include "graphics.h" +#include "bitmap.h" +#include "terra.h" +#include "proto.h" +#include "save.h" +#include "team.h" + +#include "area.cpp" +#include "square.cpp" +#include "terra.cpp" diff --git a/Main/Source/bodypart.cpp b/Main/Source/bodypart.cpp new file mode 100644 index 0000000..4c2ecd8 --- /dev/null +++ b/Main/Source/bodypart.cpp @@ -0,0 +1,3640 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through itemset.cpp */ + +int bodypart::GetGraphicsContainerIndex() const { return GR_HUMANOID; } +int bodypart::GetArticleMode() const { return IsUnique() ? FORCE_THE : 0; } +truth bodypart::IsAlive() const { return MainMaterial->GetBodyFlags() & IS_ALIVE; } +int bodypart::GetSpecialFlags() const { return SpecialFlags|ST_OTHER_BODYPART; } +col16 bodypart::GetMaterialColorA(int) const { return GetMainMaterial()->GetSkinColor(); } +truth bodypart::IsWarm() const { return MainMaterial->GetBodyFlags() & IS_WARM; } +truth bodypart::UseMaterialAttributes() const { return MainMaterial->GetBodyFlags() & USE_MATERIAL_ATTRIBUTES || !Master || Master->AlwaysUseMaterialAttributes(); } +truth bodypart::CanRegenerate() const { return MainMaterial->GetBodyFlags() & CAN_REGENERATE; } +truth bodypart::CanHaveParasite() const { return MainMaterial->GetBodyFlags() & CAN_HAVE_PARASITE; } +square* bodypart::GetSquareUnder(int I) const { return Master ? Slot[0]->GetSquareUnder(I) : Slot[I]->GetSquareUnder(); } +lsquare* bodypart::GetLSquareUnder(int I) const { return static_cast(Master ? Slot[0]->GetSquareUnder(I) : Slot[I]->GetSquareUnder()); } +item* bodypart::GetExternalBodyArmor() const { return GetHumanoidMaster()->GetBodyArmor(); } +item* bodypart::GetExternalCloak() const { return GetHumanoidMaster()->GetCloak(); } +item* bodypart::GetExternalHelmet() const { return GetHumanoidMaster()->GetHelmet(); } +item* bodypart::GetExternalBelt() const { return GetHumanoidMaster()->GetBelt(); } +truth bodypart::AllowFluidBe() const { return !Master || !Master->IsPolymorphed(); } + +int head::GetBodyPartIndex() const { return HEAD_INDEX; } +int head::GetBiteMinDamage() const { return int(BiteDamage * 0.75); } +int head::GetBiteMaxDamage() const { return int(BiteDamage * 1.25 + 1); } + +int torso::GetBodyPartIndex() const { return TORSO_INDEX; } + +int normaltorso::GetGraphicsContainerIndex() const { return GR_CHARACTER; } + +int arm::GetMinDamage() const { return int(Damage * 0.75); } +int arm::GetMaxDamage() const { return int(Damage * 1.25 + 1); } +double arm::GetBlockValue() const { return GetToHitValue() * GetWielded()->GetBlockModifier() / 10000; } + +int rightarm::GetBodyPartIndex() const { return RIGHT_ARM_INDEX; } +int rightarm::GetSpecialFlags() const { return SpecialFlags|ST_RIGHT_ARM; } + +int leftarm::GetBodyPartIndex() const { return LEFT_ARM_INDEX; } +int leftarm::GetSpecialFlags() const { return SpecialFlags|ST_LEFT_ARM; } + +int groin::GetBodyPartIndex() const { return GROIN_INDEX; } +int groin::GetSpecialFlags() const { return SpecialFlags|ST_GROIN; } + +int leg::GetKickMinDamage() const { return int(KickDamage * 0.75); } +int leg::GetKickMaxDamage() const { return int(KickDamage * 1.25 + 1); } + +int rightleg::GetBodyPartIndex() const { return RIGHT_LEG_INDEX; } +int rightleg::GetSpecialFlags() const { return SpecialFlags|ST_RIGHT_LEG; } + +int leftleg::GetBodyPartIndex() const { return LEFT_LEG_INDEX; } +int leftleg::GetSpecialFlags() const { return SpecialFlags|ST_LEFT_LEG; } + +v2 eddytorso::GetBitmapPos(int Frame) const { return torso::GetBitmapPos(Frame) + v2((Frame&0x6) << 3, 0); } + +head* corpse::Behead() { return Deceased->Behead(); } +truth corpse::CanBeCloned() const { return GetDeceased()->CanBeCloned(); } +int corpse::GetAttachedGod() const { return GetDeceased()->GetTorso()->GetAttachedGod(); } + +v2 ennerhead::GetBitmapPos(int Frame) const { return Frame & 16 ? head::GetBitmapPos(Frame) : head::GetBitmapPos(Frame) + v2(16, 0); } + +alpha blinkdogtorso::GetAlphaA(int Frame) const { return (Frame & 31) != 31 ? 255 : 0; } + +void bodypart::Save(outputfile& SaveFile) const +{ + item::Save(SaveFile); + SaveFile << BitmapPos << ColorB << ColorC << ColorD << SpecialFlags << WobbleData << HP; + SaveFile << OwnerDescription << BloodMaterial << NormalMaterial << Scar << DamageID; +} + +void bodypart::Load(inputfile& SaveFile) +{ + item::Load(SaveFile); + SaveFile >> BitmapPos >> ColorB >> ColorC >> ColorD >> SpecialFlags >> WobbleData >> HP; + SaveFile >> OwnerDescription >> BloodMaterial >> NormalMaterial >> Scar >> DamageID; +} + +int bodypart::GetStrengthValue() const +{ + if(!UseMaterialAttributes()) + return long(GetStrengthModifier()) * Master->GetAttribute(ENDURANCE) / 2000; + else + return long(GetStrengthModifier()) * GetMainMaterial()->GetStrengthValue() / 2000; +} + +int head::GetTotalResistance(int Type) const +{ + if(Master) + { + int Resistance = GetResistance(Type) + Master->GetGlobalResistance(Type); + + if(GetHelmet()) + Resistance += GetHelmet()->GetResistance(Type); + + if(GetExternalBodyArmor()) + Resistance += GetExternalBodyArmor()->GetResistance(Type) >> 2; + + return Resistance; + } + else + return GetResistance(Type); +} + +int normaltorso::GetTotalResistance(int Type) const +{ + if(Master) + return GetResistance(Type) + Master->GetGlobalResistance(Type); + else + return GetResistance(Type); +} + +int humanoidtorso::GetTotalResistance(int Type) const +{ + if(Master) + { + int Resistance = GetResistance(Type) + Master->GetGlobalResistance(Type); + + if(GetBodyArmor()) + Resistance += GetBodyArmor()->GetResistance(Type); + + if(GetBelt()) + Resistance += GetBelt()->GetResistance(Type); + + return Resistance; + } + else + return GetResistance(Type); +} + +int arm::GetTotalResistance(int Type) const +{ + if(Master) + { + int Resistance = GetResistance(Type) + Master->GetGlobalResistance(Type); + + if(GetExternalBodyArmor()) + Resistance += GetExternalBodyArmor()->GetResistance(Type) >> 1; + + if(GetGauntlet()) + Resistance += GetGauntlet()->GetResistance(Type); + + return Resistance; + } + else + return GetResistance(Type); +} + +int groin::GetTotalResistance(int Type) const +{ + if(Master) + { + int Resistance = GetResistance(Type) + Master->GetGlobalResistance(Type); + + if(GetExternalBodyArmor()) + Resistance += GetExternalBodyArmor()->GetResistance(Type); + + if(GetHumanoidMaster()->GetBelt()) + Resistance += GetHumanoidMaster()->GetBelt()->GetResistance(Type); + + return Resistance; + } + else + return GetResistance(Type); +} + +int leg::GetTotalResistance(int Type) const +{ + if(Master) + { + int Resistance = GetResistance(Type) + Master->GetGlobalResistance(Type); + + if(GetExternalBodyArmor()) + Resistance += GetExternalBodyArmor()->GetResistance(Type) >> 1; + + if(GetBoot()) + Resistance += GetBoot()->GetResistance(Type); + + return Resistance; + } + else + return GetResistance(Type); +} + +void head::Save(outputfile& SaveFile) const +{ + bodypart::Save(SaveFile); + SaveFile << (int)BaseBiteStrength; + SaveFile << HelmetSlot << AmuletSlot; +} + +void head::Load(inputfile& SaveFile) +{ + bodypart::Load(SaveFile); + SaveFile >> (int&)BaseBiteStrength; + SaveFile >> HelmetSlot >> AmuletSlot; +} + +void humanoidtorso::Save(outputfile& SaveFile) const +{ + bodypart::Save(SaveFile); + SaveFile << BodyArmorSlot << CloakSlot << BeltSlot; +} + +void humanoidtorso::Load(inputfile& SaveFile) +{ + bodypart::Load(SaveFile); + SaveFile >> BodyArmorSlot >> CloakSlot >> BeltSlot; +} + +void arm::Save(outputfile& SaveFile) const +{ + bodypart::Save(SaveFile); + SaveFile << (int)BaseUnarmedStrength; + SaveFile << StrengthExperience << DexterityExperience; + SaveFile << WieldedSlot << GauntletSlot << RingSlot; + SaveFile << WieldedGraphicData; +} + +void arm::Load(inputfile& SaveFile) +{ + bodypart::Load(SaveFile); + SaveFile >> (int&)BaseUnarmedStrength; + SaveFile >> StrengthExperience >> DexterityExperience; + SaveFile >> WieldedSlot >> GauntletSlot >> RingSlot; + SaveFile >> WieldedGraphicData; +} + +void leg::Save(outputfile& SaveFile) const +{ + bodypart::Save(SaveFile); + SaveFile << BaseKickStrength << StrengthExperience << AgilityExperience; + SaveFile << BootSlot; +} + +void leg::Load(inputfile& SaveFile) +{ + bodypart::Load(SaveFile); + SaveFile >> BaseKickStrength >> StrengthExperience >> AgilityExperience; + SaveFile >> BootSlot; +} + +truth bodypart::ReceiveDamage(character* Damager, int Damage, int Type, int) +{ + if(Master) + { + if(Type & POISON && !IsAlive()) + return false; + + int BHP = HP; + + if(HP <= Damage && !CanBeSevered(Type)) + Damage = GetHP() - 1; + + if(!Damage) + return false; + + EditHP(1, -Damage); + + if(Type & DRAIN && IsAlive()) + for(int c = 0; c < Damage; ++c) + Damager->HealHitPoint(); + + truth WasBadlyHurt = IsBadlyHurt(); + + if(HP <= 0) + return true; + + if(DamageTypeCanScar(Type) && !(RAND_N(25 + 25 * HP / MaxHP))) + GenerateScar(Damage, Type); + + if(Master->IsPlayer()) + if(HP == 1 && BHP > 1) + { + if(IsAlive()) + ADD_MESSAGE("Your %s bleeds very badly.", GetBodyPartName().CStr()); + else + ADD_MESSAGE("Your %s is in very bad condition.", GetBodyPartName().CStr()); + + if(Master->BodyPartIsVital(GetBodyPartIndex())) + game::AskForKeyPress(CONST_S("Vital bodypart in serious danger! [press any key to continue]")); + } + else if(IsBadlyHurt() && !WasBadlyHurt) + { + if(IsAlive()) + ADD_MESSAGE("Your %s bleeds.", GetBodyPartName().CStr()); + else + ADD_MESSAGE("Your %s is in bad condition.", GetBodyPartName().CStr()); + + if(Master->BodyPartIsVital(GetBodyPartIndex())) + game::AskForKeyPress(CONST_S("Vital bodypart in danger! [press any key to continue]")); + } + + SignalPossibleUsabilityChange(); + } + + return false; +} + +truth bodypart::CanBeSevered(int Type) const +{ + if((HP == MaxHP && HP != 1 && !Master->IsExtraFragile()) + || (Type & (POISON|SOUND) && GetBodyPartIndex() != TORSO_INDEX)) + return false; + + if(!Master->BodyPartIsVital(GetBodyPartIndex()) || Master->IsExtraFragile()) + return true; + + bodypart* Torso = Master->GetTorso(); + return Torso->HP != Torso->MaxHP || Torso->HP == 1; +} + +double arm::GetUnarmedDamage() const +{ + double WeaponStrength = GetBaseUnarmedStrength() * GetBaseUnarmedStrength(); + item* Gauntlet = GetGauntlet(); + + if(Gauntlet) + WeaponStrength += Gauntlet->GetWeaponStrength(); + + double Base = sqrt(5e-5 * WeaponStrength); + + if(Gauntlet) + Base += Gauntlet->GetDamageBonus(); + + double Damage = Base * sqrt(1e-7 * GetAttribute(ARM_STRENGTH)) + * GetHumanoidMaster()->GetCWeaponSkill(UNARMED)->GetBonus(); + + return Damage; +} + +double arm::GetUnarmedToHitValue() const +{ + double BonusMultiplier = 10.; + item* Gauntlet = GetGauntlet(); + + if(Gauntlet) + BonusMultiplier += Gauntlet->GetTHVBonus(); + + return GetAttribute(DEXTERITY) + * sqrt(2.5 * Master->GetAttribute(PERCEPTION)) + * GetHumanoidMaster()->GetCWeaponSkill(UNARMED)->GetBonus() + * Master->GetMoveEase() + * BonusMultiplier / 5000000; +} + +long arm::GetUnarmedAPCost() const +{ + return long(10000000000. / (APBonus(GetAttribute(DEXTERITY)) * Master->GetMoveEase() * Master->GetCWeaponSkill(UNARMED)->GetBonus())); +} + +void arm::CalculateDamage() +{ + if(!Master) + return; + + if(!IsUsable()) + Damage = 0; + else if(GetWielded()) + Damage = GetWieldedDamage(); + else if(PairArmAllowsMelee()) + Damage = GetUnarmedDamage(); + else + Damage = 0; +} + +void arm::CalculateToHitValue() +{ + if(!Master) + return; + + if(GetWielded()) + ToHitValue = GetWieldedToHitValue(); + else if(PairArmAllowsMelee()) + ToHitValue = GetUnarmedToHitValue(); + else + ToHitValue = 0; +} + +void arm::CalculateAPCost() +{ + if(!Master) + return; + + if(GetWielded()) + APCost = GetWieldedAPCost(); + else if(PairArmAllowsMelee()) + APCost = GetUnarmedAPCost(); + else return; + + if(APCost < 100) + APCost = 100; +} + +truth arm::PairArmAllowsMelee() const +{ + const arm* PairArm = GetPairArm(); + return !PairArm || !PairArm->IsUsable() || !PairArm->GetWielded() + || PairArm->GetWielded()->IsShield(Master); +} + +double arm::GetWieldedDamage() const +{ + citem* Wielded = GetWielded(); + + if(Wielded->IsShield(Master)) + return 0; + + int HitStrength = GetAttribute(ARM_STRENGTH); + int Requirement = Wielded->GetStrengthRequirement(); + + if(TwoHandWieldIsActive()) + { + HitStrength += GetPairArm()->GetAttribute(ARM_STRENGTH); + Requirement >>= 2; + } + + if(HitStrength > Requirement) + { + /* I have no idea whether this works. It needs to be checked */ + + return Wielded->GetBaseDamage() * sqrt(1e-13 * HitStrength) + * GetCurrentSWeaponSkillBonus() + * GetHumanoidMaster()->GetCWeaponSkill(Wielded->GetWeaponCategory())->GetBonus(); + } + else + return 0; +} + +double arm::GetWieldedToHitValue() const +{ + int HitStrength = GetWieldedHitStrength(); + + if(HitStrength <= 0) + return 0; + + citem* Wielded = GetWielded(); + + double Base = 2e-11 + * Min(HitStrength, 10) + * GetHumanoidMaster()->GetCWeaponSkill(Wielded->GetWeaponCategory())->GetBonus() + * GetCurrentSWeaponSkillBonus() + * Master->GetMoveEase() + * (10000. / (1000 + Wielded->GetWeight()) + Wielded->GetTHVBonus()); + double ThisToHit = GetAttribute(DEXTERITY) * sqrt(2.5 * Master->GetAttribute(PERCEPTION)); + const arm* PairArm = GetPairArm(); + + if(PairArm && PairArm->IsUsable()) + { + citem* PairWielded = PairArm->GetWielded(); + + if(!PairWielded) + { + if(Wielded->IsTwoHanded() && !Wielded->IsShield(Master)) + return Base * (ThisToHit + PairArm->GetAttribute(DEXTERITY) + * sqrt(2.5 * Master->GetAttribute(PERCEPTION))) / 2; + } + else if(!Wielded->IsShield(Master) && !PairWielded->IsShield(Master)) + return Base * ThisToHit / (1.0 + (500.0 + PairWielded->GetWeight()) + / (1000.0 + (Wielded->GetWeight() << 1))); + } + + return Base * ThisToHit; +} + +long arm::GetWieldedAPCost() const +{ + citem* Wielded = GetWielded(); + + if(Wielded->IsShield(Master)) + return 0; + + int HitStrength = GetWieldedHitStrength(); + + if(HitStrength <= 0) + return 0; + + return long(1 / (1e-14 * APBonus(GetAttribute(DEXTERITY)) * Master->GetMoveEase() * GetHumanoidMaster()->GetCWeaponSkill(Wielded->GetWeaponCategory())->GetBonus() * (GetCurrentSWeaponSkillBonus() * Min(HitStrength, 10)))); +} + +void head::CalculateDamage() +{ + if(!Master) + return; + + BiteDamage = 7.07e-6 * GetBaseBiteStrength() * GetHumanoidMaster()->GetCWeaponSkill(BITE)->GetBonus(); +} + +void head::CalculateToHitValue() +{ + if(!Master) + return; + + BiteToHitValue = Master->GetAttribute(AGILITY) * sqrt(2.5 * Master->GetAttribute(PERCEPTION)) * GetHumanoidMaster()->GetCWeaponSkill(KICK)->GetBonus() * Master->GetMoveEase() / 1000000; +} + +void head::CalculateAPCost() +{ + if(!Master) + return; + + BiteAPCost = Max(long(10000000000. / (APBonus(Master->GetAttribute(AGILITY)) * Master->GetMoveEase() * Master->GetCWeaponSkill(BITE)->GetBonus())), 100L); +} + +void leg::CalculateDamage() +{ + if(!Master) + return; + + double WeaponStrength = GetBaseKickStrength() * GetBaseKickStrength(); + item* Boot = GetBoot(); + + if(Boot) + WeaponStrength += Boot->GetWeaponStrength(); + + double Base = sqrt(5e-5 * WeaponStrength); + + if(Boot) + Base += Boot->GetDamageBonus(); + + KickDamage = Base * sqrt(1e-7 * GetAttribute(LEG_STRENGTH)) + * GetHumanoidMaster()->GetCWeaponSkill(KICK)->GetBonus(); +} + +void leg::CalculateToHitValue() +{ + if(!Master) + return; + + double BonusMultiplier = 10.; + item* Boot = GetBoot(); + + if(Boot) + BonusMultiplier += Boot->GetTHVBonus(); + + KickToHitValue = GetAttribute(AGILITY) + * sqrt(2.5 * Master->GetAttribute(PERCEPTION)) + * GetHumanoidMaster()->GetCWeaponSkill(KICK)->GetBonus() + * Master->GetMoveEase() + * BonusMultiplier / 10000000; +} + +void leg::CalculateAPCost() +{ + if(!Master) + return; + + KickAPCost = Max(long(20000000000. / (APBonus(GetAttribute(AGILITY)) * Master->GetMoveEase() * Master->GetCWeaponSkill(KICK)->GetBonus())), 100L); +} + +humanoid* bodypart::GetHumanoidMaster() const +{ + return static_cast(Master); +} + +void corpse::Save(outputfile& SaveFile) const +{ + item::Save(SaveFile); + SaveFile << Deceased; +} + +void corpse::Load(inputfile& SaveFile) +{ + item::Load(SaveFile); + SaveFile >> Deceased; + Deceased->SetMotherEntity(this); + Enable(); +} + +void corpse::AddPostFix(festring& String, int) const +{ + String << " of "; + GetDeceased()->AddName(String, INDEFINITE); +} + +int corpse::GetOfferValue(int Receiver) const +{ + int OfferValue = 0; + + for(int c = 0; c < GetDeceased()->GetBodyParts(); ++c) + { + bodypart* BodyPart = GetDeceased()->GetBodyPart(c); + + if(BodyPart) + OfferValue += BodyPart->GetOfferValue(Receiver); + } + + return OfferValue; +} + +double corpse::GetWeaponStrength() const +{ + return GetFormModifier() * GetDeceased()->GetTorso()->GetMainMaterial()->GetStrengthValue() * sqrt(GetDeceased()->GetTorso()->GetMainMaterial()->GetWeight()); +} + +truth corpse::CanBeEatenByAI(ccharacter* Eater) const +{ + for(int c = 0; c < GetDeceased()->GetBodyParts(); ++c) + { + bodypart* BodyPart = GetDeceased()->GetBodyPart(c); + + if(BodyPart && !BodyPart->CanBeEatenByAI(Eater)) + return false; + } + + return true; +} + +int corpse::GetStrengthValue() const +{ + return long(GetStrengthModifier()) * GetDeceased()->GetTorso()->GetMainMaterial()->GetStrengthValue() / 2000; +} + +corpse::~corpse() +{ + delete Deceased; +} + +col16 corpse::GetMaterialColorA(int) const +{ + return GetDeceased()->GetTorso()->GetMainMaterial()->GetColor(); +} + +alpha corpse::GetAlphaA(int) const +{ + return GetDeceased()->GetTorso()->GetMainMaterial()->GetAlpha(); +} + +col16 corpse::GetMaterialColorB(int) const +{ + torso* Torso = GetDeceased()->GetTorso(); + return Torso->IsAlive() ? material::GetDataBase(GetDeceased()->GetBloodMaterial())->Color : Torso->GetMainMaterial()->GetColor(); +} + +alpha corpse::GetAlphaB(int) const +{ + torso* Torso = GetDeceased()->GetTorso(); + return Torso->IsAlive() ? 175 : Torso->GetMainMaterial()->GetAlpha(); +} + +int corpse::GetSparkleFlags() const +{ + torso* Torso = GetDeceased()->GetTorso(); + material* Material = Torso->GetMainMaterial(); + return Material->IsSparkling() ? SPARKLING_A|(!Torso->IsAlive() ? SPARKLING_B : 0) : 0; +} + +v2 corpse::GetBitmapPos(int) const +{ + if(GetDeceased()->GetSize() < 50) + return v2(32, 64); + else if(GetDeceased()->GetSize() < 150) + return v2(16, 192); + else + return v2(16, 272); +} + +int corpse::GetSize() const +{ + return GetDeceased()->GetSize(); +} + +void corpse::SetDeceased(character* What) +{ + Deceased = What; + Deceased->SetMotherEntity(this); + SignalVolumeAndWeightChange(); + SignalEmitationIncrease(Deceased->GetEmitation()); + UpdatePictures(); + Enable(); +} + +void head::DropEquipment(stack* Stack) +{ + if(Stack) + { + if(GetHelmet()) + GetHelmet()->MoveTo(Stack); + + if(GetAmulet()) + GetAmulet()->MoveTo(Stack); + } + else + { + if(GetHelmet()) + GetSlot()->AddFriendItem(GetHelmet()); + + if(GetAmulet()) + GetSlot()->AddFriendItem(GetAmulet()); + } +} + +void humanoidtorso::DropEquipment(stack* Stack) +{ + if(Stack) + { + if(GetBodyArmor()) + GetBodyArmor()->MoveTo(Stack); + + if(GetCloak()) + GetCloak()->MoveTo(Stack); + + if(GetBelt()) + GetBelt()->MoveTo(Stack); + } + else + { + if(GetBodyArmor()) + GetSlot()->AddFriendItem(GetBodyArmor()); + + if(GetCloak()) + GetSlot()->AddFriendItem(GetCloak()); + + if(GetBelt()) + GetSlot()->AddFriendItem(GetBelt()); + } +} + +void arm::DropEquipment(stack* Stack) +{ + if(Stack) + { + if(GetWielded()) + GetWielded()->MoveTo(Stack); + + if(GetGauntlet()) + GetGauntlet()->MoveTo(Stack); + + if(GetRing()) + GetRing()->MoveTo(Stack); + } + else + { + if(GetWielded()) + GetSlot()->AddFriendItem(GetWielded()); + + if(GetGauntlet()) + GetSlot()->AddFriendItem(GetGauntlet()); + + if(GetRing()) + GetSlot()->AddFriendItem(GetRing()); + } +} + +void leg::DropEquipment(stack* Stack) +{ + if(Stack) + { + if(GetBoot()) + GetBoot()->MoveTo(Stack); + } + else + { + if(GetBoot()) + GetSlot()->AddFriendItem(GetBoot()); + } +} + +head::~head() +{ + delete GetHelmet(); + delete GetAmulet(); +} + +humanoidtorso::~humanoidtorso() +{ + delete GetBodyArmor(); + delete GetCloak(); + delete GetBelt(); +} + +arm::~arm() +{ + delete GetWielded(); + delete GetGauntlet(); + delete GetRing(); +} + +leg::~leg() +{ + delete GetBoot(); +} + +truth corpse::IsDestroyable(ccharacter* Char) const +{ + for(int c = 0; c < GetDeceased()->GetBodyParts(); ++c) + { + bodypart* BodyPart = GetDeceased()->GetBodyPart(c); + + if(BodyPart && !BodyPart->IsDestroyable(Char)) + return false; + } + + return true; +} + +long corpse::GetTruePrice() const +{ + long Price = 0; + + for(int c = 0; c < GetDeceased()->GetBodyParts(); ++c) + { + bodypart* BodyPart = GetDeceased()->GetBodyPart(c); + + if(BodyPart) + Price += BodyPart->GetTruePrice(); + } + + return Price; +} + +material* corpse::GetMaterial(int I) const +{ + return GetDeceased()->GetTorso()->GetMaterial(I); +} + +int bodypart::GetSparkleFlags() const +{ + return (GetMainMaterial()->SkinColorIsSparkling() ? SPARKLING_A : 0) + | (Flags >> BODYPART_SPARKLE_SHIFT & (SPARKLING_B|SPARKLING_C|SPARKLING_D)); +} + +truth corpse::RaiseTheDead(character* Summoner) +{ + if(Summoner && Summoner->IsPlayer()) + game::DoEvilDeed(50); + + GetDeceased()->Enable(); + + if(GetDeceased()->TryToRiseFromTheDead()) + { + v2 Pos = GetPos(); + RemoveFromSlot(); + GetDeceased()->SetMotherEntity(0); + + if(Summoner && GetDeceased()->IsCharmable() && !GetDeceased()->IsPlayer()) + GetDeceased()->ChangeTeam(Summoner->GetTeam()); + + GetDeceased()->PutToOrNear(Pos); + GetDeceased()->SignalStepFrom(0); + Deceased = 0; + SendToHell(); + return true; + } + else + { + GetDeceased()->Disable(); + return false; + } +} + +head::head() +{ + HelmetSlot.Init(this, HELMET_INDEX); + AmuletSlot.Init(this, AMULET_INDEX); +} + +humanoidtorso::humanoidtorso() +{ + BodyArmorSlot.Init(this, BODY_ARMOR_INDEX); + CloakSlot.Init(this, CLOAK_INDEX); + BeltSlot.Init(this, BELT_INDEX); +} + +rightarm::rightarm() +{ + WieldedSlot.Init(this, RIGHT_WIELDED_INDEX); + GauntletSlot.Init(this, RIGHT_GAUNTLET_INDEX); + RingSlot.Init(this, RIGHT_RING_INDEX); +} + +leftarm::leftarm() +{ + WieldedSlot.Init(this, LEFT_WIELDED_INDEX); + GauntletSlot.Init(this, LEFT_GAUNTLET_INDEX); + RingSlot.Init(this, LEFT_RING_INDEX); +} + +rightleg::rightleg() +{ + BootSlot.Init(this, RIGHT_BOOT_INDEX); +} + +leftleg::leftleg() +{ + BootSlot.Init(this, LEFT_BOOT_INDEX); +} + +void arm::Hit(character* Enemy, v2 HitPos, int Direction, int Flags) +{ + long StrExp = 50, DexExp = 50; + truth THW = false; + item* Wielded = GetWielded(); + + if(Wielded) + { + long Weight = Wielded->GetWeight(); + StrExp = Limit(15 * Weight / 200L, 75L, 300L); + DexExp = Weight ? Limit(75000L / Weight, 75L, 300L) : 300; + THW = TwoHandWieldIsActive(); + } + + switch(Enemy->TakeHit(Master, Wielded ? Wielded : GetGauntlet(), this, HitPos, GetTypeDamage(Enemy), GetToHitValue(), RAND() % 26 - RAND() % 26, Wielded ? WEAPON_ATTACK : UNARMED_ATTACK, Direction, !(RAND() % Master->GetCriticalModifier()), Flags & SADIST_HIT)) + { + case HAS_HIT: + case HAS_BLOCKED: + case HAS_DIED: + case DID_NO_DAMAGE: + EditExperience(ARM_STRENGTH, StrExp, 1 << 9); + + if(THW && GetPairArm()) + GetPairArm()->EditExperience(ARM_STRENGTH, StrExp, 1 << 9); + + case HAS_DODGED: + EditExperience(DEXTERITY, DexExp, 1 << 9); + + if(THW && GetPairArm()) + GetPairArm()->EditExperience(DEXTERITY, DexExp, 1 << 9); + } +} + +int arm::GetAttribute(int Identifier, truth AllowBonus) const +{ + if(Identifier == ARM_STRENGTH) + { + int Base = !UseMaterialAttributes() + ? int(StrengthExperience * EXP_DIVISOR) + : GetMainMaterial()->GetStrengthValue(); + + if(AllowBonus) + Base += StrengthBonus; + + return Max(!IsBadlyHurt() || !AllowBonus ? Base : Base / 3, 1); + } + else if(Identifier == DEXTERITY) + { + int Base = !UseMaterialAttributes() + ? int(DexterityExperience * EXP_DIVISOR) + : GetMainMaterial()->GetFlexibility() << 2; + + if(AllowBonus) + Base += DexterityBonus; + + return Max(IsUsable() || !AllowBonus ? Base : Base / 3, 1); + } + else + { + ABORT("Illegal arm attribute %d request!", Identifier); + return 0xACCA; + } +} + +truth arm::EditAttribute(int Identifier, int Value) +{ + if(!Master) + return false; + + if(Identifier == ARM_STRENGTH) + { + if(!UseMaterialAttributes() + && Master->RawEditAttribute(StrengthExperience, Value)) + { + Master->CalculateBattleInfo(); + + if(Master->IsPlayerKind()) + UpdatePictures(); + + return true; + } + } + else if(Identifier == DEXTERITY) + if(!UseMaterialAttributes() + && Master->RawEditAttribute(DexterityExperience, Value)) + { + Master->CalculateBattleInfo(); + return true; + } + + return false; +} + +void arm::EditExperience(int Identifier, double Value, double Speed) +{ + if(!Master) + return; + + if(Identifier == ARM_STRENGTH) + { + if(!UseMaterialAttributes()) + { + int Change = Master->RawEditExperience(StrengthExperience, + Master->GetNaturalExperience(ARM_STRENGTH), + Value, Speed); + + if(Change) + { + cchar* Adj = Change > 0 ? "stronger" : "weaker"; + + if(Master->IsPlayer()) + ADD_MESSAGE("Your %s feels %s!", GetBodyPartName().CStr(), Adj); + else if(Master->IsPet() && Master->CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s looks %s.", Master->CHAR_NAME(DEFINITE), Adj); + + Master->CalculateBattleInfo(); + + if(Master->IsPlayerKind()) + UpdatePictures(); + } + } + } + else if(Identifier == DEXTERITY) + { + if(!UseMaterialAttributes()) + { + int Change = Master->RawEditExperience(DexterityExperience, + Master->GetNaturalExperience(DEXTERITY), + Value, Speed); + + if(Change) + { + cchar* Adj = Change > 0 ? "quite dextrous" : "clumsy"; + + if(Master->IsPlayer()) + ADD_MESSAGE("Your %s feels %s!", GetBodyPartName().CStr(), Adj); + else if(Master->IsPet() && Master->CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s looks %s.", Master->CHAR_NAME(DEFINITE), Adj); + + Master->CalculateBattleInfo(); + } + } + } + else + ABORT("Illegal arm attribute %d experience edit request!", Identifier); +} + +int leg::GetAttribute(int Identifier, truth AllowBonus) const +{ + if(Identifier == LEG_STRENGTH) + { + int Base = !UseMaterialAttributes() + ? int(StrengthExperience * EXP_DIVISOR) + : GetMainMaterial()->GetStrengthValue(); + + if(AllowBonus) + Base += StrengthBonus; + + return Max(!IsBadlyHurt() || !AllowBonus ? Base : Base / 3, 1); + } + else if(Identifier == AGILITY) + { + int Base = !UseMaterialAttributes() + ? int(AgilityExperience * EXP_DIVISOR) + : GetMainMaterial()->GetFlexibility() << 2; + + if(AllowBonus) + Base += AgilityBonus; + + return Max(IsUsable() || !AllowBonus ? Base : Base / 3, 1); + } + else + { + ABORT("Illegal leg attribute %d request!", Identifier); + return 0xECCE; + } +} + +truth leg::EditAttribute(int Identifier, int Value) +{ + if(!Master) + return false; + + if(Identifier == LEG_STRENGTH) + { + if(!UseMaterialAttributes() + && Master->RawEditAttribute(StrengthExperience, Value)) + { + Master->CalculateBurdenState(); + Master->CalculateBattleInfo(); + return true; + } + } + else if(Identifier == AGILITY) + if(!UseMaterialAttributes() + && Master->RawEditAttribute(AgilityExperience, Value)) + { + Master->CalculateBattleInfo(); + return true; + } + + return false; +} + +void leg::EditExperience(int Identifier, double Value, double Speed) +{ + if(!Master) + return; + + if(Identifier == LEG_STRENGTH) + { + if(!UseMaterialAttributes()) + { + int Change = Master->RawEditExperience(StrengthExperience, + Master->GetNaturalExperience(LEG_STRENGTH), + Value, Speed); + + if(Change) + { + cchar* Adj = Change > 0 ? "stronger" : "weaker"; + + if(Master->IsPlayer()) + ADD_MESSAGE("Your %s feels %s!", GetBodyPartName().CStr(), Adj); + else if(Master->IsPet() && Master->CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s looks %s.", Master->CHAR_NAME(DEFINITE), Adj); + + Master->CalculateBurdenState(); + Master->CalculateBattleInfo(); + } + } + } + else if(Identifier == AGILITY) + { + if(!UseMaterialAttributes()) + { + int Change = Master->RawEditExperience(AgilityExperience, + Master->GetNaturalExperience(AGILITY), + Value, Speed); + + if(Change) + { + cchar* Adj = Change > 0 ? "very agile" : "slower"; + + if(Master->IsPlayer()) + ADD_MESSAGE("Your %s feels %s!", GetBodyPartName().CStr(), Adj); + else if(Master->IsPet() && Master->CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s looks %s.", Master->CHAR_NAME(DEFINITE), Adj); + + Master->CalculateBattleInfo(); + } + } + } + else + ABORT("Illegal leg attribute %d experience edit request!", Identifier); +} + +void head::InitSpecialAttributes() +{ + BaseBiteStrength = Master->GetBaseBiteStrength(); +} + +void arm::InitSpecialAttributes() +{ + if(!Master->IsHuman() || Master->IsInitializing()) + { + StrengthExperience = Master->GetNaturalExperience(ARM_STRENGTH); + DexterityExperience = Master->GetNaturalExperience(DEXTERITY); + } + else + { + StrengthExperience = game::GetAveragePlayerArmStrengthExperience(); + DexterityExperience = game::GetAveragePlayerDexterityExperience(); + } + + LimitRef(StrengthExperience, MIN_EXP, MAX_EXP); + LimitRef(DexterityExperience, MIN_EXP, MAX_EXP); + BaseUnarmedStrength = Master->GetBaseUnarmedStrength(); +} + +void leg::InitSpecialAttributes() +{ + if(!Master->IsHuman() || Master->IsInitializing()) + { + StrengthExperience = Master->GetNaturalExperience(LEG_STRENGTH); + AgilityExperience = Master->GetNaturalExperience(AGILITY); + } + else + { + StrengthExperience = game::GetAveragePlayerLegStrengthExperience(); + AgilityExperience = game::GetAveragePlayerAgilityExperience(); + } + + LimitRef(StrengthExperience, MIN_EXP, MAX_EXP); + LimitRef(AgilityExperience, MIN_EXP, MAX_EXP); + BaseKickStrength = Master->GetBaseKickStrength(); +} + +void bodypart::SignalEquipmentAdd(gearslot* Slot) +{ + if(Master) + Master->SignalEquipmentAdd(Slot->GetEquipmentIndex()); +} + +void bodypart::SignalEquipmentRemoval(gearslot* Slot, citem* Item) +{ + if(Master) + Master->SignalEquipmentRemoval(Slot->GetEquipmentIndex(), Item); +} + +void bodypart::Mutate() +{ + GetMainMaterial()->SetVolume(long(GetVolume() * (1.5 - (RAND() & 1023) / 1023.))); +} + +void arm::Mutate() +{ + bodypart::Mutate(); + StrengthExperience = StrengthExperience * (1.5 - (RAND() & 1023) / 1023.); + DexterityExperience = DexterityExperience * (1.5 - (RAND() & 1023) / 1023.); + LimitRef(StrengthExperience, MIN_EXP, MAX_EXP); + LimitRef(DexterityExperience, MIN_EXP, MAX_EXP); +} + +void leg::Mutate() +{ + bodypart::Mutate(); + StrengthExperience = StrengthExperience * (1.5 - (RAND() & 1023) / 1023.); + AgilityExperience = AgilityExperience * (1.5 - (RAND() & 1023) / 1023.); + LimitRef(StrengthExperience, MIN_EXP, MAX_EXP); + LimitRef(AgilityExperience, MIN_EXP, MAX_EXP); +} + +arm* rightarm::GetPairArm() const +{ + return GetHumanoidMaster() ? GetHumanoidMaster()->GetLeftArm() : 0; +} + +arm* leftarm::GetPairArm() const +{ + return GetHumanoidMaster() ? GetHumanoidMaster()->GetRightArm() : 0; +} + +sweaponskill** rightarm::GetCurrentSWeaponSkill() const +{ + return &GetHumanoidMaster()->CurrentRightSWeaponSkill; +} + +sweaponskill** leftarm::GetCurrentSWeaponSkill() const +{ + return &GetHumanoidMaster()->CurrentLeftSWeaponSkill; +} + +alpha bodypart::GetMaxAlpha() const +{ + if(Master && Master->StateIsActivated(INVISIBLE)) + return 150; + else + return 255; +} + +void bodypart::AddPostFix(festring& String, int) const +{ + if(!OwnerDescription.IsEmpty()) + String << ' ' << OwnerDescription; +} + +void corpse::CalculateVolumeAndWeight() +{ + Volume = Deceased->GetVolume(); + Weight = Deceased->GetWeight(); +} + +item* head::GetEquipment(int I) const +{ + switch(I) + { + case 0: return GetHelmet(); + case 1: return GetAmulet(); + } + + return 0; +} + +item* humanoidtorso::GetEquipment(int I) const +{ + switch(I) + { + case 0: return GetBodyArmor(); + case 1: return GetCloak(); + case 2: return GetBelt(); + } + + return 0; +} + +item* arm::GetEquipment(int I) const +{ + switch(I) + { + case 0: return GetWielded(); + case 1: return GetGauntlet(); + case 2: return GetRing(); + } + + return 0; +} + +item* leg::GetEquipment(int I) const +{ + return !I ? GetBoot() : 0; +} + +void bodypart::CalculateVolumeAndWeight() +{ + item::CalculateVolumeAndWeight(); + CarriedWeight = 0; + BodyPartVolume = Volume; + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment) + { + Volume += Equipment->GetVolume(); + CarriedWeight += Equipment->GetWeight(); + } + } + + Weight += CarriedWeight; +} + +void corpse::CalculateEmitation() +{ + Emitation = Deceased->GetEmitation(); +} + +void bodypart::CalculateEmitation() +{ + item::CalculateEmitation(); + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment) + game::CombineLights(Emitation, Equipment->GetEmitation()); + } +} + +void bodypart::CalculateMaxHP(ulong Flags) +{ + int HPDelta = MaxHP - HP, OldMaxHP = MaxHP; + MaxHP = 0; + + if(Master) + { + if(!UseMaterialAttributes()) + { + long Endurance = Master->GetAttribute(ENDURANCE); + double DoubleHP = GetBodyPartVolume() * Endurance * Endurance / 200000; + + for(int c = 0; c < Scar.size(); ++c) + DoubleHP *= (100. - Scar[c].Severity * 4) / 100; + + MaxHP = int(DoubleHP); + } + else + { + long SV = GetMainMaterial()->GetStrengthValue(); + MaxHP = (GetBodyPartVolume() * SV >> 4) * SV / 250000; + } + + if(MaxHP < 1) + MaxHP = 1; + + if(Flags & MAY_CHANGE_HPS) + { + if(MaxHP - HPDelta > 1) + HP = MaxHP - HPDelta; + else + HP = 1; + } + else + { + //OldMaxHP - MaxHP; + } + + if(Flags & CHECK_USABILITY) + SignalPossibleUsabilityChange(); + } +} + +void bodypart::SignalVolumeAndWeightChange() +{ + item::SignalVolumeAndWeightChange(); + + if(Master && !Master->IsInitializing()) + { + CalculateMaxHP(); + Master->CalculateHP(); + Master->CalculateMaxHP(); + Master->SignalBodyPartVolumeAndWeightChange(); + square* SquareUnder = GetSquareUnder(); + + if(UpdateArmorPictures() && SquareUnder) + SquareUnder->SendNewDrawRequest(); + } +} + +/*{ + for(;;) + { + damageid& D = DamageID.back(); + D. + } +}*/ + +void bodypart::SetHP(int What) +{ + HP = What; + + if(Master) + { + Master->CalculateHP(); + SignalPossibleUsabilityChange(); + } +} + +void bodypart::EditHP(int SrcID, int What) +{ + HP += What; + + if(What < 0) + RemoveDamageIDs(-What); + else + AddDamageID(SrcID, What); + + if(Master) + { + Master->CalculateHP(); + SignalPossibleUsabilityChange(); + } +} + +void arm::SignalVolumeAndWeightChange() +{ + bodypart::SignalVolumeAndWeightChange(); + + if(Master && !Master->IsInitializing()) + { + GetHumanoidMaster()->EnsureCurrentSWeaponSkillIsCorrect(*GetCurrentSWeaponSkill(), GetWielded()); + CalculateAttributeBonuses(); + CalculateAttackInfo(); + UpdateWieldedPicture(); + + if(GetSquareUnder()) + GetSquareUnder()->SendNewDrawRequest(); + } +} + +void leg::SignalVolumeAndWeightChange() +{ + bodypart::SignalVolumeAndWeightChange(); + + if(Master && !Master->IsInitializing()) + { + CalculateAttributeBonuses(); + CalculateAttackInfo(); + } +} + +void humanoidtorso::SignalVolumeAndWeightChange() +{ + bodypart::SignalVolumeAndWeightChange(); + + if(Master && !Master->IsInitializing()) + { + humanoid* HumanoidMaster = GetHumanoidMaster(); + + if(HumanoidMaster->GetRightArm()) + HumanoidMaster->GetRightArm()->CalculateAttributeBonuses(); + + if(HumanoidMaster->GetLeftArm()) + HumanoidMaster->GetLeftArm()->CalculateAttributeBonuses(); + + if(HumanoidMaster->GetRightLeg()) + HumanoidMaster->GetRightLeg()->CalculateAttributeBonuses(); + + if(HumanoidMaster->GetLeftLeg()) + HumanoidMaster->GetLeftLeg()->CalculateAttributeBonuses(); + } +} + +void bodypart::CalculateAttackInfo() +{ + CalculateDamage(); + CalculateToHitValue(); + CalculateAPCost(); +} + +truth arm::TwoHandWieldIsActive() const +{ + citem* Wielded = GetWielded(); + + if(Wielded->IsTwoHanded() && !Wielded->IsShield(Master)) + { + arm* PairArm = GetPairArm(); + return PairArm && PairArm->IsUsable() && !PairArm->GetWielded(); + } + else + return false; +} + +double bodypart::GetTimeToDie(int Damage, double ToHitValue, double DodgeValue, truth AttackIsBlockable, truth UseMaxHP) const +{ + double Durability; + int TotalResistance = GetTotalResistance(PHYSICAL_DAMAGE); + int Damage3 = (Damage << 1) + Damage; + int Damage5 = (Damage << 2) + Damage; + int TrueDamage = (19 * (Max((Damage3 >> 2) - TotalResistance, 0) + + Max((Damage5 >> 2) + 1 - (TotalResistance >> 1), 0)) + + (Max(((Damage3 + (Damage3 >> 1)) >> 2) - TotalResistance, 0) + + Max(((Damage5 + (Damage5 >> 1)) >> 2) + 3 - (TotalResistance >> 1), 0))) / 40; + + int HP = UseMaxHP ? GetMaxHP() : GetHP(); + + if(TrueDamage > 0) + { + double AverageDamage; + + if(AttackIsBlockable) + { + blockvector Block; + Master->CreateBlockPossibilityVector(Block, ToHitValue); + + if(Block.size()) + { + double ChanceForNoBlock = 1.0; + AverageDamage = 0; + + for(uint c = 0; c < Block.size(); ++c) + { + ChanceForNoBlock -= Block[c].first; + + if(TrueDamage - Block[c].second > 0) + AverageDamage += Block[c].first * (TrueDamage - Block[c].second); + } + + AverageDamage += ChanceForNoBlock * TrueDamage; + } + else + AverageDamage = TrueDamage; + } + else + AverageDamage = TrueDamage; + + Durability = HP / (AverageDamage * GetRoughChanceToHit(ToHitValue, DodgeValue)); + + if(Durability < 1) + Durability = 1; + + if(Durability > 1000) + Durability = 1000; + } + else + Durability = 1000; + + return Durability; +} + +double bodypart::GetRoughChanceToHit(double ToHitValue, double DodgeValue) const +{ + return GLOBAL_WEAK_BODYPART_HIT_MODIFIER * ToHitValue * GetBodyPartVolume() / ((DodgeValue / ToHitValue + 1) * DodgeValue * Master->GetBodyVolume() * 100); +} + +double torso::GetRoughChanceToHit(double ToHitValue, double DodgeValue) const +{ + return 1 / (DodgeValue / ToHitValue + 1); +} + +void bodypart::RandomizePosition() +{ + SpecialFlags |= 1 + RAND() % 7; + UpdatePictures(); +} + +double arm::GetBlockChance(double EnemyToHitValue) const +{ + citem* Wielded = GetWielded(); + return Wielded ? Min(1.0 / (1 + EnemyToHitValue / (GetToHitValue() * Wielded->GetBlockModifier()) * 10000), 1.0) : 0; +} + +int arm::GetBlockCapability() const +{ + citem* Wielded = GetWielded(); + + if(!Wielded) + return 0; + + int HitStrength = GetWieldedHitStrength(); + + if(HitStrength <= 0) + return 0; + + return Min(HitStrength, 10) * Wielded->GetStrengthValue() * GetHumanoidMaster()->GetCWeaponSkill(Wielded->GetWeaponCategory())->GetBonus() * (*GetCurrentSWeaponSkill())->GetBonus() / 10000000; +} + +void arm::WieldedSkillHit(int Hits) +{ + item* Wielded = GetWielded(); + + if(Master->GetCWeaponSkill(Wielded->GetWeaponCategory())->AddHit(Hits)) + { + CalculateAttackInfo(); + + if(Master->IsPlayer()) + { + int Category = Wielded->GetWeaponCategory(); + GetHumanoidMaster()->GetCWeaponSkill(Category)->AddLevelUpMessage(Category); + } + } + + if((*GetCurrentSWeaponSkill())->AddHit(Hits)) + { + CalculateAttackInfo(); + + if(Master->IsPlayer()) + (*GetCurrentSWeaponSkill())->AddLevelUpMessage(Wielded->CHAR_NAME(UNARTICLED)); + } +} + +head::head(const head& Head) : mybase(Head), BaseBiteStrength(Head.BaseBiteStrength) +{ + HelmetSlot.Init(this, HELMET_INDEX); + AmuletSlot.Init(this, AMULET_INDEX); +} + +humanoidtorso::humanoidtorso(const humanoidtorso& Torso) : mybase(Torso) +{ + BodyArmorSlot.Init(this, BODY_ARMOR_INDEX); + CloakSlot.Init(this, CLOAK_INDEX); + BeltSlot.Init(this, BELT_INDEX); +} + +arm::arm(const arm& Arm) : mybase(Arm), StrengthExperience(Arm.StrengthExperience), DexterityExperience(Arm.DexterityExperience), BaseUnarmedStrength(Arm.BaseUnarmedStrength) +{ +} + +rightarm::rightarm(const rightarm& Arm) : mybase(Arm) +{ + WieldedSlot.Init(this, RIGHT_WIELDED_INDEX); + GauntletSlot.Init(this, RIGHT_GAUNTLET_INDEX); + RingSlot.Init(this, RIGHT_RING_INDEX); +} + +leftarm::leftarm(const leftarm& Arm) : mybase(Arm) +{ + WieldedSlot.Init(this, LEFT_WIELDED_INDEX); + GauntletSlot.Init(this, LEFT_GAUNTLET_INDEX); + RingSlot.Init(this, LEFT_RING_INDEX); +} + +leg::leg(const leg& Leg) : mybase(Leg), StrengthExperience(Leg.StrengthExperience), AgilityExperience(Leg.AgilityExperience), BaseKickStrength(Leg.BaseKickStrength) +{ +} + +rightleg::rightleg(const rightleg& Leg) : mybase(Leg) +{ + BootSlot.Init(this, RIGHT_BOOT_INDEX); +} + +leftleg::leftleg(const leftleg& Leg) : mybase(Leg) +{ + BootSlot.Init(this, LEFT_BOOT_INDEX); +} + +corpse::corpse(const corpse& Corpse) : mybase(Corpse) +{ + Deceased = Corpse.Deceased->Duplicate(); + Deceased->SetMotherEntity(this); +} + +void bodypart::SignalSpoil(material* Material) +{ + if(Master) + Master->SignalSpoil(); + else + item::SignalSpoil(Material); +} + +void corpse::SignalSpoil(material*) +{ + GetDeceased()->Disappear(this, "spoil", &item::IsVeryCloseToSpoiling); +} + +void corpse::SignalDisappearance() +{ + GetDeceased()->Disappear(this, "disappear", &item::IsVeryCloseToDisappearance); +} + +truth bodypart::CanBePiledWith(citem* Item, ccharacter* Viewer) const +{ + return item::CanBePiledWith(Item, Viewer) + && OwnerDescription == static_cast(Item)->OwnerDescription; +} + +truth corpse::CanBePiledWith(citem* Item, ccharacter* Viewer) const +{ + if(GetType() != Item->GetType() + || GetConfig() != Item->GetConfig() + || GetWeight() != Item->GetWeight() + || Viewer->GetCWeaponSkillLevel(this) != Viewer->GetCWeaponSkillLevel(Item) + || Viewer->GetSWeaponSkillLevel(this) != Viewer->GetSWeaponSkillLevel(Item)) + return false; + + const corpse* Corpse = static_cast(Item); + + if(Deceased->GetBodyParts() != Corpse->Deceased->GetBodyParts()) + return false; + + for(int c = 0; c < Deceased->GetBodyParts(); ++c) + { + bodypart* BodyPart1 = Deceased->GetBodyPart(c); + bodypart* BodyPart2 = Corpse->Deceased->GetBodyPart(c); + + if(!BodyPart1 && !BodyPart2) + continue; + + if(!BodyPart1 || !BodyPart2 || !BodyPart1->CanBePiledWith(BodyPart2, Viewer)) + return false; + } + + if(Deceased->GetName(UNARTICLED) != Corpse->Deceased->GetName(UNARTICLED)) + return false; + + return true; +} + +void bodypart::Be() +{ + if(Master) + { + if(HP < MaxHP && ++SpillBloodCounter >= 4) + { + if(Master->IsEnabled()) + { + if(IsBadlyHurt() && !Master->IsPolymorphed() && !(RAND() & 3)) + SpillBlood(1); + } + else if(!Master->IsPolymorphed() && !(RAND() & 3)) + { + SpillBlood(1); + HP += Max(((MaxHP - HP) >> 2), 1); + } + + SpillBloodCounter = 0; + } + + if(Master->AllowSpoil() || !Master->IsEnabled()) + MainMaterial->Be(ItemFlags); + + if(Exists() && LifeExpectancy) + if(LifeExpectancy == 1) + Master->SignalDisappearance(); + else + --LifeExpectancy; + } + else + { + if(HP < MaxHP && ++SpillBloodCounter >= 4) + { + if(!(RAND() & 3)) + { + SpillBlood(1); + HP += Max(((MaxHP - HP) >> 2), 1); + } + + SpillBloodCounter = 0; + } + + item::Be(); + } +} + +void bodypart::SpillBlood(int HowMuch, v2 Pos) +{ + if(HowMuch && (!Master || Master->SpillsBlood()) && (IsAlive() || MainMaterial->IsLiquid()) && !game::IsInWilderness()) + GetNearLSquare(Pos)->SpillFluid(0, CreateBlood(long(HowMuch * sqrt(BodyPartVolume) / 2)), false, false); +} + +void bodypart::SpillBlood(int HowMuch) +{ + if(HowMuch && (!Master || Master->SpillsBlood()) && (IsAlive() || MainMaterial->IsLiquid()) && !game::IsInWilderness()) + for(int c = 0; c < GetSquaresUnder(); ++c) + if(GetLSquareUnder(c)) + GetLSquareUnder(c)->SpillFluid(0, CreateBlood(long(HowMuch * sqrt(BodyPartVolume) / 2)), false, false); +} + +void bodypart::SignalEnchantmentChange() +{ + if(Master && !Master->IsInitializing()) + { + Master->CalculateAttributeBonuses(); + Master->CalculateBattleInfo(); + } +} + +void arm::SignalEquipmentAdd(gearslot* Slot) +{ + int EquipmentIndex = Slot->GetEquipmentIndex(); + + if(Master && !Master->IsInitializing()) + { + item* Equipment = Slot->GetItem(); + + if(Equipment->IsInCorrectSlot(EquipmentIndex)) + ApplyEquipmentAttributeBonuses(Equipment); + + if(EquipmentIndex == RIGHT_GAUNTLET_INDEX || EquipmentIndex == LEFT_GAUNTLET_INDEX) + ApplyDexterityPenalty(Equipment); + + if(EquipmentIndex == RIGHT_WIELDED_INDEX || EquipmentIndex == LEFT_WIELDED_INDEX) + { + UpdateWieldedPicture(); + GetSquareUnder()->SendNewDrawRequest(); + } + } + + if(Master) + Master->SignalEquipmentAdd(EquipmentIndex); +} + +void arm::SignalEquipmentRemoval(gearslot* Slot, citem* Item) +{ + int EquipmentIndex = Slot->GetEquipmentIndex(); + + if(Master && !Master->IsInitializing()) + if(EquipmentIndex == RIGHT_WIELDED_INDEX || EquipmentIndex == LEFT_WIELDED_INDEX) + { + UpdateWieldedPicture(); + square* Square = GetSquareUnder(); + + if(Square) + Square->SendNewDrawRequest(); + } + + if(Master) + Master->SignalEquipmentRemoval(EquipmentIndex, Item); +} + +void leg::SignalEquipmentAdd(gearslot* Slot) +{ + int EquipmentIndex = Slot->GetEquipmentIndex(); + + if(Master && !Master->IsInitializing()) + { + item* Equipment = Slot->GetItem(); + + if(Equipment->IsInCorrectSlot(EquipmentIndex)) + ApplyEquipmentAttributeBonuses(Equipment); + + if(EquipmentIndex == RIGHT_BOOT_INDEX || EquipmentIndex == LEFT_BOOT_INDEX) + ApplyAgilityPenalty(Equipment); + } + + if(Master) + Master->SignalEquipmentAdd(EquipmentIndex); +} + +void arm::ApplyEquipmentAttributeBonuses(item* Item) +{ + if(Item->AffectsArmStrength()) + StrengthBonus += Item->GetEnchantment(); + + if(Item->AffectsDexterity()) + DexterityBonus += Item->GetEnchantment(); +} + +void leg::ApplyEquipmentAttributeBonuses(item* Item) +{ + if(Item->AffectsLegStrength()) + { + StrengthBonus += Item->GetEnchantment(); + + if(Master) + Master->CalculateBurdenState(); + } + + if(Item->AffectsAgility()) + AgilityBonus += Item->GetEnchantment(); +} + +void arm::CalculateAttributeBonuses() +{ + StrengthBonus = DexterityBonus = 0; + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment && Equipment->IsInCorrectSlot()) + ApplyEquipmentAttributeBonuses(Equipment); + } + + ApplyDexterityPenalty(GetGauntlet()); + + if(Master) + { + ApplyDexterityPenalty(GetExternalCloak()); + ApplyDexterityPenalty(GetExternalBodyArmor()); + ApplyStrengthBonus(GetExternalHelmet()); + ApplyStrengthBonus(GetExternalCloak()); + ApplyStrengthBonus(GetExternalBodyArmor()); + ApplyStrengthBonus(GetExternalBelt()); + ApplyDexterityBonus(GetExternalHelmet()); + ApplyDexterityBonus(GetExternalCloak()); + ApplyDexterityBonus(GetExternalBodyArmor()); + ApplyDexterityBonus(GetExternalBelt()); + } + + if(!UseMaterialAttributes()) + { + StrengthBonus -= CalculateScarAttributePenalty(GetAttribute(ARM_STRENGTH, false)); + DexterityBonus -= CalculateScarAttributePenalty(GetAttribute(DEXTERITY, false)) ; + } +} + +void leg::CalculateAttributeBonuses() +{ + StrengthBonus = AgilityBonus = 0; + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment && Equipment->IsInCorrectSlot()) + ApplyEquipmentAttributeBonuses(Equipment); + } + + ApplyAgilityPenalty(GetBoot()); + + if(Master) + { + ApplyAgilityPenalty(GetExternalCloak()); + ApplyAgilityPenalty(GetExternalBodyArmor()); + ApplyStrengthBonus(GetExternalHelmet()); + ApplyStrengthBonus(GetExternalCloak()); + ApplyStrengthBonus(GetExternalBodyArmor()); + ApplyStrengthBonus(GetExternalBelt()); + ApplyAgilityBonus(GetExternalHelmet()); + ApplyAgilityBonus(GetExternalCloak()); + ApplyAgilityBonus(GetExternalBodyArmor()); + ApplyAgilityBonus(GetExternalBelt()); + } + + if(!UseMaterialAttributes()) + { + StrengthBonus -= CalculateScarAttributePenalty(GetAttribute(LEG_STRENGTH, false)); + AgilityBonus -= CalculateScarAttributePenalty(GetAttribute(AGILITY, false)) ; + } +} + +void humanoidtorso::SignalEquipmentAdd(gearslot* Slot) +{ + if(!Master) + return; + + humanoid* Master = GetHumanoidMaster(); + int EquipmentIndex = Slot->GetEquipmentIndex(); + + if(!Master->IsInitializing() + && (EquipmentIndex == CLOAK_INDEX || EquipmentIndex == BODY_ARMOR_INDEX)) + { + item* Item = Slot->GetItem(); + + if(Master->GetRightArm()) + Master->GetRightArm()->ApplyDexterityPenalty(Item); + + if(Master->GetLeftArm()) + Master->GetLeftArm()->ApplyDexterityPenalty(Item); + + if(Master->GetRightLeg()) + Master->GetRightLeg()->ApplyAgilityPenalty(Item); + + if(Master->GetLeftLeg()) + Master->GetLeftLeg()->ApplyAgilityPenalty(Item); + } + + Master->SignalEquipmentAdd(EquipmentIndex); +} + +int arm::GetWieldedHitStrength() const +{ + int HitStrength = GetAttribute(ARM_STRENGTH); + int Requirement = GetWielded()->GetStrengthRequirement(); + + if(TwoHandWieldIsActive()) + { + HitStrength += GetPairArm()->GetAttribute(ARM_STRENGTH); + Requirement >>= 2; + } + + return HitStrength - Requirement; +} + +void arm::ApplyStrengthBonus(item* Item) +{ + if(Item && Item->AffectsArmStrength()) + StrengthBonus += Item->GetEnchantment() / 2; +} + +void arm::ApplyDexterityBonus(item* Item) +{ + if(Item && Item->AffectsDexterity()) + DexterityBonus += Item->GetEnchantment() / 2; +} + +void leg::ApplyStrengthBonus(item* Item) +{ + if(Item && Item->AffectsLegStrength()) + StrengthBonus += Item->GetEnchantment() / 2; +} + +void leg::ApplyAgilityBonus(item* Item) +{ + if(Item && Item->AffectsAgility()) + AgilityBonus += Item->GetEnchantment() / 2; +} + +void arm::ApplyDexterityPenalty(item* Item) +{ + if(Item) + DexterityBonus -= Item->GetInElasticityPenalty(GetAttribute(DEXTERITY, false)); +} + +void leg::ApplyAgilityPenalty(item* Item) +{ + if(Item) + AgilityBonus -= Item->GetInElasticityPenalty(GetAttribute(AGILITY, false)); +} + +int corpse::GetSpoilLevel() const +{ + int FlyAmount = 0; + + for(int c = 0; c < GetDeceased()->GetBodyParts(); ++c) + { + bodypart* BodyPart = GetDeceased()->GetBodyPart(c); + + if(BodyPart && FlyAmount < BodyPart->GetSpoilLevel()) + FlyAmount = BodyPart->GetSpoilLevel(); + } + + return FlyAmount; +} + +void bodypart::SignalSpoilLevelChange(material* Material) +{ + if(Master) + Master->SignalSpoilLevelChange(); + else + item::SignalSpoilLevelChange(Material); +} + +truth head::DamageArmor(character* Damager, int Damage, int Type) +{ + long AV[3] = { 0, 0, 0 }, AVSum = 0; + item* Armor[3]; + + if((Armor[0] = GetHelmet())) + AVSum += AV[0] = Max(Armor[0]->GetStrengthValue(), 1); + + if((Armor[1] = GetExternalBodyArmor())) + AVSum += AV[1] = Max(Armor[1]->GetStrengthValue() >> 2, 1); + + if((Armor[2] = GetExternalCloak())) + AVSum += AV[2] = Max(Armor[2]->GetStrengthValue(), 1); + + return AVSum ? Armor[femath::WeightedRand(AV, AVSum)]->ReceiveDamage(Damager, Damage, Type) : false; +} + +truth humanoidtorso::DamageArmor(character* Damager, int Damage, int Type) +{ + long AV[3] = { 0, 0, 0 }, AVSum = 0; + item* Armor[3]; + + if((Armor[0] = GetBodyArmor())) + AVSum += AV[0] = Max(Armor[0]->GetStrengthValue(), 1); + + if((Armor[1] = GetBelt())) + AVSum += AV[1] = Max(Armor[1]->GetStrengthValue(), 1); + + if((Armor[2] = GetCloak())) + AVSum += AV[2] = Max(Armor[2]->GetStrengthValue(), 1); + + return AVSum ? Armor[femath::WeightedRand(AV, AVSum)]->ReceiveDamage(Damager, Damage, Type) : false; +} + +truth arm::DamageArmor(character* Damager, int Damage, int Type) +{ + long AV[3] = { 0, 0, 0 }, AVSum = 0; + item* Armor[3]; + + if((Armor[0] = GetGauntlet())) + AVSum += AV[0] = Max(Armor[0]->GetStrengthValue(), 1); + + if((Armor[1] = GetExternalBodyArmor())) + AVSum += AV[1] = Max(Armor[1]->GetStrengthValue() >> 1, 1); + + if((Armor[2] = GetExternalCloak())) + AVSum += AV[2] = Max(Armor[2]->GetStrengthValue(), 1); + + return AVSum ? Armor[femath::WeightedRand(AV, AVSum)]->ReceiveDamage(Damager, Damage, Type) : false; +} + +truth groin::DamageArmor(character* Damager, int Damage, int Type) +{ + return Master->GetTorso()->DamageArmor(Damager, Damage, Type); +} + +truth leg::DamageArmor(character* Damager, int Damage, int Type) +{ + long AV[3] = { 0, 0, 0 }, AVSum = 0; + item* Armor[3]; + + if((Armor[0] = GetBoot())) + AVSum += AV[0] = Max(Armor[0]->GetStrengthValue(), 1); + + if((Armor[1] = GetExternalBodyArmor())) + AVSum += AV[1] = Max(Armor[1]->GetStrengthValue() >> 1, 1); + + if((Armor[2] = GetExternalCloak())) + AVSum += AV[2] = Max(Armor[2]->GetStrengthValue(), 1); + + return AVSum ? Armor[femath::WeightedRand(AV, AVSum)]->ReceiveDamage(Damager, Damage, Type) : false; +} + +truth bodypart::CanBeEatenByAI(ccharacter* Who) const +{ + return item::CanBeEatenByAI(Who) && !(Who->IsPet() && PLAYER->HasHadBodyPart(this)); +} + +int bodypart::GetConditionColorIndex() const +{ + if(HP <= 1 && MaxHP > 1) + return 0; + else if((HP << 1) + HP < MaxHP) + return 1; + else if((HP << 1) + HP < MaxHP << 1) + return 2; + else if(HP < MaxHP) + return 3; + else + return 4; +} + +truth arm::CheckIfWeaponTooHeavy(cchar* WeaponDescription) const +{ + if(!IsUsable()) + { + ADD_MESSAGE("%s %s is not usable.", Master->CHAR_POSSESSIVE_PRONOUN, GetBodyPartName().CStr()); + return !game::TruthQuestion(CONST_S("Continue anyway? [y/N]")); + } + + int HitStrength = GetAttribute(ARM_STRENGTH); + int Requirement = GetWielded()->GetStrengthRequirement(); + + if(TwoHandWieldIsActive()) + { + HitStrength += GetPairArm()->GetAttribute(ARM_STRENGTH); + Requirement >>= 2; + + if(HitStrength - Requirement < 10) + { + if(HitStrength <= Requirement) + ADD_MESSAGE("%s cannot use %s. Wielding it with two hands requires %d strength.", Master->CHAR_DESCRIPTION(DEFINITE), WeaponDescription, (Requirement >> 1) + 1); + else if(HitStrength - Requirement < 4) + ADD_MESSAGE("Using %s even with two hands is extremely difficult for %s.", WeaponDescription, Master->CHAR_DESCRIPTION(DEFINITE)); + else if(HitStrength - Requirement < 7) + ADD_MESSAGE("%s %s much trouble using %s even with two hands.", Master->CHAR_DESCRIPTION(DEFINITE), Master->IsPlayer() ? "have" : "has", WeaponDescription); + else + ADD_MESSAGE("It is somewhat difficult for %s to use %s even with two hands.", Master->CHAR_DESCRIPTION(DEFINITE), WeaponDescription); + + return !game::TruthQuestion(CONST_S("Continue anyway? [y/N]")); + } + } + else + { + if(HitStrength - Requirement < 10) + { + festring OtherHandInfo; + cchar* HandInfo = ""; + + if(GetWielded()->IsTwoHanded()) + { + if(GetPairArm() && !GetPairArm()->IsUsable()) + OtherHandInfo = Master->GetPossessivePronoun() + " other arm is unusable. "; + + HandInfo = " with one hand"; + } + + if(HitStrength <= Requirement) + ADD_MESSAGE("%s%s cannot use %s. Wielding it%s requires %d strength.", OtherHandInfo.CStr(), Master->GetDescription(DEFINITE).CapitalizeCopy().CStr(), WeaponDescription, HandInfo, Requirement + 1); + else if(HitStrength - Requirement < 4) + ADD_MESSAGE("%sUsing %s%s is extremely difficult for %s.", OtherHandInfo.CStr(), WeaponDescription, HandInfo, Master->CHAR_DESCRIPTION(DEFINITE)); + else if(HitStrength - Requirement < 7) + ADD_MESSAGE("%s%s %s much trouble using %s%s.", OtherHandInfo.CStr(), Master->GetDescription(DEFINITE).CapitalizeCopy().CStr(), Master->IsPlayer() ? "have" : "has", WeaponDescription, HandInfo); + else + ADD_MESSAGE("%sIt is somewhat difficult for %s to use %s%s.", OtherHandInfo.CStr(), Master->CHAR_DESCRIPTION(DEFINITE), WeaponDescription, HandInfo); + + return !game::TruthQuestion(CONST_S("Continue anyway? [y/N]")); + } + } + + return false; +} + +int corpse::GetArticleMode() const +{ + return Deceased->LeftOversAreUnique() ? FORCE_THE : 0; +} + +head* head::Behead() +{ + RemoveFromSlot(); + return this; +} + +truth arm::EditAllAttributes(int Amount) +{ + LimitRef(StrengthExperience += Amount * EXP_MULTIPLIER, MIN_EXP, MAX_EXP); + LimitRef(DexterityExperience += Amount * EXP_MULTIPLIER, MIN_EXP, MAX_EXP); + return (Amount < 0 + && (StrengthExperience != MIN_EXP || DexterityExperience != MIN_EXP)) + || (Amount > 0 + && (StrengthExperience != MAX_EXP || DexterityExperience != MAX_EXP)); +} + +truth leg::EditAllAttributes(int Amount) +{ + LimitRef(StrengthExperience += Amount * EXP_MULTIPLIER, MIN_EXP, MAX_EXP); + LimitRef(AgilityExperience += Amount * EXP_MULTIPLIER, MIN_EXP, MAX_EXP); + return (Amount < 0 + && (StrengthExperience != MIN_EXP || AgilityExperience != MIN_EXP)) + || (Amount > 0 + && (StrengthExperience != MAX_EXP || AgilityExperience != MAX_EXP)); +} + +#ifdef WIZARD + +void arm::AddAttackInfo(felist& List) const +{ + if(GetDamage()) + { + festring Entry = CONST_S(" "); + + if(GetWielded()) + { + GetWielded()->AddName(Entry, UNARTICLED); + + if(TwoHandWieldIsActive()) + Entry << " (b)"; + } + else + Entry << "melee attack"; + + Entry.Resize(50); + Entry << GetMinDamage() << '-' << GetMaxDamage(); + Entry.Resize(60); + Entry << int(GetToHitValue()); + Entry.Resize(70); + Entry << GetAPCost(); + List.AddEntry(Entry, LIGHT_GRAY); + } +} + +void arm::AddDefenceInfo(felist& List) const +{ + if(GetWielded()) + { + festring Entry = CONST_S(" "); + GetWielded()->AddName(Entry, UNARTICLED); + Entry.Resize(50); + Entry << int(GetBlockValue()); + Entry.Resize(70); + Entry << GetBlockCapability(); + List.AddEntry(Entry, LIGHT_GRAY); + } +} + +#else + +void arm::AddAttackInfo(felist&) const { } +void arm::AddDefenceInfo(felist&) const { } + +#endif + +void arm::UpdateWieldedPicture() +{ + if(!Master || !Master->PictureUpdatesAreForbidden()) + { + truth WasAnimated = MasterIsAnimated(); + item* Wielded = GetWielded(); + + if(Wielded && Master) + { + int SpecialFlags = (IsRightArm() ? 0 : MIRROR)|ST_WIELDED|(Wielded->GetSpecialFlags()&~0x3F); + Wielded->UpdatePictures(WieldedGraphicData, + Master->GetWieldedPosition(), + SpecialFlags, + GetMaxAlpha(), + GR_HUMANOID, + static_cast(&item::GetWieldedBitmapPos)); + + if(ShowFluids()) + Wielded->CheckFluidGearPictures(Wielded->GetWieldedBitmapPos(0), SpecialFlags, false); + } + else + WieldedGraphicData.Retire(); + + if(!WasAnimated != !MasterIsAnimated()) + SignalAnimationStateChange(WasAnimated); + } +} + +void arm::DrawWielded(blitdata& BlitData) const +{ + DrawEquipment(WieldedGraphicData, BlitData); + + if(ShowFluids() && GetWielded()) + GetWielded()->DrawFluidGearPictures(BlitData, IsRightArm() ? 0 : MIRROR); +} + +void arm::UpdatePictures() +{ + bodypart::UpdatePictures(); + UpdateWieldedPicture(); +} + +void bodypart::Draw(blitdata& BlitData) const +{ + cint AF = GraphicData.AnimationFrames; + cint F = !(BlitData.CustomData & ALLOW_ANIMATE) || AF == 1 ? 0 : GET_TICK() & (AF - 1); + cbitmap* P = GraphicData.Picture[F]; + + if(BlitData.CustomData & ALLOW_ALPHA) + P->AlphaPriorityBlit(BlitData); + else + P->MaskedPriorityBlit(BlitData); + + if(Fluid && ShowFluids()) + DrawFluids(BlitData); + + DrawArmor(BlitData); +} + +void leg::AddAttackInfo(felist& List) const +{ + festring Entry = CONST_S(" kick attack"); + Entry.Resize(50); + Entry << GetKickMinDamage() << '-' << GetKickMaxDamage(); + Entry.Resize(60); + Entry << int(GetKickToHitValue()); + Entry.Resize(70); + Entry << GetKickAPCost(); + List.AddEntry(Entry, LIGHT_GRAY); +} + +void corpse::PreProcessForBone() +{ + item::PreProcessForBone(); + + if(!Deceased->PreProcessForBone()) + { + RemoveFromSlot(); + SendToHell(); + } +} + +void corpse::PostProcessForBone() +{ + item::PostProcessForBone(); + + if(!Deceased->PostProcessForBone()) + { + RemoveFromSlot(); + SendToHell(); + } +} + +void corpse::FinalProcessForBone() +{ + item::FinalProcessForBone(); + Deceased->FinalProcessForBone(); +} + +truth bodypart::IsRepairable(ccharacter*) const +{ + return !CanRegenerate() && (GetHP() < GetMaxHP() || IsRusted()); +} + +truth corpse::SuckSoul(character* Soul, character* Summoner) +{ + v2 Pos = Soul->GetPos(); + + if(Deceased->SuckSoul(Soul)) + { + Soul->Remove(); + character* Deceased = GetDeceased(); + + if(RaiseTheDead(Summoner)) + { + Soul->SendToHell(); + return true; + } + else + { + Deceased->SetSoulID(Soul->GetID()); + Soul->PutTo(Pos); + return false; + } + } + else + return false; +} + +double arm::GetTypeDamage(ccharacter* Enemy) const +{ + if(!GetWielded() || !GetWielded()->IsGoodWithPlants() || !Enemy->IsPlant()) + return Damage; + else + return Damage * 1.5; +} + +void largetorso::Draw(blitdata& BlitData) const +{ + LargeDraw(BlitData); +} + +void largecorpse::Draw(blitdata& BlitData) const +{ + LargeDraw(BlitData); +} + +void largetorso::SignalStackAdd(stackslot* StackSlot, void (stack::*AddHandler)(item*, truth)) +{ + if(!Slot[0]) + { + Slot[0] = StackSlot; + v2 Pos = GetPos(); + level* Level = GetLevel(); + + for(int c = 1; c < 4; ++c) + (Level->GetLSquare(Pos + game::GetLargeMoveVector(12 + c))->GetStack()->*AddHandler)(this, false); + } + else + for(int c = 1; c < 4; ++c) + if(!Slot[c]) + { + Slot[c] = StackSlot; + return; + } +} + +int largetorso::GetSquareIndex(v2 Pos) const +{ + v2 RelativePos = Pos - GetPos(); + return RelativePos.X + (RelativePos.Y << 1); +} + +void largecorpse::SignalStackAdd(stackslot* StackSlot, void (stack::*AddHandler)(item*, truth)) +{ + if(!Slot[0]) + { + Slot[0] = StackSlot; + v2 Pos = GetPos(); + level* Level = GetLevel(); + + for(int c = 1; c < 4; ++c) + (Level->GetLSquare(Pos + game::GetLargeMoveVector(12 + c))->GetStack()->*AddHandler)(this, false); + } + else + for(int c = 1; c < 4; ++c) + if(!Slot[c]) + { + Slot[c] = StackSlot; + return; + } +} + +int largecorpse::GetSquareIndex(v2 Pos) const +{ + v2 RelativePos = Pos - GetPos(); + return RelativePos.X + (RelativePos.Y << 1); +} + +character* corpse::TryNecromancy(character* Summoner) +{ + if(Summoner && Summoner->IsPlayer()) + game::DoEvilDeed(50); + + character* Zombie = GetDeceased()->CreateZombie(); + + if(Zombie) + { + v2 Pos = GetPos(); + RemoveFromSlot(); + Zombie->ChangeTeam(Summoner ? Summoner->GetTeam() : game::GetTeam(MONSTER_TEAM)); + Zombie->PutToOrNear(Pos); + Zombie->SignalStepFrom(0); + SendToHell(); + return Zombie; + } + + return 0; +} + +item* head::GetArmorToReceiveFluid(truth) const +{ + item* Helmet = GetHelmet(); + + if(Helmet && Helmet->GetCoverPercentile() > RAND() % 100) + return Helmet; + else + return 0; +} + +item* humanoidtorso::GetArmorToReceiveFluid(truth) const +{ + item* Cloak = GetCloak(); + + if(Cloak && !(RAND() % 3)) + return Cloak; + + item* Belt = GetBelt(); + + if(Belt && !(RAND() % 10)) + return Belt; + + item* BodyArmor = GetBodyArmor(); + return BodyArmor ? BodyArmor : 0; +} + +item* arm::GetArmorToReceiveFluid(truth) const +{ + item* Cloak = GetExternalCloak(); + + if(Cloak && !(RAND() % 3)) + return Cloak; + + item* Wielded = GetWielded(); + + if(Wielded && !(RAND() % 3)) + return Wielded; + + item* Gauntlet = GetGauntlet(); + + if(Gauntlet && RAND() & 1) + return Gauntlet; + + item* BodyArmor = GetExternalBodyArmor(); + return BodyArmor ? BodyArmor : 0; +} + +item* groin::GetArmorToReceiveFluid(truth) const +{ + item* Cloak = GetExternalCloak(); + + if(Cloak && !(RAND() % 3)) + return Cloak; + + item* BodyArmor = GetExternalBodyArmor(); + return BodyArmor ? BodyArmor : 0; +} + +item* leg::GetArmorToReceiveFluid(truth SteppedOn) const +{ + if(SteppedOn) + { + item* Boot = GetBoot(); + return Boot ? Boot : 0; + } + else + { + item* Cloak = GetExternalCloak(); + + if(Cloak && !(RAND() % 3)) + return Cloak; + + item* Boot = GetBoot(); + + if(Boot && RAND() & 1) + return Boot; + + item* BodyArmor = GetExternalBodyArmor(); + return BodyArmor ? BodyArmor : 0; + } +} + +void bodypart::SpillFluid(character* Spiller, liquid* Liquid, int SquareIndex) +{ + if(Master) + { + item* Armor = GetArmorToReceiveFluid(false); + + if(Armor) + Armor->SpillFluid(Spiller, Liquid); + else if(GetMaster()) + { + if(Liquid->GetVolume()) + AddFluid(Liquid, "", SquareIndex, false); + else + delete Liquid; + } + } + else + item::SpillFluid(Spiller, Liquid, SquareIndex); +} + +void bodypart::StayOn(liquid* Liquid) +{ + item* Armor = GetArmorToReceiveFluid(true); + + if(Armor) + Liquid->TouchEffect(Armor, CONST_S("")); + else if(GetMaster()) + Liquid->TouchEffect(GetMaster(), GetBodyPartIndex()); +} + +liquid* bodypart::CreateBlood(long Volume) const +{ + return liquid::Spawn(GetBloodMaterial(), Volume); +} + +int corpse::GetRustDataA() const +{ + return Deceased->GetTorso()->GetMainMaterial()->GetRustData(); +} + +void bodypart::UpdateArmorPicture(graphicdata& GData, item* Armor, int SpecialFlags, v2 (item::*Retriever)(int) const, truth BodyArmor) const +{ + if(Armor && Master) + { + Armor->UpdatePictures(GData, + ZERO_V2, + SpecialFlags|Armor->GetSpecialFlags(), + GetMaxAlpha(), + GR_HUMANOID, + static_cast(Retriever)); + Armor->CheckFluidGearPictures((Armor->*Retriever)(0), SpecialFlags, BodyArmor); + } + else + GData.Retire(); +} + +truth playerkindhead::UpdateArmorPictures() +{ + UpdateHeadArmorPictures(HelmetGraphicData); + return true; +} + +truth playerkindtorso::UpdateArmorPictures() +{ + UpdateTorsoArmorPictures(TorsoArmorGraphicData, + CloakGraphicData, + BeltGraphicData); + return true; +} + +truth playerkindrightarm::UpdateArmorPictures() +{ + UpdateArmArmorPictures(ArmArmorGraphicData, + GauntletGraphicData, + ST_RIGHT_ARM); + return true; +} + +truth playerkindleftarm::UpdateArmorPictures() +{ + UpdateArmArmorPictures(ArmArmorGraphicData, + GauntletGraphicData, + ST_LEFT_ARM); + return true; +} + +truth playerkindgroin::UpdateArmorPictures() +{ + UpdateGroinArmorPictures(GroinArmorGraphicData); + return true; +} + +truth playerkindrightleg::UpdateArmorPictures() +{ + UpdateLegArmorPictures(LegArmorGraphicData, + BootGraphicData, + ST_RIGHT_LEG); + return true; +} + +truth playerkindleftleg::UpdateArmorPictures() +{ + UpdateLegArmorPictures(LegArmorGraphicData, + BootGraphicData, + ST_LEFT_LEG); + return true; +} + +void head::UpdateHeadArmorPictures(graphicdata& HelmetGraphicData) const +{ + if(!Master || !Master->PictureUpdatesAreForbidden()) + { + UpdateArmorPicture(HelmetGraphicData, + GetHelmet(), + ST_OTHER_BODYPART, + &item::GetHelmetBitmapPos); + } +} + +void humanoidtorso::UpdateTorsoArmorPictures(graphicdata& TorsoArmorGraphicData, graphicdata& CloakGraphicData, graphicdata& BeltGraphicData) const +{ + if(!Master || !Master->PictureUpdatesAreForbidden()) + { + UpdateArmorPicture(TorsoArmorGraphicData, + GetBodyArmor(), + ST_OTHER_BODYPART, + &item::GetTorsoArmorBitmapPos, + true); + UpdateArmorPicture(CloakGraphicData, + GetCloak(), + ST_OTHER_BODYPART, + &item::GetCloakBitmapPos); + UpdateArmorPicture(BeltGraphicData, + GetBelt(), + ST_OTHER_BODYPART, + &item::GetBeltBitmapPos); + } +} + +void arm::UpdateArmArmorPictures(graphicdata& ArmArmorGraphicData, graphicdata& GauntletGraphicData, int SpecialFlags) const +{ + if(!Master || !Master->PictureUpdatesAreForbidden()) + { + UpdateArmorPicture(ArmArmorGraphicData, + Master ? GetExternalBodyArmor() : 0, + SpecialFlags, + GetAttribute(ARM_STRENGTH, false) >= 20 ? &item::GetAthleteArmArmorBitmapPos : &item::GetArmArmorBitmapPos, + true); + UpdateArmorPicture(GauntletGraphicData, + GetGauntlet(), + SpecialFlags, + &item::GetGauntletBitmapPos); + } +} + +void groin::UpdateGroinArmorPictures(graphicdata& GroinArmorGraphicData) const +{ + if(!Master || !Master->PictureUpdatesAreForbidden()) + { + UpdateArmorPicture(GroinArmorGraphicData, + Master ? GetExternalBodyArmor() : 0, + ST_GROIN, + &item::GetLegArmorBitmapPos, + true); + } +} + +void leg::UpdateLegArmorPictures(graphicdata& LegArmorGraphicData, graphicdata& BootGraphicData, int SpecialFlags) const +{ + if(!Master || !Master->PictureUpdatesAreForbidden()) + { + UpdateArmorPicture(LegArmorGraphicData, + Master ? GetExternalBodyArmor() : 0, + SpecialFlags, + &item::GetLegArmorBitmapPos, + true); + UpdateArmorPicture(BootGraphicData, + GetBoot(), + SpecialFlags, + &item::GetBootBitmapPos); + } +} + +void bodypart::DrawEquipment(const graphicdata& GraphicData, blitdata& BlitData) const +{ + int EAF = GraphicData.AnimationFrames; + + if(EAF) + { + int F = !(BlitData.CustomData & ALLOW_ANIMATE) || EAF == 1 ? 0 : GET_TICK() & (EAF - 1); + GraphicData.Picture[F]->AlphaPriorityBlit(BlitData); + } +} + +void playerkindhead::DrawArmor(blitdata& BlitData) const +{ + DrawEquipment(HelmetGraphicData, BlitData); + + if(GetHelmet()) + GetHelmet()->DrawFluidGearPictures(BlitData); +} + +void playerkindtorso::DrawArmor(blitdata& BlitData) const +{ + DrawEquipment(TorsoArmorGraphicData, BlitData); + + if(GetBodyArmor()) + GetBodyArmor()->DrawFluidGearPictures(BlitData); + + DrawEquipment(CloakGraphicData, BlitData); + + if(GetCloak()) + GetCloak()->DrawFluidGearPictures(BlitData); + + DrawEquipment(BeltGraphicData, BlitData); + + if(GetBelt()) + GetBelt()->DrawFluidGearPictures(BlitData); +} + +void playerkindrightarm::DrawArmor(blitdata& BlitData) const +{ + DrawEquipment(ArmArmorGraphicData, BlitData); + + if(Master && GetExternalBodyArmor()) + GetExternalBodyArmor()->DrawFluidBodyArmorPictures(BlitData, ST_RIGHT_ARM); + + DrawEquipment(GauntletGraphicData, BlitData); + + if(GetGauntlet()) + GetGauntlet()->DrawFluidGearPictures(BlitData); +} + +void playerkindleftarm::DrawArmor(blitdata& BlitData) const +{ + DrawEquipment(ArmArmorGraphicData, BlitData); + + if(Master && GetExternalBodyArmor()) + GetExternalBodyArmor()->DrawFluidBodyArmorPictures(BlitData, ST_LEFT_ARM); + + DrawEquipment(GauntletGraphicData, BlitData); + + if(GetGauntlet()) + GetGauntlet()->DrawFluidGearPictures(BlitData); +} + +void playerkindgroin::DrawArmor(blitdata& BlitData) const +{ + DrawEquipment(GroinArmorGraphicData, BlitData); + + if(Master && GetExternalBodyArmor()) + GetExternalBodyArmor()->DrawFluidBodyArmorPictures(BlitData, ST_GROIN); +} + +void playerkindrightleg::DrawArmor(blitdata& BlitData) const +{ + DrawEquipment(LegArmorGraphicData, BlitData); + + if(Master && GetExternalBodyArmor()) + GetExternalBodyArmor()->DrawFluidBodyArmorPictures(BlitData, ST_RIGHT_LEG); + + DrawEquipment(BootGraphicData, BlitData); + + if(GetBoot()) + GetBoot()->DrawFluidGearPictures(BlitData); +} + +void playerkindleftleg::DrawArmor(blitdata& BlitData) const +{ + DrawEquipment(LegArmorGraphicData, BlitData); + + if(Master && GetExternalBodyArmor()) + GetExternalBodyArmor()->DrawFluidBodyArmorPictures(BlitData, ST_LEFT_LEG); + + DrawEquipment(BootGraphicData, BlitData); + + if(GetBoot()) + GetBoot()->DrawFluidGearPictures(BlitData); +} + +void playerkindhead::Save(outputfile& SaveFile) const +{ + head::Save(SaveFile); + SaveFile << HelmetGraphicData; +} + +void playerkindhead::Load(inputfile& SaveFile) +{ + head::Load(SaveFile); + SaveFile >> HelmetGraphicData; +} + +void playerkindtorso::Save(outputfile& SaveFile) const +{ + humanoidtorso::Save(SaveFile); + SaveFile << TorsoArmorGraphicData << CloakGraphicData << BeltGraphicData; +} + +void playerkindtorso::Load(inputfile& SaveFile) +{ + humanoidtorso::Load(SaveFile); + SaveFile >> TorsoArmorGraphicData >> CloakGraphicData >> BeltGraphicData; +} + +void playerkindrightarm::Save(outputfile& SaveFile) const +{ + rightarm::Save(SaveFile); + SaveFile << ArmArmorGraphicData << GauntletGraphicData; +} + +void playerkindrightarm::Load(inputfile& SaveFile) +{ + rightarm::Load(SaveFile); + SaveFile >> ArmArmorGraphicData >> GauntletGraphicData; +} + +void playerkindleftarm::Save(outputfile& SaveFile) const +{ + leftarm::Save(SaveFile); + SaveFile << ArmArmorGraphicData << GauntletGraphicData; +} + +void playerkindleftarm::Load(inputfile& SaveFile) +{ + leftarm::Load(SaveFile); + SaveFile >> ArmArmorGraphicData >> GauntletGraphicData; +} + +void playerkindgroin::Save(outputfile& SaveFile) const +{ + groin::Save(SaveFile); + SaveFile << GroinArmorGraphicData; +} + +void playerkindgroin::Load(inputfile& SaveFile) +{ + groin::Load(SaveFile); + SaveFile >> GroinArmorGraphicData; +} + +void playerkindrightleg::Save(outputfile& SaveFile) const +{ + rightleg::Save(SaveFile); + SaveFile << LegArmorGraphicData << BootGraphicData; +} + +void playerkindrightleg::Load(inputfile& SaveFile) +{ + rightleg::Load(SaveFile); + SaveFile >> LegArmorGraphicData >> BootGraphicData; +} + +void playerkindleftleg::Save(outputfile& SaveFile) const +{ + leftleg::Save(SaveFile); + SaveFile << LegArmorGraphicData << BootGraphicData; +} + +void playerkindleftleg::Load(inputfile& SaveFile) +{ + leftleg::Load(SaveFile); + SaveFile >> LegArmorGraphicData >> BootGraphicData; +} + +truth bodypart::MasterIsAnimated() const +{ + return Master && !Master->IsInitializing() && Master->IsAnimated(); +} + +void bodypart::UpdatePictures() +{ + truth WasAnimated = MasterIsAnimated(); + + item::UpdatePictures(); + UpdateArmorPictures(); + + if(!WasAnimated != !MasterIsAnimated()) + SignalAnimationStateChange(WasAnimated); +} + +void playerkindtorso::SignalVolumeAndWeightChange() +{ + humanoidtorso::SignalVolumeAndWeightChange(); + + if(Master && !Master->IsInitializing()) + Master->UpdatePictures(); +} + +void bodypart::ReceiveAcid(material* Material, cfestring& LocationName, long Modifier) +{ + if(Master && MainMaterial->GetInteractionFlags() & CAN_DISSOLVE) + { + long Tries = Modifier / 1000; + Modifier -= Tries * 1000; //opt%? + int Damage = 0; + + for(long c = 0; c < Tries; ++c) + if(!(RAND() % 100)) + ++Damage; + + if(Modifier && !(RAND() % 100000 / Modifier)) + ++Damage; + + if(Damage) + { + ulong Minute = game::GetTotalMinutes(); + character* Master = this->Master; + + if(Master->GetLastAcidMsgMin() != Minute && (Master->CanBeSeenByPlayer() || Master->IsPlayer())) + { + Master->SetLastAcidMsgMin(Minute); + cchar* MName = Material->GetName(false, false).CStr(); + + if(Master->IsPlayer()) + { + cchar* TName = LocationName.IsEmpty() ? GetBodyPartName().CStr() : LocationName.CStr(); + ADD_MESSAGE("Acidous %s dissolves your %s.", MName, TName); + } + else + ADD_MESSAGE("Acidous %s dissolves %s.", MName, Master->CHAR_NAME(DEFINITE)); + } + + Master->ReceiveBodyPartDamage(0, Damage, ACID, GetBodyPartIndex(), YOURSELF, false, false, false); + ulong DeathFlags = Material->IsStuckTo(Master) ? IGNORE_TRAPS : 0; + Master->CheckDeath(CONST_S("dissolved by ") + Material->GetName(), 0, DeathFlags); + } + } +} + +void bodypart::TryToRust(long LiquidModifier) +{ + if(MainMaterial->TryToRust(LiquidModifier << 4)) + { + cchar* MoreMsg = MainMaterial->GetRustLevel() == NOT_RUSTED ? "" : " more"; + + if(Master) + { + if(Master->IsPlayer()) + ADD_MESSAGE("Your %s rusts%s.", CHAR_NAME(UNARTICLED), MoreMsg); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("The %s of %s rusts%s.", CHAR_NAME(UNARTICLED), Master->CHAR_NAME(DEFINITE), MoreMsg); + } + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s rusts%s.", CHAR_NAME(DEFINITE), MoreMsg); + + MainMaterial->SetRustLevel(MainMaterial->GetRustLevel() + 1); + } +} + +material* corpse::GetConsumeMaterial(ccharacter* Consumer, materialpredicate Predicate) const +{ + for(int c = GetDeceased()->GetBodyParts() - 1; c; --c) + { + bodypart* BodyPart = GetDeceased()->GetBodyPart(c); + + if(BodyPart) + { + material* CM = BodyPart->GetConsumeMaterial(Consumer, Predicate); + + if(CM) + return CM; + } + } + + return GetDeceased()->GetTorso()->GetConsumeMaterial(Consumer, Predicate); +} + +void corpse::Cannibalize() +{ + item::Cannibalize(); + + for(int c = 0; c < GetDeceased()->GetBodyParts(); ++c) + { + bodypart* BodyPart = GetDeceased()->GetBodyPart(c); + + if(BodyPart) + BodyPart->Cannibalize(); + } +} + +material* bodypart::RemoveMaterial(material* Material) +{ + if(Master && GetBodyPartIndex() == TORSO_INDEX) + return Master->GetMotherEntity()->RemoveMaterial(Material); // gum + else + return item::RemoveMaterial(Material); +} + +void arm::CopyAttributes(const bodypart* BodyPart) +{ + const arm* Arm = static_cast(BodyPart); + StrengthExperience = Arm->StrengthExperience; + DexterityExperience = Arm->DexterityExperience; +} + +void leg::CopyAttributes(const bodypart* BodyPart) +{ + const leg* Leg = static_cast(BodyPart); + StrengthExperience = Leg->StrengthExperience; + AgilityExperience = Leg->AgilityExperience; +} + +truth corpse::DetectMaterial(const material* Material) const +{ + return GetDeceased()->DetectMaterial(Material); +} + +void bodypart::DestroyBodyPart(stack* Stack) +{ + int Lumps = 1 + RAND() % 3; + long LumpVolume = Volume / Lumps >> 2; + + if(LumpVolume >= 10) + for(int c = 0; c < Lumps; ++c) + { + item* Lump = GetMainMaterial()->CreateNaturalForm(LumpVolume + RAND() % LumpVolume); + Stack->AddItem(Lump); + } + + SendToHell(); +} + +v2 magicmushroomtorso::GetBitmapPos(int Frame) const +{ + v2 BasePos = torso::GetBitmapPos(Frame); + Frame &= 0x3F; + + if(!(Frame & 0x30)) + { + if(Frame <= 8) + return v2(BasePos.X + 64 - (abs(Frame - 4) << 4), BasePos.Y); + else + return v2(BasePos.X + 64 - (abs(Frame - 12) << 4), BasePos.Y + 16); + } + else + return BasePos; +} + +v2 dogtorso::GetBitmapPos(int Frame) const +{ + v2 BasePos = torso::GetBitmapPos(Frame); + + if(Frame >= GraphicData.AnimationFrames >> 1) + BasePos.X += 32; + + return v2(BasePos.X + ((Frame & 4) << 2), BasePos.Y); +} + +void dogtorso::Draw(blitdata& BlitData) const +{ + cint AF = GraphicData.AnimationFrames >> 1; + int Index = !(BlitData.CustomData & ALLOW_ANIMATE) || AF == 1 ? 0 : GET_TICK() & (AF - 1); + + if(GetHP() << 1 <= GetMaxHP()) + Index += AF; + + cbitmap* P = GraphicData.Picture[Index]; + + if(BlitData.CustomData & ALLOW_ALPHA) + P->AlphaPriorityBlit(BlitData); + else + P->MaskedPriorityBlit(BlitData); +} + +void corpse::SetLifeExpectancy(int Base, int RandPlus) +{ + Deceased->SetLifeExpectancy(Base, RandPlus); +} + +void corpse::Be() +{ + for(int c = 0; c < Deceased->GetBodyParts(); ++c) + { + bodypart* BodyPart = Deceased->GetBodyPart(c); + + if(BodyPart) + BodyPart->Be(); + } +} + +void bodypart::SetLifeExpectancy(int Base, int RandPlus) +{ + LifeExpectancy = RandPlus > 1 ? Base + RAND_N(RandPlus) : Base; + + if(!Master) + Enable(); +} + +void bodypart::SpecialEatEffect(character* Eater, int Amount) +{ + Amount >>= 6; + + if(Amount && (!Master || Master->SpillsBlood()) && (IsAlive() || MainMaterial->IsLiquid()) && !game::IsInWilderness()) + { + if(Eater->GetVirtualHead()) + Eater->GetVirtualHead()->SpillFluid(Eater, CreateBlood(Amount)); + + Eater->GetTorso()->SpillFluid(Eater, CreateBlood(Amount)); + } +} + +truth corpse::IsValuable() const +{ + for(int c = 0; c < Deceased->GetBodyParts(); ++c) + { + bodypart* BodyPart = Deceased->GetBodyPart(c); + + if(BodyPart && BodyPart->IsValuable()) + return true; + } + + return false; +} + +truth corpse::Necromancy(character* Necromancer) +{ + if(Necromancer && Necromancer->IsPlayer()) + game::DoEvilDeed(50); + + character* Zombie = GetDeceased()->CreateZombie(); + + if(Zombie) + { + Zombie->ChangeTeam(Necromancer ? Necromancer->GetTeam() : game::GetTeam(MONSTER_TEAM)); + Zombie->PutToOrNear(GetPos()); + RemoveFromSlot(); + SendToHell(); + + if(Zombie->CanBeSeenByPlayer()) + ADD_MESSAGE("%s rises back to cursed undead life.", Zombie->CHAR_DESCRIPTION(INDEFINITE)); + + Zombie->SignalStepFrom(0); + return true; + } + else + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s vibrates for some time.", CHAR_NAME(DEFINITE)); + + return false; + } +} + +alpha mysticfrogtorso::GetOutlineAlpha(int Frame) const +{ + Frame &= 31; + return Frame * (31 - Frame) >> 1; +} + +col16 mysticfrogtorso::GetOutlineColor(int Frame) const +{ + switch((Frame&127) >> 5) + { + case 0: return BLUE; + case 1: return GREEN; + case 2: return RED; + case 3: return YELLOW; + } + + return TRANSPARENT_COLOR; +} + +void bodypart::UpdateFlags() +{ + if((HP << 1) + HP < MaxHP || (HP == 1 && MaxHP != 1)) + Flags |= BADLY_HURT; + else + Flags &= ~BADLY_HURT; + + if(Master->BodyPartIsStuck(GetBodyPartIndex())) + Flags |= STUCK; + else + Flags &= ~STUCK; +} + +void head::SignalPossibleUsabilityChange() +{ + ulong OldFlags = Flags; + UpdateFlags(); + + if(!Master->IsInitializing() && HP > 0 + && Flags & BADLY_HURT && !(OldFlags & BADLY_HURT)) + switch(RAND_N(8)) + { + case 0: + case 1: + case 2: Master->LoseConsciousness(50 + RAND_N(50)); break; + case 3: + case 4: + case 5: Master->BeginTemporaryState(CONFUSED, 500 + RAND_N(500)); break; + case 6: + if(Master->IsPlayer() && !RAND_N(3)) + { + if(RAND_N(5)) + { + ADD_MESSAGE("Your memory becomes blurred."); + GetLevel()->Amnesia(25 + RAND_N(50)); + Master->EditExperience(INTELLIGENCE, -80, 1 << 13); + game::SendLOSUpdateRequest(); + } + else + { + ADD_MESSAGE("A terrible concussion garbles your consciousness."); + Master->BeginTemporaryState(CONFUSED, 5000 + RAND_N(5000)); + Master->EditExperience(INTELLIGENCE, -100, 1 << 14); + GetLevel()->BlurMemory(); + game::SendLOSUpdateRequest(); + } + } + else + Master->EditExperience(INTELLIGENCE, -60, 1 << 12); + + break; + case 7: + Master->ForgetRandomThing(); + } +} + +void arm::SignalPossibleUsabilityChange() +{ + ulong OldFlags = Flags; + UpdateFlags(); + + if(Flags != OldFlags && !Master->IsInitializing()) + Master->CalculateBattleInfo(); +} + +void leg::SignalPossibleUsabilityChange() +{ + ulong OldFlags = Flags; + UpdateFlags(); + + if(Flags != OldFlags && !Master->IsInitializing()) + Master->CalculateBattleInfo(); +} + +void bodypart::IncreaseHP() +{ + ++HP; + RemoveDamageIDs(1); + SignalPossibleUsabilityChange(); +} + +void bodypart::FastRestoreHP() +{ + HP = MaxHP; + DamageID.clear(); + SignalPossibleUsabilityChange(); +} + +void bodypart::RestoreHP() +{ + HP = MaxHP; + DamageID.clear(); + SignalPossibleUsabilityChange(); + Master->CalculateHP(); +} + +void bodypart::SetIsUnique(truth What) +{ + if(What) + Flags |= UNIQUE; + else + Flags &= ~UNIQUE; +} + +void bodypart::SetIsInfectedByLeprosy(truth What) +{ + MainMaterial->SetIsInfectedByLeprosy(What); +} + +void bodypart::SetSparkleFlags(int What) +{ + cint S = SPARKLING_B|SPARKLING_C|SPARKLING_D; + Flags = Flags & ~(S << BODYPART_SPARKLE_SHIFT) | ((What & S) << BODYPART_SPARKLE_SHIFT); +} + +truth arm::IsAnimated() const +{ + return WieldedGraphicData.AnimationFrames > 1; +} + +void bodypart::SignalAnimationStateChange(truth WasAnimated) +{ + if(WasAnimated) + { + for(int c = 0; c < GetSquaresUnder(); ++c) + { + square* Square = GetSquareUnder(c); + + if(Square) + Square->DecAnimatedEntities(); + } + } + else + { + for(int c = 0; c < GetSquaresUnder(); ++c) + { + square* Square = GetSquareUnder(c); + + if(Square) + Square->IncAnimatedEntities(); + } + } +} + +truth bodypart::MaterialIsChangeable(ccharacter*) const +{ + return !Master || !Master->BodyPartIsVital(GetBodyPartIndex()) || UseMaterialAttributes(); +} + +truth bodypart::AddAdjective(festring& String, truth Articled) const +{ + if(!Master) + { + if(Articled) + String << "a "; + + String << "severed "; + return true; + } + else + return false; +} + +void bodypart::RemoveRust() +{ + item::RemoveRust(); + RestoreHP(); +} + +long bodypart::GetFixPrice() const +{ + return GetMaxHP() - GetHP() + GetMainMaterial()->GetRustLevel() * 25; +} + +truth bodypart::IsFixableBySmith(ccharacter*) const +{ + return (GetMainMaterial()->GetCategoryFlags() & IS_METAL + && (GetHP() < GetMaxHP() || IsRusted())); +} + +truth bodypart::IsFixableByTailor(ccharacter*) const +{ + return (GetMainMaterial()->GetCategoryFlags() & CAN_BE_TAILORED + && GetHP() < GetMaxHP()); +} + +item* bodypart::Fix() +{ + RestoreHP(); + return this; +} + +void bodypart::SignalMaterialChange() +{ + if(Master) + RestoreHP(); +} + +truth bodypart::ShowMaterial() const +{ + return MainMaterial->GetConfig() != NormalMaterial; +} + +col16 lobhsetorso::GetMaterialColorD(int Frame) const +{ + Frame &= 31; + int Modifier = Frame * (31 - Frame); + return MakeRGB16(220 - (Modifier >> 2), 220 - (Modifier >> 1), 0); +} + +truth bodypart::IsDestroyable(ccharacter*) const +{ + return !Master || !Master->BodyPartIsVital(GetBodyPartIndex()); +} + +truth bodypart::DamageTypeCanScar(int Type) +{ + return !(Type == POISON || Type == DRAIN); +} + +void bodypart::GenerateScar(int Damage, int Type) +{ + Scar.push_back(scar()); + scar& NewScar = Scar.back(); + NewScar.Severity = 1 + RAND_N(1 + 5 * Damage / GetMaxHP()); + + if(GetMaster()->IsPlayer()) + { + int ScarColor = MakeShadeColor(GetMainMaterial()->GetColor()); + NewScar.PanelBitmap = igraph::GenerateScarBitmap(GetBodyPartIndex(), + NewScar.Severity, + ScarColor); + ADD_MESSAGE("Your %s is scarred.", CHAR_NAME(UNARTICLED)); + } + else + NewScar.PanelBitmap = 0; + + CalculateMaxHP(); + GetMaster()->CalculateMaxHP(); + GetMaster()->CalculateAttributeBonuses(); + CalculateAttackInfo(); +} + +void bodypart::DrawScars(cblitdata& B) const +{ + for(int c = 0; c < Scar.size(); ++c) + { + if(!Scar[c].PanelBitmap) + { + int ScarColor = MakeShadeColor(GetMainMaterial()->GetColor()); + Scar[c].PanelBitmap = igraph::GenerateScarBitmap(GetBodyPartIndex(), + Scar[c].Severity, + ScarColor); + } + + Scar[c].PanelBitmap->NormalMaskedBlit(B); + } +} + +outputfile& operator<<(outputfile& SaveFile, const scar& Scar) +{ + SaveFile << Scar.Severity << Scar.PanelBitmap; + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, scar& Scar) +{ + SaveFile >> Scar.Severity >> Scar.PanelBitmap; + return SaveFile; +} + +int bodypart::CalculateScarAttributePenalty(int Attribute) const +{ + double DoubleAttribute = Attribute; + + for(int c = 0; c < Scar.size(); ++c) + DoubleAttribute *= (100. - Scar[c].Severity * 4) / 100; + + return Min(Attribute - int(DoubleAttribute), Attribute - 1); +} + +bodypart::~bodypart() +{ + for(int c = 0; c < Scar.size(); ++c) + delete Scar[c].PanelBitmap; +} + +bodypart::bodypart(const bodypart& B) : mybase(B), OwnerDescription(B.OwnerDescription), Master(B.Master), CarriedWeight(B.CarriedWeight), BodyPartVolume(B.BodyPartVolume), BitmapPos(B.BitmapPos), ColorB(B.ColorB), ColorC(B.ColorC), ColorD(B.ColorD), SpecialFlags(B.SpecialFlags), HP(B.HP), MaxHP(B.MaxHP), BloodMaterial(B.BloodMaterial), NormalMaterial(B.NormalMaterial), SpillBloodCounter(B.SpillBloodCounter), WobbleData(B.WobbleData), Scar(B.Scar) +{ + for(int c = 0; c < Scar.size(); ++c) + if(Scar[c].PanelBitmap) + Scar[c].PanelBitmap = new bitmap(Scar[c].PanelBitmap); +} + +/* Amount should be > 0 */ + +void bodypart::RemoveDamageIDs(int Amount) +{ + /*while(Amount) + { + damageid& D = DamageID.front(); + int CurrentAmount = D.Amount; + + if(Amount < CurrentAmount) + { + D.Amount -= Amount; + Amount = 0; + } + else + { + DamageID.pop_front(); + Amount -= CurrentAmount; + } + }*/ +} + +/* Amount should be > 0 */ + +void bodypart::AddDamageID(int SrcID, int Amount) +{ + /*damageid D = { SrcID, Amount }; + DamageID.push_back(D);*/ +} + +int arm::GetCurrentSWeaponSkillBonus() const +{ + return (*GetCurrentSWeaponSkill() + ? (*GetCurrentSWeaponSkill())->GetBonus() : 1); +} + +v2 battorso::GetBitmapPos(int Frame) const +{ + v2 BasePos = torso::GetBitmapPos(Frame); + Frame &= 0xF; + return v2(BasePos.X + ((Frame &~ 3) << 2), BasePos.Y); +} + +v2 spidertorso::GetBitmapPos(int Frame) const +{ + v2 BasePos = torso::GetBitmapPos(Frame); + Frame &= 0xF; + return v2(BasePos.X + ((Frame &~ 7) << 1), BasePos.Y); +} + +truth arm::HasSadistWeapon() const +{ + item* Wielded = GetWielded(); + return Wielded && Wielded->IsSadistWeapon(); +} + +truth corpse::AddStateDescription(festring& Name, truth Articled) const +{ + if(!Spoils()) + return false; + + truth Hasted = true, Slowed = true; + + for(int c = 0; c < GetDeceased()->GetBodyParts(); ++c) + { + bodypart* BodyPart = GetDeceased()->GetBodyPart(c); + + if(BodyPart) + { + if(!(BodyPart->ItemFlags & HASTE)) + Hasted = false; + + if(!(BodyPart->ItemFlags & SLOW)) + Slowed = false; + } + } + + if((Hasted | Slowed) && Articled) + Name << "a "; + + if(Hasted) + Name << "hasted "; + + if(Slowed) + Name << "slowed "; + + return true; +} + diff --git a/Main/Source/char.cpp b/Main/Source/char.cpp new file mode 100644 index 0000000..81ac965 --- /dev/null +++ b/Main/Source/char.cpp @@ -0,0 +1,10835 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through charset.cpp */ + +/* These statedata structs contain functions and values used for handling + * states. Remember to update them. All normal states must have + * PrintBeginMessage and PrintEndMessage functions and a Description string. + * BeginHandler, EndHandler, Handler (called each tick) and IsAllowed are + * optional, enter zero if the state doesn't need one. If the SECRET flag + * is set, Description is not shown in the panel without magical means. + * You can also set some source (SRC_*) and duration (DUR_*) flags, which + * control whether the state can be randomly activated in certain situations. + * These flags can be found in ivandef.h. RANDOMIZABLE sets all source + * & duration flags at once. */ + +struct statedata +{ + char* Description; + int Flags; + void (character::*PrintBeginMessage)() const; + void (character::*PrintEndMessage)() const; + void (character::*BeginHandler)(); + void (character::*EndHandler)(); + void (character::*Handler)(); + truth (character::*IsAllowed)() const; + void (character::*SituationDangerModifier)(double&) const; +}; + +statedata StateData[STATES] = +{ + { + "Polymorphed", + NO_FLAGS, + 0, + 0, + 0, + &character::EndPolymorph, + 0, + 0, + 0 + }, { + "Hasted", + RANDOMIZABLE&~(SRC_MUSHROOM|SRC_EVIL), + &character::PrintBeginHasteMessage, + &character::PrintEndHasteMessage, + 0, + 0, + 0, + 0, + 0 + }, { + "Slowed", + RANDOMIZABLE&~SRC_GOOD, + &character::PrintBeginSlowMessage, + &character::PrintEndSlowMessage, + 0, + 0, + 0, + 0, + 0 + }, { + "PolyControl", + RANDOMIZABLE&~(SRC_MUSHROOM|SRC_EVIL|SRC_GOOD), + &character::PrintBeginPolymorphControlMessage, + &character::PrintEndPolymorphControlMessage, + 0, + 0, + 0, + 0, + 0 + }, { + "LifeSaved", + SECRET, + &character::PrintBeginLifeSaveMessage, + &character::PrintEndLifeSaveMessage, + 0, + 0, + 0, + 0, + 0 + }, { + "Lycanthropy", + SECRET|SRC_FOUNTAIN|SRC_CONFUSE_READ|DUR_FLAGS, + &character::PrintBeginLycanthropyMessage, + &character::PrintEndLycanthropyMessage, + 0, + 0, + &character::LycanthropyHandler, + 0, + &character::LycanthropySituationDangerModifier + }, { + "Invisible", + RANDOMIZABLE&~(SRC_MUSHROOM|SRC_EVIL), + &character::PrintBeginInvisibilityMessage, + &character::PrintEndInvisibilityMessage, + &character::BeginInvisibility, &character::EndInvisibility, + 0, + 0, + 0 + }, { + "Infravision", + RANDOMIZABLE&~(SRC_MUSHROOM|SRC_EVIL), + &character::PrintBeginInfraVisionMessage, + &character::PrintEndInfraVisionMessage, + &character::BeginInfraVision, + &character::EndInfraVision, + 0, + 0, + 0 + }, { + "ESP", + RANDOMIZABLE&~SRC_EVIL, + &character::PrintBeginESPMessage, + &character::PrintEndESPMessage, + &character::BeginESP, + &character::EndESP, + 0, + 0, + 0 + }, { + "Poisoned", + DUR_TEMPORARY, + &character::PrintBeginPoisonedMessage, + &character::PrintEndPoisonedMessage, + 0, + 0, + &character::PoisonedHandler, + &character::CanBePoisoned, + &character::PoisonedSituationDangerModifier + }, { + "Teleporting", + SECRET|RANDOMIZABLE&~(SRC_MUSHROOM|SRC_GOOD), + &character::PrintBeginTeleportMessage, + &character::PrintEndTeleportMessage, + 0, + 0, + &character::TeleportHandler, + 0, + 0 + }, { + "Polymorphing", + SECRET|RANDOMIZABLE&~(SRC_MUSHROOM|SRC_GOOD), + &character::PrintBeginPolymorphMessage, + &character::PrintEndPolymorphMessage, + 0, + 0, + &character::PolymorphHandler, + 0, + &character::PolymorphingSituationDangerModifier + }, { + "TeleControl", + RANDOMIZABLE&~(SRC_MUSHROOM|SRC_EVIL), + &character::PrintBeginTeleportControlMessage, + &character::PrintEndTeleportControlMessage, + 0, + 0, + 0, + 0, + 0 + }, { + "Panicked", + NO_FLAGS, + &character::PrintBeginPanicMessage, + &character::PrintEndPanicMessage, + &character::BeginPanic, + &character::EndPanic, + 0, + &character::CanPanic, + &character::PanicSituationDangerModifier + }, { + "Confused", + SECRET|RANDOMIZABLE&~(DUR_PERMANENT|SRC_GOOD), + &character::PrintBeginConfuseMessage, + &character::PrintEndConfuseMessage, + 0, + 0, + 0, + &character::CanBeConfused, + &character::ConfusedSituationDangerModifier + }, { + "Parasitized", + SECRET|RANDOMIZABLE&~DUR_TEMPORARY, + &character::PrintBeginParasitizedMessage, + &character::PrintEndParasitizedMessage, + 0, + 0, + &character::ParasitizedHandler, + &character::CanBeParasitized, + &character::ParasitizedSituationDangerModifier + }, { + "Searching", + NO_FLAGS, + &character::PrintBeginSearchingMessage, + &character::PrintEndSearchingMessage, + 0, + 0, + &character::SearchingHandler, + 0, + 0 + }, { + "GasImmunity", + SECRET|RANDOMIZABLE&~(SRC_GOOD|SRC_EVIL), + &character::PrintBeginGasImmunityMessage, + &character::PrintEndGasImmunityMessage, + 0, + 0, + 0, + 0, + 0 + }, { + "Levitating", + RANDOMIZABLE&~SRC_EVIL, + &character::PrintBeginLevitationMessage, + &character::PrintEndLevitationMessage, + 0, + &character::EndLevitation, + 0, + 0, + 0 + }, { + "Leprosy", + SECRET|RANDOMIZABLE&~DUR_TEMPORARY, + &character::PrintBeginLeprosyMessage, + &character::PrintEndLeprosyMessage, + &character::BeginLeprosy, + &character::EndLeprosy, + &character::LeprosyHandler, + 0, + &character::LeprosySituationDangerModifier + }, { + "Hiccups", + SRC_FOUNTAIN|SRC_CONFUSE_READ|DUR_FLAGS, + &character::PrintBeginHiccupsMessage, + &character::PrintEndHiccupsMessage, + 0, + 0, + &character::HiccupsHandler, + 0, + &character::HiccupsSituationDangerModifier + }, { + "Vampirism", + DUR_FLAGS, //perhaps no fountain, no secret and no confuse read either: SECRET|SRC_FOUNTAIN|SRC_CONFUSE_READ| + &character::PrintBeginVampirismMessage, + &character::PrintEndVampirismMessage, + 0, + 0, + &character::VampirismHandler, + 0, + &character::VampirismSituationDangerModifier + }, { + "Detecting", + SECRET|RANDOMIZABLE&~(SRC_MUSHROOM|SRC_EVIL), + &character::PrintBeginDetectMessage, + &character::PrintEndDetectMessage, + 0, + 0, + &character::DetectHandler, + 0, + 0 + } +}; + +characterprototype::characterprototype(const characterprototype* Base, + characterspawner Spawner, + charactercloner Cloner, + cchar* ClassID) +: Base(Base), Spawner(Spawner), Cloner(Cloner), ClassID(ClassID) +{ Index = protocontainer::Add(this); } +std::list::iterator character::GetTeamIterator() +{ return TeamIterator; } +void character::SetTeamIterator(std::list::iterator What) +{ TeamIterator = What; } +void character::CreateInitialEquipment(int SpecialFlags) +{ AddToInventory(DataBase->Inventory, SpecialFlags); } +void character::EditAP(long What) +{ AP = Limit(AP + What, -12000, 1200); } +int character::GetRandomStepperBodyPart() const { return TORSO_INDEX; } +void character::GainIntrinsic(long What) +{ BeginTemporaryState(What, PERMANENT); } +truth character::IsUsingArms() const { return GetAttackStyle() & USE_ARMS; } +truth character::IsUsingLegs() const { return GetAttackStyle() & USE_LEGS; } +truth character::IsUsingHead() const { return GetAttackStyle() & USE_HEAD; } +void character::CalculateAllowedWeaponSkillCategories() +{ AllowedWeaponSkillCategories = MARTIAL_SKILL_CATEGORIES; } +festring character::GetBeVerb() const +{ return IsPlayer() ? CONST_S("are") : CONST_S("is"); } +void character::SetEndurance(int What) +{ BaseExperience[ENDURANCE] = What * EXP_MULTIPLIER; } +void character::SetPerception(int What) +{ BaseExperience[PERCEPTION] = What * EXP_MULTIPLIER; } +void character::SetIntelligence(int What) +{ BaseExperience[INTELLIGENCE] = What * EXP_MULTIPLIER; } +void character::SetWisdom(int What) +{ BaseExperience[WISDOM] = What * EXP_MULTIPLIER; } +void character::SetWillPower(int What) +{ BaseExperience[WILL_POWER] = What * EXP_MULTIPLIER; } +void character::SetCharisma(int What) +{ BaseExperience[CHARISMA] = What * EXP_MULTIPLIER; } +void character::SetMana(int What) +{ BaseExperience[MANA] = What * EXP_MULTIPLIER; } +truth character::IsOnGround() const +{ return MotherEntity && MotherEntity->IsOnGround(); } +truth character::LeftOversAreUnique() const +{ return GetArticleMode() || AssignedName.GetSize(); } +truth character::HomeDataIsValid() const +{ return (HomeData && HomeData->Level == GetLSquareUnder()->GetLevelIndex() + && HomeData->Dungeon == GetLSquareUnder()->GetDungeonIndex()); } +void character::SetHomePos(v2 Pos) { HomeData->Pos = Pos; } +cchar* character::FirstPersonUnarmedHitVerb() const { return "hit"; } +cchar* character::FirstPersonCriticalUnarmedHitVerb() const +{ return "critically hit"; } +cchar* character::ThirdPersonUnarmedHitVerb() const { return "hits"; } +cchar* character::ThirdPersonCriticalUnarmedHitVerb() const +{ return "critically hits"; } +cchar* character::FirstPersonKickVerb() const { return "kick"; } +cchar* character::FirstPersonCriticalKickVerb() const +{ return "critically kick"; } +cchar* character::ThirdPersonKickVerb() const { return "kicks"; } +cchar* character::ThirdPersonCriticalKickVerb() const +{ return "critically kicks"; } +cchar* character::FirstPersonBiteVerb() const { return "bite"; } +cchar* character::FirstPersonCriticalBiteVerb() const +{ return "critically bite"; } +cchar* character::ThirdPersonBiteVerb() const { return "bites"; } +cchar* character::ThirdPersonCriticalBiteVerb() const +{ return "critically bites"; } +cchar* character::UnarmedHitNoun() const { return "attack"; } +cchar* character::KickNoun() const { return "kick"; } +cchar* character::BiteNoun() const { return "attack"; } +cchar* character::GetEquipmentName(int) const { return ""; } +const std::list& character::GetOriginalBodyPartID(int I) const +{ return OriginalBodyPartID[I]; } +square* character::GetNeighbourSquare(int I) const +{ return GetSquareUnder()->GetNeighbourSquare(I); } +lsquare* character::GetNeighbourLSquare(int I) const +{ return static_cast(GetSquareUnder())->GetNeighbourLSquare(I); } +wsquare* character::GetNeighbourWSquare(int I) const +{ return static_cast(GetSquareUnder())->GetNeighbourWSquare(I); } +god* character::GetMasterGod() const { return game::GetGod(GetConfig()); } +col16 character::GetBodyPartColorA(int, truth) const +{ return GetSkinColor(); } +col16 character::GetBodyPartColorB(int, truth) const +{ return GetTorsoMainColor(); } +col16 character::GetBodyPartColorC(int, truth) const +{ return GetBeltColor(); } // sorry... +col16 character::GetBodyPartColorD(int, truth) const +{ return GetTorsoSpecialColor(); } +int character::GetRandomApplyBodyPart() const { return TORSO_INDEX; } +truth character::MustBeRemovedFromBone() const +{ return IsUnique() && !CanBeGenerated(); } +truth character::IsPet() const { return GetTeam()->GetID() == PLAYER_TEAM; } +character* character::GetLeader() const { return GetTeam()->GetLeader(); } +int character::GetMoveType() const +{ return (!StateIsActivated(LEVITATION) + ? DataBase->MoveType + : DataBase->MoveType | FLY); } +festring character::GetZombieDescription() const +{ return " of " + GetName(INDEFINITE); } +truth character::BodyPartCanBeSevered(int I) const { return I; } +truth character::HasBeenSeen() const +{ return DataBase->Flags & HAS_BEEN_SEEN; } +truth character::IsTemporary() const +{ return GetTorso()->GetLifeExpectancy(); } +cchar* character::GetNormalDeathMessage() const { return "killed @k"; } + +int characterdatabase::* ExpPtr[ATTRIBUTES] = +{ + &characterdatabase::DefaultEndurance, + &characterdatabase::DefaultPerception, + &characterdatabase::DefaultIntelligence, + &characterdatabase::DefaultWisdom, + &characterdatabase::DefaultWillPower, + &characterdatabase::DefaultCharisma, + &characterdatabase::DefaultMana, + &characterdatabase::DefaultArmStrength, + &characterdatabase::DefaultLegStrength, + &characterdatabase::DefaultDexterity, + &characterdatabase::DefaultAgility +}; + +contentscript characterdatabase::* EquipmentDataPtr[EQUIPMENT_DATAS] = +{ + &characterdatabase::Helmet, + &characterdatabase::Amulet, + &characterdatabase::Cloak, + &characterdatabase::BodyArmor, + &characterdatabase::Belt, + &characterdatabase::RightWielded, + &characterdatabase::LeftWielded, + &characterdatabase::RightRing, + &characterdatabase::LeftRing, + &characterdatabase::RightGauntlet, + &characterdatabase::LeftGauntlet, + &characterdatabase::RightBoot, + &characterdatabase::LeftBoot +}; + +character::character(ccharacter& Char) +: entity(Char), id(Char), NP(Char.NP), AP(Char.AP), + TemporaryState(Char.TemporaryState&~POLYMORPHED), + Team(Char.Team), GoingTo(ERROR_V2), Money(0), + AssignedName(Char.AssignedName), Action(0), + DataBase(Char.DataBase), MotherEntity(0), + PolymorphBackup(0), EquipmentState(0), SquareUnder(0), + AllowedWeaponSkillCategories(Char.AllowedWeaponSkillCategories), + BodyParts(Char.BodyParts), + RegenerationCounter(Char.RegenerationCounter), + SquaresUnder(Char.SquaresUnder), LastAcidMsgMin(0), + Stamina(Char.Stamina), MaxStamina(Char.MaxStamina), + BlocksSinceLastTurn(0), GenerationDanger(Char.GenerationDanger), + CommandFlags(Char.CommandFlags), WarnFlags(0), + ScienceTalks(Char.ScienceTalks), TrapData(0), CounterToMindWormHatch(0) +{ + Flags &= ~C_PLAYER; + Flags |= C_INITIALIZING|C_IN_NO_MSG_MODE; + Stack = new stack(0, this, HIDDEN); + + int c; + + for(c = 0; c < STATES; ++c) + TemporaryStateCounter[c] = Char.TemporaryStateCounter[c]; + + if(Team) + TeamIterator = Team->Add(this); + + for(c = 0; c < BASE_ATTRIBUTES; ++c) + BaseExperience[c] = Char.BaseExperience[c]; + + BodyPartSlot = new bodypartslot[BodyParts]; + OriginalBodyPartID = new std::list[BodyParts]; + CWeaponSkill = new cweaponskill[AllowedWeaponSkillCategories]; + SquareUnder = new square*[SquaresUnder]; + + if(SquaresUnder == 1) + *SquareUnder = 0; + else + memset(SquareUnder, 0, SquaresUnder * sizeof(square*)); + + for(c = 0; c < BodyParts; ++c) + { + BodyPartSlot[c].SetMaster(this); + bodypart* CharBodyPart = Char.GetBodyPart(c); + OriginalBodyPartID[c] = Char.OriginalBodyPartID[c]; + + if(CharBodyPart) + { + bodypart* BodyPart = static_cast(CharBodyPart->Duplicate()); + SetBodyPart(c, BodyPart); + BodyPart->CalculateEmitation(); + } + } + + for(c = 0; c < AllowedWeaponSkillCategories; ++c) + CWeaponSkill[c] = Char.CWeaponSkill[c]; + + HomeData = Char.HomeData ? new homedata(*Char.HomeData) : 0; + ID = game::CreateNewCharacterID(this); +} + +character::character() +: entity(HAS_BE), NP(50000), AP(0), TemporaryState(0), Team(0), + GoingTo(ERROR_V2), Money(0), Action(0), MotherEntity(0), + PolymorphBackup(0), EquipmentState(0), SquareUnder(0), + RegenerationCounter(0), HomeData(0), LastAcidMsgMin(0), + BlocksSinceLastTurn(0), GenerationDanger(DEFAULT_GENERATION_DANGER), + WarnFlags(0), ScienceTalks(0), TrapData(0), CounterToMindWormHatch(0) +{ + Stack = new stack(0, this, HIDDEN); +} + +character::~character() +{ + if(Action) + delete Action; + + if(Team) + Team->Remove(GetTeamIterator()); + + delete Stack; + int c; + + for(c = 0; c < BodyParts; ++c) + delete GetBodyPart(c); + + delete [] BodyPartSlot; + delete [] OriginalBodyPartID; + delete PolymorphBackup; + delete [] SquareUnder; + delete [] CWeaponSkill; + delete HomeData; + + for(trapdata* T = TrapData; T;) + { + trapdata* ToDel = T; + T = T->Next; + delete ToDel; + } + + game::RemoveCharacterID(ID); +} + +void character::Hunger() +{ + switch(GetBurdenState()) + { + case OVER_LOADED: + case STRESSED: + EditNP(-8); + EditExperience(LEG_STRENGTH, 150, 1 << 2); + EditExperience(AGILITY, -50, 1 << 2); + break; + case BURDENED: + EditNP(-2); + EditExperience(LEG_STRENGTH, 75, 1 << 1); + EditExperience(AGILITY, -25, 1 << 1); + break; + case UNBURDENED: + EditNP(-1); + break; + } + + switch(GetHungerState()) + { + case STARVING: + EditExperience(ARM_STRENGTH, -75, 1 << 3); + EditExperience(LEG_STRENGTH, -75, 1 << 3); + break; + case VERY_HUNGRY: + EditExperience(ARM_STRENGTH, -50, 1 << 2); + EditExperience(LEG_STRENGTH, -50, 1 << 2); + break; + case HUNGRY: + EditExperience(ARM_STRENGTH, -25, 1 << 1); + EditExperience(LEG_STRENGTH, -25, 1 << 1); + break; + case SATIATED: + EditExperience(AGILITY, -25, 1 << 1); + break; + case BLOATED: + EditExperience(AGILITY, -50, 1 << 2); + break; + case OVER_FED: + EditExperience(AGILITY, -75, 1 << 3); + break; + } + + CheckStarvationDeath(CONST_S("starved to death")); +} + +int character::TakeHit(character* Enemy, item* Weapon, + bodypart* EnemyBodyPart, v2 HitPos, + double Damage, double ToHitValue, + int Success, int Type, int GivenDir, + truth Critical, truth ForceHit) +{ + int Dir = Type == BITE_ATTACK ? YOURSELF : GivenDir; + double DodgeValue = GetDodgeValue(); + + if(!Enemy->IsPlayer() && GetAttackWisdomLimit() != NO_LIMIT) + Enemy->EditExperience(WISDOM, 75, 1 << 13); + + if(!Enemy->CanBeSeenBy(this)) + ToHitValue *= 2; + + if(!CanBeSeenBy(Enemy)) + DodgeValue *= 2; + + if(Enemy->StateIsActivated(CONFUSED)) + ToHitValue *= 0.75; + + switch(Enemy->GetTirednessState()) + { + case FAINTING: + ToHitValue *= 0.50; + case EXHAUSTED: + ToHitValue *= 0.75; + } + + switch(GetTirednessState()) + { + case FAINTING: + DodgeValue *= 0.50; + case EXHAUSTED: + DodgeValue *= 0.75; + } + + if(!ForceHit) + { + if(!IsRetreating()) + SetGoingTo(Enemy->GetPos()); + else + SetGoingTo(GetPos() - ((Enemy->GetPos() - GetPos()) << 4)); + + if(!Enemy->IsRetreating()) + Enemy->SetGoingTo(GetPos()); + else + Enemy->SetGoingTo(Enemy->GetPos() + - ((GetPos() - Enemy->GetPos()) << 4)); + } + + /* Effectively, the average chance to hit is 100% / (DV/THV + 1). */ + + if(RAND() % int(100 + ToHitValue / DodgeValue * (100 + Success)) < 100 + && !Critical && !ForceHit) + { + Enemy->AddMissMessage(this); + EditExperience(AGILITY, 150, 1 << 7); + EditExperience(PERCEPTION, 75, 1 << 7); + + if(Enemy->CanBeSeenByPlayer()) + DeActivateVoluntaryAction(CONST_S("The attack of ") + + Enemy->GetName(DEFINITE) + + CONST_S(" interrupts you.")); + else + DeActivateVoluntaryAction(CONST_S("The attack interrupts you.")); + + return HAS_DODGED; + } + + int TrueDamage = int(Damage * (100 + Success) / 100) + + (RAND() % 3 ? 1 : 0); + + if(Critical) + { + TrueDamage += TrueDamage >> 1; + ++TrueDamage; + } + + int BodyPart = ChooseBodyPartToReceiveHit(ToHitValue, DodgeValue); + + if(Critical) + { + switch(Type) + { + case UNARMED_ATTACK: + Enemy->AddPrimitiveHitMessage(this, Enemy->FirstPersonCriticalUnarmedHitVerb(), Enemy->ThirdPersonCriticalUnarmedHitVerb(), BodyPart); + break; + case WEAPON_ATTACK: + Enemy->AddWeaponHitMessage(this, Weapon, BodyPart, true); + break; + case KICK_ATTACK: + Enemy->AddPrimitiveHitMessage(this, Enemy->FirstPersonCriticalKickVerb(), Enemy->ThirdPersonCriticalKickVerb(), BodyPart); + break; + case BITE_ATTACK: + Enemy->AddPrimitiveHitMessage(this, Enemy->FirstPersonCriticalBiteVerb(), Enemy->ThirdPersonCriticalBiteVerb(), BodyPart); + break; + } + } + else + { + switch(Type) + { + case UNARMED_ATTACK: + Enemy->AddPrimitiveHitMessage(this, + Enemy->FirstPersonUnarmedHitVerb(), + Enemy->ThirdPersonUnarmedHitVerb(), + BodyPart); + break; + case WEAPON_ATTACK: + Enemy->AddWeaponHitMessage(this, Weapon, BodyPart, false); + break; + case KICK_ATTACK: + Enemy->AddPrimitiveHitMessage(this, + Enemy->FirstPersonKickVerb(), + Enemy->ThirdPersonKickVerb(), + BodyPart); + break; + case BITE_ATTACK: + Enemy->AddPrimitiveHitMessage(this, + Enemy->FirstPersonBiteVerb(), + Enemy->ThirdPersonBiteVerb(), + BodyPart); + break; + } + } + + if(!Critical && TrueDamage && Enemy->AttackIsBlockable(Type)) + { + TrueDamage = CheckForBlock(Enemy, Weapon, ToHitValue, + TrueDamage, Success, Type); + + if(!TrueDamage || (Weapon && !Weapon->Exists())) + { + if(Enemy->CanBeSeenByPlayer()) + DeActivateVoluntaryAction(CONST_S("The attack of ") + + Enemy->GetName(DEFINITE) + + CONST_S(" interrupts you.")); + else + DeActivateVoluntaryAction(CONST_S("The attack interrupts you.")); + + return HAS_BLOCKED; + } + } + + int WeaponSkillHits = CalculateWeaponSkillHits(Enemy); + int DoneDamage = ReceiveBodyPartDamage(Enemy, TrueDamage, + PHYSICAL_DAMAGE, BodyPart, + Dir, false, Critical, true, + Type == BITE_ATTACK + && Enemy->BiteCapturesBodyPart()); + truth Succeeded = (GetBodyPart(BodyPart) + && HitEffect(Enemy, Weapon, HitPos, Type, + BodyPart, Dir, !DoneDamage)) + || DoneDamage; + + if(Succeeded) + Enemy->WeaponSkillHit(Weapon, Type, WeaponSkillHits); + + if(Weapon) + { + if(Weapon->Exists() && DoneDamage < TrueDamage) + Weapon->ReceiveDamage(Enemy, TrueDamage - DoneDamage, PHYSICAL_DAMAGE); + + if(Weapon->Exists() && DoneDamage + && SpillsBlood() && GetBodyPart(BodyPart) + && (GetBodyPart(BodyPart)->IsAlive() + || GetBodyPart(BodyPart)->GetMainMaterial()->IsLiquid())) + Weapon->SpillFluid(0, CreateBlood(15 + RAND() % 15)); + } + + if(Enemy->AttackIsBlockable(Type)) + SpecialBodyDefenceEffect(Enemy, EnemyBodyPart, Type); + + if(!Succeeded) + { + if(Enemy->CanBeSeenByPlayer()) + DeActivateVoluntaryAction(CONST_S("The attack of ") + + Enemy->GetName(DEFINITE) + + CONST_S(" interrupts you.")); + else + DeActivateVoluntaryAction(CONST_S("The attack interrupts you.")); + + return DID_NO_DAMAGE; + } + + if(CheckDeath(GetNormalDeathMessage(), Enemy, + Enemy->IsPlayer() ? FORCE_MSG : 0)) + return HAS_DIED; + + if(Enemy->CanBeSeenByPlayer()) + DeActivateVoluntaryAction(CONST_S("The attack of ") + + Enemy->GetName(DEFINITE) + + CONST_S(" interrupts you.")); + else + DeActivateVoluntaryAction(CONST_S("The attack interrupts you.")); + + return HAS_HIT; +} + +struct svpriorityelement +{ + svpriorityelement(int BodyPart, int StrengthValue) + : BodyPart(BodyPart), StrengthValue(StrengthValue) { } + bool operator<(const svpriorityelement& AnotherPair) const + { return StrengthValue > AnotherPair.StrengthValue; } + int BodyPart; + int StrengthValue; +}; + +int character::ChooseBodyPartToReceiveHit(double ToHitValue, + double DodgeValue) +{ + if(BodyParts == 1) + return 0; + + std::priority_queue SVQueue; + + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart + && (BodyPart->GetHP() != 1 + || BodyPart->CanBeSevered(PHYSICAL_DAMAGE))) + SVQueue.push(svpriorityelement(c, ModifyBodyPartHitPreference(c, BodyPart->GetStrengthValue() + BodyPart->GetHP()))); + } + + while(SVQueue.size()) + { + svpriorityelement E = SVQueue.top(); + int ToHitPercentage = int(GLOBAL_WEAK_BODYPART_HIT_MODIFIER + * ToHitValue + * GetBodyPart(E.BodyPart)->GetBodyPartVolume() + / (DodgeValue * GetBodyVolume())); + ToHitPercentage = ModifyBodyPartToHitChance(E.BodyPart, ToHitPercentage); + + if(ToHitPercentage < 1) + ToHitPercentage = 1; + else if(ToHitPercentage > 95) + ToHitPercentage = 95; + + if(ToHitPercentage > RAND() % 100) + return E.BodyPart; + + SVQueue.pop(); + } + + return 0; +} + +void character::Be() +{ + if(game::ForceJumpToPlayerBe()) + { + if(!IsPlayer()) + return; + else + game::SetForceJumpToPlayerBe(false); + } + else + { + truth ForceBe = HP != MaxHP || AllowSpoil(); + + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart && (ForceBe || BodyPart->NeedsBe())) + BodyPart->Be(); + } + + HandleStates(); + + if(!IsEnabled()) + return; + + if(GetTeam() == PLAYER->GetTeam()) + { + for(int c = 0; c < AllowedWeaponSkillCategories; ++c) + if(CWeaponSkill[c].Tick() && IsPlayer()) + CWeaponSkill[c].AddLevelDownMessage(c); + + SWeaponSkillTick(); + } + + if(IsPlayer()) + { + if(GetHungerState() == STARVING && !(RAND() % 50)) + LoseConsciousness(250 + RAND_N(250), true); + + if(!Action || Action->AllowFoodConsumption()) + Hunger(); + } + + if(Stamina != MaxStamina) + RegenerateStamina(); + + if(HP != MaxHP) + Regenerate(); + + if(Action && AP >= 1000) + ActionAutoTermination(); + + if(Action && AP >= 1000) + { + Action->Handle(); + + if(!IsEnabled()) + return; + } + else + EditAP(GetStateAPGain(100)); + } + + if(AP >= 1000) + { + SpecialTurnHandler(); + BlocksSinceLastTurn = 0; + + if(IsPlayer()) + { + static int Timer = 0; + + if(ivanconfig::GetAutoSaveInterval() && !GetAction() + && ++Timer >= ivanconfig::GetAutoSaveInterval()) + { + game::Save(game::GetAutoSaveFileName()); + Timer = 0; + } + + game::CalculateNextDanger(); + + if(!StateIsActivated(POLYMORPHED)) + game::UpdatePlayerAttributeAverage(); + + if(!game::IsInWilderness()) + Search(GetAttribute(PERCEPTION)); + + if(!Action) + GetPlayerCommand(); + else + { + if(Action->ShowEnvironment()) + { + static int Counter = 0; + + if(++Counter == 10) + { + game::DrawEverything(); + Counter = 0; + } + } + + msgsystem::ThyMessagesAreNowOld(); + + if(Action->IsVoluntary() && READ_KEY()) + Action->Terminate(false); + } + } + else + { + if(!Action && !game::IsInWilderness()) + GetAICommand(); + } + } +} + +void character::Move(v2 MoveTo, truth TeleportMove, truth Run) +{ + if(!IsEnabled()) + return; + + /* Test whether the player is stuck to something */ + + if(!TeleportMove && !TryToUnStickTraps(MoveTo - GetPos())) + return; + + if(Run && !IsPlayer() && TorsoIsAlive() + && (Stamina <= 10000 / Max(GetAttribute(LEG_STRENGTH), 1) + || (!StateIsActivated(PANIC) && Stamina < MaxStamina >> 2))) + Run = false; + + RemoveTraps(); + + if(GetBurdenState() != OVER_LOADED || TeleportMove) + { + lsquare* OldSquareUnder[MAX_SQUARES_UNDER]; + + if(!game::IsInWilderness()) + for(int c = 0; c < GetSquaresUnder(); ++c) + OldSquareUnder[c] = GetLSquareUnder(c); + + Remove(); + PutTo(MoveTo); + + if(!TeleportMove) + { + /* Multitiled creatures should behave differently, maybe? */ + + if(Run) + { + int ED = GetSquareUnder()->GetEntryDifficulty(); + EditAP(-GetMoveAPRequirement(ED) >> 1); + EditNP(-24 * ED); + EditExperience(AGILITY, 125, ED << 7); + int Base = 10000; + + if(IsPlayer()) + switch(GetHungerState()) + { + case SATIATED: + Base = 11000; + break; + case BLOATED: + Base = 12500; + break; + case OVER_FED: + Base = 15000; + break; + } + + EditStamina(-Base / Max(GetAttribute(LEG_STRENGTH), 1), true); + } + else + { + int ED = GetSquareUnder()->GetEntryDifficulty(); + EditAP(-GetMoveAPRequirement(ED)); + EditNP(-12 * ED); + EditExperience(AGILITY, 75, ED << 7); + } + } + + if(IsPlayer()) + ShowNewPosInfo(); + + if(!game::IsInWilderness()) + SignalStepFrom(OldSquareUnder); + } + else + { + if(IsPlayer()) + { + cchar* CrawlVerb = StateIsActivated(LEVITATION) ? "float" : "crawl"; + ADD_MESSAGE("You try very hard to %s forward. But your load is too heavy.", CrawlVerb); + } + + EditAP(-1000); + } +} + +void character::GetAICommand() +{ + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + if(CheckForEnemies(true, true, true)) + return; + + if(CheckForUsefulItemsOnGround()) + return; + + if(CheckForDoors()) + return; + + if(CheckSadism()) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +truth character::MoveTowardsTarget(truth Run) +{ + v2 Pos = GetPos(); + v2 TPos; + + if(!Route.empty()) + { + TPos = Route.back(); + Route.pop_back(); + } + else + TPos = GoingTo; + + v2 MoveTo[3]; + + if(TPos.X < Pos.X) + { + if(TPos.Y < Pos.Y) + { + MoveTo[0] = v2(-1, -1); + MoveTo[1] = v2(-1, 0); + MoveTo[2] = v2( 0, -1); + } + + if(TPos.Y == Pos.Y) + { + MoveTo[0] = v2(-1, 0); + MoveTo[1] = v2(-1, -1); + MoveTo[2] = v2(-1, 1); + } + + if(TPos.Y > Pos.Y) + { + MoveTo[0] = v2(-1, 1); + MoveTo[1] = v2(-1, 0); + MoveTo[2] = v2( 0, 1); + } + } + + if(TPos.X == Pos.X) + { + if(TPos.Y < Pos.Y) + { + MoveTo[0] = v2( 0, -1); + MoveTo[1] = v2(-1, -1); + MoveTo[2] = v2( 1, -1); + } + + if(TPos.Y == Pos.Y) + { + TerminateGoingTo(); + return false; + } + + if(TPos.Y > Pos.Y) + { + MoveTo[0] = v2( 0, 1); + MoveTo[1] = v2(-1, 1); + MoveTo[2] = v2( 1, 1); + } + } + + if(TPos.X > Pos.X) + { + if(TPos.Y < Pos.Y) + { + MoveTo[0] = v2(1, -1); + MoveTo[1] = v2(1, 0); + MoveTo[2] = v2(0, -1); + } + + if(TPos.Y == Pos.Y) + { + MoveTo[0] = v2(1, 0); + MoveTo[1] = v2(1, -1); + MoveTo[2] = v2(1, 1); + } + + if(TPos.Y > Pos.Y) + { + MoveTo[0] = v2(1, 1); + MoveTo[1] = v2(1, 0); + MoveTo[2] = v2(0, 1); + } + } + + v2 ModifiedMoveTo = ApplyStateModification(MoveTo[0]); + + if(TryMove(ModifiedMoveTo, true, Run)) return true; + + int L = (Pos - TPos).GetManhattanLength(); + + if(RAND() & 1) + Swap(MoveTo[1], MoveTo[2]); + + if(Pos.IsAdjacent(TPos)) + { + TerminateGoingTo(); + return false; + } + + if((Pos + MoveTo[1] - TPos).GetManhattanLength() <= L + && TryMove(ApplyStateModification(MoveTo[1]), true, Run)) + return true; + + if((Pos + MoveTo[2] - TPos).GetManhattanLength() <= L + && TryMove(ApplyStateModification(MoveTo[2]), true, Run)) + return true; + + Illegal.insert(Pos + ModifiedMoveTo); + + if(CreateRoute()) + return true; + + return false; +} + +int character::CalculateNewSquaresUnder(lsquare** NewSquare, v2 Pos) const +{ + if(GetLevel()->IsValidPos(Pos)) + { + *NewSquare = GetNearLSquare(Pos); + return 1; + } + else + return 0; +} + +truth character::TryMove(v2 MoveVector, truth Important, truth Run) +{ + lsquare* MoveToSquare[MAX_SQUARES_UNDER]; + character* Pet[MAX_SQUARES_UNDER]; + character* Neutral[MAX_SQUARES_UNDER]; + character* Hostile[MAX_SQUARES_UNDER]; + v2 PetPos[MAX_SQUARES_UNDER]; + v2 NeutralPos[MAX_SQUARES_UNDER]; + v2 HostilePos[MAX_SQUARES_UNDER]; + v2 MoveTo = GetPos() + MoveVector; + int Direction = game::GetDirectionForVector(MoveVector); + + if(Direction == DIR_ERROR) + ABORT("Direction fault."); + + if(!game::IsInWilderness()) + { + int Squares = CalculateNewSquaresUnder(MoveToSquare, MoveTo); + + if(Squares) + { + int Pets = 0; + int Neutrals = 0; + int Hostiles = 0; + + for(int c = 0; c < Squares; ++c) + { + character* Char = MoveToSquare[c]->GetCharacter(); + + if(Char && Char != this) + { + v2 Pos = MoveToSquare[c]->GetPos(); + + if(IsAlly(Char)) + { + Pet[Pets] = Char; + PetPos[Pets++] = Pos; + } + else if(Char->GetRelation(this) != HOSTILE) + { + Neutral[Neutrals] = Char; + NeutralPos[Neutrals++] = Pos; + } + else + { + Hostile[Hostiles] = Char; + HostilePos[Hostiles++] = Pos; + } + } + } + + if(Hostiles == 1) + return Hit(Hostile[0], HostilePos[0], Direction); + else if(Hostiles) + { + int Index = RAND() % Hostiles; + return Hit(Hostile[Index], HostilePos[Index], Direction); + } + + if(Neutrals == 1) + { + if(!IsPlayer() && !Pets && Important && CanMoveOn(MoveToSquare[0])) + return HandleCharacterBlockingTheWay(Neutral[0], NeutralPos[0], Direction); + else + return IsPlayer() && Hit(Neutral[0], NeutralPos[0], Direction); + } + else if(Neutrals) + if(IsPlayer()) + { + int Index = RAND() % Neutrals; + return Hit(Neutral[Index], NeutralPos[Index], Direction); + } + else + return false; + + if(!IsPlayer()) + for(int c = 0; c < Squares; ++c) + if(MoveToSquare[c]->IsScary(this)) + return false; + + if(Pets == 1) + { + if(IsPlayer() && !ivanconfig::GetBeNice() + && Pet[0]->IsMasochist() && HasSadistAttackMode() + && game::TruthQuestion("Do you want to punish " + Pet[0]->GetObjectPronoun() + "? [y/N]")) + return Hit(Pet[0], PetPos[0], Direction, SADIST_HIT); + else + return (Important + && (CanMoveOn(MoveToSquare[0]) + || (IsPlayer() + && game::GoThroughWallsCheatIsActive())) + && Displace(Pet[0])); + } + else if(Pets) + return false; + + if(CanMove() + && CanMoveOn(MoveToSquare[0]) + || (game::GoThroughWallsCheatIsActive() && IsPlayer())) + { + Move(MoveTo, false, Run); + + if(IsEnabled() && GetPos() == GoingTo) + TerminateGoingTo(); + + return true; + } + else + for(int c = 0; c < Squares; ++c) + { + olterrain* Terrain = MoveToSquare[c]->GetOLTerrain(); + + if(Terrain && Terrain->CanBeOpened()) + { + if(CanOpen()) + { + if(Terrain->IsLocked()) + { + if(IsPlayer()) + { + ADD_MESSAGE("The %s is locked.", Terrain->GetNameSingular().CStr()); /* not sure if this is better than "the door is locked", but I guess it _might_ be slighltly better */ + return false; + } + else if(Important && CheckKick()) + { + room* Room = MoveToSquare[c]->GetRoom(); + + if(!Room || Room->AllowKick(this, MoveToSquare[c])) + { + int HP = Terrain->GetHP(); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s kicks %s.", CHAR_NAME(DEFINITE), Terrain->CHAR_NAME(DEFINITE)); + + Kick(MoveToSquare[c], Direction); + olterrain* NewTerrain = MoveToSquare[c]->GetOLTerrain(); + + if(NewTerrain == Terrain && Terrain->GetHP() == HP) // BUG! + { + Illegal.insert(MoveTo); + CreateRoute(); + } + + return true; + } + } + } + else + { + if(!IsPlayer() || game::TruthQuestion(CONST_S("Do you want to open ") + Terrain->GetName(DEFINITE) + "? [y/N]", false, game::GetMoveCommandKeyBetweenPoints(PLAYER->GetPos(), MoveToSquare[0]->GetPos()))) + return MoveToSquare[c]->Open(this); + else + return false; + } + } + else + { + if(IsPlayer()) + { + ADD_MESSAGE("This monster type cannot open doors."); + return false; + } + else if(Important) + { + Illegal.insert(MoveTo); + return CreateRoute(); + } + } + } + } + + return false; + } + else + { + if(IsPlayer() && !IsStuck() && GetLevel()->IsOnGround() && game::TruthQuestion(CONST_S("Do you want to leave ") + game::GetCurrentDungeon()->GetLevelDescription(game::GetCurrentLevelIndex()) + "? [y/N]")) + { + if(HasPetrussNut() && !HasGoldenEagleShirt()) + { + game::TextScreen(CONST_S("An undead and sinister voice greets you as you leave the city behind:\n\n\"MoRtAl! ThOu HaSt SlAuGtHeReD pEtRuS aNd PlEaSeD mE!\nfRoM tHiS dAy On, ThOu ArT tHe DeArEsT sErVaNt Of AlL eViL!\"\n\nYou are victorious!")); + game::GetCurrentArea()->SendNewDrawRequest(); + game::DrawEverything(); + ShowAdventureInfo(); + festring Msg = CONST_S("killed Petrus and became the Avatar of Chaos"); + PLAYER->AddScoreEntry(Msg, 3, false); + game::End(Msg); + return true; + } + + if(game::TryTravel(WORLD_MAP, WORLD_MAP, game::GetCurrentDungeonIndex())) + return true; + } + + return false; + } +} + else + { + /** No multitile support */ + + if(CanMove() + && GetArea()->IsValidPos(MoveTo) + && (CanMoveOn(GetNearWSquare(MoveTo)) + || game::GoThroughWallsCheatIsActive())) + { + if(!game::GoThroughWallsCheatIsActive()) + { + charactervector& V = game::GetWorldMap()->GetPlayerGroup(); + truth Discard = false; + + for(uint c = 0; c < V.size(); ++c) + if(!V[c]->CanMoveOn(GetNearWSquare(MoveTo))) + { + if(!Discard) + { + ADD_MESSAGE("One or more of your team members cannot cross this terrain."); + + if(!game::TruthQuestion("Discard them? [y/N]")) + return false; + + Discard = true; + } + + if(Discard) + delete V[c]; + + V.erase(V.begin() + c--); + } + } + + Move(MoveTo, false); + return true; + } + else + return false; + } +} + +void character::CreateCorpse(lsquare* Square) +{ + if(!BodyPartsDisappearWhenSevered() && !game::AllBodyPartsVanish()) + { + corpse* Corpse = corpse::Spawn(0, NO_MATERIALS); + Corpse->SetDeceased(this); + Square->AddItem(Corpse); + Disable(); + } + else + SendToHell(); +} + +void character::Die(ccharacter* Killer, cfestring& Msg, ulong DeathFlags) +{ + /* Note: This function musn't delete any objects, since one of these may be + the one currently processed by pool::Be()! */ + + if(!IsEnabled()) + return; + + RemoveTraps(); + + if(IsPlayer()) + { + ADD_MESSAGE("You die."); + + if(game::WizardModeIsActive()) + { + game::DrawEverything(); + + if(!game::TruthQuestion(CONST_S("Do you want to do this, cheater? [y/n]"), REQUIRES_ANSWER)) + { + RestoreBodyParts(); + ResetSpoiling(); + RestoreHP(); + RestoreStamina(); + ResetStates(); + SetNP(SATIATED_LEVEL); + SendNewDrawRequest(); + return; + } + } + } + else if(CanBeSeenByPlayer() && !(DeathFlags & DISALLOW_MSG)) + ProcessAndAddMessage(GetDeathMessage()); + else if(DeathFlags & FORCE_MSG) + ADD_MESSAGE("You sense the death of something."); + + if(!(DeathFlags & FORBID_REINCARNATION)) + { + if(StateIsActivated(LIFE_SAVED) + && CanMoveOn(!game::IsInWilderness() ? GetSquareUnder() : PLAYER->GetSquareUnder())) + { + SaveLife(); + return; + } + + if(SpecialSaveLife()) + return; + } + else if(StateIsActivated(LIFE_SAVED)) + RemoveLifeSavers(); + + Flags |= C_IN_NO_MSG_MODE; + character* Ghost = 0; + + if(IsPlayer()) + { + game::RemoveSaves(); + + if(!game::IsInWilderness()) + { + Ghost = game::CreateGhost(); + Ghost->Disable(); + } + } + + square* SquareUnder[MAX_SQUARES_UNDER]; + lsquare** LSquareUnder = reinterpret_cast(SquareUnder); + memset(SquareUnder, 0, sizeof(SquareUnder)); + Disable(); + + if(IsPlayer() || !game::IsInWilderness()) + { + for(int c = 0; c < SquaresUnder; ++c) + SquareUnder[c] = GetSquareUnder(c); + + Remove(); + } + else + { + charactervector& V = game::GetWorldMap()->GetPlayerGroup(); + V.erase(std::find(V.begin(), V.end(), this)); + } + + if(!game::IsInWilderness()) + { + if(!StateIsActivated(POLYMORPHED)) + { + if(!IsPlayer() && !IsTemporary() && !Msg.IsEmpty()) + game::SignalDeath(this, Killer, Msg); + + if(!(DeathFlags & DISALLOW_CORPSE)) + CreateCorpse(LSquareUnder[0]); + else + SendToHell(); + } + else + { + if(!IsPlayer() && !IsTemporary() && !Msg.IsEmpty()) + game::SignalDeath(GetPolymorphBackup(), Killer, Msg); + + GetPolymorphBackup()->CreateCorpse(LSquareUnder[0]); + GetPolymorphBackup()->Flags &= ~C_POLYMORPHED; + SetPolymorphBackup(0); + SendToHell(); + } + } + else + { + if(!IsPlayer() && !IsTemporary() && !Msg.IsEmpty()) + game::SignalDeath(this, Killer, Msg); + + SendToHell(); + } + + if(IsPlayer()) + { + if(!game::IsInWilderness()) + for(int c = 0; c < GetSquaresUnder(); ++c) + LSquareUnder[c]->SetTemporaryEmitation(GetEmitation()); + + ShowAdventureInfo(); + + if(!game::IsInWilderness()) + for(int c = 0; c < GetSquaresUnder(); ++c) + LSquareUnder[c]->SetTemporaryEmitation(0); + } + + if(!game::IsInWilderness()) + { + if(GetSquaresUnder() == 1) + { + stack* StackUnder = LSquareUnder[0]->GetStack(); + GetStack()->MoveItemsTo(StackUnder); + doforbodypartswithparam()(this, &bodypart::DropEquipment, StackUnder); + } + else + { + while(GetStack()->GetItems()) + GetStack()->GetBottom()->MoveTo(LSquareUnder[RAND_N(GetSquaresUnder())]->GetStack()); + + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart) + BodyPart->DropEquipment(LSquareUnder[RAND_N(GetSquaresUnder())]->GetStack()); + } + } + } + + if(GetTeam()->GetLeader() == this) + GetTeam()->SetLeader(0); + + Flags &= ~C_IN_NO_MSG_MODE; + + if(IsPlayer()) + { + AddScoreEntry(Msg); + + if(!game::IsInWilderness()) + { + Ghost->PutTo(LSquareUnder[0]->GetPos()); + Ghost->Enable(); + game::CreateBone(); + } + + game::TextScreen(CONST_S("Unfortunately you died."), ZERO_V2, WHITE, true, true, &game::ShowDeathSmiley); + game::End(Msg); + } +} + +void character::AddMissMessage(ccharacter* Enemy) const +{ + festring Msg; + + if(Enemy->IsPlayer()) + Msg = GetDescription(DEFINITE) + " misses you!"; + else if(IsPlayer()) + Msg = CONST_S("You miss ") + Enemy->GetDescription(DEFINITE) + '!'; + else if(CanBeSeenByPlayer() || Enemy->CanBeSeenByPlayer()) + Msg = GetDescription(DEFINITE) + " misses " + Enemy->GetDescription(DEFINITE) + '!'; + else + return; + + ADD_MESSAGE("%s", Msg.CStr()); +} + +void character::AddBlockMessage(ccharacter* Enemy, citem* Blocker, cfestring& HitNoun, truth Partial) const +{ + festring Msg; + festring BlockVerb = (Partial ? " to partially block the " : " to block the ") + HitNoun; + + if(IsPlayer()) + Msg << "You manage" << BlockVerb << " with your " << Blocker->GetName(UNARTICLED) << '!'; + else if(Enemy->IsPlayer() || Enemy->CanBeSeenByPlayer()) + { + if(CanBeSeenByPlayer()) + Msg << GetName(DEFINITE) << " manages" << BlockVerb << " with " << GetPossessivePronoun() << ' ' << Blocker->GetName(UNARTICLED) << '!'; + else + Msg << "Something manages" << BlockVerb << " with something!"; + } + else + return; + + ADD_MESSAGE("%s", Msg.CStr()); +} + +void character::AddPrimitiveHitMessage(ccharacter* Enemy, cfestring& FirstPersonHitVerb, cfestring& ThirdPersonHitVerb, int BodyPart) const +{ + festring Msg; + festring BodyPartDescription; + + if(BodyPart && (Enemy->CanBeSeenByPlayer() || Enemy->IsPlayer())) + BodyPartDescription << " in the " << Enemy->GetBodyPartName(BodyPart); + + if(Enemy->IsPlayer()) + Msg << GetDescription(DEFINITE) << ' ' << ThirdPersonHitVerb << " you" << BodyPartDescription << '!'; + else if(IsPlayer()) + Msg << "You " << FirstPersonHitVerb << ' ' << Enemy->GetDescription(DEFINITE) << BodyPartDescription << '!'; + else if(CanBeSeenByPlayer() || Enemy->CanBeSeenByPlayer()) + Msg << GetDescription(DEFINITE) << ' ' << ThirdPersonHitVerb << ' ' << Enemy->GetDescription(DEFINITE) + BodyPartDescription << '!'; + else + return; + + ADD_MESSAGE("%s", Msg.CStr()); +} + +cchar*const HitVerb[] = { "strike", "slash", "stab" }; +cchar*const HitVerb3rdPersonEnd[] = { "s", "es", "s" }; + +void character::AddWeaponHitMessage(ccharacter* Enemy, citem* Weapon, int BodyPart, truth Critical) const +{ + festring Msg; + festring BodyPartDescription; + + if(BodyPart && (Enemy->CanBeSeenByPlayer() || Enemy->IsPlayer())) + BodyPartDescription << " in the " << Enemy->GetBodyPartName(BodyPart); + + int FittingTypes = 0; + int DamageFlags = Weapon->GetDamageFlags(); + int DamageType = 0; + + for(int c = 0; c < DAMAGE_TYPES; ++c) + if(1 << c & DamageFlags) + { + if(!FittingTypes || !RAND_N(FittingTypes + 1)) + DamageType = c; + + ++FittingTypes; + } + + if(!FittingTypes) + ABORT("No damage flags specified for %s!", Weapon->CHAR_NAME(UNARTICLED)); + + festring NewHitVerb = Critical ? " critically " : " "; + NewHitVerb << HitVerb[DamageType]; + cchar*const E = HitVerb3rdPersonEnd[DamageType]; + + if(Enemy->IsPlayer()) + { + Msg << GetDescription(DEFINITE) << NewHitVerb << E << " you" << BodyPartDescription; + + if(CanBeSeenByPlayer()) + Msg << " with " << GetPossessivePronoun() << ' ' << Weapon->GetName(UNARTICLED); + + Msg << '!'; + } + else if(IsPlayer()) + Msg << "You" << NewHitVerb << ' ' << Enemy->GetDescription(DEFINITE) << BodyPartDescription << '!'; + else if(CanBeSeenByPlayer() || Enemy->CanBeSeenByPlayer()) + { + Msg << GetDescription(DEFINITE) << NewHitVerb << E << ' ' << Enemy->GetDescription(DEFINITE) << BodyPartDescription; + + if(CanBeSeenByPlayer()) + Msg << " with " << GetPossessivePronoun() << ' ' << Weapon->GetName(UNARTICLED); + + Msg << '!'; + } + else + return; + + ADD_MESSAGE("%s", Msg.CStr()); +} + +truth character::HasHeadOfElpuri() const +{ + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if(i->IsHeadOfElpuri()) + return true; + + return combineequipmentpredicates()(this, &item::IsHeadOfElpuri, 1); +} + +truth character::HasCurdledBlood() const +{ + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if(i->IsKleinBottle()) + if(i->GetSecondaryMaterial()) + if(i->GetSecondaryMaterial()->GetConfig() == CURDLED_OMMEL_BLOOD) + return true; + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Item = GetEquipment(c); + + if(Item && Item->IsKleinBottle()) + if(Item->GetSecondaryMaterial()) + if(Item->GetSecondaryMaterial()->GetConfig() == CURDLED_OMMEL_BLOOD) + return true; + } + + return false; //combineequipmentpredicates()(this, &item::IsKleinBottle, 1); +} + +truth character::HasOmmelBlood() const +{ + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if(i->IsKleinBottle()) + if(i->GetSecondaryMaterial()) + if(i->GetSecondaryMaterial()->GetConfig() == OMMEL_BLOOD) + return true; + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Item = GetEquipment(c); + + if(Item && Item->IsKleinBottle()) + if(Item->GetSecondaryMaterial()) + if(Item->GetSecondaryMaterial()->GetConfig() == OMMEL_BLOOD) + return true; + } + + //item* KB = Arm->GetWielded(); + //// + // if(KB->IsKleinBottle()) + // if(KB->GetSecondaryMaterial()) + // if(KB->GetSecondaryMaterial()->GetConfig() == OMMEL_BLOOD) + return true; + + return false; //combineequipmentpredicates()(this, &item::IsKleinBottle, 1); +} + +truth character::CurdleOmmelBlood() const +{ + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if(i->IsKleinBottle()) + if(i->GetSecondaryMaterial()) + if(i->GetSecondaryMaterial()->GetConfig() == OMMEL_BLOOD) + { + i->ChangeSecondaryMaterial(MAKE_MATERIAL(CURDLED_OMMEL_BLOOD)); + return true; + } + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Item = GetEquipment(c); + + if(Item && Item->IsKleinBottle()) + if(Item->GetSecondaryMaterial()) + if(Item->GetSecondaryMaterial()->GetConfig() == OMMEL_BLOOD) + { + Item->ChangeSecondaryMaterial(MAKE_MATERIAL(CURDLED_OMMEL_BLOOD)); + return true; + } + } + + //item* KB = Arm->GetWielded(); + // + //if(KB->IsKleinBottle()) + // if(KB->GetSecondaryMaterial()) + // if(KB->GetSecondaryMaterial()->GetConfig() == OMMEL_BLOOD) + // { + // KB->ChangeSecondaryMaterial(MAKE_MATERIAL(CURDLED_OMMEL_BLOOD)); + // return true; + // } + + return false; //combineequipmentpredicates()(this, &item::IsKleinBottle, 1); +} + +truth character::RemoveCurdledOmmelBlood() +{ + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if(i->IsKleinBottle()) + if(i->GetSecondaryMaterial()) + if(i->GetSecondaryMaterial()->GetConfig() == CURDLED_OMMEL_BLOOD) + { + item* Item = *i; + Item->RemoveFromSlot(); + Item->SendToHell(); + return true; + } + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Item = GetEquipment(c); + + if(Item && Item->IsKleinBottle()) + if(Item->GetSecondaryMaterial()) + if(Item->GetSecondaryMaterial()->GetConfig() == CURDLED_OMMEL_BLOOD) + { + Item->RemoveFromSlot(); + Item->SendToHell(); + return true; + } + } + + //item* KB = Arm->GetWielded(); + //if(KB && KB->IsKleinBottle()) + // if(KB->GetSecondaryMaterial()) + // if(KB->GetSecondaryMaterial()->GetConfig() == CURDLED_OMMEL_BLOOD) + // { + // KB->RemoveFromSlot(); + // KB->SendToHell(); + // return true; + // } + + return false; +} + +truth character::HasPetrussNut() const +{ + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if(i->IsPetrussNut()) + return true; + + return combineequipmentpredicates()(this, &item::IsPetrussNut, 1); +} + +truth character::HasGoldenEagleShirt() const +{ + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if(i->IsGoldenEagleShirt()) + return true; + + return combineequipmentpredicates()(this, &item::IsGoldenEagleShirt, 1); +} + +truth character::RemoveEncryptedScroll() +{ + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if(i->IsEncryptedScroll()) + { + item* Item = *i; + Item->RemoveFromSlot(); + Item->SendToHell(); + return true; + } + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Item = GetEquipment(c); + + if(Item && Item->IsEncryptedScroll()) + { + Item->RemoveFromSlot(); + Item->SendToHell(); + return true; + } + } + + return false; +} + +truth character::RemoveMondedrPass() +{ + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if(i->IsMondedrPass()) + { + item* Item = *i; + Item->RemoveFromSlot(); + Item->SendToHell(); + return true; + } + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Item = GetEquipment(c); + + if(Item && Item->IsMondedrPass()) + { + Item->RemoveFromSlot(); + Item->SendToHell(); + return true; + } + } + + return false; +} + +truth character::RemoveRingOfThieves() +{ + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if(i->IsRingOfThieves()) + { + item* Item = *i; + Item->RemoveFromSlot(); + Item->SendToHell(); + return true; + } + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Item = GetEquipment(c); + + if(Item && Item->IsRingOfThieves()) + { + Item->RemoveFromSlot(); + Item->SendToHell(); + return true; + } + } + + return false; +} + +truth character::ReadItem(item* ToBeRead) +{ + if(ToBeRead->CanBeRead(this)) + { + if(!GetLSquareUnder()->IsDark() || game::GetSeeWholeMapCheatMode()) + { + if(StateIsActivated(CONFUSED) && !(RAND() & 7)) + { + if(!ToBeRead->IsDestroyable(this)) + ADD_MESSAGE("You read some words of %s and understand exactly nothing.", ToBeRead->CHAR_NAME(DEFINITE)); + else + { + ADD_MESSAGE("%s is very confusing. Or perhaps you are just too confused?", ToBeRead->CHAR_NAME(DEFINITE)); + ActivateRandomState(SRC_CONFUSE_READ, 1000 + RAND() % 1500); + ToBeRead->RemoveFromSlot(); + ToBeRead->SendToHell(); + } + + EditAP(-1000); + return true; + } + + if(ToBeRead->Read(this)) + { + if(!game::WizardModeIsActive()) + { + /* This AP is used to take the stuff out of backpack */ + DexterityAction(5); + } + + return true; + } + else + return false; + } + else + { + if(IsPlayer()) + ADD_MESSAGE("It's too dark here to read."); + + return false; + } + } + else + { + if(IsPlayer()) + ADD_MESSAGE("You can't read this."); + + return false; + } +} + +void character::CalculateBurdenState() +{ + int OldBurdenState = BurdenState; + long SumOfMasses = GetCarriedWeight(); + long CarryingStrengthUnits = long(GetCarryingStrength()) * 2500; + + if(SumOfMasses > (CarryingStrengthUnits << 1) + CarryingStrengthUnits) + BurdenState = OVER_LOADED; + else if(SumOfMasses > CarryingStrengthUnits << 1) + BurdenState = STRESSED; + else if(SumOfMasses > CarryingStrengthUnits) + BurdenState = BURDENED; + else + BurdenState = UNBURDENED; + + if(!IsInitializing() && BurdenState != OldBurdenState) + CalculateBattleInfo(); +} + +void character::Save(outputfile& SaveFile) const +{ + SaveFile << (ushort)GetType(); + Stack->Save(SaveFile); + SaveFile << ID; + int c; + + for(c = 0; c < BASE_ATTRIBUTES; ++c) + SaveFile << BaseExperience[c]; + + SaveFile << ExpModifierMap; + SaveFile << NP << AP << Stamina << GenerationDanger << ScienceTalks + << CounterToMindWormHatch; + SaveFile << TemporaryState << EquipmentState << Money << GoingTo << RegenerationCounter << Route << Illegal << CurrentSweatMaterial; + SaveFile.Put(!!IsEnabled()); + SaveFile << HomeData << BlocksSinceLastTurn << CommandFlags; + SaveFile << WarnFlags << (ushort)Flags; + + for(c = 0; c < BodyParts; ++c) + SaveFile << BodyPartSlot[c] << OriginalBodyPartID[c]; + + SaveLinkedList(SaveFile, TrapData); + SaveFile << Action; + + for(c = 0; c < STATES; ++c) + SaveFile << TemporaryStateCounter[c]; + + if(GetTeam()) + { + SaveFile.Put(true); + SaveFile << Team->GetID(); + } + else + SaveFile.Put(false); + + if(GetTeam() && GetTeam()->GetLeader() == this) + SaveFile.Put(true); + else + SaveFile.Put(false); + + SaveFile << AssignedName << PolymorphBackup; + + for(c = 0; c < AllowedWeaponSkillCategories; ++c) + SaveFile << CWeaponSkill[c]; + + SaveFile << (ushort)GetConfig(); +} + +void character::Load(inputfile& SaveFile) +{ + LoadSquaresUnder(); + int c; + Stack->Load(SaveFile); + SaveFile >> ID; + game::AddCharacterID(this, ID); + + for(c = 0; c < BASE_ATTRIBUTES; ++c) + SaveFile >> BaseExperience[c]; + + SaveFile >> ExpModifierMap; + SaveFile >> NP >> AP >> Stamina >> GenerationDanger >> ScienceTalks + >> CounterToMindWormHatch; + SaveFile >> TemporaryState >> EquipmentState >> Money >> GoingTo >> RegenerationCounter >> Route >> Illegal >> CurrentSweatMaterial; + + if(!SaveFile.Get()) + Disable(); + + SaveFile >> HomeData >> BlocksSinceLastTurn >> CommandFlags; + SaveFile >> WarnFlags; + WarnFlags &= ~WARNED; + Flags |= ReadType(SaveFile) & ~ENTITY_FLAGS; + + for(c = 0; c < BodyParts; ++c) + { + SaveFile >> BodyPartSlot[c] >> OriginalBodyPartID[c]; + + item* BodyPart = *BodyPartSlot[c]; + + if(BodyPart) + BodyPart->Disable(); + } + + LoadLinkedList(SaveFile, TrapData); + SaveFile >> Action; + + if(Action) + Action->SetActor(this); + + for(c = 0; c < STATES; ++c) + SaveFile >> TemporaryStateCounter[c]; + + if(SaveFile.Get()) + SetTeam(game::GetTeam(ReadType(SaveFile))); + + if(SaveFile.Get()) + GetTeam()->SetLeader(this); + + SaveFile >> AssignedName >> PolymorphBackup; + + for(c = 0; c < AllowedWeaponSkillCategories; ++c) + SaveFile >> CWeaponSkill[c]; + + databasecreator::InstallDataBase(this, ReadType(SaveFile)); + + if(IsEnabled() && !game::IsInWilderness()) + for(c = 1; c < GetSquaresUnder(); ++c) + GetSquareUnder(c)->SetCharacter(this); +} + +truth character::Engrave(cfestring& What) +{ + GetLSquareUnder()->Engrave(What); + return true; +} + +truth character::MoveRandomly() +{ + if(!IsEnabled()) + return false; + + for(int c = 0; c < 10; ++c) + { + v2 ToTry = game::GetMoveVector(RAND() & 7); + + if(GetLevel()->IsValidPos(GetPos() + ToTry)) + { + lsquare* Square = GetNearLSquare(GetPos() + ToTry); + + if(!Square->IsDangerous(this) + && !Square->IsScary(this) + && TryMove(ToTry, false, false)) + return true; + } + } + + return false; +} + +truth character::TestForPickup(item* ToBeTested) const +{ + if(MakesBurdened(ToBeTested->GetWeight() + GetCarriedWeight())) + return false; + + return true; +} + +void character::AddScoreEntry(cfestring& Description, double Multiplier, truth AddEndLevel) const +{ + if(!game::WizardModeIsReallyActive()) + { + highscore HScore; + + if(!HScore.CheckVersion()) + { + if(game::Menu(0, v2(RES.X >> 1, RES.Y >> 1), CONST_S("The highscore version doesn't match.\rDo you want to erase previous records and start a new file?\rNote, if you answer no, the score of your current game will be lost!\r"), CONST_S("Yes\rNo\r"), LIGHT_GRAY)) + return; + + HScore.Clear(); + } + + festring Desc = game::GetPlayerName(); + Desc << ", " << Description; + + if(AddEndLevel) + Desc << " in " + (game::IsInWilderness() ? "the world map" : game::GetCurrentDungeon()->GetLevelDescription(game::GetCurrentLevelIndex())); + + HScore.Add(long(game::GetScore() * Multiplier), Desc); + HScore.Save(); + } +} + +truth character::CheckDeath(cfestring& Msg, ccharacter* Murderer, ulong DeathFlags) +{ + if(!IsEnabled()) + return true; + + if(game::IsSumoWrestling() && IsDead()) + { + game::EndSumoWrestling(!!IsPlayer()); + return true; + } + + if(DeathFlags & FORCE_DEATH || IsDead()) + { + if(Murderer && Murderer->IsPlayer() && GetTeam()->GetKillEvilness()) + game::DoEvilDeed(GetTeam()->GetKillEvilness()); + + festring SpecifierMsg; + int SpecifierParts = 0; + + if(GetPolymorphBackup()) + { + SpecifierMsg << " polymorphed into "; + id::AddName(SpecifierMsg, INDEFINITE); + ++SpecifierParts; + } + + if(!(DeathFlags & IGNORE_TRAPS) && IsStuck()) + { + if(SpecifierParts++) + SpecifierMsg << " and"; + + SpecifierMsg << " caught in " << GetTrapDescription(); + } + + if(GetAction() + && !(DeathFlags & IGNORE_UNCONSCIOUSNESS + && GetAction()->IsUnconsciousness())) + { + festring ActionMsg = GetAction()->GetDeathExplanation(); + + if(!ActionMsg.IsEmpty()) + { + if(SpecifierParts > 1) + SpecifierMsg = ActionMsg << ',' << SpecifierMsg; + else + { + if(SpecifierParts) + SpecifierMsg << " and"; + + SpecifierMsg << ActionMsg; + } + + ++SpecifierParts; + } + } + + festring NewMsg = Msg; + + if(Murderer == this) + { + SEARCH_N_REPLACE(NewMsg, "@bkp", CONST_S("by ") + GetPossessivePronoun(false) + " own"); + SEARCH_N_REPLACE(NewMsg, "@bk", CONST_S("by ") + GetObjectPronoun(false) + "self"); + SEARCH_N_REPLACE(NewMsg, "@k", GetObjectPronoun(false) + "self"); + } + else + { + SEARCH_N_REPLACE(NewMsg, "@bkp", CONST_S("by ") + Murderer->GetName(INDEFINITE) + "'s"); + SEARCH_N_REPLACE(NewMsg, "@bk", CONST_S("by ") + Murderer->GetName(INDEFINITE)); + SEARCH_N_REPLACE(NewMsg, "@k", CONST_S("by ") + Murderer->GetName(INDEFINITE)); + } + + if(SpecifierParts) + NewMsg << " while" << SpecifierMsg; + + if(IsPlayer() && game::WizardModeIsActive()) + ADD_MESSAGE("Death message: %s. Score: %ld.", NewMsg.CStr(), game::GetScore()); + + Die(Murderer, NewMsg, DeathFlags); + return true; + } + else + return false; +} + +truth character::CheckStarvationDeath(cfestring& Msg) +{ + if(GetNP() < 1 && UsesNutrition()) + return CheckDeath(Msg, 0, FORCE_DEATH); + else + return false; +} + +void character::ThrowItem(int Direction, item* ToBeThrown) +{ + if(Direction > 7) + ABORT("Throw in TOO odd direction..."); + + ToBeThrown->Fly(this, Direction, GetAttribute(ARM_STRENGTH)); +} + +void character::HasBeenHitByItem(character* Thrower, item* Thingy, int Damage, double ToHitValue, int Direction) +{ + if(IsPlayer()) + ADD_MESSAGE("%s hits you.", Thingy->CHAR_NAME(DEFINITE)); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s hits %s.", Thingy->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + + int BodyPart = ChooseBodyPartToReceiveHit(ToHitValue, DodgeValue); + int WeaponSkillHits = Thrower ? CalculateWeaponSkillHits(Thrower) : 0; + int DoneDamage = ReceiveBodyPartDamage(Thrower, Damage, PHYSICAL_DAMAGE, BodyPart, Direction); + truth Succeeded = (GetBodyPart(BodyPart) && HitEffect(Thrower, Thingy, Thingy->GetPos(), THROW_ATTACK, BodyPart, Direction, !DoneDamage)) || DoneDamage; + + if(Succeeded && Thrower) + Thrower->WeaponSkillHit(Thingy, THROW_ATTACK, WeaponSkillHits); + + festring DeathMsg = CONST_S("killed by a flying ") + Thingy->GetName(UNARTICLED); + + if(CheckDeath(DeathMsg, Thrower)) + return; + + if(Thrower) + { + if(Thrower->CanBeSeenByPlayer()) + DeActivateVoluntaryAction(CONST_S("The attack of ") + Thrower->GetName(DEFINITE) + CONST_S(" interrupts you.")); + else + DeActivateVoluntaryAction(CONST_S("The attack interrupts you.")); + } + else + DeActivateVoluntaryAction(CONST_S("The hit interrupts you.")); +} + +truth character::DodgesFlyingItem(item* Item, double ToHitValue) +{ + return !Item->EffectIsGood() && RAND() % int(100 + ToHitValue / DodgeValue * 100) < 100; +} + +void character::GetPlayerCommand() +{ + truth HasActed = false; + + while(!HasActed) + { + game::DrawEverything(); + + if(game::GetDangerFound()) + { + if(game::GetDangerFound() > 500.) + { + if(game::GetCausePanicFlag()) + { + game::SetCausePanicFlag(false); + BeginTemporaryState(PANIC, 500 + RAND_N(500)); + } + + game::AskForKeyPress(CONST_S("You are horrified by your situation! [press any key to continue]")); + } + else if(ivanconfig::GetWarnAboutDanger()) + if(game::GetDangerFound() > 50.) + game::AskForKeyPress(CONST_S("You sense great danger! [press any key to continue]")); + else + game::AskForKeyPress(CONST_S("You sense danger! [press any key to continue]")); + + game::SetDangerFound(0); + } + + game::SetIsInGetCommand(true); + int Key = GET_KEY(); + game::SetIsInGetCommand(false); + + if(Key != '+' && Key != '-' && Key != 'M') // gum + msgsystem::ThyMessagesAreNowOld(); + + truth ValidKeyPressed = false; + int c; + + for(c = 0; c < DIRECTION_COMMAND_KEYS; ++c) + if(Key == game::GetMoveCommandKey(c)) + { + HasActed = TryMove(ApplyStateModification(game::GetMoveVector(c)), true, game::PlayerIsRunning()); + ValidKeyPressed = true; + } + + for(c = 1; commandsystem::GetCommand(c); ++c) + if(Key == commandsystem::GetCommand(c)->GetKey()) + { + if(game::IsInWilderness() && !commandsystem::GetCommand(c)->IsUsableInWilderness()) + ADD_MESSAGE("This function cannot be used while in wilderness."); + else + if(!game::WizardModeIsActive() && commandsystem::GetCommand(c)->IsWizardModeFunction()) + ADD_MESSAGE("Activate wizardmode to use this function."); + else + HasActed = commandsystem::GetCommand(c)->GetLinkedFunction()(this); + + ValidKeyPressed = true; + break; + } + + if(!ValidKeyPressed) + ADD_MESSAGE("Unknown key. Press '?' for a list of commands."); + } + + game::IncreaseTurn(); +} + +void character::Vomit(v2 Pos, int Amount, truth ShowMsg) +{ + if(!CanVomit()) + return; + + if(ShowMsg) + { + if(IsPlayer()) + ADD_MESSAGE("You vomit."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s vomits.", CHAR_NAME(DEFINITE)); + } + + if(VomittingIsUnhealthy()) + { + EditExperience(ARM_STRENGTH, -75, 1 << 9); + EditExperience(LEG_STRENGTH, -75, 1 << 9); + } + + if(IsPlayer()) + { + EditNP(-2500 - RAND() % 2501); + CheckStarvationDeath(CONST_S("vomited himself to death")); + } + + if(StateIsActivated(PARASITIZED) && !(RAND() & 7)) + { + if(IsPlayer()) + ADD_MESSAGE("You notice a dead broad tapeworm among your former stomach contents."); + + DeActivateTemporaryState(PARASITIZED); + } + + if(!game::IsInWilderness()) + GetNearLSquare(Pos)->ReceiveVomit(this, liquid::Spawn(GetVomitMaterial(), long(sqrt(GetBodyVolume()) * Amount / 1000))); +} + +truth character::Polymorph(character* NewForm, int Counter) +{ + if(!IsPolymorphable() || (!IsPlayer() && game::IsInWilderness())) + { + delete NewForm; + return false; + } + + RemoveTraps(); + + if(GetAction()) + GetAction()->Terminate(false); + + NewForm->SetAssignedName(""); + + if(IsPlayer()) + ADD_MESSAGE("Your body glows in a crimson light. You transform into %s!", NewForm->CHAR_NAME(INDEFINITE)); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s glows in a crimson light and %s transforms into %s!", CHAR_NAME(DEFINITE), GetPersonalPronoun().CStr(), NewForm->CHAR_NAME(INDEFINITE)); + + Flags |= C_IN_NO_MSG_MODE; + NewForm->Flags |= C_IN_NO_MSG_MODE; + NewForm->ChangeTeam(GetTeam()); + NewForm->GenerationDanger = GenerationDanger; + + if(GetTeam()->GetLeader() == this) + GetTeam()->SetLeader(NewForm); + + v2 Pos = GetPos(); + Remove(); + NewForm->PutToOrNear(Pos); + NewForm->SetAssignedName(GetAssignedName()); + NewForm->ActivateTemporaryState(POLYMORPHED); + NewForm->SetTemporaryStateCounter(POLYMORPHED, Counter); + + if(TemporaryStateIsActivated(POLYMORPHED)) + { + NewForm->SetPolymorphBackup(GetPolymorphBackup()); + SetPolymorphBackup(0); + SendToHell(); + } + else + { + NewForm->SetPolymorphBackup(this); + Flags |= C_POLYMORPHED; + Disable(); + } + + GetStack()->MoveItemsTo(NewForm->GetStack()); + NewForm->SetMoney(GetMoney()); + DonateEquipmentTo(NewForm); + Flags &= ~C_IN_NO_MSG_MODE; + NewForm->Flags &= ~C_IN_NO_MSG_MODE; + NewForm->CalculateAll(); + + if(IsPlayer()) + { + Flags &= ~C_PLAYER; + game::SetPlayer(NewForm); + game::SendLOSUpdateRequest(); + UpdateESPLOS(); + } + + NewForm->TestWalkability(); + return true; +} + +void character::BeKicked(character* Kicker, item* Boot, bodypart* Leg, v2 HitPos, double KickDamage, double ToHitValue, int Success, int Direction, truth Critical, truth ForceHit) +{ + switch(TakeHit(Kicker, Boot, Leg, HitPos, KickDamage, ToHitValue, Success, KICK_ATTACK, Direction, Critical, ForceHit)) + { + case HAS_HIT: + case HAS_BLOCKED: + case DID_NO_DAMAGE: + if(IsEnabled() && !CheckBalance(KickDamage)) + { + if(IsPlayer()) + ADD_MESSAGE("The kick throws you off balance."); + else if(Kicker->IsPlayer()) + ADD_MESSAGE("The kick throws %s off balance.", CHAR_DESCRIPTION(DEFINITE)); + + v2 FallToPos = GetPos() + game::GetMoveVector(Direction); + FallTo(Kicker, FallToPos); + } + } +} + +/* Return true if still in balance */ + +truth character::CheckBalance(double KickDamage) +{ + return !CanMove() + || IsStuck() + || !KickDamage + || (!IsFlying() + && KickDamage * 5 < RAND() % GetSize()); +} + +void character::FallTo(character* GuiltyGuy, v2 Where) +{ + EditAP(-500); + lsquare* MoveToSquare[MAX_SQUARES_UNDER]; + int Squares = CalculateNewSquaresUnder(MoveToSquare, Where); + + if(Squares) + { + truth NoRoom = false; + + for(int c = 0; c < Squares; ++c) + { + olterrain* Terrain = MoveToSquare[c]->GetOLTerrain(); + + if(Terrain && !CanMoveOn(Terrain)) + { + NoRoom = true; + break; + } + } + + if(NoRoom) + { + if(HasHead()) + { + if(IsPlayer()) + ADD_MESSAGE("You hit your head on the wall."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s hits %s head on the wall.", CHAR_NAME(DEFINITE), GetPossessivePronoun().CStr()); + } + + ReceiveDamage(GuiltyGuy, 1 + RAND() % 5, PHYSICAL_DAMAGE, HEAD); + CheckDeath(CONST_S("killed by hitting a wall due to being kicked @bk"), GuiltyGuy); + } + else + { + if(IsFreeForMe(MoveToSquare[0])) + Move(Where, true); + + // Place code that handles characters bouncing to each other here + } + } +} + +truth character::CheckCannibalism(cmaterial* What) const +{ + return GetTorso()->GetMainMaterial()->IsSameAs(What); +} + +void character::StandIdleAI() +{ + SeekLeader(GetLeader()); + + if(CheckForEnemies(true, true, true)) + return; + + if(CheckForUsefulItemsOnGround()) + return; + + if(FollowLeader(GetLeader())) + return; + + if(CheckForDoors()) + return; + + if(MoveTowardsHomePos()) + return; + + if(CheckSadism()) + return; + + EditAP(-1000); +} + +truth character::LoseConsciousness(int Counter, truth HungerFaint) +{ + if(!AllowUnconsciousness()) + return false; + + action* Action = GetAction(); + + if(Action) + { + if(HungerFaint && !Action->AllowUnconsciousness()) + return false; + + if(Action->IsUnconsciousness()) + { + static_cast(Action)->RaiseCounterTo(Counter); + return true; + } + else + Action->Terminate(false); + } + + if(IsPlayer()) + ADD_MESSAGE("You lose consciousness."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s loses consciousness.", CHAR_NAME(DEFINITE)); + + unconsciousness* Unconsciousness = unconsciousness::Spawn(this); + Unconsciousness->SetCounter(Counter); + SetAction(Unconsciousness); + return true; +} + +void character::DeActivateVoluntaryAction(cfestring& Reason) +{ + if(GetAction() && GetAction()->IsVoluntary()) + { + if(IsPlayer()) + { + if(Reason.GetSize()) + ADD_MESSAGE("%s", Reason.CStr()); + + if(game::TruthQuestion(CONST_S("Continue ") + GetAction()->GetDescription() + "? [y/N]")) + GetAction()->ActivateInDNDMode(); + else + GetAction()->Terminate(false); + } + else + GetAction()->Terminate(false); + } +} + +void character::ActionAutoTermination() +{ + if(!GetAction() || !GetAction()->IsVoluntary() || GetAction()->InDNDMode()) + return; + + v2 Pos = GetPos(); + + for(int c = 0; c < game::GetTeams(); ++c) + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled() + && (*i)->CanBeSeenBy(this, false, true) + && ((*i)->CanMove() || (*i)->GetPos().IsAdjacent(Pos)) + && (*i)->CanAttack()) + { + if(IsPlayer()) + { + ADD_MESSAGE("%s seems to be hostile.", (*i)->CHAR_NAME(DEFINITE)); + + if(game::TruthQuestion(CONST_S("Continue ") + GetAction()->GetDescription() + "? [y/N]")) + GetAction()->ActivateInDNDMode(); + else + GetAction()->Terminate(false); + } + else + GetAction()->Terminate(false); + + return; + } +} + +truth character::CheckForEnemies(truth CheckDoors, truth CheckGround, truth MayMoveRandomly, truth RunTowardsTarget) +{ + if(!IsEnabled()) + return false; + + truth HostileCharsNear = false; + character* NearestChar = 0; + long NearestDistance = 0x7FFFFFFF; + v2 Pos = GetPos(); + + for(int c = 0; c < game::GetTeams(); ++c) + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled() && GetAttribute(WISDOM) < (*i)->GetAttackWisdomLimit()) + { + long ThisDistance = Max(abs((*i)->GetPos().X - Pos.X), abs((*i)->GetPos().Y - Pos.Y)); + + if(ThisDistance <= GetLOSRangeSquare()) + HostileCharsNear = true; + + if((ThisDistance < NearestDistance + || (ThisDistance == NearestDistance && !(RAND() % 3))) + && (*i)->CanBeSeenBy(this, false, IsGoingSomeWhere()) + && (!IsGoingSomeWhere() || HasClearRouteTo((*i)->GetPos()))) + { + NearestChar = *i; + NearestDistance = ThisDistance; + } + } + + if(NearestChar) + { + if(GetAttribute(INTELLIGENCE) >= 10 || IsSpy()) + game::CallForAttention(GetPos(), 100); + + if(SpecialEnemySightedReaction(NearestChar)) + return true; + + if(IsExtraCoward() && !StateIsActivated(PANIC) && NearestChar->GetRelativeDanger(this) >= 0.5) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s sees %s.", CHAR_NAME(DEFINITE), NearestChar->CHAR_DESCRIPTION(DEFINITE)); + + BeginTemporaryState(PANIC, 500 + RAND() % 500); + } + + if(!IsRetreating()) + { + if(CheckGround && NearestDistance > 2 && CheckForUsefulItemsOnGround(false)) + return true; + + SetGoingTo(NearestChar->GetPos()); + } + else + SetGoingTo(Pos - ((NearestChar->GetPos() - Pos) << 4)); + + return MoveTowardsTarget(true); + } + else + { + character* Leader = GetLeader(); + + if(Leader == this) + Leader = 0; + + if(!Leader && IsGoingSomeWhere()) + { + if(!MoveTowardsTarget(RunTowardsTarget)) + { + TerminateGoingTo(); + return false; + } + else + { + if(!IsEnabled()) + return true; + + if(GetPos() == GoingTo) + TerminateGoingTo(); + + return true; + } + } + else + { + if((!Leader || (Leader && !IsGoingSomeWhere())) && HostileCharsNear) + { + if(CheckDoors && CheckForDoors()) + return true; + + if(CheckGround && CheckForUsefulItemsOnGround()) + return true; + + if(MayMoveRandomly && MoveRandomly()) // one has heard that an enemy is near but doesn't know where + return true; + } + + return false; + } + } +} + +truth character::CheckForDoors() +{ + if(!CanOpen() || !IsEnabled()) + return false; + + for(int d = 0; d < GetNeighbourSquares(); ++d) + { + lsquare* Square = GetNeighbourLSquare(d); + + if(Square && Square->GetOLTerrain() && Square->GetOLTerrain()->Open(this)) + return true; + } + + return false; +} + +truth character::CheckForUsefulItemsOnGround(truth CheckFood) +{ + if(StateIsActivated(PANIC) || !IsEnabled()) + return false; + + itemvector ItemVector; + GetStackUnder()->FillItemVector(ItemVector); + + for(uint c = 0; c < ItemVector.size(); ++c) + if(ItemVector[c]->CanBeSeenBy(this) && ItemVector[c]->IsPickable(this)) + { + if(!(CommandFlags & DONT_CHANGE_EQUIPMENT) + && TryToEquip(ItemVector[c])) + return true; + + if(CheckFood && UsesNutrition() && !CheckIfSatiated() + && TryToConsume(ItemVector[c])) + return true; + + //if( (ItemVector[c])->IsThrowingWeapon() && IsRangedAttacker() //&& HasTheseCategoriesForThrowing() + // && TryToAddToInventory(ItemVector[c])) + // return true; + // + +/* Part of an attempt to generalize the throwing item pick-up routine... Grr ...had to resort to having a specialised algorithm for each character who throws stuff*/ + //if( (//ItemVector[c])->IsThrowingWeapon() + // IsRangedAttacker() + // && ((ItemVector[c])->GetType() & GetWhatCategoryToThrow() /*& !WEAPON*/) + // && TryToAddToInventory(ItemVector[c]))) + // return true; + + //if( (//ItemVector[c])->IsThrowingWeapon() + // IsRangedAttacker() + // && ((ItemVector[c])->GetType() & WEAPON) + // && ((ItemVector[c])->GetConfig() & GetWhatWeaponConfigToThrow()) + // && TryToAddToInventory(ItemVector[c]))) + // return true; + + if((ItemVector[c]->GetThrowItemTypes() & GetWhatThrowItemTypesToThrow()) + && IsRangedAttacker() + && TryToAddToInventory(ItemVector[c])) + return true; + } + return false; +} + +truth character::FollowLeader(character* Leader) +{ + if(!Leader || Leader == this || !IsEnabled()) + return false; + + if(CommandFlags & FOLLOW_LEADER && Leader->CanBeSeenBy(this) && Leader->SquareUnderCanBeSeenBy(this, true)) + { + v2 Distance = GetPos() - GoingTo; + + if(abs(Distance.X) <= 2 && abs(Distance.Y) <= 2) + return false; + else + return MoveTowardsTarget(false); + } + else + if(IsGoingSomeWhere()) + if(!MoveTowardsTarget(true)) + { + TerminateGoingTo(); + return false; + } + else + return true; + else + return false; +} + +void character::SeekLeader(ccharacter* Leader) +{ + if(Leader && Leader != this) + if(Leader->CanBeSeenBy(this) && (Leader->SquareUnderCanBeSeenBy(this, true) || !IsGoingSomeWhere())) + { + if(CommandFlags & FOLLOW_LEADER) + SetGoingTo(Leader->GetPos()); + } + else if(!IsGoingSomeWhere()) + { + team* Team = GetTeam(); + + for(std::list::const_iterator i = Team->GetMember().begin(); + i != Team->GetMember().end(); ++i) + if((*i)->IsEnabled() + && (*i)->GetID() != GetID() + && (CommandFlags & FOLLOW_LEADER) + == ((*i)->CommandFlags & FOLLOW_LEADER) + && (*i)->CanBeSeenBy(this)) + { + v2 Pos = (*i)->GetPos(); + v2 Distance = GetPos() - Pos; + + if(abs(Distance.X) > 2 && abs(Distance.Y) > 2) + { + SetGoingTo(Pos); + break; + } + } + } +} + +int character::GetMoveEase() const +{ + switch(BurdenState) + { + case OVER_LOADED: + case STRESSED: return 50; + case BURDENED: return 75; + case UNBURDENED: return 100; + } + + return 666; +} + +int character::GetLOSRange() const +{ + if(!game::IsInWilderness()) + return GetAttribute(PERCEPTION) * GetLevel()->GetLOSModifier() / 48; + else + return 3; +} + +truth character::Displace(character* Who, truth Forced) +{ + if(GetBurdenState() == OVER_LOADED) + { + if(IsPlayer()) + { + cchar* CrawlVerb = StateIsActivated(LEVITATION) ? "float" : "crawl"; + ADD_MESSAGE("You try very hard to %s forward. But your load is too heavy.", CrawlVerb); + EditAP(-1000); + return true; + } + else + return false; + } + + double Danger = GetRelativeDanger(Who); + int PriorityDifference = Limit(GetDisplacePriority() - Who->GetDisplacePriority(), -31, 31); + + if(IsPlayer()) + ++PriorityDifference; + else if(Who->IsPlayer()) + --PriorityDifference; + + if(PriorityDifference >= 0) + Danger *= 1 << PriorityDifference; + else + Danger /= 1 << -PriorityDifference; + + if(IsSmall() && Who->IsSmall() + && (Forced || Danger > 1. || !(Who->IsPlayer() || Who->IsBadPath(GetPos()))) + && !IsStuck() && !Who->IsStuck() + && (!Who->GetAction() || Who->GetAction()->TryDisplace()) + && CanMove() && Who->CanMove() && Who->CanMoveOn(GetLSquareUnder())) + { + if(IsPlayer()) + ADD_MESSAGE("You displace %s!", Who->CHAR_DESCRIPTION(DEFINITE)); + else if(Who->IsPlayer()) + ADD_MESSAGE("%s displaces you!", CHAR_DESCRIPTION(DEFINITE)); + else if(CanBeSeenByPlayer() || Who->CanBeSeenByPlayer()) + ADD_MESSAGE("%s displaces %s!", CHAR_DESCRIPTION(DEFINITE), Who->CHAR_DESCRIPTION(DEFINITE)); + + lsquare* OldSquareUnder1[MAX_SQUARES_UNDER]; + lsquare* OldSquareUnder2[MAX_SQUARES_UNDER]; + int c; + + for(c = 0; c < GetSquaresUnder(); ++c) + OldSquareUnder1[c] = GetLSquareUnder(c); + + for(c = 0; c < Who->GetSquaresUnder(); ++c) + OldSquareUnder2[c] = Who->GetLSquareUnder(c); + + v2 Pos = GetPos(); + v2 WhoPos = Who->GetPos(); + Remove(); + Who->Remove(); + PutTo(WhoPos); + Who->PutTo(Pos); + EditAP(-GetMoveAPRequirement(GetSquareUnder()->GetEntryDifficulty()) - 500); + EditNP(-12 * GetSquareUnder()->GetEntryDifficulty()); + EditExperience(AGILITY, 75, GetSquareUnder()->GetEntryDifficulty() << 7); + + if(IsPlayer()) + ShowNewPosInfo(); + + if(Who->IsPlayer()) + Who->ShowNewPosInfo(); + + SignalStepFrom(OldSquareUnder1); + Who->SignalStepFrom(OldSquareUnder2); + return true; + } + else + { + if(IsPlayer()) + { + ADD_MESSAGE("%s resists!", Who->CHAR_DESCRIPTION(DEFINITE)); + EditAP(-1000); + return true; + } + else + return false; + } +} + +void character::SetNP(long What) +{ + int OldState = GetHungerState(); + NP = What; + + if(IsPlayer()) + { + int NewState = GetHungerState(); + + if(NewState == STARVING && OldState > STARVING) + DeActivateVoluntaryAction(CONST_S("You are getting really hungry.")); + else if(NewState == VERY_HUNGRY && OldState > VERY_HUNGRY) + DeActivateVoluntaryAction(CONST_S("You are getting very hungry.")); + else if(NewState == HUNGRY && OldState > HUNGRY) + DeActivateVoluntaryAction(CONST_S("You are getting hungry.")); + } +} + +void character::ShowNewPosInfo() const +{ + msgsystem::EnterBigMessageMode(); + v2 Pos = GetPos(); + + if(Pos.X < game::GetCamera().X + 3 || Pos.X >= game::GetCamera().X + game::GetScreenXSize() - 3) + game::UpdateCameraX(); + + if(Pos.Y < game::GetCamera().Y + 3 || Pos.Y >= game::GetCamera().Y + game::GetScreenYSize() - 3) + game::UpdateCameraY(); + + game::SendLOSUpdateRequest(); + game::DrawEverythingNoBlit(); + UpdateESPLOS(); + + if(!game::IsInWilderness()) + { + if(GetLSquareUnder()->IsDark() && !game::GetSeeWholeMapCheatMode()) + ADD_MESSAGE("It's dark in here!"); + + GetLSquareUnder()->ShowSmokeMessage(); + itemvectorvector PileVector; + GetStackUnder()->Pile(PileVector, this, CENTER); + + if(PileVector.size()) + { + truth Feel = !GetLSquareUnder()->IsTransparent() || GetLSquareUnder()->IsDark(); + + if(PileVector.size() == 1) + { + if(Feel) + ADD_MESSAGE("You feel %s lying here.", PileVector[0][0]->GetName(INDEFINITE, PileVector[0].size()).CStr()); + else + ADD_MESSAGE("%s %s lying here.", PileVector[0][0]->GetName(INDEFINITE, PileVector[0].size()).CStr(), PileVector[0].size() == 1 ? "is" : "are"); + } + else + { + int Items = 0; + + for(uint c = 0; c < PileVector.size(); ++c) + if((Items += PileVector[c].size()) > 3) + break; + + if(Items > 3) + { + if(Feel) + ADD_MESSAGE("You feel several items lying here."); + else + ADD_MESSAGE("Several items are lying here."); + } + else if(Items) + { + if(Feel) + ADD_MESSAGE("You feel a few items lying here."); + else + ADD_MESSAGE("A few items are lying here."); + } + } + } + + festring SideItems; + GetLSquareUnder()->GetSideItemDescription(SideItems); + + if(!SideItems.IsEmpty()) + ADD_MESSAGE("There is %s.", SideItems.CStr()); + + if(GetLSquareUnder()->HasEngravings()) + { + if(CanRead()) + ADD_MESSAGE("Something has been engraved here: \"%s\"", GetLSquareUnder()->GetEngraved()); + else + ADD_MESSAGE("Something has been engraved here."); + } + } + + msgsystem::LeaveBigMessageMode(); +} + +void character::Hostility(character* Enemy) +{ + if(Enemy == this || !Enemy || !Team || !Enemy->Team) + return; + + if(Enemy->IsMasochist() && GetRelation(Enemy) == FRIEND) + return; + + if(!IsAlly(Enemy)) + GetTeam()->Hostility(Enemy->GetTeam()); + else if(IsPlayer() && !Enemy->IsPlayer()) // I believe both may be players due to polymorph feature... + { + if(Enemy->CanBeSeenByPlayer()) + ADD_MESSAGE("%s becomes enraged.", Enemy->CHAR_NAME(DEFINITE)); + + Enemy->ChangeTeam(game::GetTeam(BETRAYED_TEAM)); + } +} + +stack* character::GetGiftStack() const +{ + if(GetLSquareUnder()->GetRoomIndex() && !GetLSquareUnder()->GetRoom()->AllowDropGifts()) + return GetStack(); + else + return GetStackUnder(); +} + +truth character::MoveRandomlyInRoom() +{ + for(int c = 0; c < 10; ++c) + { + v2 ToTry = game::GetMoveVector(RAND() & 7); + + if(GetLevel()->IsValidPos(GetPos() + ToTry)) + { + lsquare* Square = GetNearLSquare(GetPos() + ToTry); + + if(!Square->IsDangerous(this) + && !Square->IsScary(this) + && (!Square->GetOLTerrain() + || !Square->GetOLTerrain()->IsDoor()) + && TryMove(ToTry, false, false)) + return true; + } + } + + return false; +} + +void character::GoOn(go* Go, truth FirstStep) +{ + v2 MoveVector = ApplyStateModification(game::GetMoveVector(Go->GetDirection())); + lsquare* MoveToSquare[MAX_SQUARES_UNDER]; + int Squares = CalculateNewSquaresUnder(MoveToSquare, GetPos() + MoveVector); + + if(!Squares || !CanMoveOn(MoveToSquare[0])) + { + Go->Terminate(false); + return; + } + + uint OldRoomIndex = GetLSquareUnder()->GetRoomIndex(); + uint CurrentRoomIndex = MoveToSquare[0]->GetRoomIndex(); + + if((OldRoomIndex && (CurrentRoomIndex != OldRoomIndex)) && !FirstStep) + { + Go->Terminate(false); + return; + } + + for(int c = 0; c < Squares; ++c) + if((MoveToSquare[c]->GetCharacter() && GetTeam() != MoveToSquare[c]->GetCharacter()->GetTeam()) || MoveToSquare[c]->IsDangerous(this)) + { + Go->Terminate(false); + return; + } + + int OKDirectionsCounter = 0; + + for(int d = 0; d < GetNeighbourSquares(); ++d) + { + lsquare* Square = GetNeighbourLSquare(d); + + if(Square && CanMoveOn(Square)) + ++OKDirectionsCounter; + } + + if(!Go->IsWalkingInOpen()) + { + if(OKDirectionsCounter > 2) + { + Go->Terminate(false); + return; + } + } + else + if(OKDirectionsCounter <= 2) + Go->SetIsWalkingInOpen(false); + + square* BeginSquare = GetSquareUnder(); + + if(!TryMove(MoveVector, true, game::PlayerIsRunning()) + || BeginSquare == GetSquareUnder() + || (CurrentRoomIndex && (OldRoomIndex != CurrentRoomIndex))) + { + Go->Terminate(false); + return; + } + + if(GetStackUnder()->GetVisibleItems(this)) + { + Go->Terminate(false); + return; + } + + game::DrawEverything(); +} + +void character::SetTeam(team* What) +{ + if(Team) + int esko = esko = 2; + + Team = What; + SetTeamIterator(What->Add(this)); +} + +void character::ChangeTeam(team* What) +{ + if(Team) + Team->Remove(GetTeamIterator()); + + Team = What; + SendNewDrawRequest(); + + if(Team) + SetTeamIterator(Team->Add(this)); +} + +truth character::ChangeRandomAttribute(int HowMuch) +{ + for(int c = 0; c < 50; ++c) + { + int AttribID = RAND() % ATTRIBUTES; + + if(EditAttribute(AttribID, HowMuch)) + return true; + } + + return false; +} + +int character::RandomizeReply(long& Said, int Replies) +{ + truth NotSaid = false; + + for(int c = 0; c < Replies; ++c) + if(!(Said & (1 << c))) + { + NotSaid = true; + break; + } + + if(!NotSaid) + Said = 0; + + long ToSay; + while(Said & 1 << (ToSay = RAND() % Replies)); + Said |= 1 << ToSay; + return ToSay; +} + +void character::DisplayInfo(festring& Msg) +{ + if(IsPlayer()) + Msg << " You are " << GetStandVerb() << " here."; + else + { + Msg << ' ' << GetName(INDEFINITE).CapitalizeCopy() << " is " << GetStandVerb() << " here. " << GetPersonalPronoun().CapitalizeCopy(); + cchar* Separator1 = GetAction() ? "," : " and"; + cchar* Separator2 = " and"; + + if(GetTeam() == PLAYER->GetTeam()) + Msg << " is tame"; + else + { + int Relation = GetRelation(PLAYER); + + if(Relation == HOSTILE) + Msg << " is hostile"; + else if(Relation == UNCARING) + { + Msg << " does not care about you"; + Separator1 = Separator2 = " and is"; + } + else + Msg << " is friendly"; + } + + if(StateIsActivated(PANIC)) + { + Msg << Separator1 << " panicked"; + Separator2 = " and"; + } + + if(GetAction()) + Msg << Separator2 << ' ' << GetAction()->GetDescription(); + + Msg << '.'; + } +} + +void character::TestWalkability() +{ + if(!IsEnabled()) + return; + + square* SquareUnder = !game::IsInWilderness() + ? GetSquareUnder() : PLAYER->GetSquareUnder(); + + if(SquareUnder->IsFatalToStay() && !CanMoveOn(SquareUnder)) + { + truth Alive = false; + + if(!game::IsInWilderness() || IsPlayer()) + for(int d = 0; d < GetNeighbourSquares(); ++d) + { + square* Square = GetNeighbourSquare(d); + + if(Square && CanMoveOn(Square) && IsFreeForMe(Square)) + { + if(IsPlayer()) + ADD_MESSAGE("%s.", SquareUnder->SurviveMessage(this)); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s %s.", CHAR_NAME(DEFINITE), SquareUnder->MonsterSurviveMessage(this)); + + Move(Square->GetPos(), true); // actually, this shouldn't be a teleport move + SquareUnder->SurviveEffect(this); + Alive = true; + break; + } + } + + if(!Alive) + { + if(IsPlayer()) + { + Remove(); + SendToHell(); + festring DeathMsg = festring(SquareUnder->DeathMessage(this)); + game::AskForKeyPress(DeathMsg + ". [press any key to continue]"); + festring Msg = SquareUnder->ScoreEntry(this); + PLAYER->AddScoreEntry(Msg); + game::End(Msg); + } + else + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s %s.", CHAR_NAME(DEFINITE), SquareUnder->MonsterDeathVerb(this)); + + Die(0, SquareUnder->ScoreEntry(this), DISALLOW_MSG); + } + } + } +} + +int character::GetSize() const +{ + return GetTorso()->GetSize(); +} + +void character::SetMainMaterial(material* NewMaterial, int SpecialFlags) +{ + NewMaterial->SetVolume(GetBodyPart(0)->GetMainMaterial()->GetVolume()); + GetBodyPart(0)->SetMainMaterial(NewMaterial, SpecialFlags); + + for(int c = 1; c < BodyParts; ++c) + { + NewMaterial = NewMaterial->SpawnMore(GetBodyPart(c)->GetMainMaterial()->GetVolume()); + GetBodyPart(c)->SetMainMaterial(NewMaterial, SpecialFlags); + } +} + +void character::ChangeMainMaterial(material* NewMaterial, int SpecialFlags) +{ + NewMaterial->SetVolume(GetBodyPart(0)->GetMainMaterial()->GetVolume()); + GetBodyPart(0)->ChangeMainMaterial(NewMaterial, SpecialFlags); + + for(int c = 1; c < BodyParts; ++c) + { + NewMaterial = NewMaterial->SpawnMore(GetBodyPart(c)->GetMainMaterial()->GetVolume()); + GetBodyPart(c)->ChangeMainMaterial(NewMaterial, SpecialFlags); + } +} + +void character::SetSecondaryMaterial(material*, int) +{ + ABORT("Illegal character::SetSecondaryMaterial call!"); +} + +void character::ChangeSecondaryMaterial(material*, int) +{ + ABORT("Illegal character::ChangeSecondaryMaterial call!"); +} + +void character::TeleportRandomly(truth Intentional) +{ + v2 TelePos = ERROR_V2; + + if(StateIsActivated(TELEPORT_CONTROL)) + { + if(IsPlayer()) + { + v2 Input = game::PositionQuestion(CONST_S("Where do you wish to teleport? [direction keys move cursor, space accepts]"), GetPos(), &game::TeleportHandler, 0, false); + + if(Input == ERROR_V2) // esc pressed + Input = GetPos(); + + lsquare* Square = GetNearLSquare(Input); + + if(CanMoveOn(Square) || game::GoThroughWallsCheatIsActive()) + { + if(Square->GetPos() == GetPos()) + { + ADD_MESSAGE("You disappear and reappear."); + return; + } + + if(IsFreeForMe(Square)) + { + if((Input - GetPos()).GetLengthSquare() <= GetTeleportRangeSquare()) + { + EditExperience(INTELLIGENCE, 100, 1 << 10); + TelePos = Input; + } + else + ADD_MESSAGE("You cannot concentrate yourself enough to control a teleport that far."); + } + else + { + character* C = Square->GetCharacter(); + + if(C) + ADD_MESSAGE("For a moment you feel very much like %s.", C->CHAR_NAME(INDEFINITE)); + else + ADD_MESSAGE("You feel that something weird has happened, but can't really tell what exactly."); + } + } + else + ADD_MESSAGE("You feel like having been hit by something really hard from the inside."); + } + else if(!Intentional) + { + if(IsGoingSomeWhere() && GetLevel()->IsValidPos(GoingTo)) + { + v2 Where = GetLevel()->GetNearestFreeSquare(this, GoingTo); + + if(Where != ERROR_V2 && (Where - GetPos()).GetLengthSquare() <= GetTeleportRangeSquare()) + { + EditExperience(INTELLIGENCE, 100, 1 << 10); + Where = TelePos; + } + } + } + } + + if(IsPlayer()) + ADD_MESSAGE("A rainbow-colored whirlpool twists the existence around you. You are sucked through a tunnel piercing a myriad of surreal universes. Luckily you return to this dimension in one piece."); + + if(TelePos == ERROR_V2) + TelePos = GetLevel()->GetRandomSquare(this); + + room* PossibleRoom = game::GetCurrentLevel()->GetLSquare(TelePos)->GetRoom(); + + if(!PossibleRoom) //if it's outside of a room + { + if(TelePos != ERROR_V2) + Move(TelePos, true); + else + Move(GetLevel()->GetRandomSquare(this), true); + + if(!IsPlayer() && CanBeSeenByPlayer()) + ADD_MESSAGE("%s appears.", CHAR_NAME(INDEFINITE)); + + if(GetAction() && GetAction()->IsVoluntary()) + GetAction()->Terminate(false); + } + else if(PossibleRoom && PossibleRoom->IsOKToTeleportInto()) // If it's inside of a room, check whether a ward is active that might impede the player + { + if(TelePos != ERROR_V2) + Move(TelePos, true); + else + Move(GetLevel()->GetRandomSquare(this), true); + + if(!IsPlayer() && CanBeSeenByPlayer()) + ADD_MESSAGE("%s appears.", CHAR_NAME(INDEFINITE)); + + if(GetAction() && GetAction()->IsVoluntary()) + GetAction()->Terminate(false); + } + else + { + if(IsPlayer()) + ADD_MESSAGE("A mighty force blasts you back to where you were standing. A ward prevents you from teleporting."); + + game::GetCurrentLevel()->Explosion(this, CONST_S("killed by an explosion triggered when attempting to teleport into room protected by a ward"), PLAYER->GetPos(), 300 >> 3, false); + /* + beamdata Beam + ( + this, + CONST_S("killed by an explosion triggered when attempting to teleport into room protected by a ward"), + YOURSELF, + 3 // or 0 ? + ); + lsquare* Square = GetNearLSquare(GetPos()); + Square->DrawParticles(RED); + Square->FireBall(Beam);*/ + } + +} + +void character::DoDetecting() +{ + material* TempMaterial; + + for(;;) + { + festring Temp = game::DefaultQuestion(CONST_S("What material do you want to detect?"), + game::GetDefaultDetectMaterial()); + TempMaterial = protosystem::CreateMaterial(Temp); + + if(TempMaterial) + break; + else + game::DrawEverythingNoBlit(); + } + + level* Level = GetLevel(); + int Squares = Level->DetectMaterial(TempMaterial); + + if(Squares > GetAttribute(INTELLIGENCE) * (25 + RAND() % 51)) + { + ADD_MESSAGE("An enormous burst of geographical information overwhelms your consciousness. Your mind cannot cope with it and your memories blur."); + Level->BlurMemory(); + BeginTemporaryState(CONFUSED, 1000 + RAND() % 1000); + EditExperience(INTELLIGENCE, -100, 1 << 12); + } + else if(!Squares) + { + ADD_MESSAGE("You feel a sudden urge to imagine the dark void of a starless night sky."); + EditExperience(INTELLIGENCE, 200, 1 << 12); + } + else + { + ADD_MESSAGE("You feel attracted to all things made of %s.", TempMaterial->GetName(false, false).CStr()); + game::PositionQuestion(CONST_S("Detecting material [direction keys move cursor, space exits]"), GetPos(), 0, 0, false); + EditExperience(INTELLIGENCE, 300, 1 << 12); + } + + delete TempMaterial; + Level->CalculateLuminances(); + game::SendLOSUpdateRequest(); +} + +void character::RestoreHP() +{ + doforbodyparts()(this, &bodypart::FastRestoreHP); + HP = MaxHP; +} + +void character::RestoreLivingHP() +{ + HP = 0; + + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart && BodyPart->CanRegenerate()) + { + BodyPart->FastRestoreHP(); + HP += BodyPart->GetHP(); + } + } +} + +truth character::AllowDamageTypeBloodSpill(int Type) +{ + switch(Type&0xFFF) + { + case PHYSICAL_DAMAGE: + case SOUND: + case ENERGY: + return true; + case ACID: + case FIRE: + case DRAIN: + case POISON: + case ELECTRICITY: + case MUSTARD_GAS_DAMAGE: + case PSI: + return false; + } + + ABORT("Unknown blood effect destroyed the dungeon!"); + return false; +} + +/* Returns truly done damage */ + +int character::ReceiveBodyPartDamage(character* Damager, int Damage, int Type, int BodyPartIndex, int Direction, truth PenetrateResistance, truth Critical, truth ShowNoDamageMsg, truth CaptureBodyPart) +{ + bodypart* BodyPart = GetBodyPart(BodyPartIndex); + + if(!Damager || Damager->AttackMayDamageArmor()) + BodyPart->DamageArmor(Damager, Damage, Type); + + if(!PenetrateResistance) + Damage -= (BodyPart->GetTotalResistance(Type) >> 1) + RAND() % ((BodyPart->GetTotalResistance(Type) >> 1) + 1); + + if(int(Damage) < 1) + if(Critical) + Damage = 1; + else + { + if(ShowNoDamageMsg) + { + if(IsPlayer()) + ADD_MESSAGE("You are not hurt."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s is not hurt.", GetPersonalPronoun().CStr()); + } + + return 0; + } + + if(Critical && AllowDamageTypeBloodSpill(Type) && !game::IsInWilderness()) + { + BodyPart->SpillBlood(2 + (RAND() & 1)); + + for(int d = 0; d < GetNeighbourSquares(); ++d) + { + lsquare* Square = GetNeighbourLSquare(d); + + if(Square && Square->IsFlyable()) + BodyPart->SpillBlood(1, Square->GetPos()); + } + } + + if(BodyPart->ReceiveDamage(Damager, Damage, Type, Direction) + && BodyPartCanBeSevered(BodyPartIndex)) + { + if(DamageTypeDestroysBodyPart(Type)) + { + if(IsPlayer()) + ADD_MESSAGE("Your %s is destroyed!", BodyPart->GetBodyPartName().CStr()); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s %s is destroyed!", GetPossessivePronoun().CStr(), BodyPart->GetBodyPartName().CStr()); + + GetBodyPart(BodyPartIndex)->DropEquipment(); + item* Severed = SevereBodyPart(BodyPartIndex); + + if(Severed) + Severed->DestroyBodyPart(!game::IsInWilderness() ? GetStackUnder() : GetStack()); + + SendNewDrawRequest(); + + if(IsPlayer()) + game::AskForKeyPress(CONST_S("Bodypart destroyed! [press any key to continue]")); + } + else + { + if(IsPlayer()) + ADD_MESSAGE("Your %s is severed off!", BodyPart->GetBodyPartName().CStr()); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s %s is severed off!", GetPossessivePronoun().CStr(), BodyPart->GetBodyPartName().CStr()); + + item* Severed = SevereBodyPart(BodyPartIndex); + SendNewDrawRequest(); + + if(Severed) + { + if(CaptureBodyPart) + Damager->GetLSquareUnder()->AddItem(Severed); + else if(!game::IsInWilderness()) + { + /** No multi-tile humanoid support! */ + + GetStackUnder()->AddItem(Severed); + + if(Direction != YOURSELF) + Severed->Fly(0, Direction, Damage); + } + else + GetStack()->AddItem(Severed); + + Severed->DropEquipment(); + } + else if(IsPlayer() || CanBeSeenByPlayer()) + ADD_MESSAGE("It vanishes."); + + if(IsPlayer()) + game::AskForKeyPress(CONST_S("Bodypart severed! [press any key to continue]")); + } + + if(CanPanicFromSeveredBodyPart() + && RAND() % 100 < GetPanicLevel() + && !StateIsActivated(PANIC) && !IsDead()) + BeginTemporaryState(PANIC, 1000 + RAND() % 1001); + + SpecialBodyPartSeverReaction(); + } + + if(!IsDead()) + CheckPanic(500); + + return Damage; +} + +/* Returns 0 if bodypart disappears */ + +item* character::SevereBodyPart(int BodyPartIndex, truth ForceDisappearance, stack* EquipmentDropStack) +{ + bodypart* BodyPart = GetBodyPart(BodyPartIndex); + + if(StateIsActivated(LEPROSY)) + BodyPart->GetMainMaterial()->SetIsInfectedByLeprosy(true); + + if(ForceDisappearance + || BodyPartsDisappearWhenSevered() + || StateIsActivated(POLYMORPHED) + || game::AllBodyPartsVanish()) + { + BodyPart->DropEquipment(EquipmentDropStack); + BodyPart->RemoveFromSlot(); + CalculateAttributeBonuses(); + CalculateBattleInfo(); + BodyPart->SendToHell(); + SignalPossibleTransparencyChange(); + RemoveTraps(BodyPartIndex); + return 0; + } + else + { + BodyPart->SetOwnerDescription("of " + GetName(INDEFINITE)); + BodyPart->SetIsUnique(LeftOversAreUnique()); + UpdateBodyPartPicture(BodyPartIndex, true); + BodyPart->RemoveFromSlot(); + BodyPart->RandomizePosition(); + CalculateAttributeBonuses(); + CalculateBattleInfo(); + BodyPart->Enable(); + SignalPossibleTransparencyChange(); + RemoveTraps(BodyPartIndex); + return BodyPart; + } +} + +/* The second int is actually TargetFlags, which is not used here, but seems to be used in humanoid::ReceiveDamage. Returns true if the character really receives damage */ + +truth character::ReceiveDamage(character* Damager, int Damage, int Type, int, int Direction, truth, truth PenetrateArmor, truth Critical, truth ShowMsg) +{ + truth Affected = ReceiveBodyPartDamage(Damager, Damage, Type, 0, Direction, PenetrateArmor, Critical, ShowMsg); + + if(DamageTypeAffectsInventory(Type)) + { + for(int c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment) + Equipment->ReceiveDamage(Damager, Damage, Type); + } + + GetStack()->ReceiveDamage(Damager, Damage, Type); + } + + return Affected; +} + +festring character::GetDescription(int Case) const +{ + if(IsPlayer()) + return CONST_S("you"); + else if(CanBeSeenByPlayer()) + return GetName(Case); + else + return CONST_S("something"); +} + +festring character::GetPersonalPronoun(truth PlayersView) const +{ + if(IsPlayer() && PlayersView) + return CONST_S("you"); + else if(GetSex() == UNDEFINED + || (PlayersView && !CanBeSeenByPlayer() && !game::GetSeeWholeMapCheatMode())) + return CONST_S("it"); + else if(GetSex() == MALE) + return CONST_S("he"); + else + return CONST_S("she"); +} + +festring character::GetPossessivePronoun(truth PlayersView) const +{ + if(IsPlayer() && PlayersView) + return CONST_S("your"); + else if(GetSex() == UNDEFINED + || (PlayersView && !CanBeSeenByPlayer() && !game::GetSeeWholeMapCheatMode())) + return CONST_S("its"); + else if(GetSex() == MALE) + return CONST_S("his"); + else + return CONST_S("her"); +} + +festring character::GetObjectPronoun(truth PlayersView) const +{ + if(IsPlayer() && PlayersView) + return CONST_S("you"); + else if(GetSex() == UNDEFINED + || (PlayersView && !CanBeSeenByPlayer() && !game::GetSeeWholeMapCheatMode())) + return CONST_S("it"); + else if(GetSex() == MALE) + return CONST_S("him"); + else + return CONST_S("her"); +} + +void character::AddName(festring& String, int Case) const +{ + if(AssignedName.IsEmpty()) + id::AddName(String, Case); + else if(!(Case & PLURAL)) + { + if(!ShowClassDescription()) + String << AssignedName; + else + { + String << AssignedName << ' '; + id::AddName(String, (Case|ARTICLE_BIT)&~INDEFINE_BIT); + } + } + else + { + id::AddName(String, Case); + String << " named " << AssignedName; + } +} + +int character::GetHungerState() const +{ + if(!UsesNutrition()) + return NOT_HUNGRY; + else if(GetNP() > OVER_FED_LEVEL) + return OVER_FED; + else if(GetNP() > BLOATED_LEVEL) + return BLOATED; + else if(GetNP() > SATIATED_LEVEL) + return SATIATED; + else if(GetNP() > NOT_HUNGER_LEVEL) + return NOT_HUNGRY; + else if(GetNP() > HUNGER_LEVEL) + return HUNGRY; + else if(GetNP() > VERY_HUNGER_LEVEL) + return VERY_HUNGRY; + else + return STARVING; +} + +truth character::CanConsume(material* Material) const +{ + return GetConsumeFlags() & Material->GetConsumeType(); +} + +void character::SetTemporaryStateCounter(long State, int What) +{ + for(int c = 0; c < STATES; ++c) + if((1 << c) & State) + TemporaryStateCounter[c] = What; +} + +void character::EditTemporaryStateCounter(long State, int What) +{ + for(int c = 0; c < STATES; ++c) + if((1 << c) & State) + TemporaryStateCounter[c] += What; +} + +int character::GetTemporaryStateCounter(long State) const +{ + for(int c = 0; c < STATES; ++c) + if((1 << c) & State) + return TemporaryStateCounter[c]; + + ABORT("Illegal GetTemporaryStateCounter request!"); + return 0; +} + +truth character::CheckKick() const +{ + if(!CanKick()) + { + if(IsPlayer()) + ADD_MESSAGE("This race can't kick."); + + return false; + } + else + return true; +} + +int character::GetResistance(int Type) const +{ + switch(Type&0xFFF) + { + case PHYSICAL_DAMAGE: + case SOUND: + case DRAIN: + case MUSTARD_GAS_DAMAGE: + case PSI: + return 0; + case ENERGY: return GetEnergyResistance(); + case FIRE: return GetFireResistance(); + case POISON: return GetPoisonResistance(); + case ELECTRICITY: return GetElectricityResistance(); + case ACID: return GetAcidResistance(); + } + + ABORT("Resistance lack detected!"); + return 0; +} + +void character::Regenerate() +{ + if(HP == MaxHP) + return; + + long RegenerationBonus = 0; + truth NoHealableBodyParts = true; + + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart && BodyPart->CanRegenerate()) + { + RegenerationBonus += BodyPart->GetMaxHP(); + + if(NoHealableBodyParts && BodyPart->GetHP() < BodyPart->GetMaxHP()) + NoHealableBodyParts = false; + } + } + + if(!RegenerationBonus || NoHealableBodyParts) + return; + + RegenerationBonus *= (50 + GetAttribute(ENDURANCE)); + + if(Action && Action->IsRest()) + if(SquaresUnder == 1) + RegenerationBonus *= GetSquareUnder()->GetRestModifier() << 1; + else + { + int Lowest = GetSquareUnder(0)->GetRestModifier(); + + for(int c = 1; c < GetSquaresUnder(); ++c) + { + int Mod = GetSquareUnder(c)->GetRestModifier(); + + if(Mod < Lowest) + Lowest = Mod; + } + + RegenerationBonus *= Lowest << 1; + } + + RegenerationCounter += RegenerationBonus; + + while(RegenerationCounter > 1250000) + { + bodypart* BodyPart = HealHitPoint(); + + if(!BodyPart) + break; + + EditNP(-Max(7500 / MaxHP, 1)); + RegenerationCounter -= 1250000; + int HP = BodyPart->GetHP(); + EditExperience(ENDURANCE, Min(1000 * BodyPart->GetMaxHP() / (HP * HP), 300), 1000); + } +} + +void character::PrintInfo() const +{ + felist Info(CONST_S("Information about ") + GetName(DEFINITE)); + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if((EquipmentEasilyRecognized(c) || game::WizardModeIsActive()) && Equipment) + { + int ImageKey = game::AddToItemDrawVector(itemvector(1, Equipment)); + Info.AddEntry(festring(GetEquipmentName(c)) + ": " + Equipment->GetName(INDEFINITE), LIGHT_GRAY, 0, ImageKey, true); + } + } + + if(Info.IsEmpty()) + ADD_MESSAGE("There's nothing special to tell about %s.", CHAR_NAME(DEFINITE)); + else + { + game::SetStandardListAttributes(Info); + Info.SetEntryDrawer(game::ItemEntryDrawer); + Info.Draw(); + } + + game::ClearItemDrawVector(); +} + +truth character::TryToRiseFromTheDead() +{ + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart) + { + BodyPart->ResetSpoiling(); + + if(BodyPart->CanRegenerate() || BodyPart->GetHP() < 1) + BodyPart->SetHP(1); + } + } + + ResetStates(); + return true; +} + +truth character::RaiseTheDead(character*) +{ + truth Useful = false; + + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(!BodyPart && CanCreateBodyPart(c)) + { + CreateBodyPart(c)->SetHP(1); + + if(IsPlayer()) + ADD_MESSAGE("Suddenly you grow a new %s.", GetBodyPartName(c).CStr()); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s grows a new %s.", CHAR_NAME(DEFINITE), GetBodyPartName(c).CStr()); + + Useful = true; + } + else if(BodyPart && BodyPart->CanRegenerate() && BodyPart->GetHP() < 1) + BodyPart->SetHP(1); + } + + if(!Useful) + { + if(IsPlayer()) + ADD_MESSAGE("You shudder."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s shudders.", CHAR_NAME(DEFINITE)); + } + + return Useful; +} + +void character::SetSize(int Size) +{ + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart) + BodyPart->SetSize(GetBodyPartSize(c, Size)); + } +} + +long character::GetBodyPartSize(int I, int TotalSize) const +{ + if(I == TORSO_INDEX) + return TotalSize; + else + { + ABORT("Weird bodypart size request for a character!"); + return 0; + } +} + +long character::GetBodyPartVolume(int I) const +{ + if(I == TORSO_INDEX) + return GetTotalVolume(); + else + { + ABORT("Weird bodypart volume request for a character!"); + return 0; + } +} + +void character::CreateBodyParts(int SpecialFlags) +{ + for(int c = 0; c < BodyParts; ++c) + if(CanCreateBodyPart(c)) + CreateBodyPart(c, SpecialFlags); +} + +void character::RestoreBodyParts() +{ + for(int c = 0; c < BodyParts; ++c) + if(!GetBodyPart(c) && CanCreateBodyPart(c)) + CreateBodyPart(c); +} + +void character::UpdatePictures() +{ + if(!PictureUpdatesAreForbidden()) + for(int c = 0; c < BodyParts; ++c) + UpdateBodyPartPicture(c, false); +} + +bodypart* character::MakeBodyPart(int I) const +{ + if(I == TORSO_INDEX) + return normaltorso::Spawn(0, NO_MATERIALS); + else + { + ABORT("Weird bodypart to make for a character!"); + return 0; + } +} + +bodypart* character::CreateBodyPart(int I, int SpecialFlags) +{ + bodypart* BodyPart = MakeBodyPart(I); + material* Material = CreateBodyPartMaterial(I, GetBodyPartVolume(I)); + BodyPart->InitMaterials(Material, false); + BodyPart->SetSize(GetBodyPartSize(I, GetTotalSize())); + BodyPart->SetBloodMaterial(GetBloodMaterial()); + BodyPart->SetNormalMaterial(Material->GetConfig()); + SetBodyPart(I, BodyPart); + BodyPart->InitSpecialAttributes(); + + if(!(SpecialFlags & NO_PIC_UPDATE)) + UpdateBodyPartPicture(I, false); + + if(!IsInitializing()) + { + CalculateBattleInfo(); + SendNewDrawRequest(); + SignalPossibleTransparencyChange(); + } + + return BodyPart; +} + +v2 character::GetBodyPartBitmapPos(int I, truth) const +{ + if(I == TORSO_INDEX) + return GetTorsoBitmapPos(); + else + { + ABORT("Weird bodypart BitmapPos request for a character!"); + return v2(); + } +} + +void character::UpdateBodyPartPicture(int I, truth Severed) +{ + bodypart* BP = GetBodyPart(I); + + if(BP) + { + BP->SetBitmapPos(GetBodyPartBitmapPos(I, Severed)); + BP->GetMainMaterial()->SetSkinColor(GetBodyPartColorA(I, Severed)); + BP->GetMainMaterial()->SetSkinColorIsSparkling(GetBodyPartSparkleFlags(I) & SPARKLING_A); + BP->SetMaterialColorB(GetBodyPartColorB(I, Severed)); + BP->SetMaterialColorC(GetBodyPartColorC(I, Severed)); + BP->SetMaterialColorD(GetBodyPartColorD(I, Severed)); + BP->SetSparkleFlags(GetBodyPartSparkleFlags(I)); + BP->SetSpecialFlags(GetSpecialBodyPartFlags(I)); + BP->SetWobbleData(GetBodyPartWobbleData(I)); + BP->UpdatePictures(); + } +} + +void character::LoadDataBaseStats() +{ + for(int c = 0; c < BASE_ATTRIBUTES; ++c) + { + BaseExperience[c] = DataBase->NaturalExperience[c]; + + if(BaseExperience[c]) + LimitRef(BaseExperience[c], MIN_EXP, MAX_EXP); + } + + SetMoney(GetDefaultMoney()); + SetInitialSweatMaterial(GetDefaultSweatMaterial()); + const fearray& Skills = GetKnownCWeaponSkills(); + + if(Skills.Size) + { + const fearray& Hits = GetCWeaponSkillHits(); + + if(Hits.Size == 1) + { + for(uint c = 0; c < Skills.Size; ++c) + if(Skills[c] < AllowedWeaponSkillCategories) + CWeaponSkill[Skills[c]].AddHit(Hits[0] * 100); + } + else if(Hits.Size == Skills.Size) + { + for(uint c = 0; c < Skills.Size; ++c) + if(Skills[c] < AllowedWeaponSkillCategories) + CWeaponSkill[Skills[c]].AddHit(Hits[c] * 100); + } + else + ABORT("Illegal weapon skill hit array size detected!"); + } +} + +character* characterprototype::SpawnAndLoad(inputfile& SaveFile) const +{ + character* Char = Spawner(0, LOAD); + Char->Load(SaveFile); + Char->CalculateAll(); + return Char; +} + +void character::Initialize(int NewConfig, int SpecialFlags) +{ + Flags |= C_INITIALIZING|C_IN_NO_MSG_MODE; + CalculateBodyParts(); + CalculateAllowedWeaponSkillCategories(); + CalculateSquaresUnder(); + BodyPartSlot = new bodypartslot[BodyParts]; + OriginalBodyPartID = new std::list[BodyParts]; + CWeaponSkill = new cweaponskill[AllowedWeaponSkillCategories]; + SquareUnder = new square*[SquaresUnder]; + + if(SquaresUnder == 1) + *SquareUnder = 0; + else + memset(SquareUnder, 0, SquaresUnder * sizeof(square*)); + + int c; + + for(c = 0; c < BodyParts; ++c) + BodyPartSlot[c].SetMaster(this); + + if(!(SpecialFlags & LOAD)) + { + ID = game::CreateNewCharacterID(this); + databasecreator::InstallDataBase(this, NewConfig); + LoadDataBaseStats(); + TemporaryState |= GetClassStates(); + + if(TemporaryState) + for(c = 0; c < STATES; ++c) + if(TemporaryState & (1 << c)) + TemporaryStateCounter[c] = PERMANENT; + + CreateBodyParts(SpecialFlags | NO_PIC_UPDATE); + InitSpecialAttributes(); + CommandFlags = GetDefaultCommandFlags(); + + if(GetAttribute(INTELLIGENCE, false) < 8) // gum + CommandFlags &= ~DONT_CONSUME_ANYTHING_VALUABLE; + + if(!GetDefaultName().IsEmpty()) + SetAssignedName(GetDefaultName()); + } + + if(!(SpecialFlags & LOAD)) + PostConstruct(); + + if(!(SpecialFlags & LOAD)) + { + if(!(SpecialFlags & NO_EQUIPMENT)) + CreateInitialEquipment((SpecialFlags & NO_EQUIPMENT_PIC_UPDATE) >> 1); + + if(!(SpecialFlags & NO_PIC_UPDATE)) + UpdatePictures(); + + CalculateAll(); + RestoreHP(); + RestoreStamina(); + } + + Flags &= ~(C_INITIALIZING|C_IN_NO_MSG_MODE); +} + +truth character::TeleportNear(character* Caller) +{ + v2 Where = GetLevel()->GetNearestFreeSquare(this, Caller->GetPos()); + + if(Where == ERROR_V2) + return false; + + Move(Where, true); + return true; +} + +void character::ReceiveHeal(long Amount) +{ + int c; + + for(c = 0; c < Amount / 10; ++c) + if(!HealHitPoint()) + break; + + Amount -= c * 10; + + if(RAND() % 10 < Amount) + HealHitPoint(); + + if(Amount >= 250 || RAND() % 250 < Amount) + { + bodypart* NewBodyPart = GenerateRandomBodyPart(); + + if(!NewBodyPart) + return; + + NewBodyPart->SetHP(1); + + if(IsPlayer()) + ADD_MESSAGE("You grow a new %s.", NewBodyPart->GetBodyPartName().CStr()); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s grows a new %s.", CHAR_NAME(DEFINITE), NewBodyPart->GetBodyPartName().CStr()); + } +} + +void character::AddHealingLiquidConsumeEndMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel better."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s looks healthier.", CHAR_NAME(DEFINITE)); +} + +void character::ReceiveSchoolFood(long SizeOfEffect) +{ + SizeOfEffect += RAND() % SizeOfEffect; + + if(SizeOfEffect >= 250) + VomitAtRandomDirection(SizeOfEffect); + + if(!(RAND() % 3) && SizeOfEffect >= 500 + && EditAttribute(ENDURANCE, SizeOfEffect / 500)) + { + if(IsPlayer()) + ADD_MESSAGE("You gain a little bit of toughness for surviving this stuff."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s looks tougher.", CHAR_NAME(DEFINITE)); + } + + BeginTemporaryState(POISONED, (SizeOfEffect >> 1)); +} + +void character::AddSchoolFoodConsumeEndMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("Yuck! This stuff tasted like vomit and old mousepads."); +} + +void character::AddSchoolFoodHitMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("Yuck! This stuff feels like vomit and old mousepads."); +} + +void character::ReceiveNutrition(long SizeOfEffect) +{ + EditNP(SizeOfEffect); +} + +void character::ReceiveOmmelUrine(long Amount) +{ + EditExperience(ARM_STRENGTH, 500, Amount << 4); + EditExperience(LEG_STRENGTH, 500, Amount << 4); + + if(IsPlayer()) + game::DoEvilDeed(Amount / 25); +} + +void character::ReceiveOmmelCerumen(long Amount) +{ + EditExperience(INTELLIGENCE, 750, Amount << 5); + EditExperience(WISDOM, 750, Amount << 5); + + if(IsPlayer()) + game::DoEvilDeed(Amount / 25); +} + +void character::ReceiveOmmelSweat(long Amount) +{ + EditExperience(AGILITY, 500, Amount << 4); + EditExperience(DEXTERITY, 500, Amount << 4); + RestoreStamina(); + + if(IsPlayer()) + game::DoEvilDeed(Amount / 25); +} + +void character::ReceiveOmmelTears(long Amount) +{ + EditExperience(PERCEPTION, 500, Amount << 4); + EditExperience(CHARISMA, 500, Amount << 4); + + if(IsPlayer()) + game::DoEvilDeed(Amount / 25); +} + +void character::ReceiveOmmelSnot(long Amount) +{ + EditExperience(ENDURANCE, 500, Amount << 5); + RestoreLivingHP(); + + if(IsPlayer()) + game::DoEvilDeed(Amount / 25); +} + +void character::ReceiveOmmelBone(long Amount) +{ + EditExperience(ARM_STRENGTH, 500, Amount << 6); + EditExperience(LEG_STRENGTH, 500, Amount << 6); + EditExperience(DEXTERITY, 500, Amount << 6); + EditExperience(AGILITY, 500, Amount << 6); + EditExperience(ENDURANCE, 500, Amount << 6); + EditExperience(PERCEPTION, 500, Amount << 6); + EditExperience(INTELLIGENCE, 500, Amount << 6); + EditExperience(WISDOM, 500, Amount << 6); + EditExperience(CHARISMA, 500, Amount << 6); + RestoreLivingHP(); + RestoreStamina(); + + if(IsPlayer()) + game::DoEvilDeed(Amount / 25); +} + +void character::AddOmmelConsumeEndMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel a primitive force coursing through your veins."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s looks more powerful.", CHAR_NAME(DEFINITE)); +} + +void character::ReceivePepsi(long Amount) +{ + ReceiveDamage(0, Amount / 100, POISON, TORSO); + EditExperience(PERCEPTION, Amount, 1 << 14); + + if(CheckDeath(CONST_S("was poisoned by pepsi"), 0)) + return; + + if(IsPlayer()) + game::DoEvilDeed(Amount / 10); +} + +void character::AddPepsiConsumeEndMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("Urgh. You feel your guruism fading away."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s looks very lame.", CHAR_NAME(DEFINITE)); +} + +void character::ReceiveDarkness(long Amount) +{ + EditExperience(INTELLIGENCE, -Amount / 5, 1 << 13); + EditExperience(WISDOM, -Amount / 5, 1 << 13); + EditExperience(CHARISMA, -Amount / 5, 1 << 13); + + if(IsPlayer()) + game::DoEvilDeed(int(Amount / 50)); +} + +void character::AddFrogFleshConsumeEndMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("Arg. You feel the fate of a navastater placed upon you..."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s looks like a navastater.", CHAR_NAME(DEFINITE)); +} + +void character::ReceiveKoboldFlesh(long) +{ + /* As it is commonly known, the possibility of fainting per 500 cubic + centimeters of kobold flesh is exactly 5%. */ + + if(!(RAND() % 20)) + { + if(IsPlayer()) + ADD_MESSAGE("You lose control of your legs and fall down."); + + LoseConsciousness(250 + RAND_N(250)); + } +} + +void character::AddKoboldFleshConsumeEndMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("This stuff tasted really funny."); +} + +void character::AddKoboldFleshHitMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel very funny."); +} + +void character::AddBoneConsumeEndMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel like a hippie."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s barks happily.", CHAR_NAME(DEFINITE)); // this suspects that nobody except dogs can eat bones +} + +truth character::RawEditAttribute(double& Experience, int Amount) const +{ + /* Check if the attribute is disabled for creature */ + + if(!Experience) + return false; + + if((Amount < 0 && Experience < 2 * EXP_MULTIPLIER) + || (Amount > 0 && Experience > 999 * EXP_MULTIPLIER)) + return false; + + Experience += Amount * EXP_MULTIPLIER; + LimitRef(Experience, MIN_EXP, MAX_EXP); + return true; +} + +//truth character::ChangeSweatMaterial(int NewSweatMaterial) const +//{ +// if(!IsPlayer()) +// return false; +// +// if(CurrentSweatMaterial) +// { +// EditCurrentSweatMaterial(NewSweatMaterial); +// return true; +// } +// +// else +// return false; +// +//} + +void character::DrawPanel(truth AnimationDraw) const +{ + if(AnimationDraw) + { + DrawStats(true); + return; + } + + igraph::BlitBackGround(v2(19 + (game::GetScreenXSize() << 4), 0), v2(RES.X - 19 - (game::GetScreenXSize() << 4), RES.Y)); + igraph::BlitBackGround(v2(16, 45 + (game::GetScreenYSize() << 4)), v2(game::GetScreenXSize() << 4, 9)); + FONT->Printf(DOUBLE_BUFFER, v2(16, 45 + (game::GetScreenYSize() << 4)), WHITE, "%s", GetPanelName().CStr()); + game::UpdateAttributeMemory(); + int PanelPosX = RES.X - 96; + int PanelPosY = DrawStats(false); + PrintAttribute("End", ENDURANCE, PanelPosX, PanelPosY++); + PrintAttribute("Per", PERCEPTION, PanelPosX, PanelPosY++); + PrintAttribute("Int", INTELLIGENCE, PanelPosX, PanelPosY++); + PrintAttribute("Wis", WISDOM, PanelPosX, PanelPosY++); + PrintAttribute("Wil", WILL_POWER, PanelPosX, PanelPosY++); + PrintAttribute("Cha", CHARISMA, PanelPosX, PanelPosY++); + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), WHITE, "Siz %d", GetSize()); + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), + IsInBadCondition() ? RED : WHITE, "HP %d/%d", GetHP(), GetMaxHP()); + ++PanelPosY; + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), WHITE, "Gold: %ld", GetMoney()); + ++PanelPosY; + + if(game::IsInWilderness()) + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), WHITE, "Worldmap"); + else + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), WHITE, "%s", game::GetCurrentDungeon()->GetShortLevelDescription(game::GetCurrentLevelIndex()).CapitalizeCopy().CStr()); + + ivantime Time; + game::GetTime(Time); + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), WHITE, "Day %d", Time.Day); + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), WHITE, "Time %d:%s%d", Time.Hour, Time.Min < 10 ? "0" : "", Time.Min); + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), WHITE, "Turn %ld", game::GetTurn()); + + ++PanelPosY; + + if(GetAction()) + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), WHITE, "%s", festring(GetAction()->GetDescription()).CapitalizeCopy().CStr()); + + for(int c = 0; c < STATES; ++c) + if(!(StateData[c].Flags & SECRET) && StateIsActivated(1 << c) && (1 << c != HASTE || !StateIsActivated(SLOW)) && (1 << c != SLOW || !StateIsActivated(HASTE))) + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), (1 << c) & EquipmentState || TemporaryStateCounter[c] == PERMANENT ? BLUE : WHITE, "%s", StateData[c].Description); + + /* Make this more elegant!!! */ + + if(GetHungerState() == STARVING) + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), RED, "Starving"); + else if(GetHungerState() == VERY_HUNGRY) + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), BLUE, "Very hungry"); + else if(GetHungerState() == HUNGRY) + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), BLUE, "Hungry"); + else if(GetHungerState() == SATIATED) + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), WHITE, "Satiated"); + else if(GetHungerState() == BLOATED) + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), WHITE, "Bloated"); + else if(GetHungerState() == OVER_FED) + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), WHITE, "Overfed!"); + + switch(GetBurdenState()) + { + case OVER_LOADED: + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), RED, "Overload!"); + break; + case STRESSED: + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), BLUE, "Stressed"); + break; + case BURDENED: + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), BLUE, "Burdened"); + } + + switch(GetTirednessState()) + { + case FAINTING: + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), RED, "Fainting"); + break; + case EXHAUSTED: + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), WHITE, "Exhausted"); + break; + } + + if(game::PlayerIsRunning()) + { + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), WHITE, GetRunDescriptionLine(0)); + cchar* SecondLine = GetRunDescriptionLine(1); + + if(strlen(SecondLine)) + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), WHITE, SecondLine); + } + /* // This was found to come up twice on the panel + switch(GetTirednessState()) + { + case FAINTING: + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), RED, "Fainting"); + break; + case EXHAUSTED: + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY++ * 10), WHITE, "Exhausted"); + break; + }*/ +} + +void character::CalculateDodgeValue() +{ + DodgeValue = 0.05 * GetMoveEase() * GetAttribute(AGILITY) / sqrt(GetSize()); + + if(IsFlying()) + DodgeValue *= 2; + + if(DodgeValue < 1) + DodgeValue = 1; +} + +truth character::DamageTypeAffectsInventory(int Type) +{ + switch(Type&0xFFF) + { + case SOUND: + case ENERGY: + case ACID: + case FIRE: + case ELECTRICITY: + return true; + case PHYSICAL_DAMAGE: + case POISON: + case DRAIN: + case MUSTARD_GAS_DAMAGE: + case PSI: + return false; + } + + ABORT("Unknown reaping effect destroyed dungeon!"); + return false; +} + +int character::CheckForBlockWithArm(character* Enemy, item* Weapon, arm* Arm, double WeaponToHitValue, int Damage, int Success, int Type) +{ + int BlockStrength = Arm->GetBlockCapability(); + double BlockValue = Arm->GetBlockValue(); + + if(BlockStrength && BlockValue) + { + item* Blocker = Arm->GetWielded(); + + if(RAND() % int(100 + WeaponToHitValue / BlockValue / (1 << BlocksSinceLastTurn) * (100 + Success)) < 100) + { + int NewDamage = BlockStrength < Damage ? Damage - BlockStrength : 0; + + switch(Type) + { + case UNARMED_ATTACK: + AddBlockMessage(Enemy, Blocker, Enemy->UnarmedHitNoun(), NewDamage); + break; + case WEAPON_ATTACK: + AddBlockMessage(Enemy, Blocker, "attack", NewDamage); + break; + case KICK_ATTACK: + AddBlockMessage(Enemy, Blocker, Enemy->KickNoun(), NewDamage); + break; + case BITE_ATTACK: + AddBlockMessage(Enemy, Blocker, Enemy->BiteNoun(), NewDamage); + break; + } + + long Weight = Blocker->GetWeight(); + long StrExp = Limit(15 * Weight / 200L, 75L, 300L); + long DexExp = Weight ? Limit(75000L / Weight, 75L, 300L) : 300; + Arm->EditExperience(ARM_STRENGTH, StrExp, 1 << 8); + Arm->EditExperience(DEXTERITY, DexExp, 1 << 8); + EditStamina(-10000 / GetAttribute(ARM_STRENGTH), false); + + if(Arm->TwoHandWieldIsActive()) + { + arm* PairArm = Arm->GetPairArm(); + PairArm->EditExperience(ARM_STRENGTH, StrExp, 1 << 8); + PairArm->EditExperience(DEXTERITY, DexExp, 1 << 8); + } + + Blocker->WeaponSkillHit(Enemy->CalculateWeaponSkillHits(this)); + Blocker->ReceiveDamage(this, Damage, PHYSICAL_DAMAGE); + + Blocker->BlockEffect(this, Enemy, Weapon, Type); + + if(Weapon) + Weapon->ReceiveDamage(Enemy, Damage - NewDamage, PHYSICAL_DAMAGE); + + if(BlocksSinceLastTurn < 16) + ++BlocksSinceLastTurn; + + return NewDamage; + } + } + + return Damage; +} + +long character::GetStateAPGain(long BaseAPGain) const +{ + if(!StateIsActivated(HASTE) == !StateIsActivated(SLOW)) + return BaseAPGain; + else if(StateIsActivated(HASTE)) + return (BaseAPGain * 5) >> 2; + else + return (BaseAPGain << 2) / 5; +} + +void character::SignalEquipmentAdd(int EquipmentIndex) +{ + item* Equipment = GetEquipment(EquipmentIndex); + + if(Equipment->IsInCorrectSlot(EquipmentIndex)) + { + long AddedStates = Equipment->GetGearStates(); + + if(AddedStates) + for(int c = 0; c < STATES; ++c) + if(AddedStates & (1 << c)) + { + if(!StateIsActivated(1 << c)) + { + if(!IsInNoMsgMode()) + (this->*StateData[c].PrintBeginMessage)(); + + EquipmentState |= 1 << c; + + if(StateData[c].BeginHandler) + (this->*StateData[c].BeginHandler)(); + } + else + EquipmentState |= 1 << c; + } + } + + if(!IsInitializing() && Equipment->IsInCorrectSlot(EquipmentIndex)) + ApplyEquipmentAttributeBonuses(Equipment); +} + +void character::SignalEquipmentRemoval(int, citem* Item) +{ + CalculateEquipmentState(); + + if(CalculateAttributeBonuses()) + CheckDeath(festring("lost ") + GetPossessivePronoun(false) + " vital " + Item->GetName(INDEFINITE)); +} + +void character::CalculateEquipmentState() +{ + int c; + long Back = EquipmentState; + EquipmentState = 0; + + for(c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment && Equipment->IsInCorrectSlot(c)) + EquipmentState |= Equipment->GetGearStates(); + } + + for(c = 0; c < STATES; ++c) + if(Back & (1 << c) && !StateIsActivated(1 << c)) + { + if(StateData[c].EndHandler) + { + (this->*StateData[c].EndHandler)(); + + if(!IsEnabled()) + return; + } + + if(!IsInNoMsgMode()) + (this->*StateData[c].PrintEndMessage)(); + } +} + +/* Counter = duration in ticks */ + +void character::BeginTemporaryState(long State, int Counter) +{ + if(!Counter) + return; + + int Index; + + if(State == POLYMORPHED) + ABORT("No Polymorphing with BeginTemporaryState!"); + + for(Index = 0; Index < STATES; ++Index) + if(1 << Index == State) + break; + + if(Index == STATES) + ABORT("BeginTemporaryState works only when State == 2 ^ n!"); + + if(TemporaryStateIsActivated(State)) + { + int OldCounter = GetTemporaryStateCounter(State); + + if(OldCounter != PERMANENT) + EditTemporaryStateCounter(State, Max(Counter, 50 - OldCounter)); + } + else if(StateData[Index].IsAllowed == 0 || (this->*StateData[Index].IsAllowed)()) + { + SetTemporaryStateCounter(State, Max(Counter, 50)); + + if(!EquipmentStateIsActivated(State)) + { + if(!IsInNoMsgMode()) + (this->*StateData[Index].PrintBeginMessage)(); + + ActivateTemporaryState(State); + + if(StateData[Index].BeginHandler) + (this->*StateData[Index].BeginHandler)(); + } + else + ActivateTemporaryState(State); + } +} + +void character::HandleStates() +{ + if(!TemporaryState && !EquipmentState) + return; + + for(int c = 0; c < STATES; ++c) + { + if(TemporaryState & (1 << c) && TemporaryStateCounter[c] != PERMANENT) + { + if(!--TemporaryStateCounter[c]) + { + TemporaryState &= ~(1 << c); + + if(!(EquipmentState & (1 << c))) + { + if(StateData[c].EndHandler) + { + (this->*StateData[c].EndHandler)(); + + if(!IsEnabled()) + return; + } + + if(!TemporaryStateCounter[c]) + (this->*StateData[c].PrintEndMessage)(); + } + } + } + + if(StateIsActivated(1 << c)) + if(StateData[c].Handler) + (this->*StateData[c].Handler)(); + + if(!IsEnabled()) + return; + } +} + +void character::PrintBeginPolymorphControlMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel your mind has total control over your body."); +} + +void character::PrintEndPolymorphControlMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You are somehow uncertain of your willpower."); +} + +void character::PrintBeginLifeSaveMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You hear Hell's gates being locked just now."); +} + +void character::PrintEndLifeSaveMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel the Afterlife is welcoming you once again."); +} + +void character::PrintBeginLycanthropyMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You suddenly notice you've always loved full moons."); +} + +void character::PrintEndLycanthropyMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel the wolf inside you has had enough of your bad habits."); +} + +void character::PrintBeginVampirismMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You suddenly decide you have always hated garlic."); +} + +void character::PrintEndVampirismMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You recall your delight of the morning sunshine back in New Attnam. You are a vampire no longer."); +} + +void character::PrintBeginInvisibilityMessage() const +{ + if((PLAYER->StateIsActivated(INFRA_VISION) && IsWarm()) || (PLAYER->StateIsActivated(ESP) && GetAttribute(INTELLIGENCE) >= 5)) + { + if(IsPlayer()) + ADD_MESSAGE("You seem somehow transparent."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s seems somehow transparent.", CHAR_NAME(DEFINITE)); + } + else + { + if(IsPlayer()) + ADD_MESSAGE("You fade away."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s disappears!", CHAR_NAME(DEFINITE)); + } +} + +void character::PrintEndInvisibilityMessage() const +{ + if((PLAYER->StateIsActivated(INFRA_VISION) && IsWarm()) || (PLAYER->StateIsActivated(ESP) && GetAttribute(INTELLIGENCE) >= 5)) + { + if(IsPlayer()) + ADD_MESSAGE("Your notice your transparency has ended."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("The appearance of %s seems far more solid now.", CHAR_NAME(INDEFINITE)); + } + else + { + if(IsPlayer()) + ADD_MESSAGE("You reappear."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s appears from nowhere!", CHAR_NAME(INDEFINITE)); + } +} + +void character::PrintBeginInfraVisionMessage() const +{ + if(IsPlayer()) + if(StateIsActivated(INVISIBLE) && IsWarm() && !(StateIsActivated(ESP) && GetAttribute(INTELLIGENCE) >= 5)) + ADD_MESSAGE("You reappear."); + else + ADD_MESSAGE("You feel your perception being magically altered."); +} + +void character::PrintEndInfraVisionMessage() const +{ + if(IsPlayer()) + if(StateIsActivated(INVISIBLE) && IsWarm() && !(StateIsActivated(ESP) && GetAttribute(INTELLIGENCE) >= 5)) + ADD_MESSAGE("You disappear."); + else + ADD_MESSAGE("You feel your perception returning to normal."); +} + +void character::PrintBeginESPMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You suddenly feel like being only a tiny part of a great network of intelligent minds."); +} + +void character::PrintEndESPMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You are filled with desire to be just yourself from now on."); +} + +void character::PrintBeginHasteMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("Time slows down to a crawl."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s looks faster!", CHAR_NAME(DEFINITE)); +} + +void character::PrintEndHasteMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("Everything seems to move much faster now."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s looks slower!", CHAR_NAME(DEFINITE)); +} + +void character::PrintBeginSlowMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("Everything seems to move much faster now."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s looks slower!", CHAR_NAME(DEFINITE)); +} + +void character::PrintEndSlowMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("Time slows down to a crawl."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s looks faster!", CHAR_NAME(DEFINITE)); +} + +void character::EndPolymorph() +{ + ForceEndPolymorph(); +} + +character* character::ForceEndPolymorph() +{ + if(IsPlayer()) + ADD_MESSAGE("You return to your true form."); + else if(game::IsInWilderness()) + { + ActivateTemporaryState(POLYMORPHED); + SetTemporaryStateCounter(POLYMORPHED, 10); + return this; // fast gum solution, state ends when the player enters a dungeon + } + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s returns to %s true form.", CHAR_NAME(DEFINITE), GetPossessivePronoun().CStr()); + + RemoveTraps(); + + if(GetAction()) + GetAction()->Terminate(false); + + v2 Pos = GetPos(); + SendToHell(); + Remove(); + character* Char = GetPolymorphBackup(); + Flags |= C_IN_NO_MSG_MODE; + Char->Flags |= C_IN_NO_MSG_MODE; + Char->ChangeTeam(GetTeam()); + + if(GetTeam()->GetLeader() == this) + GetTeam()->SetLeader(Char); + + SetPolymorphBackup(0); + Char->PutToOrNear(Pos); + Char->Enable(); + Char->Flags &= ~C_POLYMORPHED; + GetStack()->MoveItemsTo(Char->GetStack()); + DonateEquipmentTo(Char); + Char->SetMoney(GetMoney()); + Flags &= ~C_IN_NO_MSG_MODE; + Char->Flags &= ~C_IN_NO_MSG_MODE; + Char->CalculateAll(); + Char->SetAssignedName(GetAssignedName()); + + if(IsPlayer()) + { + Flags &= ~C_PLAYER; + game::SetPlayer(Char); + game::SendLOSUpdateRequest(); + UpdateESPLOS(); + } + + Char->TestWalkability(); + return Char; +} + +void character::LycanthropyHandler() +{ + if(!(RAND() % 2000)) + { + if(StateIsActivated(POLYMORPH_CONTROL) + && !game::TruthQuestion(CONST_S("Do you wish to change into a werewolf? [y/N]"))) + return; + + Polymorph(werewolfwolf::Spawn(), 1000 + RAND() % 2000); + } +} + +void character::SaveLife() +{ + if(TemporaryStateIsActivated(LIFE_SAVED)) + { + if(IsPlayer()) + ADD_MESSAGE("But wait! You glow briefly red and seem to be in a better shape!"); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("But wait, suddenly %s glows briefly red and seems to be in a better shape!", GetPersonalPronoun().CStr()); + + DeActivateTemporaryState(LIFE_SAVED); + } + else + { + item* LifeSaver = 0; + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment && Equipment->IsInCorrectSlot(c) && Equipment->GetGearStates() & LIFE_SAVED) + LifeSaver = Equipment; + } + + if(!LifeSaver) + ABORT("The Universe can only kill you once!"); + + if(IsPlayer()) + ADD_MESSAGE("But wait! Your %s glows briefly red and disappears and you seem to be in a better shape!", LifeSaver->CHAR_NAME(UNARTICLED)); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("But wait, suddenly %s %s glows briefly red and disappears and %s seems to be in a better shape!", GetPossessivePronoun().CStr(), LifeSaver->CHAR_NAME(UNARTICLED), GetPersonalPronoun().CStr()); + + LifeSaver->RemoveFromSlot(); + LifeSaver->SendToHell(); + } + + if(IsPlayer()) + game::AskForKeyPress(CONST_S("Life saved! [press any key to continue]")); + + RestoreBodyParts(); + ResetSpoiling(); + RestoreHP(); + RestoreStamina(); + ResetStates(); + + if(GetNP() < SATIATED_LEVEL) + SetNP(SATIATED_LEVEL); + + SendNewDrawRequest(); + + if(GetAction()) + GetAction()->Terminate(false); +} + +character* character::PolymorphRandomly(int MinDanger, int MaxDanger, int Time) +{ + character* NewForm = 0; + + if(StateIsActivated(POLYMORPH_CONTROL)) + { + if(IsPlayer()) + { + if(!GetNewFormForPolymorphWithControl(NewForm)) + return NewForm; + } + else + NewForm = protosystem::CreateMonster(MinDanger * 10, MaxDanger * 10, NO_EQUIPMENT); + } + else + NewForm = protosystem::CreateMonster(MinDanger, MaxDanger, NO_EQUIPMENT); + + Polymorph(NewForm, Time); + return NewForm; +} + +/* In reality, the reading takes Time / (Intelligence * 10) turns */ + +void character::StartReading(item* Item, long Time) +{ + study* Read = study::Spawn(this); + Read->SetLiteratureID(Item->GetID()); + + if(game::WizardModeIsActive()) + Time = 1; + + Read->SetCounter(Time); + SetAction(Read); + + if(IsPlayer()) + ADD_MESSAGE("You start reading %s.", Item->CHAR_NAME(DEFINITE)); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s starts reading %s.", CHAR_NAME(DEFINITE), Item->CHAR_NAME(DEFINITE)); +} + +/* Call when one makes something with his/her/its hands. + * Difficulty of 5 takes about one turn, so it's the most common to use. */ + +void character::DexterityAction(int Difficulty) +{ + EditAP(-20000 * Difficulty / APBonus(GetAttribute(DEXTERITY))); + EditExperience(DEXTERITY, Difficulty * 15, 1 << 7); +} + +/* If Theoretically != false, range is not a factor. */ + +truth character::CanBeSeenByPlayer(truth Theoretically, truth IgnoreESP) const +{ + if(IsEnabled() && !game::IsGenerating() && (Theoretically || GetSquareUnder())) + { + truth MayBeESPSeen = PLAYER->IsEnabled() && !IgnoreESP && PLAYER->StateIsActivated(ESP) && GetAttribute(INTELLIGENCE) >= 5; + truth MayBeInfraSeen = PLAYER->IsEnabled() && PLAYER->StateIsActivated(INFRA_VISION) && IsWarm(); + truth Visible = !StateIsActivated(INVISIBLE) || MayBeESPSeen || MayBeInfraSeen; + + if(game::IsInWilderness()) + return Visible; + + if(MayBeESPSeen + && (Theoretically + || GetDistanceSquareFrom(PLAYER) <= PLAYER->GetESPRangeSquare())) + return true; + else if(!Visible) + return false; + else + return Theoretically || SquareUnderCanBeSeenByPlayer(MayBeInfraSeen); + } + else + return false; +} + +truth character::CanBeSeenBy(ccharacter* Who, truth Theoretically, truth IgnoreESP) const +{ + if(Who->IsPlayer()) + return CanBeSeenByPlayer(Theoretically, IgnoreESP); + else + { + if(IsEnabled() && !game::IsGenerating() && (Theoretically || GetSquareUnder())) + { + truth MayBeESPSeen = Who->IsEnabled() && !IgnoreESP && Who->StateIsActivated(ESP) && GetAttribute(INTELLIGENCE) >= 5; + truth MayBeInfraSeen = Who->IsEnabled() && Who->StateIsActivated(INFRA_VISION) && IsWarm(); + truth Visible = !StateIsActivated(INVISIBLE) || MayBeESPSeen || MayBeInfraSeen; + + if(game::IsInWilderness()) + return Visible; + + if(MayBeESPSeen + && (Theoretically + || GetDistanceSquareFrom(Who) <= Who->GetESPRangeSquare())) + return true; + else if(!Visible) + return false; + else + return Theoretically || SquareUnderCanBeSeenBy(Who, MayBeInfraSeen); + } + else + return false; + } +} + +truth character::SquareUnderCanBeSeenByPlayer(truth IgnoreDarkness) const +{ + if(!GetSquareUnder()) + return false; + + int S1 = SquaresUnder, S2 = PLAYER->SquaresUnder; + + if(S1 == 1 && S2 == 1) + { + if(GetSquareUnder()->CanBeSeenByPlayer(IgnoreDarkness)) + return true; + + if(IgnoreDarkness) + { + int LOSRangeSquare = PLAYER->GetLOSRangeSquare(); + + if((GetPos() - PLAYER->GetPos()).GetLengthSquare() <= LOSRangeSquare) + { + eyecontroller::Map = GetLevel()->GetMap(); + return mapmath::DoLine(PLAYER->GetPos().X, PLAYER->GetPos().Y, GetPos().X, GetPos().Y, SKIP_FIRST); + } + } + + return false; + } + else + { + for(int c1 = 0; c1 < S1; ++c1) + { + lsquare* Square = GetLSquareUnder(c1); + + if(Square->CanBeSeenByPlayer(IgnoreDarkness)) + return true; + else if(IgnoreDarkness) + { + v2 Pos = Square->GetPos(); + int LOSRangeSquare = PLAYER->GetLOSRangeSquare(); + + for(int c2 = 0; c2 < S2; ++c2) + { + v2 PlayerPos = PLAYER->GetPos(c2); + + if((Pos - PlayerPos).GetLengthSquare() <= LOSRangeSquare) + { + eyecontroller::Map = GetLevel()->GetMap(); + + if(mapmath::DoLine(PlayerPos.X, PlayerPos.Y, Pos.X, Pos.Y, SKIP_FIRST)) + return true; + } + } + } + } + + return false; + } +} + +truth character::SquareUnderCanBeSeenBy(ccharacter* Who, truth IgnoreDarkness) const +{ + int S1 = SquaresUnder, S2 = Who->SquaresUnder; + int LOSRangeSquare = Who->GetLOSRangeSquare(); + + if(S1 == 1 && S2 == 1) + return GetSquareUnder()->CanBeSeenFrom(Who->GetPos(), LOSRangeSquare, IgnoreDarkness); + else + { + for(int c1 = 0; c1 < S1; ++c1) + { + lsquare* Square = GetLSquareUnder(c1); + + for(int c2 = 0; c2 < S2; ++c2) + if(Square->CanBeSeenFrom(Who->GetPos(c2), LOSRangeSquare, IgnoreDarkness)) + return true; + } + + return false; + } +} + +int character::GetDistanceSquareFrom(ccharacter* Who) const +{ + int S1 = SquaresUnder, S2 = Who->SquaresUnder; + + if(S1 == 1 && S2 == 1) + return (GetPos() - Who->GetPos()).GetLengthSquare(); + else + { + v2 MinDist(0x7FFF, 0x7FFF); + int MinLength = 0xFFFF; + + for(int c1 = 0; c1 < S1; ++c1) + for(int c2 = 0; c2 < S2; ++c2) + { + v2 Dist = GetPos(c1) - Who->GetPos(c2); + + if(Dist.X < 0) + Dist.X = -Dist.X; + + if(Dist.Y < 0) + Dist.Y = -Dist.Y; + + if(Dist.X <= MinDist.X && Dist.Y <= MinDist.Y) + { + MinDist = Dist; + MinLength = Dist.GetLengthSquare(); + } + else if(Dist.X < MinDist.X || Dist.Y < MinDist.Y) + { + int Length = Dist.GetLengthSquare(); + + if(Length < MinLength) + { + MinDist = Dist; + MinLength = Length; + } + } + } + + return MinLength; + } +} + +void character::AttachBodyPart(bodypart* BodyPart) +{ + SetBodyPart(BodyPart->GetBodyPartIndex(), BodyPart); + + if(!AllowSpoil()) + BodyPart->ResetSpoiling(); + + BodyPart->ResetPosition(); + BodyPart->UpdatePictures(); + CalculateAttributeBonuses(); + CalculateBattleInfo(); + SendNewDrawRequest(); + SignalPossibleTransparencyChange(); +} + +/* Returns true if the character has all bodyparts, false if not. */ + +truth character::HasAllBodyParts() const +{ + for(int c = 0; c < BodyParts; ++c) + if(!GetBodyPart(c) && CanCreateBodyPart(c)) + return false; + + return true; +} + +bodypart* character::GenerateRandomBodyPart() +{ + int NeededBodyPart[MAX_BODYPARTS]; + int Index = 0; + + for(int c = 0; c < BodyParts; ++c) + if(!GetBodyPart(c) && CanCreateBodyPart(c)) + NeededBodyPart[Index++] = c; + + return Index ? CreateBodyPart(NeededBodyPart[RAND() % Index]) : 0; +} + +/* Searches the character's Stack and if it find some bodyparts there that are the character's old bodyparts returns a stackiterator to one of them (choosen in random). If no fitting bodyparts are found the function returns 0 */ + +bodypart* character::FindRandomOwnBodyPart(truth AllowNonLiving) const +{ + itemvector LostAndFound; + + for(int c = 0; c < BodyParts; ++c) + if(!GetBodyPart(c)) + for(std::list::iterator i = OriginalBodyPartID[c].begin(); i != OriginalBodyPartID[c].end(); ++i) + { + bodypart* Found = static_cast(SearchForItem(*i)); + + if(Found && (AllowNonLiving || Found->CanRegenerate())) + LostAndFound.push_back(Found); + } + + if(LostAndFound.empty()) + return 0; + else + return static_cast(LostAndFound[RAND() % LostAndFound.size()]); +} + +void character::PrintBeginPoisonedMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You seem to be very ill."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s looks very ill.", CHAR_NAME(DEFINITE)); +} + +void character::PrintEndPoisonedMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel better again."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s looks better.", CHAR_NAME(DEFINITE)); +} + +void character::PoisonedHandler() +{ + if(!(RAND() % 100)) + VomitAtRandomDirection(500 + RAND_N(250)); + + int Damage = 0; + + for(int Used = 0; Used < GetTemporaryStateCounter(POISONED); Used += 100) + if(!(RAND() % 100)) + ++Damage; + + if(Damage) + { + ReceiveDamage(0, Damage, POISON, ALL, 8, false, false, false, false); + CheckDeath(CONST_S("died of acute poisoning"), 0); + } +} + +truth character::IsWarm() const +{ + return combinebodypartpredicates()(this, &bodypart::IsWarm, 1); +} + +void character::BeginInvisibility() +{ + UpdatePictures(); + SendNewDrawRequest(); + SignalPossibleTransparencyChange(); +} + +void character::BeginInfraVision() +{ + if(IsPlayer()) + GetArea()->SendNewDrawRequest(); +} + +void character::BeginESP() +{ + if(IsPlayer()) + GetArea()->SendNewDrawRequest(); +} + +void character::EndInvisibility() +{ + UpdatePictures(); + SendNewDrawRequest(); + SignalPossibleTransparencyChange(); +} + +void character::EndInfraVision() +{ + if(IsPlayer() && IsEnabled()) + GetArea()->SendNewDrawRequest(); +} + +void character::EndESP() +{ + if(IsPlayer() && IsEnabled()) + GetArea()->SendNewDrawRequest(); +} + +void character::Draw(blitdata& BlitData) const +{ + col24 L = BlitData.Luminance; + + if(PLAYER->IsEnabled() + && ((PLAYER->StateIsActivated(ESP) + && GetAttribute(INTELLIGENCE) >= 5 + && (PLAYER->GetPos() - GetPos()).GetLengthSquare() <= PLAYER->GetESPRangeSquare()) + || (PLAYER->StateIsActivated(INFRA_VISION) && IsWarm()))) + BlitData.Luminance = ivanconfig::GetContrastLuminance(); + + DrawBodyParts(BlitData); + BlitData.Luminance = ivanconfig::GetContrastLuminance(); + BlitData.Src.Y = 16; + cint SquareIndex = BlitData.CustomData & SQUARE_INDEX_MASK; + + if(GetTeam() == PLAYER->GetTeam() && !IsPlayer() + && SquareIndex == GetTameSymbolSquareIndex()) + { + BlitData.Src.X = 32; + igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData); + } + + if(IsFlying() && SquareIndex == GetFlySymbolSquareIndex()) + { + BlitData.Src.X = 128; + igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData); + } + + if(IsSwimming() && SquareIndex == GetSwimmingSymbolSquareIndex()) + { + BlitData.Src.X = 240; + igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData); + } + + if(GetAction() && GetAction()->IsUnconsciousness() + && SquareIndex == GetUnconsciousSymbolSquareIndex()) + { + BlitData.Src.X = 224; + igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData); + } + + BlitData.Src.X = BlitData.Src.Y = 0; + BlitData.Luminance = L; +} + +void character::DrawBodyParts(blitdata& BlitData) const +{ + GetTorso()->Draw(BlitData); +} + +void character::PrintBeginTeleportMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel jumpy."); +} + +void character::PrintEndTeleportMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You suddenly realize you've always preferred walking to jumping."); +} + +void character::PrintBeginDetectMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel curious about your surroundings."); +} + +void character::PrintEndDetectMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You decide to rely on your intuition from now on."); +} + +void character::TeleportHandler() +{ + if(!(RAND() % 1500) && !game::IsInWilderness()) + { + if(IsPlayer()) + ADD_MESSAGE("You feel an urgent spatial relocation is now appropriate."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s disappears.", CHAR_NAME(DEFINITE)); + + TeleportRandomly(); + } +} + +void character::DetectHandler() +{ + if(IsPlayer()) //the AI can't be asked position questions! So only the player can hav this state really :/ a bit daft of me + { + if(!(RAND() % 3000) && !game::IsInWilderness()) + { + ADD_MESSAGE("Your mind wanders in search of something."); + DoDetecting(); //in fact, who knows what would happen if a dark frog had the detecting state? + } + } + else + return; +} + +void character::PrintBeginPolymorphMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("An unconfortable uncertainty of who you really are overwhelms you."); +} + +void character::PrintEndPolymorphMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel you are you and no one else."); +} + +void character::PolymorphHandler() +{ + if(!(RAND() % 1500)) + PolymorphRandomly(1, 999999, 200 + RAND() % 800); +} + +void character::PrintBeginTeleportControlMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel very controlled."); +} + +void character::PrintEndTeleportControlMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel your control slipping."); +} + +void character::DisplayStethoscopeInfo(character*) const +{ + felist Info(CONST_S("Information about ") + GetDescription(DEFINITE)); + AddSpecialStethoscopeInfo(Info); + Info.AddEntry(CONST_S("Endurance: ") + GetAttribute(ENDURANCE), LIGHT_GRAY); + Info.AddEntry(CONST_S("Perception: ") + GetAttribute(PERCEPTION), LIGHT_GRAY); + Info.AddEntry(CONST_S("Intelligence: ") + GetAttribute(INTELLIGENCE), LIGHT_GRAY); + Info.AddEntry(CONST_S("Wisdom: ") + GetAttribute(WISDOM), LIGHT_GRAY); + //Info.AddEntry(CONST_S("Willpower: ") + GetAttribute(WILL_POWER), LIGHT_GRAY); + Info.AddEntry(CONST_S("Charisma: ") + GetAttribute(CHARISMA), LIGHT_GRAY); + Info.AddEntry(CONST_S("HP: ") + GetHP() + "/" + GetMaxHP(), IsInBadCondition() ? RED : LIGHT_GRAY); + + if(GetAction()) + Info.AddEntry(festring(GetAction()->GetDescription()).CapitalizeCopy(), LIGHT_GRAY); + + for(int c = 0; c < STATES; ++c) + if(StateIsActivated(1 << c) && (1 << c != HASTE || !StateIsActivated(SLOW)) && (1 << c != SLOW || !StateIsActivated(HASTE))) + Info.AddEntry(StateData[c].Description, LIGHT_GRAY); + + switch(GetTirednessState()) + { + case FAINTING: + Info.AddEntry("Fainting", RED); + break; + case EXHAUSTED: + Info.AddEntry("Exhausted", LIGHT_GRAY); + break; + } + + game::SetStandardListAttributes(Info); + Info.Draw(); +} + +truth character::CanUseStethoscope(truth PrintReason) const +{ + if(PrintReason) + ADD_MESSAGE("This type of monster can't use a stethoscope."); + + return false; +} + +/* Effect used by at least Sophos. + * NOTICE: Doesn't check for death! */ + +void character::TeleportSomePartsAway(int NumberToTeleport) +{ + for(int c = 0; c < NumberToTeleport; ++c) + { + int RandomBodyPart = GetRandomNonVitalBodyPart(); + + if(RandomBodyPart == NONE_INDEX) + { + for(; c < NumberToTeleport; ++c) + { + GetTorso()->SetHP((GetTorso()->GetHP() << 2) / 5); + long TorsosVolume = GetTorso()->GetMainMaterial()->GetVolume() / 10; + + if(!TorsosVolume) + break; + + long Amount = (RAND() % TorsosVolume) + 1; + item* Lump = GetTorso()->GetMainMaterial()->CreateNaturalForm(Amount); + GetTorso()->GetMainMaterial()->EditVolume(-Amount); + Lump->MoveTo(GetNearLSquare(GetLevel()->GetRandomSquare())->GetStack()); + + if(IsPlayer()) + ADD_MESSAGE("Parts of you teleport away."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("Parts of %s teleport away.", CHAR_NAME(DEFINITE)); + } + } + else + { + item* SeveredBodyPart = SevereBodyPart(RandomBodyPart); + + if(SeveredBodyPart) + { + GetNearLSquare(GetLevel()->GetRandomSquare())->AddItem(SeveredBodyPart); + SeveredBodyPart->DropEquipment(); + + if(IsPlayer()) + ADD_MESSAGE("Your %s teleports away.", GetBodyPartName(RandomBodyPart).CStr()); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s %s teleports away.", GetPossessivePronoun().CStr(), GetBodyPartName(RandomBodyPart).CStr()); + } + else + { + if(IsPlayer()) + ADD_MESSAGE("Your %s disappears.", GetBodyPartName(RandomBodyPart).CStr()); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s %s disappears.", GetPossessivePronoun().CStr(), GetBodyPartName(RandomBodyPart).CStr()); + } + } + } +} + +/* Returns an index of a random bodypart that is not vital. If no non-vital bodypart is found returns NONE_INDEX */ + +int character::GetRandomNonVitalBodyPart() const +{ + int OKBodyPart[MAX_BODYPARTS]; + int OKBodyParts = 0; + + for(int c = 0; c < BodyParts; ++c) + if(GetBodyPart(c) && !BodyPartIsVital(c)) + OKBodyPart[OKBodyParts++] = c; + + return OKBodyParts ? OKBodyPart[RAND() % OKBodyParts] : NONE_INDEX; +} + +void character::CalculateVolumeAndWeight() +{ + Volume = Stack->GetVolume(); + Weight = Stack->GetWeight(); + BodyVolume = 0; + CarriedWeight = Weight; + + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart) + { + BodyVolume += BodyPart->GetBodyPartVolume(); + Volume += BodyPart->GetVolume(); + CarriedWeight += BodyPart->GetCarriedWeight(); + Weight += BodyPart->GetWeight(); + } + } +} + +void character::SignalVolumeAndWeightChange() +{ + if(!IsInitializing()) + { + CalculateVolumeAndWeight(); + + if(IsEnabled()) + CalculateBurdenState(); + + if(MotherEntity) + MotherEntity->SignalVolumeAndWeightChange(); + } +} + +void character::SignalEmitationIncrease(col24 EmitationUpdate) +{ + if(game::CompareLights(EmitationUpdate, Emitation) > 0) + { + game::CombineLights(Emitation, EmitationUpdate); + + if(MotherEntity) + MotherEntity->SignalEmitationIncrease(EmitationUpdate); + else if(SquareUnder[0] && !game::IsInWilderness()) + for(int c = 0; c < GetSquaresUnder(); ++c) + GetLSquareUnder()->SignalEmitationIncrease(EmitationUpdate); + } +} + +void character::SignalEmitationDecrease(col24 EmitationUpdate) +{ + if(game::CompareLights(EmitationUpdate, Emitation) >= 0 && Emitation) + { + col24 Backup = Emitation; + CalculateEmitation(); + + if(Backup != Emitation) + { + if(MotherEntity) + MotherEntity->SignalEmitationDecrease(EmitationUpdate); + else if(SquareUnder[0] && !game::IsInWilderness()) + for(int c = 0; c < GetSquaresUnder(); ++c) + GetLSquareUnder(c)->SignalEmitationDecrease(EmitationUpdate); + } + } +} + +void character::CalculateEmitation() +{ + Emitation = GetBaseEmitation(); + + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart) + game::CombineLights(Emitation, BodyPart->GetEmitation()); + } + + game::CombineLights(Emitation, Stack->GetEmitation()); +} + +void character::CalculateAll() +{ + Flags |= C_INITIALIZING; + CalculateAttributeBonuses(); + CalculateVolumeAndWeight(); + CalculateEmitation(); + CalculateBodyPartMaxHPs(0); + CalculateMaxStamina(); + CalculateBurdenState(); + CalculateBattleInfo(); + Flags &= ~C_INITIALIZING; +} + +void character::CalculateHP() +{ + HP = sumbodypartproperties()(this, &bodypart::GetHP); +} + +void character::CalculateMaxHP() +{ + MaxHP = sumbodypartproperties()(this, &bodypart::GetMaxHP); +} + +void character::CalculateBodyPartMaxHPs(ulong Flags) +{ + doforbodypartswithparam()(this, &bodypart::CalculateMaxHP, Flags); + CalculateMaxHP(); + CalculateHP(); +} + +truth character::EditAttribute(int Identifier, int Value) +{ + if(Identifier == ENDURANCE && UseMaterialAttributes()) + return false; + + if(RawEditAttribute(BaseExperience[Identifier], Value)) + { + if(!IsInitializing()) + { + if(Identifier == LEG_STRENGTH) + CalculateBurdenState(); + else if(Identifier == ENDURANCE) + CalculateBodyPartMaxHPs(); + else if(IsPlayer() && Identifier == PERCEPTION) + game::SendLOSUpdateRequest(); + else if(IsPlayerKind() && (Identifier == INTELLIGENCE || Identifier == WISDOM || Identifier == CHARISMA)) + UpdatePictures(); + + CalculateBattleInfo(); + } + + return true; + } + else + return false; +} + +truth character::ActivateRandomState(int Flags, int Time, long Seed) +{ + femath::SaveSeed(); + + if(Seed) + femath::SetSeed(Seed); + + long ToBeActivated = GetRandomState(Flags|DUR_TEMPORARY); + femath::LoadSeed(); + + if(!ToBeActivated) + return false; + + BeginTemporaryState(ToBeActivated, Time); + return true; +} + +truth character::GainRandomIntrinsic(int Flags) +{ + long ToBeActivated = GetRandomState(Flags|DUR_PERMANENT); + + if(!ToBeActivated) + return false; + + GainIntrinsic(ToBeActivated); + return true; +} + +/* Returns 0 if state not found */ + +long character::GetRandomState(int Flags) const +{ + long OKStates[STATES]; + int NumberOfOKStates = 0; + + for(int c = 0; c < STATES; ++c) + if(StateData[c].Flags & Flags & DUR_FLAGS && StateData[c].Flags & Flags & SRC_FLAGS) + OKStates[NumberOfOKStates++] = 1 << c; + + return NumberOfOKStates ? OKStates[RAND() % NumberOfOKStates] : 0; +} + +int characterprototype::CreateSpecialConfigurations(characterdatabase** TempConfig, int Configs, int Level) +{ + if(Level == 0 && TempConfig[0]->CreateDivineConfigurations) + Configs = databasecreator::CreateDivineConfigurations(this, TempConfig, Configs); + + if(Level == 1 && TempConfig[0]->CreateUndeadConfigurations) + for(int c = 1; c < protocontainer::GetSize(); ++c) + { + const character::prototype* Proto = protocontainer::GetProto(c); + const character::database*const* CharacterConfigData = Proto->GetConfigData(); + const character::database*const* End = CharacterConfigData + Proto->GetConfigSize(); + + for(++CharacterConfigData; CharacterConfigData != End; ++CharacterConfigData) + { + const character::database* CharacterDataBase = *CharacterConfigData; + + if(CharacterDataBase->UndeadVersions) + { + character::database* ConfigDataBase = new character::database(**TempConfig); + ConfigDataBase->InitDefaults(this, (c << 8) | CharacterDataBase->Config); + ConfigDataBase->PostFix << "of "; + + if(CharacterDataBase->Adjective.GetSize()) + { + if(CharacterDataBase->UsesLongAdjectiveArticle) + ConfigDataBase->PostFix << "an "; + else + ConfigDataBase->PostFix << "a "; + + ConfigDataBase->PostFix << CharacterDataBase->Adjective << ' '; + } + else + { + if(CharacterDataBase->UsesLongArticle) + ConfigDataBase->PostFix << "an "; + else + ConfigDataBase->PostFix << "a "; + } + + ConfigDataBase->PostFix << CharacterDataBase->NameSingular; + + if(CharacterDataBase->PostFix.GetSize()) + ConfigDataBase->PostFix << ' ' << CharacterDataBase->PostFix; + + int P1 = TempConfig[0]->UndeadAttributeModifier; + int P2 = TempConfig[0]->UndeadVolumeModifier; + int c2; + + for(c2 = 0; c2 < ATTRIBUTES; ++c2) + ConfigDataBase->*ExpPtr[c2] = CharacterDataBase->*ExpPtr[c2] * P1 / 100; + + for(c2 = 0; c2 < EQUIPMENT_DATAS; ++c2) + ConfigDataBase->*EquipmentDataPtr[c2] = contentscript(); + + ConfigDataBase->DefaultIntelligence = 5; + ConfigDataBase->DefaultWisdom = 5; + ConfigDataBase->DefaultCharisma = 5; + ConfigDataBase->TotalSize = CharacterDataBase->TotalSize; + ConfigDataBase->Sex = CharacterDataBase->Sex; + ConfigDataBase->AttributeBonus = CharacterDataBase->AttributeBonus; + ConfigDataBase->TotalVolume = CharacterDataBase->TotalVolume * P2 / 100; + + if(TempConfig[0]->UndeadCopyMaterials) + { + ConfigDataBase->HeadBitmapPos = CharacterDataBase->HeadBitmapPos; + ConfigDataBase->HairColor = CharacterDataBase->HairColor; + ConfigDataBase->EyeColor = CharacterDataBase->EyeColor; + ConfigDataBase->CapColor = CharacterDataBase->CapColor; + ConfigDataBase->FleshMaterial = CharacterDataBase->FleshMaterial; + ConfigDataBase->BloodMaterial = CharacterDataBase->BloodMaterial; + ConfigDataBase->VomitMaterial = CharacterDataBase->VomitMaterial; + ConfigDataBase->DefaultSweatMaterial = CharacterDataBase->DefaultSweatMaterial; + } + + ConfigDataBase->KnownCWeaponSkills = CharacterDataBase->KnownCWeaponSkills; + ConfigDataBase->CWeaponSkillHits = CharacterDataBase->CWeaponSkillHits; + ConfigDataBase->PostProcess(); + TempConfig[Configs++] = ConfigDataBase; + } + } + } + + if(Level == 0 && TempConfig[0]->CreateGolemMaterialConfigurations) + for(int c = 1; c < protocontainer::GetSize(); ++c) + { + const material::prototype* Proto = protocontainer::GetProto(c); + const material::database*const* MaterialConfigData = Proto->GetConfigData(); + const material::database*const* End = MaterialConfigData + Proto->GetConfigSize(); + + for(++MaterialConfigData; MaterialConfigData != End; ++MaterialConfigData) + { + const material::database* MaterialDataBase = *MaterialConfigData; + + if(MaterialDataBase->CategoryFlags & IS_GOLEM_MATERIAL) + { + character::database* ConfigDataBase = new character::database(**TempConfig); + ConfigDataBase->InitDefaults(this, MaterialDataBase->Config); + ConfigDataBase->Adjective = MaterialDataBase->NameStem; + ConfigDataBase->UsesLongAdjectiveArticle = MaterialDataBase->NameFlags & USE_AN; + ConfigDataBase->AttachedGod = MaterialDataBase->AttachedGod; + TempConfig[Configs++] = ConfigDataBase; + } + } + } + + return Configs; +} + +double character::GetTimeToDie(ccharacter* Enemy, int Damage, double ToHitValue, truth AttackIsBlockable, truth UseMaxHP) const +{ + double DodgeValue = GetDodgeValue(); + + if(!Enemy->CanBeSeenBy(this, true)) + ToHitValue *= 2; + + if(!CanBeSeenBy(Enemy, true)) + DodgeValue *= 2; + + double MinHits = 1000; + truth First = true; + + for(int c = 0; c < BodyParts; ++c) + if(BodyPartIsVital(c) && GetBodyPart(c)) + { + double Hits = GetBodyPart(c)->GetTimeToDie(Damage, ToHitValue, DodgeValue, AttackIsBlockable, UseMaxHP); + + if(First) + { + MinHits = Hits; + First = false; + } + else + MinHits = 1 / (1 / MinHits + 1 / Hits); + } + + return MinHits; +} + +double character::GetRelativeDanger(ccharacter* Enemy, truth UseMaxHP) const +{ + double Danger = Enemy->GetTimeToKill(this, UseMaxHP) / GetTimeToKill(Enemy, UseMaxHP); + int EnemyAP = Enemy->GetMoveAPRequirement(1); + int ThisAP = GetMoveAPRequirement(1); + + if(EnemyAP > ThisAP) + Danger *= 1.25; + else if(ThisAP > EnemyAP) + Danger *= 0.80; + + if(!Enemy->CanBeSeenBy(this, true)) + Danger *= Enemy->IsPlayer() ? 0.2 : 0.5; + + if(!CanBeSeenBy(Enemy, true)) + Danger *= IsPlayer() ? 5. : 2.; + + if(GetAttribute(INTELLIGENCE) < 10 && !IsPlayer()) + Danger *= 0.80; + + if(Enemy->GetAttribute(INTELLIGENCE) < 10 && !Enemy->IsPlayer()) + Danger *= 1.25; + + return Limit(Danger, 0.001, 1000.); +} + +festring character::GetBodyPartName(int I, truth Articled) const +{ + if(I == TORSO_INDEX) + return Articled ? CONST_S("a torso") : CONST_S("torso"); + else + { + ABORT("Illegal character bodypart name request!"); + return ""; + } +} + +item* character::SearchForItem(ulong ID) const +{ + item* Equipment = findequipment()(this, &item::HasID, ID); + + if(Equipment) + return Equipment; + + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if(i->GetID() == ID) + return *i; + + return 0; +} + +truth character::ContentsCanBeSeenBy(ccharacter* Viewer) const +{ + return Viewer == this; +} + +truth character::HitEffect(character* Enemy, item* Weapon, v2 HitPos, int Type, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + if(Weapon) + return Weapon->HitEffect(this, Enemy, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + switch(Type) + { + case UNARMED_ATTACK: + return Enemy->SpecialUnarmedEffect(this, HitPos, BodyPartIndex, Direction, BlockedByArmour); + case KICK_ATTACK: + return Enemy->SpecialKickEffect(this, HitPos, BodyPartIndex, Direction, BlockedByArmour); + case BITE_ATTACK: + return Enemy->SpecialBiteEffect(this, HitPos, BodyPartIndex, Direction, BlockedByArmour); + } + + return false; +} + +void character::WeaponSkillHit(item* Weapon, int Type, int Hits) +{ + int Category; + + switch(Type) + { + case UNARMED_ATTACK: + Category = UNARMED; + break; + case WEAPON_ATTACK: + Weapon->WeaponSkillHit(Hits); + return; + case KICK_ATTACK: + Category = KICK; + break; + case BITE_ATTACK: + Category = BITE; + break; + case THROW_ATTACK: + if(!IsHumanoid()) return; + Category = Weapon->GetWeaponCategory(); + break; + default: + ABORT("Illegal Type %d passed to character::WeaponSkillHit()!", Type); + return; + } + + if(GetCWeaponSkill(Category)->AddHit(Hits)) + { + CalculateBattleInfo(); + + if(IsPlayer()) + GetCWeaponSkill(Category)->AddLevelUpMessage(Category); + } +} + +/* Returns 0 if character cannot be duplicated */ + +character* character::Duplicate(ulong Flags) +{ + if(!(Flags & IGNORE_PROHIBITIONS) && !CanBeCloned()) + return 0; + + character* Char = GetProtoType()->Clone(this); + + if(Flags & MIRROR_IMAGE) + { + DuplicateEquipment(Char, Flags & ~IGNORE_PROHIBITIONS); + Char->SetLifeExpectancy(Flags >> LE_BASE_SHIFT & LE_BASE_RANGE, + Flags >> LE_RAND_SHIFT & LE_RAND_RANGE); + } + + Char->CalculateAll(); + Char->CalculateEmitation(); + Char->UpdatePictures(); + Char->Flags &= ~(C_INITIALIZING|C_IN_NO_MSG_MODE); + return Char; +} + +truth character::TryToEquip(item* Item) +{ + if(!Item->AllowEquip() + || !CanUseEquipment() + || GetAttribute(WISDOM) >= Item->GetWearWisdomLimit() + || Item->GetSquaresUnder() != 1) + return false; + + for(int e = 0; e < GetEquipments(); ++e) + if(GetBodyPartOfEquipment(e) && EquipmentIsAllowed(e)) + { + sorter Sorter = EquipmentSorter(e); + + if((Sorter == 0 || (Item->*Sorter)(this)) + && ((e != RIGHT_WIELDED_INDEX && e != LEFT_WIELDED_INDEX) + || Item->IsWeapon(this) + || Item->IsShield(this)) + && AllowEquipment(Item, e)) + { + item* OldEquipment = GetEquipment(e); + + if(BoundToUse(OldEquipment, e)) + continue; + + lsquare* LSquareUnder = GetLSquareUnder(); + stack* StackUnder = LSquareUnder->GetStack(); + msgsystem::DisableMessages(); + Flags |= C_PICTURE_UPDATES_FORBIDDEN; + LSquareUnder->Freeze(); + StackUnder->Freeze(); + double Danger = GetRelativeDanger(PLAYER); + + if(OldEquipment) + OldEquipment->RemoveFromSlot(); + + Item->RemoveFromSlot(); + SetEquipment(e, Item); + double NewDanger = GetRelativeDanger(PLAYER); + Item->RemoveFromSlot(); + StackUnder->AddItem(Item); + + if(OldEquipment) + SetEquipment(e, OldEquipment); + + msgsystem::EnableMessages(); + Flags &= ~C_PICTURE_UPDATES_FORBIDDEN; + LSquareUnder->UnFreeze(); + StackUnder->UnFreeze(); + + if(OldEquipment) + { + if(NewDanger > Danger || BoundToUse(Item, e)) + { + room* Room = GetRoom(); + + if(!Room || Room->PickupItem(this, Item, 1)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s drops %s %s and equips %s instead.", CHAR_NAME(DEFINITE), CHAR_POSSESSIVE_PRONOUN, OldEquipment->CHAR_NAME(UNARTICLED), Item->CHAR_NAME(INDEFINITE)); + + if(Room) + Room->DropItem(this, OldEquipment, 1); + + OldEquipment->MoveTo(StackUnder); + Item->RemoveFromSlot(); + SetEquipment(e, Item); + DexterityAction(5); + return true; + } + } + } + else + { + if(NewDanger > Danger + || (NewDanger == Danger + && e != RIGHT_WIELDED_INDEX && e != LEFT_WIELDED_INDEX) + || BoundToUse(Item, e)) + { + room* Room = GetRoom(); + + if(!Room || Room->PickupItem(this, Item, 1)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s picks up and equips %s.", CHAR_NAME(DEFINITE), Item->CHAR_NAME(INDEFINITE)); + + Item->RemoveFromSlot(); + SetEquipment(e, Item); + DexterityAction(5); + return true; + } + } + } + } + } + + return false; +} + +truth character::TryToConsume(item* Item) +{ + return Item->CanBeEatenByAI(this) && ConsumeItem(Item, Item->GetConsumeMaterial(this)->GetConsumeVerb()); +} + +void character::UpdateESPLOS() const +{ + if(StateIsActivated(ESP) && !game::IsInWilderness()) + for(int c = 0; c < game::GetTeams(); ++c) + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled()) + (*i)->SendNewDrawRequest(); +} + +// try to add to inventory +truth character::TryToAddToInventory(item* Item) +{ + if(//!Item->AllowEquip() // could be a grenade silly + !(GetBurdenState() > STRESSED) + || !CanUseEquipment() + //|| GetAttribute(WISDOM) >= Item->GetWearWisdomLimit() + || Item->GetSquaresUnder() != 1) + return false; + + //lsquare* LSquareUnder = GetLSquareUnder(); + //stack* StackUnder = LSquareUnder->GetStack(); + room* Room = GetRoom(); + + if(!Room || Room->PickupItem(this, Item, 1)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s picks up %s from the ground.", CHAR_NAME(DEFINITE), Item->CHAR_NAME(INDEFINITE)); + + //GetStack()->AddItem(Item); + //Item->RemoveFromSlot(); + Item->MoveTo(GetStack()); + DexterityAction(5); + return true; + } + + return false; +} +// end of try to add to inventory + +int character::GetCWeaponSkillLevel(citem* Item) const +{ + if(Item->GetWeaponCategory() < GetAllowedWeaponSkillCategories()) + return GetCWeaponSkill(Item->GetWeaponCategory())->GetLevel(); + else + return 0; +} + +void character::PrintBeginPanicMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You panic!"); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s panics.", CHAR_NAME(DEFINITE)); +} + +void character::PrintEndPanicMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You finally calm down."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s calms down.", CHAR_NAME(DEFINITE)); +} + +void character::CheckPanic(int Ticks) +{ + if(GetPanicLevel() > 1 && !StateIsActivated(PANIC) + && GetHP() * 100 < RAND() % (GetPanicLevel() * GetMaxHP() << 1)) + BeginTemporaryState(PANIC, ((Ticks * 3) >> 2) + RAND() % ((Ticks >> 1) + 1)); // 25% randomness to ticks... +} + +/* returns 0 if fails else the newly created character */ + +character* character::DuplicateToNearestSquare(character* Cloner, ulong Flags) +{ + character* NewlyCreated = Duplicate(Flags); + + if(!NewlyCreated) + return 0; + + if(Flags & CHANGE_TEAM && Cloner) + NewlyCreated->ChangeTeam(Cloner->GetTeam()); + + NewlyCreated->PutNear(GetPos()); + return NewlyCreated; +} + +void character::SignalSpoil() +{ + if(GetMotherEntity()) + GetMotherEntity()->SignalSpoil(0); + else + Disappear(0, "spoil", &item::IsVeryCloseToSpoiling); +} + +truth character::CanHeal() const +{ + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart && BodyPart->CanRegenerate() && BodyPart->GetHP() < BodyPart->GetMaxHP()) + return true; + } + + return false; +} + +int character::GetRelation(ccharacter* Who) const +{ + return GetTeam()->GetRelation(Who->GetTeam()); +} + +truth (item::*AffectTest[BASE_ATTRIBUTES])() const = +{ + &item::AffectsEndurance, + &item::AffectsPerception, + &item::AffectsIntelligence, + &item::AffectsWisdom, + &item::AffectsWillPower, + &item::AffectsCharisma, + &item::AffectsMana +}; + +/* Returns nonzero if endurance has decreased and death may occur */ + +truth character::CalculateAttributeBonuses() +{ + doforbodyparts()(this, &bodypart::CalculateAttributeBonuses); + int BackupBonus[BASE_ATTRIBUTES]; + int BackupCarryingBonus = CarryingBonus; + CarryingBonus = 0; + int c1; + + for(c1 = 0; c1 < BASE_ATTRIBUTES; ++c1) + { + BackupBonus[c1] = AttributeBonus[c1]; + AttributeBonus[c1] = 0; + } + + for(c1 = 0; c1 < GetEquipments(); ++c1) + { + item* Equipment = GetEquipment(c1); + + if(!Equipment || !Equipment->IsInCorrectSlot(c1)) + continue; + + for(int c2 = 0; c2 < BASE_ATTRIBUTES; ++c2) + if((Equipment->*AffectTest[c2])()) + AttributeBonus[c2] += Equipment->GetEnchantment(); + + if(Equipment->AffectsCarryingCapacity()) + CarryingBonus += Equipment->GetCarryingBonus(); + } + + ApplySpecialAttributeBonuses(); + + if(IsPlayer() && !IsInitializing() && AttributeBonus[PERCEPTION] != BackupBonus[PERCEPTION]) + game::SendLOSUpdateRequest(); + + if(IsPlayer() && !IsInitializing() && AttributeBonus[INTELLIGENCE] != BackupBonus[INTELLIGENCE]) + UpdateESPLOS(); + + if(!IsInitializing() && CarryingBonus != BackupCarryingBonus) + CalculateBurdenState(); + + if(!IsInitializing() && AttributeBonus[ENDURANCE] != BackupBonus[ENDURANCE]) + { + CalculateBodyPartMaxHPs(); + CalculateMaxStamina(); + return AttributeBonus[ENDURANCE] < BackupBonus[ENDURANCE]; + } + + return false; +} + +void character::ApplyEquipmentAttributeBonuses(item* Equipment) +{ + if(Equipment->AffectsEndurance()) + { + AttributeBonus[ENDURANCE] += Equipment->GetEnchantment(); + CalculateBodyPartMaxHPs(); + CalculateMaxStamina(); + } + + if(Equipment->AffectsPerception()) + { + AttributeBonus[PERCEPTION] += Equipment->GetEnchantment(); + + if(IsPlayer()) + game::SendLOSUpdateRequest(); + } + + if(Equipment->AffectsIntelligence()) + { + AttributeBonus[INTELLIGENCE] += Equipment->GetEnchantment(); + + if(IsPlayer()) + UpdateESPLOS(); + } + + if(Equipment->AffectsWisdom()) + AttributeBonus[WISDOM] += Equipment->GetEnchantment(); + + if(Equipment->AffectsWillPower()) + AttributeBonus[WILL_POWER] += Equipment->GetEnchantment(); + + if(Equipment->AffectsCharisma()) + AttributeBonus[CHARISMA] += Equipment->GetEnchantment(); + + if(Equipment->AffectsMana()) + AttributeBonus[MANA] += Equipment->GetEnchantment(); + + if(Equipment->AffectsCarryingCapacity()) + { + CarryingBonus += Equipment->GetCarryingBonus(); + CalculateBurdenState(); + } +} + +void character::ReceiveAntidote(long Amount) +{ + if(StateIsActivated(POISONED)) + { + if(GetTemporaryStateCounter(POISONED) > Amount) + { + EditTemporaryStateCounter(POISONED, -Amount); + Amount = 0; + } + else + { + if(IsPlayer()) + ADD_MESSAGE("Aaaah... You feel much better."); + + Amount -= GetTemporaryStateCounter(POISONED); + DeActivateTemporaryState(POISONED); + } + } + + if((Amount >= 100 || RAND_N(100) < Amount) && StateIsActivated(PARASITIZED)) + { + if(IsPlayer()) + ADD_MESSAGE("Something in your belly didn't seem to like this stuff."); + + DeActivateTemporaryState(PARASITIZED); + Amount -= Min(100L, Amount); + } + + if((Amount >= 100 || RAND_N(100) < Amount) && StateIsActivated(LEPROSY)) + { + if(IsPlayer()) + ADD_MESSAGE("You are not falling to pieces anymore."); + + DeActivateTemporaryState(LEPROSY); + Amount -= Min(100L, Amount); + } +} + +void character::AddAntidoteConsumeEndMessage() const +{ + if(StateIsActivated(POISONED)) // true only if the antidote didn't cure the poison completely + { + if(IsPlayer()) + ADD_MESSAGE("Your body processes the poison in your veins with rapid speed."); + } +} + +truth character::IsDead() const +{ + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPartIsVital(c) && (!BodyPart || BodyPart->GetHP() < 1)) + return true; + } + + return false; +} + +void character::SignalSpoilLevelChange() +{ + if(GetMotherEntity()) + GetMotherEntity()->SignalSpoilLevelChange(0); + else + UpdatePictures(); +} + +void character::AddOriginalBodyPartID(int I, ulong What) +{ + if(std::find(OriginalBodyPartID[I].begin(), OriginalBodyPartID[I].end(), What) == OriginalBodyPartID[I].end()) + { + OriginalBodyPartID[I].push_back(What); + + if(OriginalBodyPartID[I].size() > 100) + OriginalBodyPartID[I].erase(OriginalBodyPartID[I].begin()); + } +} + +void character::AddToInventory(const fearray >& ItemArray, int SpecialFlags) +{ + for(uint c1 = 0; c1 < ItemArray.Size; ++c1) + if(ItemArray[c1].IsValid()) + { + const interval* TimesPtr = ItemArray[c1].GetTimes(); + int Times = TimesPtr ? TimesPtr->Randomize() : 1; + + for(int c2 = 0; c2 < Times; ++c2) + { + item* Item = ItemArray[c1].Instantiate(SpecialFlags); + + if(Item) + { + Stack->AddItem(Item); + Item->SpecialGenerationHandler(); + } + } + } +} + +truth character::HasHadBodyPart(citem* Item) const +{ + for(int c = 0; c < BodyParts; ++c) + if(std::find(OriginalBodyPartID[c].begin(), OriginalBodyPartID[c].end(), Item->GetID()) != OriginalBodyPartID[c].end()) + return true; + + return GetPolymorphBackup() && GetPolymorphBackup()->HasHadBodyPart(Item); +} + +festring& character::ProcessMessage(festring& Msg) const +{ + SEARCH_N_REPLACE(Msg, "@nu", GetName(UNARTICLED)); + SEARCH_N_REPLACE(Msg, "@ni", GetName(INDEFINITE)); + SEARCH_N_REPLACE(Msg, "@nd", GetName(DEFINITE)); + SEARCH_N_REPLACE(Msg, "@du", GetDescription(UNARTICLED)); + SEARCH_N_REPLACE(Msg, "@di", GetDescription(INDEFINITE)); + SEARCH_N_REPLACE(Msg, "@dd", GetDescription(DEFINITE)); + SEARCH_N_REPLACE(Msg, "@pp", GetPersonalPronoun()); + SEARCH_N_REPLACE(Msg, "@sp", GetPossessivePronoun()); + SEARCH_N_REPLACE(Msg, "@op", GetObjectPronoun()); + SEARCH_N_REPLACE(Msg, "@Nu", GetName(UNARTICLED).CapitalizeCopy()); + SEARCH_N_REPLACE(Msg, "@Ni", GetName(INDEFINITE).CapitalizeCopy()); + SEARCH_N_REPLACE(Msg, "@Nd", GetName(DEFINITE).CapitalizeCopy()); + SEARCH_N_REPLACE(Msg, "@Du", GetDescription(UNARTICLED).CapitalizeCopy()); + SEARCH_N_REPLACE(Msg, "@Di", GetDescription(INDEFINITE).CapitalizeCopy()); + SEARCH_N_REPLACE(Msg, "@Dd", GetDescription(DEFINITE).CapitalizeCopy()); + SEARCH_N_REPLACE(Msg, "@Pp", GetPersonalPronoun().CapitalizeCopy()); + SEARCH_N_REPLACE(Msg, "@Sp", GetPossessivePronoun().CapitalizeCopy()); + SEARCH_N_REPLACE(Msg, "@Op", GetObjectPronoun().CapitalizeCopy()); + SEARCH_N_REPLACE(Msg, "@Gd", GetMasterGod()->GetName()); + return Msg; +} + +void character::ProcessAndAddMessage(festring Msg) const +{ + ADD_MESSAGE("%s", ProcessMessage(Msg).CStr()); +} + +void character::BeTalkedTo() +{ + static long Said; + + if(GetRelation(PLAYER) == HOSTILE) + ProcessAndAddMessage(GetHostileReplies()[RandomizeReply(Said, GetHostileReplies().Size)]); + else + ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, GetFriendlyReplies().Size)]); +} + +truth character::CheckZap() +{ + if(!CanZap()) + { + ADD_MESSAGE("This monster type can't zap."); + return false; + } + + return true; +} + +void character::DamageAllItems(character* Damager, int Damage, int Type) +{ + GetStack()->ReceiveDamage(Damager, Damage, Type); + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment) + Equipment->ReceiveDamage(Damager, Damage, Type); + } +} + +truth character::Equips(citem* Item) const +{ + return combineequipmentpredicateswithparam()(this, &item::HasID, Item->GetID(), 1); +} + +void character::PrintBeginConfuseMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel quite happy."); +} + +void character::PrintEndConfuseMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("The world is boring again."); +} + +v2 character::ApplyStateModification(v2 TryDirection) const +{ + if(!StateIsActivated(CONFUSED) || RAND() & 15 || game::IsInWilderness()) + return TryDirection; + else + { + v2 To = GetLevel()->GetFreeAdjacentSquare(this, GetPos(), true); + + if(To == ERROR_V2) + return TryDirection; + else + { + To -= GetPos(); + + if(To != TryDirection && IsPlayer()) + ADD_MESSAGE("Whoa! You somehow don't manage to walk straight."); + + return To; + } + } +} + +void character::AddConfuseHitMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("This stuff is confusing."); +} + +item* character::SelectFromPossessions(cfestring& Topic, sorter Sorter) +{ + itemvector ReturnVector; + SelectFromPossessions(ReturnVector, Topic, NO_MULTI_SELECT, Sorter); + return !ReturnVector.empty() ? ReturnVector[0] : 0; +} + +truth character::SelectFromPossessions(itemvector& ReturnVector, cfestring& Topic, int Flags, sorter Sorter) +{ + felist List(Topic); + truth InventoryPossible = GetStack()->SortedItems(this, Sorter); + + if(InventoryPossible) + List.AddEntry(CONST_S("choose from inventory"), LIGHT_GRAY, 20, game::AddToItemDrawVector(itemvector())); + + truth Any = false; + itemvector Item; + festring Entry; + int c; + + for(c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart && (Sorter == 0 || (BodyPart->*Sorter)(this))) + { + Item.push_back(BodyPart); + Entry.Empty(); + BodyPart->AddName(Entry, UNARTICLED); + int ImageKey = game::AddToItemDrawVector(itemvector(1, BodyPart)); + List.AddEntry(Entry, LIGHT_GRAY, 20, ImageKey, true); + Any = true; + } + } + + for(c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment && (Sorter == 0 || (Equipment->*Sorter)(this))) + { + Item.push_back(Equipment); + Entry = GetEquipmentName(c); + Entry << ':'; + Entry.Resize(20); + Equipment->AddInventoryEntry(this, Entry, 1, true); + AddSpecialEquipmentInfo(Entry, c); + int ImageKey = game::AddToItemDrawVector(itemvector(1, Equipment)); + List.AddEntry(Entry, LIGHT_GRAY, 20, ImageKey, true); + Any = true; + } + } + + if(Any) + { + game::SetStandardListAttributes(List); + List.SetFlags(SELECTABLE|DRAW_BACKGROUND_AFTERWARDS); + List.SetEntryDrawer(game::ItemEntryDrawer); + game::DrawEverythingNoBlit(); + int Chosen = List.Draw(); + game::ClearItemDrawVector(); + + if(Chosen != ESCAPED) + { + if((InventoryPossible && !Chosen) || Chosen & FELIST_ERROR_BIT) + GetStack()->DrawContents(ReturnVector, this, Topic, Flags, Sorter); + else + { + ReturnVector.push_back(Item[InventoryPossible ? Chosen - 1 : Chosen]); + + if(Flags & SELECT_PAIR && ReturnVector[0]->HandleInPairs()) + { + item* PairEquipment = GetPairEquipment(ReturnVector[0]->GetEquipmentIndex()); + + if(PairEquipment && PairEquipment->CanBePiledWith(ReturnVector[0], this)) + ReturnVector.push_back(PairEquipment); + } + } + } + } + else + { + if(!GetStack()->SortedItems(this, Sorter)) + return false; + + game::ClearItemDrawVector(); + GetStack()->DrawContents(ReturnVector, this, Topic, Flags, Sorter); + } + + return true; +} + +truth character::EquipsSomething(sorter Sorter) +{ + for(int c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment && (Sorter == 0 || (Equipment->*Sorter)(this))) + return true; + } + + return false; +} + +material* character::CreateBodyPartMaterial(int, long Volume) const +{ + return MAKE_MATERIAL(GetFleshMaterial(), Volume); +} + +truth character::CheckTalk() +{ + if(!CanTalk()) + { + ADD_MESSAGE("This monster does not know the art of talking."); + return false; + } + + return true; +} + +truth character::MoveTowardsHomePos() +{ + if(HomeDataIsValid() && IsEnabled()) + { + SetGoingTo(HomeData->Pos); + return MoveTowardsTarget(false) + || (!GetPos().IsAdjacent(HomeData->Pos) && MoveRandomly()); + } + else + return false; +} + +truth character::TryToChangeEquipment(stack* MainStack, stack* SecStack, int Chosen) +{ + if(!GetBodyPartOfEquipment(Chosen)) + { + ADD_MESSAGE("Bodypart missing!"); + return false; + } + + item* OldEquipment = GetEquipment(Chosen); + + if(!IsPlayer() && BoundToUse(OldEquipment, Chosen)) + { + ADD_MESSAGE("%s refuses to unequip %s.", CHAR_DESCRIPTION(DEFINITE), OldEquipment->CHAR_NAME(DEFINITE)); + return false; + } + + if(OldEquipment) + OldEquipment->MoveTo(MainStack); + + sorter Sorter = EquipmentSorter(Chosen); + + if(!MainStack->SortedItems(this, Sorter) + && (!SecStack || !SecStack->SortedItems(this, Sorter))) + { + ADD_MESSAGE("You haven't got any item that could be used for this purpose."); + return false; + } + else + { + game::DrawEverythingNoBlit(); + itemvector ItemVector; + int Return = MainStack->DrawContents(ItemVector, + SecStack, + this, + CONST_S("Choose ") + GetEquipmentName(Chosen) + ':', + SecStack ? CONST_S("Items in your inventory") : CONST_S(""), + SecStack ? festring(CONST_S("Items in ") + GetPossessivePronoun() + " inventory") : CONST_S(""), + SecStack ? festring(GetDescription(DEFINITE) + " is " + GetVerbalBurdenState()) : CONST_S(""), + GetVerbalBurdenStateColor(), + NONE_AS_CHOICE|NO_MULTI_SELECT, + Sorter); + + + if(Return == ESCAPED) + { + if(OldEquipment) + { + OldEquipment->RemoveFromSlot(); + SetEquipment(Chosen, OldEquipment); + } + + return false; + } + + item* Item = ItemVector.empty() ? 0 : ItemVector[0]; + + if(Item) + { + if(!IsPlayer() && !AllowEquipment(Item, Chosen)) + { + ADD_MESSAGE("%s refuses to equip %s.", CHAR_DESCRIPTION(DEFINITE), Item->CHAR_NAME(DEFINITE)); + return false; + } + + Item->RemoveFromSlot(); + SetEquipment(Chosen, Item); + + if(CheckIfEquipmentIsNotUsable(Chosen)) + Item->MoveTo(MainStack); // small bug? + } + + return Item != OldEquipment; + } +} + +void character::PrintBeginParasitizedMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel you are no longer alone."); +} + +void character::PrintEndParasitizedMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("A feeling of long welcome emptiness overwhelms you."); +} + +void character::ParasitizedHandler() +{ + EditNP(-5); + + if(!(RAND() % 250)) + { + if(IsPlayer()) + ADD_MESSAGE("Ugh. You feel something violently carving its way through your intestines."); + + ReceiveDamage(0, 1, POISON, TORSO, 8, false, false, false, false); + CheckDeath(CONST_S("killed by a vile parasite"), 0); + } +} + +truth character::CanFollow() const +{ + return CanMove() && !StateIsActivated(PANIC) && !IsStuck(); +} + +festring character::GetKillName() const +{ + if(!GetPolymorphBackup()) + return GetName(INDEFINITE); + else + { + festring KillName; + GetPolymorphBackup()->AddName(KillName, INDEFINITE); + KillName << " polymorphed into "; + id::AddName(KillName, INDEFINITE); + return KillName; + } +} + +festring character::GetPanelName() const +{ + festring Name; + Name << AssignedName << " the " << game::GetVerbalPlayerAlignment() << ' '; + id::AddName(Name, UNARTICLED); + return Name; +} + +long character::GetMoveAPRequirement(int Difficulty) const +{ + return (!StateIsActivated(PANIC) ? 10000000 : 8000000) * Difficulty / (APBonus(GetAttribute(AGILITY)) * GetMoveEase()); +} + +bodypart* character::HealHitPoint() +{ + int NeedHeal = 0, NeedHealIndex[MAX_BODYPARTS]; + + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart && BodyPart->CanRegenerate() && BodyPart->GetHP() < BodyPart->GetMaxHP()) + NeedHealIndex[NeedHeal++] = c; + } + + if(NeedHeal) + { + bodypart* BodyPart = GetBodyPart(NeedHealIndex[RAND() % NeedHeal]); + BodyPart->IncreaseHP(); + ++HP; + return BodyPart; + } + else + return 0; +} + +void character::CreateHomeData() +{ + HomeData = new homedata; + lsquare* Square = GetLSquareUnder(); + HomeData->Pos = Square->GetPos(); + HomeData->Dungeon = Square->GetDungeonIndex(); + HomeData->Level = Square->GetLevelIndex(); + HomeData->Room = Square->GetRoomIndex(); +} + +room* character::GetHomeRoom() const +{ + if(HomeDataIsValid() && HomeData->Room) + return GetLevel()->GetRoom(HomeData->Room); + else + return 0; +} + +void character::RemoveHomeData() +{ + delete HomeData; + HomeData = 0; +} + +void character::AddESPConsumeMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel a strange mental activity."); +} + +void character::SetBodyPart(int I, bodypart* What) +{ + BodyPartSlot[I].PutInItem(What); + + if(What) + { + What->SignalPossibleUsabilityChange(); + What->Disable(); + AddOriginalBodyPartID(I, What->GetID()); + + if(What->GetMainMaterial()->IsInfectedByLeprosy()) + GainIntrinsic(LEPROSY); + else if(StateIsActivated(LEPROSY)) + What->GetMainMaterial()->SetIsInfectedByLeprosy(true); + } +} + +truth character::ConsumeItem(item* Item, cfestring& ConsumeVerb) +{ + if(IsPlayer() && HasHadBodyPart(Item) && !game::TruthQuestion(CONST_S("Are you sure? You may be able to put it back... [y/N]"))) + return false; + + if(Item->IsOnGround() && GetRoom() && !GetRoom()->ConsumeItem(this, Item, 1)) + return false; + + if(IsPlayer()) + ADD_MESSAGE("You begin %s %s.", ConsumeVerb.CStr(), Item->CHAR_NAME(DEFINITE)); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s begins %s %s.", CHAR_NAME(DEFINITE), ConsumeVerb.CStr(), Item->CHAR_NAME(DEFINITE)); + + consume* Consume = consume::Spawn(this); + Consume->SetDescription(ConsumeVerb); + Consume->SetConsumingID(Item->GetID()); + SetAction(Consume); + DexterityAction(5); + return true; +} + +truth character::CheckThrow() const +{ + if(!CanThrow()) + { + ADD_MESSAGE("This monster type cannot throw."); + return false; + } + + return true; +} + +void character::GetHitByExplosion(const explosion* Explosion, int Damage) +{ + int DamageDirection = GetPos() == Explosion->Pos ? RANDOM_DIR : game::CalculateRoughDirection(GetPos() - Explosion->Pos); + + if(!IsPet() && Explosion->Terrorist && Explosion->Terrorist->IsPet()) + Explosion->Terrorist->Hostility(this); + + GetTorso()->SpillBlood((8 - Explosion->Size + RAND() % (8 - Explosion->Size)) >> 1); + v2 SpillPos = GetPos() + game::GetMoveVector(DamageDirection); + + if(GetArea()->IsValidPos(SpillPos)) + GetTorso()->SpillBlood((8 - Explosion->Size + RAND() % (8 - Explosion->Size)) >> 1, SpillPos); + + if(IsPlayer()) + ADD_MESSAGE("You are hit by the explosion!"); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s is hit by the explosion.", CHAR_NAME(DEFINITE)); + + truth WasUnconscious = GetAction() && GetAction()->IsUnconsciousness(); + ReceiveDamage(Explosion->Terrorist, Damage >> 1, FIRE, ALL, DamageDirection, true, false, false, false); + + if(IsEnabled()) + { + ReceiveDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE, ALL, DamageDirection, true, false, false, false); + CheckDeath(Explosion->DeathMsg, Explosion->Terrorist, !WasUnconscious ? IGNORE_UNCONSCIOUSNESS : 0); + } +} + +void character::SortAllItems(const sortdata& SortData) +{ + GetStack()->SortAllItems(SortData); + doforequipmentswithparam()(this, &item::SortAllItems, SortData); +} + +void character::PrintBeginSearchingMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel you can now notice even the very smallest details around you."); +} + +void character::PrintEndSearchingMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel less perceptive."); +} + +void character::SearchingHandler() +{ + if(!game::IsInWilderness()) + Search(15); +} + +void character::Search(int Perception) +{ + for(int d = 0; d < GetExtendedNeighbourSquares(); ++d) + { + lsquare* LSquare = GetNeighbourLSquare(d); + + if(LSquare) + LSquare->GetStack()->Search(this, Min(Perception, 200)); + } +} + +// surprisingly returns 0 if fails + +character* character::GetRandomNeighbour(int RelationFlags) const +{ + character* Chars[MAX_NEIGHBOUR_SQUARES]; + int Index = 0; + + for(int d = 0; d < GetNeighbourSquares(); ++d) + { + lsquare* LSquare = GetNeighbourLSquare(d); + + if(LSquare) + { + character* Char = LSquare->GetCharacter(); + + if(Char && (GetRelation(Char) & RelationFlags)) + Chars[Index++] = Char; + } + } + + return Index ? Chars[RAND() % Index] : 0; +} + +void character::ResetStates() +{ + for(int c = 0; c < STATES; ++c) + if(1 << c != POLYMORPHED && TemporaryStateIsActivated(1 << c) && TemporaryStateCounter[c] != PERMANENT) + { + TemporaryState &= ~(1 << c); + + if(StateData[c].EndHandler) + { + (this->*StateData[c].EndHandler)(); + + if(!IsEnabled()) + return; + } + } +} + +void characterdatabase::InitDefaults(const characterprototype* NewProtoType, int NewConfig) +{ + IsAbstract = false; + ProtoType = NewProtoType; + Config = NewConfig; + Alias.Clear(); +} + +void character::PrintBeginGasImmunityMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("All smells fade away."); +} + +void character::PrintEndGasImmunityMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("Yuck! The world smells bad again."); +} + +void character::ShowAdventureInfo() const +{ + if(GetStack()->GetItems() && game::TruthQuestion(CONST_S("Do you want to see your inventory? [y/n]"), REQUIRES_ANSWER)) + { + GetStack()->DrawContents(this, CONST_S("Your inventory"), NO_SELECT); + + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + i->DrawContents(this); + + doforequipmentswithparam()(this, &item::DrawContents, this); + } + + if(game::TruthQuestion(CONST_S("Do you want to see your message history? [y/n]"), REQUIRES_ANSWER)) + msgsystem::DrawMessageHistory(); + + if(!game::MassacreListsEmpty() && game::TruthQuestion(CONST_S("Do you want to see your massacre history? [y/n]"), REQUIRES_ANSWER)) + game::DisplayMassacreLists(); +} + +truth character::EditAllAttributes(int Amount) +{ + if(!Amount) + return true; + + int c; + truth MayEditMore = false; + + for(c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart && BodyPart->EditAllAttributes(Amount)) + MayEditMore = true; + } + + for(c = 0; c < BASE_ATTRIBUTES; ++c) + if(BaseExperience[c]) + { + BaseExperience[c] += Amount * EXP_MULTIPLIER; + LimitRef(BaseExperience[c], MIN_EXP, MAX_EXP); + + if((Amount < 0 && BaseExperience[c] != MIN_EXP) + || (Amount > 0 && BaseExperience[c] != MAX_EXP)) + MayEditMore = true; + } + + CalculateAll(); + RestoreHP(); + RestoreStamina(); + + if(IsPlayer()) + { + game::SendLOSUpdateRequest(); + UpdateESPLOS(); + } + + if(IsPlayerKind()) + UpdatePictures(); + + return MayEditMore; +} + +#ifdef WIZARD + +void character::AddAttributeInfo(festring& Entry) const +{ + Entry.Resize(57); + Entry << GetAttribute(ENDURANCE); + Entry.Resize(60); + Entry << GetAttribute(PERCEPTION); + Entry.Resize(63); + Entry << GetAttribute(INTELLIGENCE); + Entry.Resize(66); + Entry << GetAttribute(WISDOM); + Entry.Resize(69); + Entry << GetAttribute(CHARISMA); + Entry.Resize(72); + Entry << GetAttribute(MANA); +} + +void character::AddDefenceInfo(felist& List) const +{ + festring Entry; + + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart) + { + Entry = CONST_S(" "); + BodyPart->AddName(Entry, UNARTICLED); + Entry.Resize(60); + Entry << BodyPart->GetMaxHP(); + Entry.Resize(70); + Entry << BodyPart->GetTotalResistance(PHYSICAL_DAMAGE); + List.AddEntry(Entry, LIGHT_GRAY); + } + } +} + +void character::DetachBodyPart() +{ + ADD_MESSAGE("You haven't got any extra bodyparts."); +} + +#endif + +void character::ReceiveHolyBanana(long Amount) +{ + Amount <<= 1; + EditExperience(ARM_STRENGTH, Amount, 1 << 13); + EditExperience(LEG_STRENGTH, Amount, 1 << 13); + EditExperience(DEXTERITY, Amount, 1 << 13); + EditExperience(AGILITY, Amount, 1 << 13); + EditExperience(ENDURANCE, Amount, 1 << 13); + EditExperience(PERCEPTION, Amount, 1 << 13); + EditExperience(INTELLIGENCE, Amount, 1 << 13); + EditExperience(WISDOM, Amount, 1 << 13); + EditExperience(CHARISMA, Amount, 1 << 13); + RestoreLivingHP(); +} + +void character::AddHolyBananaConsumeEndMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel a mysterious strengthening fire coursing through your body."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("For a moment %s is surrounded by a swirling fire aura.", CHAR_NAME(DEFINITE)); +} + +void character::ReceiveHolyMango(long Amount) +{ + Amount <<= 1; + EditExperience(ARM_STRENGTH, Amount, 1 << 13); + EditExperience(LEG_STRENGTH, Amount, 1 << 13); + EditExperience(DEXTERITY, Amount, 1 << 13); + EditExperience(AGILITY, Amount, 1 << 13); + EditExperience(ENDURANCE, Amount, 1 << 13); + EditExperience(PERCEPTION, Amount, 1 << 13); + EditExperience(INTELLIGENCE, Amount, 1 << 13); + EditExperience(WISDOM, Amount, 1 << 13); + EditExperience(CHARISMA, Amount, 1 << 13); + RestoreLivingHP(); +} + +void character::AddHolyMangoConsumeEndMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel a mysterious strengthening fire coursing through your body."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("For a moment %s is surrounded by a swirling fire aura.", CHAR_NAME(DEFINITE)); +} + +truth character::PreProcessForBone() +{ + if(IsPet() && IsEnabled()) + { + Die(0, CONST_S(""), FORBID_REINCARNATION); + return true; + } + + if(GetAction()) + GetAction()->Terminate(false); + + if(TemporaryStateIsActivated(POLYMORPHED)) + { + character* PolymorphBackup = GetPolymorphBackup(); + EndPolymorph(); + PolymorphBackup->PreProcessForBone(); + return true; + } + + if(MustBeRemovedFromBone()) + return false; + else if(IsUnique() && !CanBeGenerated()) + game::SignalQuestMonsterFound(); + + RestoreLivingHP(); + ResetStates(); + RemoveTraps(); + GetStack()->PreProcessForBone(); + doforequipments()(this, &item::PreProcessForBone); + doforbodyparts()(this, &bodypart::PreProcessForBone); + game::RemoveCharacterID(ID); + ID = -ID; + game::AddCharacterID(this, ID); + return true; +} + +truth character::PostProcessForBone(double& DangerSum, int& Enemies) +{ + if(PostProcessForBone()) + { + if(GetRelation(PLAYER) == HOSTILE) + { + double Danger = GetRelativeDanger(PLAYER, true); + + if(Danger > 99.) + game::SetTooGreatDangerFound(true); + else if(!IsUnique() && !IgnoreDanger()) + { + DangerSum += Danger; + ++Enemies; + } + } + + return true; + } + else + return false; +} + +truth character::PostProcessForBone() +{ + ulong NewID = game::CreateNewCharacterID(this); + game::GetBoneCharacterIDMap().insert(std::make_pair(-ID, NewID)); + game::RemoveCharacterID(ID); + ID = NewID; + + if(IsUnique() && CanBeGenerated()) + { + if(DataBase->Flags & HAS_BEEN_GENERATED) + return false; + else + SignalGeneration(); + } + + GetStack()->PostProcessForBone(); + doforequipments()(this, &item::PostProcessForBone); + doforbodyparts()(this, &bodypart::PostProcessForBone); + return true; +} + +void character::FinalProcessForBone() +{ + Flags &= ~C_PLAYER; + GetStack()->FinalProcessForBone(); + doforequipments()(this, &item::FinalProcessForBone); + int c; + + for(c = 0; c < BodyParts; ++c) + { + for(std::list::iterator i = OriginalBodyPartID[c].begin(); i != OriginalBodyPartID[c].end();) + { + boneidmap::iterator BI = game::GetBoneItemIDMap().find(*i); + + if(BI == game::GetBoneItemIDMap().end()) + { + std::list::iterator Dirt = i++; + OriginalBodyPartID[c].erase(Dirt); + } + else + { + *i = BI->second; + ++i; + } + } + } +} + +void character::SetSoulID(ulong What) +{ + if(GetPolymorphBackup()) + GetPolymorphBackup()->SetSoulID(What); +} + +truth character::SearchForItem(citem* Item) const +{ + if(combineequipmentpredicateswithparam()(this, &item::HasID, Item->GetID(), 1)) + return true; + + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if(*i == Item) + return true; + + return false; +} + +item* character::SearchForItem(const sweaponskill* SWeaponSkill) const +{ + for(int c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment && SWeaponSkill->IsSkillOf(Equipment)) + return Equipment; + } + + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if(SWeaponSkill->IsSkillOf(*i)) + return *i; + + return 0; +} + +void character::PutNear(v2 Pos) +{ + v2 NewPos = game::GetCurrentLevel()->GetNearestFreeSquare(this, Pos, false); + + if(NewPos == ERROR_V2) + do + { + NewPos = game::GetCurrentLevel()->GetRandomSquare(this); + } + while(NewPos == Pos); + + PutTo(NewPos); +} + +void character::PutToOrNear(v2 Pos) +{ + if(game::IsInWilderness() || (CanMoveOn(game::GetCurrentLevel()->GetLSquare(Pos)) && IsFreeForMe(game::GetCurrentLevel()->GetLSquare(Pos)))) + PutTo(Pos); + else + PutNear(Pos); +} + +void character::PutTo(v2 Pos) +{ + SquareUnder[0] = game::GetCurrentArea()->GetSquare(Pos); + SquareUnder[0]->AddCharacter(this); +} + +void character::Remove() +{ + SquareUnder[0]->RemoveCharacter(); + SquareUnder[0] = 0; +} + +void character::SendNewDrawRequest() const +{ + for(int c = 0; c < SquaresUnder; ++c) + { + square* Square = GetSquareUnder(c); + + if(Square) + Square->SendNewDrawRequest(); + } +} + +truth character::IsOver(v2 Pos) const +{ + for(int c = 0; c < SquaresUnder; ++c) + { + square* Square = GetSquareUnder(c); + + if(Square && Square->GetPos() == Pos) + return true; + } + + return false; +} + +truth character::CanTheoreticallyMoveOn(const lsquare* LSquare) const +{ + return GetMoveType() & LSquare->GetTheoreticalWalkability(); +} + +truth character::CanMoveOn(const lsquare* LSquare) const +{ + return GetMoveType() & LSquare->GetWalkability(); +} + +truth character::CanMoveOn(const square* Square) const +{ + return GetMoveType() & Square->GetSquareWalkability(); +} + +truth character::CanMoveOn(const olterrain* OLTerrain) const +{ + return GetMoveType() & OLTerrain->GetWalkability(); +} + +truth character::CanMoveOn(const oterrain* OTerrain) const +{ + return GetMoveType() & OTerrain->GetWalkability(); +} + +truth character::IsFreeForMe(square* Square) const +{ + return !Square->GetCharacter() || Square->GetCharacter() == this; +} + +void character::LoadSquaresUnder() +{ + SquareUnder[0] = game::GetSquareInLoad(); +} + +truth character::AttackAdjacentEnemyAI() +{ + if(!IsEnabled()) + return false; + + character* Char[MAX_NEIGHBOUR_SQUARES]; + v2 Pos[MAX_NEIGHBOUR_SQUARES]; + int Dir[MAX_NEIGHBOUR_SQUARES]; + int Index = 0; + + for(int d = 0; d < GetNeighbourSquares(); ++d) + { + square* Square = GetNeighbourSquare(d); + + if(Square) + { + character* Enemy = Square->GetCharacter(); + + if(Enemy && (GetRelation(Enemy) == HOSTILE || StateIsActivated(CONFUSED))) + { + Dir[Index] = d; + Pos[Index] = Square->GetPos(); + Char[Index++] = Enemy; + } + } + } + + if(Index) + { + int ChosenIndex = RAND() % Index; + Hit(Char[ChosenIndex], Pos[ChosenIndex], Dir[ChosenIndex]); + return true; + } + + return false; +} + +void character::SignalStepFrom(lsquare** OldSquareUnder) +{ + int c; + lsquare* NewSquareUnder[MAX_SQUARES_UNDER]; + + for(c = 0; c < GetSquaresUnder(); ++c) + NewSquareUnder[c] = GetLSquareUnder(c); + + for(c = 0; c < GetSquaresUnder(); ++c) + if(IsEnabled() && GetLSquareUnder(c) == NewSquareUnder[c]) + NewSquareUnder[c]->StepOn(this, OldSquareUnder); +} + +int character::GetSumOfAttributes() const +{ + return GetAttribute(ENDURANCE) + GetAttribute(PERCEPTION) + GetAttribute(INTELLIGENCE) + GetAttribute(WISDOM) + GetAttribute(CHARISMA) + GetAttribute(ARM_STRENGTH) + GetAttribute(AGILITY); +} + +void character::IntelligenceAction(int Difficulty) +{ + EditAP(-20000 * Difficulty / APBonus(GetAttribute(INTELLIGENCE))); + EditExperience(INTELLIGENCE, Difficulty * 15, 1 << 7); +} + +struct walkabilitycontroller +{ + static truth Handler(int x, int y) + { + return x >= 0 && y >= 0 && x < LevelXSize && y < LevelYSize + && Map[x][y]->GetTheoreticalWalkability() & MoveType; + } + static lsquare*** Map; + static int LevelXSize, LevelYSize; + static int MoveType; +}; + +lsquare*** walkabilitycontroller::Map; +int walkabilitycontroller::LevelXSize, walkabilitycontroller::LevelYSize; +int walkabilitycontroller::MoveType; + +truth character::CreateRoute() +{ + Route.clear(); + + if(GetAttribute(INTELLIGENCE) >= 10 && !StateIsActivated(CONFUSED)) + { + v2 Pos = GetPos(); + walkabilitycontroller::Map = GetLevel()->GetMap(); + walkabilitycontroller::LevelXSize = GetLevel()->GetXSize(); + walkabilitycontroller::LevelYSize = GetLevel()->GetYSize(); + walkabilitycontroller::MoveType = GetMoveType(); + node* Node; + + for(int c = 0; c < game::GetTeams(); ++c) + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + { + character* Char = *i; + + if(Char->IsEnabled() + && !Char->Route.empty() + && Char->GetMoveType() & GetMoveType() == Char->GetMoveType()) + { + v2 CharGoingTo = Char->Route[0]; + v2 iPos = Char->Route.back(); + + if((GoingTo - CharGoingTo).GetLengthSquare() <= 100 + && (Pos - iPos).GetLengthSquare() <= 100 + && mapmath::DoLine(CharGoingTo.X, CharGoingTo.Y, GoingTo.X, GoingTo.Y, SKIP_FIRST) + && mapmath::DoLine(Pos.X, Pos.Y, iPos.X, iPos.Y, SKIP_FIRST)) + { + if(!Illegal.empty() && Illegal.find(Char->Route.back()) != Illegal.end()) + continue; + + Node = GetLevel()->FindRoute(CharGoingTo, GoingTo, Illegal, GetMoveType()); + + if(Node) + while(Node->Last) + { + Route.push_back(Node->Pos); + Node = Node->Last; + } + else + { + Route.clear(); + continue; + } + + Route.insert(Route.end(), Char->Route.begin(), Char->Route.end()); + Node = GetLevel()->FindRoute(Pos, iPos, Illegal, GetMoveType()); + + if(Node) + while(Node->Last) + { + Route.push_back(Node->Pos); + Node = Node->Last; + } + else + { + Route.clear(); + continue; + } + + IntelligenceAction(1); + return true; + } + } + } + + Node = GetLevel()->FindRoute(Pos, GoingTo, Illegal, GetMoveType()); + + if(Node) + while(Node->Last) + { + Route.push_back(Node->Pos); + Node = Node->Last; + } + else + TerminateGoingTo(); + + IntelligenceAction(5); + return true; + } + else + return false; +} + +void character::SetGoingTo(v2 What) +{ + if(GoingTo != What) + { + GoingTo = What; + Route.clear(); + Illegal.clear(); + } +} + +void character::TerminateGoingTo() +{ + GoingTo = ERROR_V2; + Route.clear(); + Illegal.clear(); +} + +truth character::CheckForFood(int Radius) +{ + if(StateIsActivated(PANIC) || !UsesNutrition() || !IsEnabled()) + return false; + + v2 Pos = GetPos(); + int x, y; + + for(int r = 1; r <= Radius; ++r) + { + x = Pos.X - r; + + if(x >= 0) + for(y = Pos.Y - r; y <= Pos.Y + r; ++y) + if(CheckForFoodInSquare(v2(x, y))) + return true; + + x = Pos.X + r; + + if(x < GetLevel()->GetXSize()) + for(y = Pos.Y - r; y <= Pos.Y + r; ++y) + if(CheckForFoodInSquare(v2(x, y))) + return true; + + y = Pos.Y - r; + + if(y >= 0) + for(x = Pos.X - r; x <= Pos.X + r; ++x) + if(CheckForFoodInSquare(v2(x, y))) + return true; + + y = Pos.Y + r; + + if(y < GetLevel()->GetYSize()) + for(x = Pos.X - r; x <= Pos.X + r; ++x) + if(CheckForFoodInSquare(v2(x, y))) + return true; + } + + return false; +} + +truth character::CheckForFoodInSquare(v2 Pos) +{ + level* Level = GetLevel(); + + if(Level->IsValidPos(Pos)) + { + lsquare* Square = Level->GetLSquare(Pos); + stack* Stack = Square->GetStack(); + + if(Stack->GetItems()) + for(stackiterator i = Stack->GetBottom(); i.HasItem(); ++i) + if(i->IsPickable(this) + && i->CanBeSeenBy(this) + && i->CanBeEatenByAI(this) + && (!Square->GetRoomIndex() + || Square->GetRoom()->AllowFoodSearch())) + { + SetGoingTo(Pos); + return MoveTowardsTarget(false); + } + } + + return false; +} + +void character::SetConfig(int NewConfig, int SpecialFlags) +{ + databasecreator::InstallDataBase(this, NewConfig); + CalculateAll(); + CheckIfSeen(); + + if(!(SpecialFlags & NO_PIC_UPDATE)) + UpdatePictures(); +} + +truth character::IsOver(citem* Item) const +{ + for(int c1 = 0; c1 < Item->GetSquaresUnder(); ++c1) + for(int c2 = 0; c2 < SquaresUnder; ++c2) + if(Item->GetPos(c1) == GetPos(c2)) + return true; + + return false; +} + +truth character::CheckConsume(cfestring& Verb) const +{ + if(!UsesNutrition()) + { + if(IsPlayer()) + ADD_MESSAGE("In this form you can't and don't need to %s.", Verb.CStr()); + + return false; + } + + return true; +} + +void character::PutTo(lsquare* To) +{ + PutTo(To->GetPos()); +} + +double character::RandomizeBabyExperience(double SumE) +{ + if(!SumE) + return 0; + + double E = (SumE / 4) - (SumE / 32) + (double(RAND()) / MAX_RAND) * (SumE / 16 + 1); + return Limit(E, MIN_EXP, MAX_EXP); +} + +liquid* character::CreateBlood(long Volume) const +{ + return liquid::Spawn(GetBloodMaterial(), Volume); +} + +void character::SpillFluid(character* Spiller, liquid* Liquid, int SquareIndex) +{ + long ReserveVolume = Liquid->GetVolume() >> 1; + Liquid->EditVolume(-ReserveVolume); + GetStack()->SpillFluid(Spiller, Liquid, long(Liquid->GetVolume() * sqrt(double(GetStack()->GetVolume()) / GetVolume()))); + Liquid->EditVolume(ReserveVolume); + int c; + long Modifier[MAX_BODYPARTS], ModifierSum = 0; + + for(c = 0; c < BodyParts; ++c) + if(GetBodyPart(c)) + { + Modifier[c] = long(sqrt(GetBodyPart(c)->GetVolume())); + + if(Modifier[c]) + Modifier[c] *= 1 + (RAND() & 3); + + ModifierSum += Modifier[c]; + } + else + Modifier[c] = 0; + + for(c = 1; c < GetBodyParts(); ++c) + if(GetBodyPart(c) && IsEnabled()) + GetBodyPart(c)->SpillFluid(Spiller, Liquid->SpawnMoreLiquid(Liquid->GetVolume() * Modifier[c] / ModifierSum), SquareIndex); + + if(IsEnabled()) + { + Liquid->SetVolume(Liquid->GetVolume() * Modifier[TORSO_INDEX] / ModifierSum); + GetTorso()->SpillFluid(Spiller, Liquid, SquareIndex); + } +} + +void character::StayOn(liquid* Liquid) +{ + Liquid->TouchEffect(this, TORSO_INDEX); +} + +truth character::IsAlly(ccharacter* Char) const +{ + return Char->GetTeam()->GetID() == GetTeam()->GetID(); +} + +void character::ResetSpoiling() +{ + doforbodyparts()(this, &bodypart::ResetSpoiling); +} + +item* character::SearchForItem(ccharacter* Char, sorter Sorter) const +{ + item* Equipment = findequipment()(this, Sorter, Char); + + if(Equipment) + return Equipment; + + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if(((*i)->*Sorter)(Char)) + return *i; + + return 0; +} + +truth character::DetectMaterial(cmaterial* Material) const +{ + return GetStack()->DetectMaterial(Material) + || combinebodypartpredicateswithparam()(this, &bodypart::DetectMaterial, Material, 1) + || combineequipmentpredicateswithparam()(this, &item::DetectMaterial, Material, 1); +} + +truth character::DamageTypeDestroysBodyPart(int Type) +{ + return (Type&0xFFF) != PHYSICAL_DAMAGE; +} + +truth character::CheckIfTooScaredToHit(ccharacter* Enemy) const +{ + if(IsPlayer() && StateIsActivated(PANIC)) + for(int d = 0; d < GetNeighbourSquares(); ++d) + { + square* Square = GetNeighbourSquare(d); + + if(Square) + { + if(CanMoveOn(Square) + && (!Square->GetCharacter() + || Square->GetCharacter()->IsPet())) + { + ADD_MESSAGE("You are too scared to attack %s.", Enemy->CHAR_DESCRIPTION(DEFINITE)); + return true; + } + } + } + + return false; +} + +void character::PrintBeginLevitationMessage() const +{ + if(!IsFlying()) + if(IsPlayer()) + ADD_MESSAGE("You rise into the air like a small hot-air balloon."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s begins to float.", CHAR_NAME(DEFINITE)); +} + +void character::PrintEndLevitationMessage() const +{ + if(!IsFlying()) + if(IsPlayer()) + ADD_MESSAGE("You descend gently onto the ground."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s drops onto the ground.", CHAR_NAME(DEFINITE)); +} + +truth character::IsLimbIndex(int I) +{ + switch(I) + { + case RIGHT_ARM_INDEX: + case LEFT_ARM_INDEX: + case RIGHT_LEG_INDEX: + case LEFT_LEG_INDEX: + return true; + } + + return false; +} + +void character::EditExperience(int Identifier, double Value, double Speed) +{ + if(!AllowExperience() + || (Identifier == ENDURANCE && UseMaterialAttributes())) + return; + + int Change = RawEditExperience(BaseExperience[Identifier], + GetNaturalExperience(Identifier), + Value, Speed); + + if(!Change) + return; + + cchar* PlayerMsg = 0, * NPCMsg = 0; + + switch(Identifier) + { + case ENDURANCE: + if(Change > 0) + { + PlayerMsg = "You feel tougher than anything!"; + + if(IsPet()) + NPCMsg = "Suddenly %s looks tougher."; + } + else + { + PlayerMsg = "You feel less healthy."; + + if(IsPet()) + NPCMsg = "Suddenly %s looks less healthy."; + } + + CalculateBodyPartMaxHPs(); + CalculateMaxStamina(); + break; + case PERCEPTION: + if(IsPlayer()) + { + if(Change > 0) + PlayerMsg = "You now see the world in much better detail than before."; + else + { + PlayerMsg = "You feel very guru."; + game::GetGod(VALPURUS)->AdjustRelation(100); + } + + game::SendLOSUpdateRequest(); + } + break; + case INTELLIGENCE: + if(IsPlayer()) + { + if(Change > 0) + PlayerMsg = "Suddenly the inner structure of the Multiverse around you looks quite simple."; + else + PlayerMsg = "It surely is hard to think today."; + + UpdateESPLOS(); + } + + if(IsPlayerKind()) + UpdatePictures(); + + break; + case WISDOM: + if(IsPlayer()) + { + if(Change > 0) + PlayerMsg = "You feel your life experience increasing all the time."; + else + PlayerMsg = "You feel like having done something unwise."; + } + + if(IsPlayerKind()) + UpdatePictures(); + + break; + case CHARISMA: + if(Change > 0) + { + PlayerMsg = "You feel very confident of your social skills."; + + if(IsPet()) + if(GetAttribute(CHARISMA) <= 15) + NPCMsg = "%s looks less ugly."; + else + NPCMsg = "%s looks more attractive."; + } + else + { + PlayerMsg = "You feel somehow disliked."; + + if(IsPet()) + if(GetAttribute(CHARISMA) < 15) + NPCMsg = "%s looks more ugly."; + else + NPCMsg = "%s looks less attractive."; + } + + if(IsPlayerKind()) + UpdatePictures(); + + break; + case MANA: + if(Change > 0) + { + PlayerMsg = "You feel magical forces coursing through your body!"; + NPCMsg = "You notice an odd glow around %s."; + } + else + { + PlayerMsg = "You feel your magical abilities withering slowly."; + NPCMsg = "You notice strange vibrations in the air around %s. But they disappear rapidly."; + } + + break; + } + + if(IsPlayer()) + ADD_MESSAGE(PlayerMsg); + else if(NPCMsg && CanBeSeenByPlayer()) + ADD_MESSAGE(NPCMsg, CHAR_NAME(DEFINITE)); + + CalculateBattleInfo(); +} + +int character::RawEditExperience(double& Exp, double NaturalExp, double Value, double Speed) const +{ + double OldExp = Exp; + + if(Speed < 0) + { + Speed = -Speed; + Value = -Value; + } + + if(!OldExp || !Value + || (Value > 0 && OldExp >= NaturalExp * (100 + Value) / 100) + || (Value < 0 && OldExp <= NaturalExp * (100 + Value) / 100)) + return 0; + + if(!IsPlayer()) + Speed *= 1.5; + + Exp += (NaturalExp * (100 + Value) - 100 * OldExp) * Speed * EXP_DIVISOR; + LimitRef(Exp, MIN_EXP, MAX_EXP); + int NewA = int(Exp * EXP_DIVISOR); + int OldA = int(OldExp * EXP_DIVISOR); + int Delta = NewA - OldA; + + if(Delta > 0) + Exp = Max(Exp, (NewA + 0.05) * EXP_MULTIPLIER); + else if(Delta < 0) + Exp = Min(Exp, (NewA + 0.95) * EXP_MULTIPLIER); + + LimitRef(Exp, MIN_EXP, MAX_EXP); + return Delta; +} + +int character::GetAttribute(int Identifier, truth AllowBonus) const +{ + int A = int(BaseExperience[Identifier] * EXP_DIVISOR); + + if(AllowBonus && Identifier == INTELLIGENCE && BrainsHurt()) + return Max((A + AttributeBonus[INTELLIGENCE]) / 3, 1); + + return A && AllowBonus ? Max(A + AttributeBonus[Identifier], 1) : A; +} + +void characterdatabase::PostProcess() +{ + double AM = (100 + AttributeBonus) * EXP_MULTIPLIER / 100; + + for(int c = 0; c < ATTRIBUTES; ++c) + NaturalExperience[c] = this->*ExpPtr[c] * AM; +} + +void character::EditDealExperience(long Price) +{ + EditExperience(CHARISMA, sqrt(Price) / 5, 1 << 9); +} + +void character::PrintBeginLeprosyMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel you're falling in pieces."); +} + +void character::PrintEndLeprosyMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel your limbs are stuck in place tightly."); // CHANGE OR DIE +} + +void character::TryToInfectWithLeprosy(ccharacter* Infector) +{ + if(!IsImmuneToLeprosy() + && ((GetRelation(Infector) == HOSTILE + && !RAND_N(50 * GetAttribute(ENDURANCE))) + || !RAND_N(500 * GetAttribute(ENDURANCE)))) + GainIntrinsic(LEPROSY); +} + +void character::SignalGeneration() +{ + const_cast(DataBase)->Flags |= HAS_BEEN_GENERATED; +} + +void character::CheckIfSeen() +{ + if(IsPlayer() || CanBeSeenByPlayer()) + SignalSeen(); +} + +void character::SignalSeen() +{ + if(!(WarnFlags & WARNED) + && GetRelation(PLAYER) == HOSTILE) + { + double Danger = GetRelativeDanger(PLAYER); + + if(Danger > 5.) + { + game::SetDangerFound(Max(game::GetDangerFound(), Danger)); + + if(Danger > 500. && !(WarnFlags & HAS_CAUSED_PANIC)) + { + WarnFlags |= HAS_CAUSED_PANIC; + game::SetCausePanicFlag(true); + } + + WarnFlags |= WARNED; + } + } + + const_cast(DataBase)->Flags |= HAS_BEEN_SEEN; +} + +int character::GetPolymorphIntelligenceRequirement() const +{ + if(DataBase->PolymorphIntelligenceRequirement == DEPENDS_ON_ATTRIBUTES) + return Max(GetAttributeAverage() - 5, 0); + else + return DataBase->PolymorphIntelligenceRequirement; +} + +void character::RemoveAllItems() +{ + GetStack()->Clean(); + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment) + { + Equipment->RemoveFromSlot(); + Equipment->SendToHell(); + } + } +} + +int character::CalculateWeaponSkillHits(ccharacter* Enemy) const +{ + if(Enemy->IsPlayer()) + { + configid ConfigID(GetType(), GetConfig()); + const dangerid& DangerID = game::GetDangerMap().find(ConfigID)->second; + return Min(int(DangerID.EquippedDanger * 2000), 1000); + } + else + return Min(int(GetRelativeDanger(Enemy, true) * 2000), 1000); +} + +truth character::CanUseEquipment(int I) const +{ + return CanUseEquipment() && I < GetEquipments() && GetBodyPartOfEquipment(I) && EquipmentIsAllowed(I); +} + +/* Target mustn't have any equipment */ + +void character::DonateEquipmentTo(character* Character) +{ + if(IsPlayer()) + { + ulong* EquipmentMemory = game::GetEquipmentMemory(); + + for(int c = 0; c < MAX_EQUIPMENT_SLOTS; ++c) + { + item* Item = GetEquipment(c); + + if(Item) + { + if(Character->CanUseEquipment(c)) + { + Item->RemoveFromSlot(); + Character->SetEquipment(c, Item); + } + else + { + EquipmentMemory[c] = Item->GetID(); + Item->MoveTo(Character->GetStack()); + } + } + else if(CanUseEquipment(c)) + EquipmentMemory[c] = 0; + else if(EquipmentMemory[c] + && Character->CanUseEquipment(c)) + { + for(stackiterator i = Character->GetStack()->GetBottom(); + i.HasItem(); ++i) + if(i->GetID() == EquipmentMemory[c]) + { + item* Item = *i; + Item->RemoveFromSlot(); + Character->SetEquipment(c, Item); + break; + } + + EquipmentMemory[c] = 0; + } + } + } + else + { + for(int c = 0; c < GetEquipments(); ++c) + { + item* Item = GetEquipment(c); + + if(Item) + { + if(Character->CanUseEquipment(c)) + { + Item->RemoveFromSlot(); + Character->SetEquipment(c, Item); + } + else + Item->MoveTo(Character->GetStackUnder()); + } + } + } +} + +void character::ReceivePeaSoup(long) +{ + lsquare* Square = GetLSquareUnder(); + + if(Square->IsFlyable()) + Square->AddSmoke(gas::Spawn(FART, 250)); +} + +void character::AddPeaSoupConsumeEndMessage() const +{ + if(IsPlayer()) + { + if(CanHear()) + ADD_MESSAGE("Mmmh! The soup is very tasty. You hear a small puff."); + else + ADD_MESSAGE("Mmmh! The soup is very tasty."); + } + else if(CanBeSeenByPlayer() && PLAYER->CanHear()) // change someday + ADD_MESSAGE("You hear a small puff."); +} + +void character::CalculateMaxStamina() +{ + MaxStamina = TorsoIsAlive() ? GetAttribute(ENDURANCE) * 10000 : 0; +} + +void character::EditStamina(int Amount, truth CanCauseUnconsciousness) +{ + if(!TorsoIsAlive()) + return; + + int UnconsciousnessStamina = MaxStamina >> 3; + + if(!CanCauseUnconsciousness && Amount < 0) + { + if(Stamina > UnconsciousnessStamina) + { + Stamina += Amount; + + if(Stamina < UnconsciousnessStamina) + Stamina = UnconsciousnessStamina; + } + + return; + } + + int OldStamina = Stamina; + Stamina += Amount; + + if(Stamina > MaxStamina) + Stamina = MaxStamina; + else if(Stamina < 0) + { + Stamina = 0; + LoseConsciousness(250 + RAND_N(250)); + } + else if(IsPlayer()) + { + if(OldStamina >= MaxStamina >> 2 && Stamina < MaxStamina >> 2) + ADD_MESSAGE("You are getting a little tired."); + else if(OldStamina >= UnconsciousnessStamina + && Stamina < UnconsciousnessStamina) + { + ADD_MESSAGE("You are seriously out of breath!"); + game::SetPlayerIsRunning(false); + } + } + + if(IsPlayer() && StateIsActivated(PANIC) + && GetTirednessState() != FAINTING) + game::SetPlayerIsRunning(true); +} + +void character::RegenerateStamina() +{ + if(GetTirednessState() != UNTIRED) + { + EditExperience(ENDURANCE, 50, 1); + + if(Sweats() && TorsoIsAlive() && !RAND_N(30) && !game::IsInWilderness()) + { + // Sweat amount proportional to endurance also + long Volume = long(.05 * sqrt(GetBodyVolume() * GetAttribute(ENDURANCE) / 10)); + + // used to be: + //long Volume = long(.05 * sqrt(GetBodyVolume())); + + if(GetTirednessState() == FAINTING) + Volume <<= 1; + + for(int c = 0; c < SquaresUnder; ++c) + GetLSquareUnder(c)->SpillFluid(0, CreateSweat(Volume), false, false); + } + } + + int Bonus = 1; + + if(Action) + { + if(Action->IsRest()) + { + if(SquaresUnder == 1) + Bonus = GetSquareUnder()->GetRestModifier() << 1; + else + { + int Lowest = GetSquareUnder(0)->GetRestModifier(); + + for(int c = 1; c < GetSquaresUnder(); ++c) + { + int Mod = GetSquareUnder(c)->GetRestModifier(); + + if(Mod < Lowest) + Lowest = Mod; + } + + Bonus = Lowest << 1; + } + } + else if(Action->IsUnconsciousness()) + Bonus = 2; + } + + int Plus1 = 100; + + switch(GetBurdenState()) + { + case OVER_LOADED: + Plus1 = 25; + break; + case STRESSED: + Plus1 = 50; + break; + case BURDENED: + Plus1 = 75; + break; + } + + int Plus2 = 100; + + if(IsPlayer()) + switch(GetHungerState()) + { + case STARVING: + Plus2 = 25; + break; + case VERY_HUNGRY: + Plus2 = 50; + break; + case HUNGRY: + Plus2 = 75; + break; + } + + Stamina += Plus1 * Plus2 * Bonus / 1000; + + if(Stamina > MaxStamina) + Stamina = MaxStamina; + + if(IsPlayer() && StateIsActivated(PANIC) + && GetTirednessState() != FAINTING) + game::SetPlayerIsRunning(true); +} + +void character::BeginPanic() +{ + if(IsPlayer() && GetTirednessState() != FAINTING) + game::SetPlayerIsRunning(true); + + DeActivateVoluntaryAction(); +} + +void character::EndPanic() +{ + if(IsPlayer()) + game::SetPlayerIsRunning(false); +} + +int character::GetTirednessState() const +{ + if(Stamina >= MaxStamina >> 2) + return UNTIRED; + else if(Stamina >= MaxStamina >> 3) + return EXHAUSTED; + else + return FAINTING; +} + +void character::ReceiveBlackUnicorn(long Amount) +{ + if(!(RAND() % 160)) + game::DoEvilDeed(Amount / 50); + + BeginTemporaryState(TELEPORT, Amount / 100); + + for(int c = 0; c < STATES; ++c) + if(StateData[c].Flags & DUR_TEMPORARY) + { + BeginTemporaryState(1 << c, Amount / 100); + + if(!IsEnabled()) + return; + } + else if(StateData[c].Flags & DUR_PERMANENT) + { + GainIntrinsic(1 << c); + + if(!IsEnabled()) + return; + } +} + +void character::ReceiveGrayUnicorn(long Amount) +{ + if(!(RAND() % 80)) + game::DoEvilDeed(Amount / 50); + + BeginTemporaryState(TELEPORT, Amount / 100); + + for(int c = 0; c < STATES; ++c) + if(1 << c != TELEPORT) + { + DecreaseStateCounter(1 << c, -Amount / 100); + + if(!IsEnabled()) + return; + } +} + +void character::ReceiveWhiteUnicorn(long Amount) +{ + if(!(RAND() % 40)) + game::DoEvilDeed(Amount / 50); + + BeginTemporaryState(TELEPORT, Amount / 100); + DecreaseStateCounter(LYCANTHROPY, -Amount / 100); + DecreaseStateCounter(POISONED, -Amount / 100); + DecreaseStateCounter(PARASITIZED, -Amount / 100); + DecreaseStateCounter(LEPROSY, -Amount / 100); + DecreaseStateCounter(VAMPIRISM, -Amount / 100); +} + +/* Counter should be negative. Removes intrinsics. */ + +void character::DecreaseStateCounter(long State, int Counter) +{ + int Index; + + for(Index = 0; Index < STATES; ++Index) + if(1 << Index == State) + break; + + if(Index == STATES) + ABORT("DecreaseTemporaryStateCounter works only when State == 2 ^ n!"); + + if(TemporaryState & State) + { + if(TemporaryStateCounter[Index] == PERMANENT + || (TemporaryStateCounter[Index] += Counter) <= 0) + { + TemporaryState &= ~State; + + if(!(EquipmentState & State)) + { + if(StateData[Index].EndHandler) + { + (this->*StateData[Index].EndHandler)(); + + if(!IsEnabled()) + return; + } + + (this->*StateData[Index].PrintEndMessage)(); + } + } + } +} + +truth character::IsImmuneToLeprosy() const +{ + return DataBase->IsImmuneToLeprosy || UseMaterialAttributes(); +} + +void character::LeprosyHandler() +{ + EditExperience(ARM_STRENGTH, -25, 1 << 1); + EditExperience(LEG_STRENGTH, -25, 1 << 1); + EditExperience(DEXTERITY, -25, 1 << 1); + EditExperience(AGILITY, -25, 1 << 1); + EditExperience(ENDURANCE, -25, 1 << 1); + EditExperience(CHARISMA, -25, 1 << 1); + CheckDeath(CONST_S("killed by leprosy")); +} + +void character::VampirismHandler() +{ + //EditExperience(ARM_STRENGTH, -25, 1 << 1); + //EditExperience(LEG_STRENGTH, -25, 1 << 1); + //EditExperience(DEXTERITY, -25, 1 << 1); + //EditExperience(AGILITY, -25, 1 << 1); + //EditExperience(ENDURANCE, -25, 1 << 1); + EditExperience(CHARISMA, -25, 1 << 1); + EditExperience(WISDOM, -25, 1 << 1); + EditExperience(INTELLIGENCE, -25, 1 << 1); + CheckDeath(CONST_S("killed by vampirism")); +} + +bodypart* character::SearchForOriginalBodyPart(int I) const +{ + for(stackiterator i1 = GetStackUnder()->GetBottom(); i1.HasItem(); ++i1) + { + for(std::list::iterator i2 = OriginalBodyPartID[I].begin(); + i2 != OriginalBodyPartID[I].end(); ++i2) + if(i1->GetID() == *i2) + return static_cast(*i1); + } + + return 0; +} + +void character::SetLifeExpectancy(int Base, int RandPlus) +{ + int c; + + for(c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart) + BodyPart->SetLifeExpectancy(Base, RandPlus); + } + + for(c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment) + Equipment->SetLifeExpectancy(Base, RandPlus); + } +} + +/* Receiver should be a fresh duplicate of this */ + +void character::DuplicateEquipment(character* Receiver, ulong Flags) +{ + for(int c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment) + { + item* Duplicate = Equipment->Duplicate(Flags); + Receiver->SetEquipment(c, Duplicate); + } + } +} + +void character::Disappear(corpse* Corpse, cchar* Verb, truth (item::*ClosePredicate)() const) +{ + truth TorsoDisappeared = false; + truth CanBeSeen = Corpse ? Corpse->CanBeSeenByPlayer() : IsPlayer() || CanBeSeenByPlayer(); + int c; + + if((GetTorso()->*ClosePredicate)()) + { + if(CanBeSeen) + if(Corpse) + ADD_MESSAGE("%s %ss.", Corpse->CHAR_NAME(DEFINITE), Verb); + else if(IsPlayer()) + ADD_MESSAGE("You %s.", Verb); + else + ADD_MESSAGE("%s %ss.", CHAR_NAME(DEFINITE), Verb); + + TorsoDisappeared = true; + + for(c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment && (Equipment->*ClosePredicate)()) + { + Equipment->RemoveFromSlot(); + Equipment->SendToHell(); + } + } + + itemvector ItemVector; + GetStack()->FillItemVector(ItemVector); + + for(uint c = 0; c < ItemVector.size(); ++c) + if(ItemVector[c] && (ItemVector[c]->*ClosePredicate)()) + { + ItemVector[c]->RemoveFromSlot(); + ItemVector[c]->SendToHell(); + } + } + + for(c = 1; c < GetBodyParts(); ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart) + if((BodyPart->*ClosePredicate)()) + { + if(!TorsoDisappeared && CanBeSeen) + if(IsPlayer()) + ADD_MESSAGE("Your %s %ss.", GetBodyPartName(c).CStr(), Verb); + else + ADD_MESSAGE("The %s of %s %ss.", GetBodyPartName(c).CStr(), CHAR_NAME(DEFINITE), Verb); + + BodyPart->DropEquipment(); + item* BodyPart = SevereBodyPart(c); + + if(BodyPart) + BodyPart->SendToHell(); + } + else if(TorsoDisappeared) + { + BodyPart->DropEquipment(); + item* BodyPart = SevereBodyPart(c); + + if(BodyPart) + if(Corpse) + Corpse->GetSlot()->AddFriendItem(BodyPart); + else if(!game::IsInWilderness()) + GetStackUnder()->AddItem(BodyPart); + else + BodyPart->SendToHell(); + } + } + + if(TorsoDisappeared) + { + if(Corpse) + { + Corpse->RemoveFromSlot(); + Corpse->SendToHell(); + } + else + CheckDeath(festring(Verb) + "ed", 0, FORCE_DEATH|DISALLOW_CORPSE|DISALLOW_MSG); + } + else + CheckDeath(festring(Verb) + "ed", 0, DISALLOW_MSG); +} + +void character::SignalDisappearance() +{ + if(GetMotherEntity()) + GetMotherEntity()->SignalDisappearance(); + else + Disappear(0, "disappear", &item::IsVeryCloseToDisappearance); +} + +truth character::HornOfFearWorks() const +{ + return CanHear() && GetPanicLevel() > RAND() % 33; +} + +void character::BeginLeprosy() +{ + doforbodypartswithparam()(this, &bodypart::SetIsInfectedByLeprosy, true); +} + +void character::EndLeprosy() +{ + doforbodypartswithparam()(this, &bodypart::SetIsInfectedByLeprosy, false); +} + +truth character::IsSameAs(ccharacter* What) const +{ + return What->GetType() == GetType() + && What->GetConfig() == GetConfig(); +} + +ulong character::GetCommandFlags() const +{ + return !StateIsActivated(PANIC) + ? CommandFlags + : CommandFlags|FLEE_FROM_ENEMIES; +} + +ulong character::GetConstantCommandFlags() const +{ + return !StateIsActivated(PANIC) + ? DataBase->ConstantCommandFlags + : DataBase->ConstantCommandFlags|FLEE_FROM_ENEMIES; +} + +ulong character::GetPossibleCommandFlags() const +{ + int Int = GetAttribute(INTELLIGENCE); + ulong Flags = ALL_COMMAND_FLAGS; + + if(!CanMove() || Int < 4) + Flags &= ~FOLLOW_LEADER; + + if(!CanMove() || Int < 6) + Flags &= ~FLEE_FROM_ENEMIES; + + if(!CanUseEquipment() || Int < 8) + Flags &= ~DONT_CHANGE_EQUIPMENT; + + if(!UsesNutrition() || Int < 8) + Flags &= ~DONT_CONSUME_ANYTHING_VALUABLE; + + return Flags; +} + +truth character::IsRetreating() const +{ + return StateIsActivated(PANIC) + || (CommandFlags & FLEE_FROM_ENEMIES && IsPet()); +} + +truth character::ChatMenu() +{ + if(GetAction() && !GetAction()->CanBeTalkedTo()) + { + ADD_MESSAGE("%s is silent.", CHAR_DESCRIPTION(DEFINITE)); + PLAYER->EditAP(-200); + return true; + } + + ulong ManagementFlags = GetManagementFlags(); + + if(ManagementFlags == CHAT_IDLY || !IsPet()) + return ChatIdly(); + + static cchar*const ChatMenuEntry[CHAT_MENU_ENTRIES] = + { + "Change equipment", + "Take items", + "Give items", + "Issue commands", + "Chat idly", + }; + + static const petmanagementfunction PMF[CHAT_MENU_ENTRIES] = + { + &character::ChangePetEquipment, + &character::TakePetItems, + &character::GivePetItems, + &character::IssuePetCommands, + &character::ChatIdly + }; + + felist List(CONST_S("Choose action:")); + game::SetStandardListAttributes(List); + List.AddFlags(SELECTABLE); + int c, i; + + for(c = 0; c < CHAT_MENU_ENTRIES; ++c) + if(1 << c & ManagementFlags) + List.AddEntry(ChatMenuEntry[c], LIGHT_GRAY); + + int Chosen = List.Draw(); + + if(Chosen & FELIST_ERROR_BIT) + return false; + + for(c = 0, i = 0; c < CHAT_MENU_ENTRIES; ++c) + if(1 << c & ManagementFlags && i++ == Chosen) + return (this->*PMF[c])(); + + return false; // dummy +} + +truth character::ChangePetEquipment() +{ + if(EquipmentScreen(PLAYER->GetStack(), GetStack())) + { + DexterityAction(3); + return true; + } + + return false; +} + +truth character::TakePetItems() +{ + truth Success = false; + stack::SetSelected(0); + + for(;;) + { + itemvector ToTake; + game::DrawEverythingNoBlit(); + GetStack()->DrawContents(ToTake, + 0, + PLAYER, + CONST_S("What do you want to take from ") + CHAR_DESCRIPTION(DEFINITE) + '?', + CONST_S(""), + CONST_S(""), + GetDescription(DEFINITE) + " is " + GetVerbalBurdenState(), + GetVerbalBurdenStateColor(), + REMEMBER_SELECTED); + + if(ToTake.empty()) + break; + + for(uint c = 0; c < ToTake.size(); ++c) + ToTake[c]->MoveTo(PLAYER->GetStack()); + + ADD_MESSAGE("You take %s.", ToTake[0]->GetName(DEFINITE, ToTake.size()).CStr()); + Success = true; + } + + if(Success) + { + DexterityAction(2); + PLAYER->DexterityAction(2); + } + + return Success; +} + +truth character::GivePetItems() +{ + truth Success = false; + stack::SetSelected(0); + + for(;;) + { + itemvector ToGive; + game::DrawEverythingNoBlit(); + PLAYER->GetStack()->DrawContents(ToGive, + 0, + this, + CONST_S("What do you want to give to ") + CHAR_DESCRIPTION(DEFINITE) + '?', + CONST_S(""), + CONST_S(""), + GetDescription(DEFINITE) + " is " + GetVerbalBurdenState(), + GetVerbalBurdenStateColor(), + REMEMBER_SELECTED); + + if(ToGive.empty()) + break; + + for(uint c = 0; c < ToGive.size(); ++c) + ToGive[c]->MoveTo(GetStack()); + + ADD_MESSAGE("You give %s to %s.", ToGive[0]->GetName(DEFINITE, ToGive.size()).CStr(), CHAR_DESCRIPTION(DEFINITE)); + Success = true; + } + + if(Success) + { + DexterityAction(2); + PLAYER->DexterityAction(2); + } + + return Success; +} + +truth character::IssuePetCommands() +{ + if(!IsConscious()) + { + ADD_MESSAGE("%s is unconscious.", CHAR_DESCRIPTION(DEFINITE)); + return false; + } + + ulong PossibleC = GetPossibleCommandFlags(); + + if(!PossibleC) + { + ADD_MESSAGE("%s cannot be commanded.", CHAR_DESCRIPTION(DEFINITE)); + return false; + } + + ulong OldC = GetCommandFlags(); + ulong NewC = OldC, VaryFlags = 0; + game::CommandScreen(CONST_S("Issue commands to ") + GetDescription(DEFINITE), PossibleC, GetConstantCommandFlags(), VaryFlags, NewC); + + if(NewC == OldC) + return false; + + SetCommandFlags(NewC); + PLAYER->EditAP(-500); + PLAYER->EditExperience(CHARISMA, 25, 1 << 7); + return true; +} + +truth character::ChatIdly() +{ + if(!TryToTalkAboutScience()) + { + BeTalkedTo(); + PLAYER->EditExperience(CHARISMA, 75, 1 << 7); + } + + PLAYER->EditAP(-1000); + return true; +} + +truth character::EquipmentScreen(stack* MainStack, stack* SecStack) +{ + if(!CanUseEquipment()) + { + ADD_MESSAGE("%s cannot use equipment.", CHAR_DESCRIPTION(DEFINITE)); + return false; + } + + int Chosen = 0; + truth EquipmentChanged = false; + felist List(CONST_S("Equipment menu [ESC exits]")); + festring Entry; + + for(;;) + { + List.Empty(); + List.EmptyDescription(); + + if(!IsPlayer()) + { + List.AddDescription(CONST_S("")); + List.AddDescription(festring(GetDescription(DEFINITE) + " is " + GetVerbalBurdenState()).CapitalizeCopy(), GetVerbalBurdenStateColor()); + } + + for(int c = 0; c < GetEquipments(); ++c) + { + Entry = GetEquipmentName(c); + Entry << ':'; + Entry.Resize(20); + item* Equipment = GetEquipment(c); + + if(Equipment) + { + Equipment->AddInventoryEntry(this, Entry, 1, true); + AddSpecialEquipmentInfo(Entry, c); + int ImageKey = game::AddToItemDrawVector(itemvector(1, Equipment)); + List.AddEntry(Entry, LIGHT_GRAY, 20, ImageKey, true); + } + else + { + Entry << (GetBodyPartOfEquipment(c) ? "-" : "can't use"); + List.AddEntry(Entry, LIGHT_GRAY, 20, game::AddToItemDrawVector(itemvector())); + } + } + + game::DrawEverythingNoBlit(); + game::SetStandardListAttributes(List); + List.SetFlags(SELECTABLE|DRAW_BACKGROUND_AFTERWARDS); + List.SetEntryDrawer(game::ItemEntryDrawer); + Chosen = List.Draw(); + game::ClearItemDrawVector(); + + if(Chosen >= GetEquipments()) + break; + + EquipmentChanged = TryToChangeEquipment(MainStack, SecStack, Chosen); + } + + if(EquipmentChanged) + DexterityAction(5); + + return EquipmentChanged; +} + +ulong character::GetManagementFlags() const +{ + ulong Flags = ALL_MANAGEMENT_FLAGS; + + if(!CanUseEquipment() || !AllowPlayerToChangeEquipment()) + Flags &= ~CHANGE_EQUIPMENT; + + if(!GetStack()->GetItems()) + Flags &= ~TAKE_ITEMS; + + if(!WillCarryItems()) + Flags &= ~GIVE_ITEMS; + + if(!GetPossibleCommandFlags()) + Flags &= ~ISSUE_COMMANDS; + + return Flags; +} + +cchar* VerbalBurdenState[] = { "overloaded", "stressed", "burdened", "unburdened" }; +col16 VerbalBurdenStateColor[] = { RED, BLUE, BLUE, WHITE }; + +cchar* character::GetVerbalBurdenState() const +{ + return VerbalBurdenState[BurdenState]; +} + +col16 character::GetVerbalBurdenStateColor() const +{ + return VerbalBurdenStateColor[BurdenState]; +} + +int character::GetAttributeAverage() const +{ + return GetSumOfAttributes() / 7; +} + +cfestring& character::GetStandVerb() const +{ + if(ForceCustomStandVerb()) + return DataBase->StandVerb; + + static festring Hovering = "hovering"; + static festring Swimming = "swimming"; + + if(StateIsActivated(LEVITATION)) + return Hovering; + + if(IsSwimming()) + return Swimming; + + return DataBase->StandVerb; +} + +truth character::CheckApply() const +{ + if(!CanApply()) + { + ADD_MESSAGE("This monster type cannot apply."); + return false; + } + + return true; +} + +void character::EndLevitation() +{ + if(!IsFlying() && GetSquareUnder()) + { + if(!game::IsInWilderness()) + SignalStepFrom(0); + + if(game::IsInWilderness() || !GetLSquareUnder()->IsFreezed()) + TestWalkability(); + } +} + +truth character::CanMove() const +{ + return !IsRooted() || StateIsActivated(LEVITATION); +} + +void character::CalculateEnchantments() +{ + doforequipments()(this, &item::CalculateEnchantment); + GetStack()->CalculateEnchantments(); +} + +truth character::GetNewFormForPolymorphWithControl(character*& NewForm) +{ + festring Topic, Temp; + NewForm = 0; + + while(!NewForm) + { + festring Temp = game::DefaultQuestion(CONST_S("What do you want to become? [press '?' for a list]"), + game::GetDefaultPolymorphTo(), + &game::PolymorphControlKeyHandler); + NewForm = protosystem::CreateMonster(Temp); + + if(NewForm) + { + if(NewForm->IsSameAs(this)) + { + delete NewForm; + ADD_MESSAGE("You choose not to polymorph."); + NewForm = this; + return false; + } + + if(PolymorphBackup && NewForm->IsSameAs(PolymorphBackup)) + { + delete NewForm; + NewForm = ForceEndPolymorph(); + return false; + } + + if(NewForm->GetPolymorphIntelligenceRequirement() + > GetAttribute(INTELLIGENCE) + && !game::WizardModeIsActive()) + { + ADD_MESSAGE("You feel your mind isn't yet powerful enough to call forth the form of %s.", NewForm->CHAR_NAME(INDEFINITE)); + delete NewForm; + NewForm = 0; + } + else + NewForm->RemoveAllItems(); + } + } + + return true; +} + +liquid* character::CreateSweat(long Volume) const +{ + return liquid::Spawn(GetCurrentSweatMaterial(), Volume); +} + +truth character::TeleportRandomItem(truth TryToHinderVisibility) +{ + if(IsImmuneToItemTeleport()) + return false; + + itemvector ItemVector; + std::vector PossibilityVector; + int TotalPossibility = 0; + + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + { + ItemVector.push_back(*i); + int Possibility = i->GetTeleportPriority(); + + if(TryToHinderVisibility) + Possibility += i->GetHinderVisibilityBonus(this); + + PossibilityVector.push_back(Possibility); + TotalPossibility += Possibility; + } + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment) + { + ItemVector.push_back(Equipment); + int Possibility = Equipment->GetTeleportPriority(); + + if(TryToHinderVisibility) + Possibility += Equipment->GetHinderVisibilityBonus(this); + + PossibilityVector.push_back(Possibility <<= 1); + TotalPossibility += Possibility; + } + } + + if(!TotalPossibility) + return false; + + int Chosen = femath::WeightedRand(PossibilityVector, TotalPossibility); + item* Item = ItemVector[Chosen]; + truth Equipped = PLAYER->Equips(Item); + truth Seen = Item->CanBeSeenByPlayer(); + Item->RemoveFromSlot(); + + if(Seen) + ADD_MESSAGE("%s disappears.", Item->CHAR_NAME(DEFINITE)); + + if(Equipped) + game::AskForKeyPress(CONST_S("Equipment lost! [press any key to continue]")); + + v2 Pos = GetPos(); + int Range = Item->GetEmitation() && TryToHinderVisibility ? 25 : 5; + rect Border(Pos + v2(-Range, -Range), Pos + v2(Range, Range)); + Pos = GetLevel()->GetRandomSquare(this, 0, &Border); + + if(Pos == ERROR_V2) + Pos = GetLevel()->GetRandomSquare(); + + GetNearLSquare(Pos)->GetStack()->AddItem(Item); + + if(Item->CanBeSeenByPlayer()) + ADD_MESSAGE("%s appears.", Item->CHAR_NAME(INDEFINITE)); + + return true; +} + +truth character::HasClearRouteTo(v2 Pos) const +{ + pathcontroller::Map = GetLevel()->GetMap(); + pathcontroller::Character = this; + v2 ThisPos = GetPos(); + return mapmath::DoLine(ThisPos.X, ThisPos.Y, Pos.X, Pos.Y, SKIP_FIRST); +} + +truth character::IsTransparent() const +{ + return !IsEnormous() || GetTorso()->GetMainMaterial()->IsTransparent() || StateIsActivated(INVISIBLE); +} + +void character::SignalPossibleTransparencyChange() +{ + if(!game::IsInWilderness()) + for(int c = 0; c < SquaresUnder; ++c) + { + lsquare* Square = GetLSquareUnder(c); + + if(Square) + Square->SignalPossibleTransparencyChange(); + } +} + +int character::GetCursorData() const +{ + int Bad = 0; + int Color = game::PlayerIsRunning() ? BLUE_CURSOR : DARK_CURSOR; + + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart && BodyPart->IsUsable()) + { + int ConditionColorIndex = BodyPart->GetConditionColorIndex(); + + if((BodyPartIsVital(c) && !ConditionColorIndex) + || (ConditionColorIndex <= 1 && ++Bad == 2)) + return Color|CURSOR_FLASH; + } + else if(++Bad == 2) + return Color|CURSOR_FLASH; + } + + Color = game::PlayerIsRunning() ? YELLOW_CURSOR : RED_CURSOR; + return Bad ? Color|CURSOR_FLASH : Color; +} + +void character::TryToName() +{ + if(!IsPet()) + ADD_MESSAGE("%s refuses to let YOU decide what %s's called.", CHAR_NAME(DEFINITE), CHAR_PERSONAL_PRONOUN); + else if(IsPlayer()) + ADD_MESSAGE("You can't rename yourself."); + else if(!IsNameable()) + ADD_MESSAGE("%s refuses to be called anything else but %s.", CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + else + { + festring Topic = CONST_S("What name will you give to ") + GetName(DEFINITE) + '?'; + festring Name = game::StringQuestion(Topic, WHITE, 0, 80, true); + + if(Name.GetSize()) + SetAssignedName(Name); + } +} + +double character::GetSituationDanger(ccharacter* Enemy, v2 ThisPos, v2 EnemyPos, truth SeesEnemy) const +{ + double Danger; + + if(IgnoreDanger() && !IsPlayer()) + { + if(Enemy->IgnoreDanger() && !Enemy->IsPlayer()) + { + Danger = double(GetHP()) * GetHPRequirementForGeneration() / (Enemy->GetHP() * Enemy->GetHPRequirementForGeneration()); + } + else + Danger = .25 * GetHPRequirementForGeneration() / Enemy->GetHP(); + } + else if(Enemy->IgnoreDanger() && !Enemy->IsPlayer()) + Danger = 4. * GetHP() / Enemy->GetHPRequirementForGeneration(); + else + Danger = GetRelativeDanger(Enemy); + + Danger *= 3. / ((EnemyPos - ThisPos).GetManhattanLength() + 2); + + if(!SeesEnemy) + Danger *= .2; + + if(StateIsActivated(PANIC)) + Danger *= .2; + + Danger *= double(GetHP()) * Enemy->GetMaxHP() / (Enemy->GetHP() * GetMaxHP()); + return Danger; +} + +void character::ModifySituationDanger(double& Danger) const +{ + switch(GetTirednessState()) + { + case FAINTING: + Danger *= 1.5; + case EXHAUSTED: + Danger *= 1.25; + } + + for(int c = 0; c < STATES; ++c) + if(StateIsActivated(1 << c) && StateData[c].SituationDangerModifier != 0) + (this->*StateData[c].SituationDangerModifier)(Danger); +} + +void character::LycanthropySituationDangerModifier(double& Danger) const +{ + character* Wolf = werewolfwolf::Spawn(); + double DangerToWolf = GetRelativeDanger(Wolf); + Danger *= pow(DangerToWolf, 0.1); + delete Wolf; +} + +void character::VampirismSituationDangerModifier(double& Danger) const +{ + character* Vampire = vampire::Spawn(); + double DangerToVampire = GetRelativeDanger(Vampire); + Danger *= pow(DangerToVampire, 0.1); + delete Vampire; +} + +void character::PoisonedSituationDangerModifier(double& Danger) const +{ + int C = GetTemporaryStateCounter(POISONED); + Danger *= (1 + (C * C) / (GetHP() * 10000. * (GetGlobalResistance(POISON) + 1))); +} + +void character::PolymorphingSituationDangerModifier(double& Danger) const +{ + if(!StateIsActivated(POLYMORPH_CONTROL)) + Danger *= 1.5; +} + +void character::PanicSituationDangerModifier(double& Danger) const +{ + Danger *= 1.5; +} + +void character::ConfusedSituationDangerModifier(double& Danger) const +{ + Danger *= 1.5; +} + +void character::ParasitizedSituationDangerModifier(double& Danger) const +{ + Danger *= 1.25; +} + +void character::LeprosySituationDangerModifier(double& Danger) const +{ + Danger *= 1.5; +} + +void character::AddRandomScienceName(festring& String) const +{ + festring Science = GetScienceTalkName().GetRandomElement().CStr(); + + if(Science[0] == '!') + { + String << Science.CStr() + 1; + return; + } + + festring Attribute = GetScienceTalkAdjectiveAttribute().GetRandomElement(); + festring Prefix; + truth NoAttrib = Attribute.IsEmpty(), NoSecondAdjective = false; + + if(!Attribute.IsEmpty() && Attribute[0] == '!') + { + NoSecondAdjective = true; + Attribute.Erase(0, 1); + } + + if(!Science.Find("the ")) + { + Science.Erase(0, 4); + + if(!Attribute.Find("the ", 0, 4)) + Attribute << " the"; + else + Attribute.Insert(0, "the ", 4); + } + + if(islower(Science[0]) + && Science.Find(' ') == festring::NPos + && Science.Find('-') == festring::NPos + && Science.Find("phobia") == festring::NPos) + { + Prefix = GetScienceTalkPrefix().GetRandomElement(); + + if(!Prefix.IsEmpty() && Science.Find(Prefix) != festring::NPos) + Prefix.Empty(); + } + + int L = Prefix.GetSize(); + + if(L && Prefix[L - 1] == Science[0]) + Science.Erase(0, 1); + + if(!NoAttrib && !NoSecondAdjective == !RAND_GOOD(3)) + { + int S1 = NoSecondAdjective ? 0 : GetScienceTalkAdjectiveAttribute().Size; + int S2 = GetScienceTalkSubstantiveAttribute().Size; + festring OtherAttribute; + int Chosen = RAND_GOOD(S1 + S2); + + if(Chosen < S1) + OtherAttribute = GetScienceTalkAdjectiveAttribute()[Chosen]; + else + OtherAttribute = GetScienceTalkSubstantiveAttribute()[Chosen - S1]; + + if(!OtherAttribute.IsEmpty() && OtherAttribute.Find("the ", 0, 4) + && Attribute.Find(OtherAttribute) == festring::NPos) + { + String << Attribute << ' ' << OtherAttribute << ' ' << Prefix << Science; + return; + } + } + + String << Attribute; + + if(!NoAttrib) + String << ' '; + + String << Prefix << Science; +} + +truth character::TryToTalkAboutScience() +{ + if(GetRelation(PLAYER) == HOSTILE + || GetScienceTalkPossibility() <= RAND_GOOD(100) + || PLAYER->GetAttribute(INTELLIGENCE) < GetScienceTalkIntelligenceRequirement() + || PLAYER->GetAttribute(WISDOM) < GetScienceTalkWisdomRequirement() + || PLAYER->GetAttribute(CHARISMA) < GetScienceTalkCharismaRequirement()) + return false; + + festring Science; + + if(RAND_GOOD(3)) + AddRandomScienceName(Science); + else + { + festring S1, S2; + AddRandomScienceName(S1); + AddRandomScienceName(S2); + + if(S1.Find(S2) == festring::NPos && S2.Find(S1) == festring::NPos) + { + switch(RAND_GOOD(3)) + { + case 0: Science = "the relation of "; break; + case 1: Science = "the differences of "; break; + case 2: Science = "the similarities of "; break; + } + + Science << S1 << " and " << S2; + } + else + AddRandomScienceName(Science); + } + + switch((RAND() + GET_TICK()) % 10) + { + case 0: + ADD_MESSAGE("You have a rather pleasant chat about %s with %s.", Science.CStr(), CHAR_DESCRIPTION(DEFINITE)); + break; + case 1: + ADD_MESSAGE("%s explains a few of %s opinions regarding %s to you.", CHAR_DESCRIPTION(DEFINITE), CHAR_POSSESSIVE_PRONOUN, Science.CStr()); + break; + case 2: + ADD_MESSAGE("%s reveals a number of %s insightful views of %s to you.", CHAR_DESCRIPTION(DEFINITE), CHAR_POSSESSIVE_PRONOUN, Science.CStr()); + break; + case 3: + ADD_MESSAGE("You exhange some information pertaining to %s with %s.", Science.CStr(), CHAR_DESCRIPTION(DEFINITE)); + break; + case 4: + ADD_MESSAGE("You engage in a pretty intriguing conversation about %s with %s.", Science.CStr(), CHAR_DESCRIPTION(DEFINITE)); + break; + case 5: + ADD_MESSAGE("You discuss at length about %s with %s.", Science.CStr(), CHAR_DESCRIPTION(DEFINITE)); + break; + case 6: + ADD_MESSAGE("You have a somewhat boring talk concerning %s with %s.", Science.CStr(), CHAR_DESCRIPTION(DEFINITE)); + break; + case 7: + ADD_MESSAGE("You are drawn into a heated argument regarding %s with %s.", Science.CStr(), CHAR_DESCRIPTION(DEFINITE)); + break; + case 8: + ADD_MESSAGE("%s delivers a long monologue concerning eg. %s.", CHAR_DESCRIPTION(DEFINITE), Science.CStr()); + break; + case 9: + ADD_MESSAGE("You dive into a brief but thought-provoking debate over %s with %s", Science.CStr(), CHAR_DESCRIPTION(DEFINITE)); + break; + } + + PLAYER->EditExperience(INTELLIGENCE, 1000, 50. * GetScienceTalkIntelligenceModifier() / ++ScienceTalks); + PLAYER->EditExperience(WISDOM, 1000, 50. * GetScienceTalkWisdomModifier() / ++ScienceTalks); + PLAYER->EditExperience(CHARISMA, 1000, 50. * GetScienceTalkCharismaModifier() / ++ScienceTalks); + return true; +} + +truth character::IsUsingWeaponOfCategory(int Category) const +{ + return ((GetMainWielded() && GetMainWielded()->GetWeaponCategory() == Category) + || (GetSecondaryWielded() && GetSecondaryWielded()->GetWeaponCategory() == Category)); +} + +truth character::TryToUnStickTraps(v2 Dir) +{ + if(!TrapData) + return true; + + std::vector TrapVector; + + for(const trapdata* T = TrapData; T; T = T->Next) + TrapVector.push_back(*TrapData); + + for(uint c = 0; c < TrapVector.size(); ++c) + if(IsEnabled()) + { + entity* Trap = game::SearchTrap(TrapVector[c].TrapID); + + if(!Trap->Exists()) + int esko = esko = 2; + + if(Trap->GetVictimID() == GetID() && Trap->TryToUnStick(this, Dir)) + break; + } + + return !TrapData && IsEnabled(); +} + +struct trapidcomparer +{ + trapidcomparer(ulong ID) : ID(ID) { } + truth operator()(const trapdata* T) const { return T->TrapID == ID; } + ulong ID; +}; + +void character::RemoveTrap(ulong ID) +{ + trapdata*& T = ListFind(TrapData, trapidcomparer(ID)); + T = T->Next; + doforbodyparts()(this, &bodypart::SignalPossibleUsabilityChange); +} + +void character::AddTrap(ulong ID, ulong BodyParts) +{ + trapdata*& T = ListFind(TrapData, trapidcomparer(ID)); + + if(T) + T->BodyParts |= BodyParts; + else + T = new trapdata(ID, GetID(), BodyParts); + + doforbodyparts()(this, &bodypart::SignalPossibleUsabilityChange); +} + +truth character::IsStuckToTrap(ulong ID) const +{ + for(const trapdata* T = TrapData; T; T = T->Next) + if(T->TrapID == ID) + return true; + + return false; +} + +void character::RemoveTraps() +{ + for(trapdata* T = TrapData; T;) + { + entity* Trap = game::SearchTrap(T->TrapID); + + if(Trap) + Trap->UnStick(); + + trapdata* ToDel = T; + T = T->Next; + delete ToDel; + } + + TrapData = 0; + doforbodyparts()(this, &bodypart::SignalPossibleUsabilityChange); +} + +void character::RemoveTraps(int BodyPartIndex) +{ + ulong Flag = 1 << BodyPartIndex; + + for(trapdata** T = &TrapData; *T;) + if((*T)->BodyParts & Flag) + { + entity* Trap = game::SearchTrap((*T)->TrapID); + + if(!((*T)->BodyParts &= ~Flag)) + { + if(Trap) + Trap->UnStick(); + + trapdata* ToDel = *T; + *T = (*T)->Next; + delete ToDel; + } + else + { + if(Trap) + Trap->UnStick(BodyPartIndex); + + T = &(*T)->Next; + } + } + else + T = &(*T)->Next; + + if(GetBodyPart(BodyPartIndex)) + GetBodyPart(BodyPartIndex)->SignalPossibleUsabilityChange(); +} + +festring character::GetTrapDescription() const +{ + festring Desc; + std::pair TrapStack[3]; + int Index = 0; + + for(const trapdata* T = TrapData; T; T = T->Next) + { + if(Index < 3) + { + entity* Trap = game::SearchTrap(T->TrapID); + + if(Trap) + { + int c; + + for(c = 0; c < Index; ++c) + if(TrapStack[c].first->GetTrapType() == Trap->GetTrapType()) + ++TrapStack[c].second; + + if(c == Index) + TrapStack[Index++] = std::make_pair(Trap, 1); + } + } + else + { + ++Index; + break; + } + } + + if(Index <= 3) + { + TrapStack[0].first->AddTrapName(Desc, TrapStack[0].second); + + if(Index == 2) + { + Desc << " and "; + TrapStack[1].first->AddTrapName(Desc, TrapStack[1].second); + } + else if(Index == 3) + { + Desc << ", "; + TrapStack[1].first->AddTrapName(Desc, TrapStack[1].second); + Desc << " and "; + TrapStack[2].first->AddTrapName(Desc, TrapStack[2].second); + } + } + else + Desc << "lots of traps"; + + return Desc; +} + +int character::RandomizeHurtBodyPart(ulong BodyParts) const +{ + int BodyPartIndex[MAX_BODYPARTS]; + int Index = 0; + + for(int c = 0; c < GetBodyParts(); ++c) + if(1 << c & BodyParts) + { + if(!GetBodyPart(c)) + int esko = esko = 2; + + BodyPartIndex[Index++] = c; + } + + if(!Index) + int esko = esko = 2; + + return BodyPartIndex[RAND_N(Index)]; +} + +truth character::BodyPartIsStuck(int I) const +{ + for(const trapdata* T = TrapData; T; T = T->Next) + if(1 << I & T->BodyParts) + return true; + + return false; +} + +void character::PrintAttribute(cchar* Desc, int I, int PanelPosX, int PanelPosY) const +{ + int Attribute = GetAttribute(I); + int NoBonusAttribute = GetAttribute(I, false); + col16 C = game::GetAttributeColor(I); + festring String = Desc; + String.Resize(5); + String << Attribute; + String.Resize(8); + FONT->Printf(DOUBLE_BUFFER, v2(PanelPosX, PanelPosY * 10), C, String.CStr()); + + if(Attribute != NoBonusAttribute) + { + int Where = PanelPosX + (String.GetSize() + 1 << 3); + FONT->Printf(DOUBLE_BUFFER, v2(Where, PanelPosY * 10), LIGHT_GRAY, + "%d", NoBonusAttribute); + } +} + +truth character::AllowUnconsciousness() const +{ + return DataBase->AllowUnconsciousness && TorsoIsAlive(); +} + +truth character::CanPanic() const +{ + return !Action || !Action->IsUnconsciousness(); +} + +int character::GetRandomBodyPart(ulong Possible) const +{ + int OKBodyPart[MAX_BODYPARTS]; + int OKBodyParts = 0; + + for(int c = 0; c < BodyParts; ++c) + if(1 << c & Possible && GetBodyPart(c)) + OKBodyPart[OKBodyParts++] = c; + + return OKBodyParts ? OKBodyPart[RAND_N(OKBodyParts)] : NONE_INDEX; +} + +void character::EditNP(long What) +{ + int OldState = GetHungerState(); + NP += What; + int NewState = GetHungerState(); + + if(OldState > VERY_HUNGRY && NewState == VERY_HUNGRY) + DeActivateVoluntaryAction(CONST_S("You are getting really hungry.")); + + if(OldState > STARVING && NewState == STARVING) + DeActivateVoluntaryAction(CONST_S("You are getting extremely hungry.")); +} + +truth character::IsSwimming() const +{ + return !IsFlying() && GetSquareUnder() + && GetSquareUnder()->GetSquareWalkability() & SWIM; +} + +void character::AddBlackUnicornConsumeEndMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel dirty and loathsome."); +} + +void character::AddGrayUnicornConsumeEndMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel neutralized."); +} + +void character::AddWhiteUnicornConsumeEndMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel purified."); +} + +void character::AddOmmelBoneConsumeEndMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel the power of all your canine ancestors combining in your body."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("For a moment %s looks extremely ferocious. You shudder.", CHAR_NAME(DEFINITE)); +} + +void character::AddLiquidHorrorConsumeEndMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("Untold horrors flash before your eyes. The melancholy of the world is on your shoulders!"); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s looks as if the melancholy of the world is on %s shoulders!.", CHAR_NAME(DEFINITE), GetPossessivePronoun().CStr()); +} + +int character::GetBodyPartSparkleFlags(int) const +{ + return ((GetNaturalSparkleFlags() & SKIN_COLOR ? SPARKLING_A : 0) + | (GetNaturalSparkleFlags() & TORSO_MAIN_COLOR ? SPARKLING_B : 0) + | (GetNaturalSparkleFlags() & TORSO_SPECIAL_COLOR ? SPARKLING_D : 0)); +} + +truth character::IsAnimated() const +{ + return combinebodypartpredicates()(this, &bodypart::IsAnimated, 1); +} + +double character::GetNaturalExperience(int Identifier) const +{ + return DataBase->NaturalExperience[Identifier]; +} + +truth character::HasBodyPart(sorter Sorter) const +{ + if(Sorter == 0) + return true; + + return combinebodypartpredicateswithparam()(this, Sorter, this, 1); +} + +truth character::PossessesItem(sorter Sorter) const +{ + if(Sorter == 0) + return true; + + return (GetStack()->SortedItems(this, Sorter) + || combinebodypartpredicateswithparam()(this, Sorter, this, 1) + || combineequipmentpredicateswithparam()(this, Sorter, this, 1)); +} + +/* 0 <= I <= 1 */ + +cchar* character::GetRunDescriptionLine(int I) const +{ + if(!GetRunDescriptionLineOne().IsEmpty()) + return !I ? GetRunDescriptionLineOne().CStr() : GetRunDescriptionLineTwo().CStr(); + + if(IsFlying()) + return !I ? "Flying" : "very fast"; + + if(IsSwimming()) + return !I ? "Swimming" : "very fast"; + + return !I ? "Running" : ""; +} + +void character::VomitAtRandomDirection(int Amount) +{ + if(game::IsInWilderness()) + return; + + /* Lacks support of multitile monsters */ + + v2 Possible[9]; + int Index = 0; + + for(int d = 0; d < 9; ++d) + { + lsquare* Square = GetLSquareUnder()->GetNeighbourLSquare(d); + + if(Square && !Square->VomitingIsDangerous(this)) + Possible[Index++] = Square->GetPos(); + } + + if(Index) + Vomit(Possible[RAND_N(Index)], Amount); + else + Vomit(GetPos(), Amount); +} + +void character::RemoveLifeSavers() +{ + for(int c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment && Equipment->IsInCorrectSlot(c) && Equipment->GetGearStates() & LIFE_SAVED) + { + Equipment->SendToHell(); + Equipment->RemoveFromSlot(); + } + } +} + +ccharacter* character::FindCarrier() const +{ + return this; //check +} + +void character::PrintBeginHiccupsMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("Your diaphragm is spasming vehemently."); +} + +void character::PrintEndHiccupsMessage() const +{ + if(IsPlayer()) + ADD_MESSAGE("You feel your annoying hiccoughs have finally subsided."); +} + +void character::HiccupsHandler() +{ + /*if(!(RAND() % 2000)) + { + if(IsPlayer()) + ADD_MESSAGE(""); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE(""); + else if((PLAYER->GetPos() - GetPos()).GetLengthSquare() <= 400) + ADD_MESSAGE(""); + + game::CallForAttention(GetPos(), 400); + }*/ +} + +void character::HiccupsSituationDangerModifier(double& Danger) const +{ + Danger *= 1.25; +} + +bool character::IsConscious() const +{ + return !Action || !Action->IsUnconsciousness(); +} + +wsquare* character::GetNearWSquare(v2 Pos) const +{ + return static_cast(GetSquareUnder()->GetArea()->GetSquare(Pos)); +} + +wsquare* character::GetNearWSquare(int x, int y) const +{ + return static_cast(GetSquareUnder()->GetArea()->GetSquare(x, y)); +} + +void character::ForcePutNear(v2 Pos) +{ + /* GUM SOLUTION!!! */ + + v2 NewPos = game::GetCurrentLevel()->GetNearestFreeSquare(PLAYER, Pos, false); + + if(NewPos == ERROR_V2) + do + { + NewPos = game::GetCurrentLevel()->GetRandomSquare(this); + } + while(NewPos == Pos); + + PutTo(NewPos); +} + +void character::ReceiveMustardGas(int BodyPart, long Volume) +{ + if(Volume) + GetBodyPart(BodyPart)->AddFluid(liquid::Spawn(MUSTARD_GAS_LIQUID, Volume), CONST_S("skin"), 0, true); +} + +void character::ReceiveMustardGasLiquid(int BodyPartIndex, long Modifier) +{ + bodypart* BodyPart = GetBodyPart(BodyPartIndex); + + if(BodyPart->GetMainMaterial()->GetInteractionFlags() & IS_AFFECTED_BY_MUSTARD_GAS) + { + long Tries = Modifier; + Modifier -= Tries; //opt%? + int Damage = 0; + + for(long c = 0; c < Tries; ++c) + if(!(RAND() % 100)) + ++Damage; + + if(Modifier && !(RAND() % 1000 / Modifier)) + ++Damage; + + if(Damage) + { + ulong Minute = game::GetTotalMinutes(); + + if(GetLastAcidMsgMin() != Minute && (CanBeSeenByPlayer() || IsPlayer())) + { + SetLastAcidMsgMin(Minute); + + if(IsPlayer()) + ADD_MESSAGE("Mustard gas dissolves the skin of your %s.", + BodyPart->GetBodyPartName().CStr()); + else + ADD_MESSAGE("Mustard gas dissolves %s.", CHAR_NAME(DEFINITE)); + } + + ReceiveBodyPartDamage(0, Damage, MUSTARD_GAS_DAMAGE, + BodyPartIndex, YOURSELF, false, false, false); + CheckDeath(CONST_S("killed by a fatal exposure to mustard gas")); + } + } +} + +truth character::IsBadPath(v2 Pos) const +{ + if(!IsGoingSomeWhere()) + return false; + + v2 TPos = !Route.empty() ? Route.back() : GoingTo; + + return ((TPos - Pos).GetManhattanLength() + > (TPos - GetPos()).GetManhattanLength()); +} + +double& character::GetExpModifierRef(expid E) +{ + return ExpModifierMap.insert(std::make_pair(E, 1.)).first->second; +} + +/* Should probably do more. Now only makes Player forget gods */ +truth character::ForgetRandomThing() +{ + if(IsPlayer()) + { + /* hopefully this code isn't some where else */ + std::vector Known; + + for(int c = 1; c <= GODS; ++c) + if(game::GetGod(c)->IsKnown()) + Known.push_back(game::GetGod(c)); + + if(Known.empty()) + return false; + + int RandomGod = RAND_N(Known.size()); + Known.at(RAND_N(Known.size()))->SetIsKnown(false); + ADD_MESSAGE("You forget how to pray to %s.", + Known.at(RandomGod)->GetName()); + return true; + } + return false; +} + +int character::CheckForBlock(character* Enemy, item* Weapon, + double ToHitValue, int Damage, + int Success, int Type) +{ + return Damage; +} + +void character::ApplyAllGodsKnownBonus() +{ + stack* AddPlace = GetStackUnder(); + + if(game::IsInWilderness()) + AddPlace = GetStack(); + else + AddPlace = GetStackUnder(); + + pantheonbook* NewBook = pantheonbook::Spawn(); + AddPlace->AddItem(NewBook); + + ADD_MESSAGE("\"MORTAL! BEHOLD THE HOLY SAGA\""); + ADD_MESSAGE("%s materializes near your feet.", + NewBook->CHAR_NAME(INDEFINITE)); +} + +void character::ReceiveSirenSong(character* Siren) +{ + if(Siren->GetTeam() == GetTeam()) + return; + + if(!RAND_N(4)) + { + if(IsPlayer()) + ADD_MESSAGE("The beautiful melody of %s makes you feel sleepy.", + Siren->CHAR_NAME(DEFINITE)); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("The beautiful melody of %s makes %s look sleepy."); + + Stamina -= (1 + RAND_N(4)) * 10000; + return; + } + + if(!IsPlayer() && IsCharmable() && !RAND_N(5)) + { + ChangeTeam(Siren->GetTeam()); + ADD_MESSAGE("%s seems to be totally brainwashed by %s melodies.", CHAR_NAME(DEFINITE), + Siren->CHAR_NAME(DEFINITE)); + return; + } + + if(!RAND_N(4)) + { + item* What = GiveMostExpensiveItem(Siren); + + if(What) + { + if(IsPlayer()) + { + ADD_MESSAGE("%s music persuades you to give %s to %s as a present.", + Siren->CHAR_NAME(DEFINITE), What->CHAR_NAME(DEFINITE), + Siren->CHAR_OBJECT_PRONOUN); + } + else + { + ADD_MESSAGE("%s is persuaded to give %s to %s because of %s beautiful singing.", + CHAR_NAME(DEFINITE), + What->CHAR_NAME(INDEFINITE), + Siren->CHAR_NAME(DEFINITE), + Siren->CHAR_OBJECT_PRONOUN); + + } + } + else + { + if(IsPlayer()) + ADD_MESSAGE("You would like to give something to %s.", Siren->CHAR_NAME(DEFINITE)); + } + + return; + } +} + +// return 0, if no item found + +item* character::FindMostExpensiveItem() const +{ + int MaxPrice = -1; + item* MostExpensive = 0; + + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if((*i)->GetPrice() > MaxPrice) + { + MaxPrice = (*i)->GetPrice(); + MostExpensive = (*i); + } + + for(int c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment && Equipment->GetPrice() > MaxPrice) + { + MaxPrice = Equipment->GetPrice(); + MostExpensive = Equipment; + } + } + + return MostExpensive; +} + +// returns 0 if no items available + +item* character::GiveMostExpensiveItem(character* ToWhom) +{ + item* ToGive = FindMostExpensiveItem(); + + if(!ToGive) + return 0; + + truth Equipped = PLAYER->Equips(ToGive); + ToGive->RemoveFromSlot(); + + if(Equipped) + game::AskForKeyPress(CONST_S("Equipment lost! [press any key to continue]")); + + ToWhom->ReceiveItemAsPresent(ToGive); + EditAP(-1000); + return ToGive; +} + +void character::ReceiveItemAsPresent(item* Present) +{ + if(TestForPickup(Present)) + GetStack()->AddItem(Present); + else + GetStackUnder()->AddItem(Present); +} + +/* returns 0 if no enemies in sight */ + +character* character::GetNearestEnemy() const +{ + character* NearestEnemy = 0; + long NearestEnemyDistance = 0x7FFFFFFF; + v2 Pos = GetPos(); + + for(int c = 0; c < game::GetTeams(); ++c) + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + { + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled()) + { + long ThisDistance = Max(abs((*i)->GetPos().X - Pos.X), + abs((*i)->GetPos().Y - Pos.Y)); + + if((ThisDistance < NearestEnemyDistance + || (ThisDistance == NearestEnemyDistance && !(RAND() % 3))) + && (*i)->CanBeSeenBy(this)) + { + NearestEnemy = *i; + NearestEnemyDistance = ThisDistance; + } + } + } + + return NearestEnemy; +} + +truth character::MindWormCanPenetrateSkull(mindworm*) const +{ + return false; +} + +truth character::CanTameWithDulcis(const character* Tamer) const +{ + int TamingDifficulty = GetTamingDifficulty(); + + if(TamingDifficulty == NO_TAMING) + return false; + + if(GetAttachedGod() == DULCIS) + return true; + + int Modifier = Tamer->GetAttribute(WISDOM) + Tamer->GetAttribute(CHARISMA); + + if(Tamer->IsPlayer()) + Modifier += game::GetGod(DULCIS)->GetRelation() / 20; + else if(Tamer->GetAttachedGod() == DULCIS) + Modifier += 50; + + if(TamingDifficulty == 0) + if(!IgnoreDanger()) + TamingDifficulty = int(10 * GetRelativeDanger(Tamer)); + else + TamingDifficulty = 10 * GetHPRequirementForGeneration() + / Max(Tamer->GetHP(), 1); + + return Modifier >= TamingDifficulty * 3; +} + +truth character::CanTameWithLyre(const character* Tamer) const +{ + int TamingDifficulty = GetTamingDifficulty(); + + if(TamingDifficulty == NO_TAMING) + return false; + + if(TamingDifficulty == 0) + if(!IgnoreDanger()) + TamingDifficulty = int(10 * GetRelativeDanger(Tamer)); + else + TamingDifficulty = 10 * GetHPRequirementForGeneration() + / Max(Tamer->GetHP(), 1); + + return Tamer->GetAttribute(CHARISMA) >= TamingDifficulty; +} + +truth character::CanTameWithScroll(const character* Tamer) const +{ + int TamingDifficulty = GetTamingDifficulty(); + return (TamingDifficulty != NO_TAMING + && (TamingDifficulty == 0 + || Tamer->GetAttribute(INTELLIGENCE) * 4 + + Tamer->GetAttribute(CHARISMA) + >= TamingDifficulty * 5)); +} + +truth character::CheckSadism() +{ + if(!IsSadist() || !HasSadistAttackMode() || !IsSmall()) // gum + return false; + + if(!RAND_N(10)) + { + for(int d = 0; d < 8; ++d) + { + square* Square = GetNeighbourSquare(d); + + if(Square) + { + character* Char = Square->GetCharacter(); + + if(Char && Char->IsMasochist() && GetRelation(Char) == FRIEND + && Char->GetHP() * 3 >= Char->GetMaxHP() * 2 + && Hit(Char, Square->GetPos(), d, SADIST_HIT)) + { + TerminateGoingTo(); + return true; + } + } + } + } + + return false; +} + +truth character::CheckForBeverage() +{ + if(StateIsActivated(PANIC) || !IsEnabled() || !UsesNutrition() || CheckIfSatiated()) + return false; + + itemvector ItemVector; + GetStack()->FillItemVector(ItemVector); + + for(uint c = 0; c < ItemVector.size(); ++c) + if(ItemVector[c]->IsBeverage(this) && TryToConsume(ItemVector[c])) + return true; + + return false; +} + +void character::Haste() +{ + doforbodyparts()(this, &bodypart::Haste); + doforequipments()(this, &item::Haste); + BeginTemporaryState(HASTE, 500 + RAND() % 1000); +} + +void character::Slow() +{ + doforbodyparts()(this, &bodypart::Slow); + doforequipments()(this, &item::Slow); + BeginTemporaryState(SLOW, 500 + RAND() % 1000); +} + +truth character::CheckInventoryForItemToThrow(item* ToBeChecked) +{ + if(ToBeChecked->GetThrowItemTypes() & GetWhatThrowItemTypesToThrow()) + return true; + else + return false; +} + +truth character::CheckThrowItemOpportunity() +{ + if(!IsRangedAttacker() || !CanThrow() || !IsHumanoid() || !IsSmall() || !IsEnabled()) // total gum + return false; + + + /* + for(int d = 0; d < 8; ++d) // If there are adjacent hostiles, then do not throw, melee instead! + { + square* Square = GetNeighbourSquare(d); + + if(Square) + { + character* Char = Square->GetCharacter(); + if(Char && GetRelation(Char) == HOSTILE) + { + return false; + } + } + }*/ + + //Check the rest of the visible area for hostiles + v2 Pos = GetPos(); + v2 TestPos; + int RangeMax = GetLOSRange(); + int CandidateDirections[7] = {0, 0, 0, 0, 0, 0, 0}; + //int DoNotTestThisSquare = 0; + //int XLevel = GetLevel()->GetXSize(); + //int YLevel = GetLevel()->GetYSize(); + int HostileFound = 0; + + for(int r = 1; r <= RangeMax; ++r) + { + for(int dir = 0; dir < 8; ++dir) + { + switch(dir) + { + case 0: + TestPos = v2(Pos.X - r, Pos.Y - r); + break; + case 1: + TestPos = v2(Pos.X, Pos.Y - r); + break; + case 2: + TestPos = v2(Pos.X + r, Pos.Y - r); + break; + case 3: + TestPos = v2(Pos.X - r, Pos.Y); + break; + case 4: + TestPos = v2(Pos.X + r, Pos.Y); + break; + case 5: + TestPos = v2(Pos.X - r, Pos.Y + r); + break; + case 6: + TestPos = v2(Pos.X, Pos.Y + r); + break; + case 7: + TestPos = v2(Pos.X + r, Pos.Y + r); + break; + } + + level* Level = GetLevel(); + + //{DoNotTestThisSquare = 1;} + // These positions need to be ignored if they are outside the play area, otherwise the game crashes + //if( (TestPos.X < 0) || (TestPos.Y < 0) || (TestPos.X > XLevel) || (TestPos.Y > YLevel) ) + + + //if(!DoNotTestThisSquare) + if(Level->IsValidPos(TestPos)) + { + //lsquare* TestLSquare = GetNearLSquare(TestPos); + square* TestSquare = GetNearSquare(TestPos); + character* Dude = TestSquare->GetCharacter(); + + if((Dude && Dude->IsEnabled() && (GetRelation(Dude) != HOSTILE) + && Dude->CanBeSeenBy(this, false, true)) + /*|| !TestLSquare->IsFlyable()*/ ) // I question the merit of this line + { + CandidateDirections[dir] = BLOCKED; + } + + if(Dude && Dude->IsEnabled() && (GetRelation(Dude) == HOSTILE) + && Dude->CanBeSeenBy(this, false, true) + && (CandidateDirections[dir] != BLOCKED)) + { + //then load this candidate position direction into the vector of possible throw directions + CandidateDirections[dir] = SUCCESS; + // maybe put a little flag here + HostileFound = 1; + } + } + //DoNotTestThisSquare = 0; + } + } + + int ThrowDirection = 0; + int TargetFound = 0; + + if(HostileFound) + { + for(int dir = 0; dir < 8; ++dir) + { + if( (CandidateDirections[dir] == SUCCESS) && !TargetFound) + { + ThrowDirection = dir; + TargetFound = 1; + } + } + if(!TargetFound) + { + return false; + } + } + else + return false; + + + //check inventory for throwing weapon + itemvector ItemVector; + GetStack()->FillItemVector(ItemVector); + item* ToBeThrown = 0; + + //for(uint c = 0; c < ItemVector.size(); ++c) + //if(ItemVector[c]->IsThrowingWeapon()) + //{ + // ToBeThrown = ItemVector[c]; + // break; + //} + + for(uint c = 0; c < ItemVector.size(); ++c) + if(CheckInventoryForItemToThrow(ItemVector[c])) + { + ToBeThrown = ItemVector[c]; + break; + } + + if(!ToBeThrown) + return false; + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s throws %s.", CHAR_NAME(DEFINITE), ToBeThrown->CHAR_NAME(INDEFINITE)); + ThrowItem(ThrowDirection, ToBeThrown); + EditExperience(ARM_STRENGTH, 75, 1 << 8); + EditExperience(DEXTERITY, 75, 1 << 8); + EditExperience(PERCEPTION, 75, 1 << 8); + EditNP(-50); + DexterityAction(5); + TerminateGoingTo(); + return true; + + // Steps: + // (1) - Acquire target as nearest enemy + // (2) - Check that this enemy is in range, and is in appropriate direction + // No friendly fire! + // (3) - check inventory for throwing weapon, select this weapon + // (4) - throw item in direction where the enemy is +} + +truth character::CheckAIZapOpportunity() +{ + if(/*!IsRangedAttacker() || */ !CanZap() || !IsHumanoid() || !IsSmall() || !IsEnabled()) // total gum + return false; + + //Check the rest of the visible area for hostiles + v2 Pos = GetPos(); + v2 TestPos; + int SensibleRange = 5; + int RangeMax = GetLOSRange(); + if(RangeMax < SensibleRange) + SensibleRange = RangeMax; + + int CandidateDirections[7] = {0, 0, 0, 0, 0, 0, 0}; + int HostileFound = 0; + + for(int r = 2; r <= SensibleRange; ++r) + { + for(int dir = 0; dir < 8; ++dir) + { + switch(dir) + { + case 0: + TestPos = v2(Pos.X - r, Pos.Y - r); + break; + case 1: + TestPos = v2(Pos.X, Pos.Y - r); + break; + case 2: + TestPos = v2(Pos.X + r, Pos.Y - r); + break; + case 3: + TestPos = v2(Pos.X - r, Pos.Y); + break; + case 4: + TestPos = v2(Pos.X + r, Pos.Y); + break; + case 5: + TestPos = v2(Pos.X - r, Pos.Y + r); + break; + case 6: + TestPos = v2(Pos.X, Pos.Y + r); + break; + case 7: + TestPos = v2(Pos.X + r, Pos.Y + r); + break; + } + + level* Level = GetLevel(); + + if(Level->IsValidPos(TestPos)) + { + square* TestSquare = GetNearSquare(TestPos); + character* Dude = TestSquare->GetCharacter(); + + if((Dude && Dude->IsEnabled() && (GetRelation(Dude) != HOSTILE) + && Dude->CanBeSeenBy(this, false, true))) + { + CandidateDirections[dir] = BLOCKED; + } + + if(Dude && Dude->IsEnabled() && (GetRelation(Dude) == HOSTILE) + && Dude->CanBeSeenBy(this, false, true) + && (CandidateDirections[dir] != BLOCKED)) + { + //then load this candidate position direction into the vector of possible zap directions + CandidateDirections[dir] = SUCCESS; + // maybe put a little flag here + HostileFound = 1; + } + } + } + } + + int ZapDirection = 0; + int TargetFound = 0; + + if(HostileFound) + { + for(int dir = 0; dir < 8; ++dir) + { + if( (CandidateDirections[dir] == SUCCESS) && !TargetFound) + { + ZapDirection = dir; + TargetFound = 1; + } + } + if(!TargetFound) + { + return false; + } + } + else + return false; + + + //check inventory for zappable item + itemvector ItemVector; + GetStack()->FillItemVector(ItemVector); + item* ToBeZapped = 0; + + for(uint c = 0; c < ItemVector.size(); ++c) + if((ItemVector[c]->GetMinCharges() > 0) && ItemVector[c]->GetPrice()) // bald-faced gum solution for choosing zappables that have shots left. MinCharges needs to be replaced. Empty wands have zero price! + { + ToBeZapped = ItemVector[c]; + break; + } + + //item* Item = Char->SelectFromPossessions(CONST_S("What do you want to zap with?"), &item::IsZappable); + + //for(uint c = 0; c < ItemVector.size(); ++c) + //if(CheckInventoryForItemToThrow(ItemVector[c])) + //{ + // ToBeThrown = ItemVector[c]; + // break; + //} + + if(!ToBeZapped) + return false; + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s zaps %s.", CHAR_NAME(DEFINITE), ToBeZapped->CHAR_NAME(INDEFINITE)); + //ThrowItem(ZapDirection, ToBeZapped); + + + if(ToBeZapped->Zap(this, GetPos(), ZapDirection)) + { + EditAP(-100000 / APBonus(GetAttribute(PERCEPTION))); + return true; + } + else + return false; + + TerminateGoingTo(); + return true; + + // Steps: + // (1) - Acquire target as nearest enemy + // (2) - Check that this enemy is in range, and is in appropriate direction + // No friendly fire! + // (3) - check inventory for zappable item + // (4) - zap item in direction where the enemy is +} + +void character::SurgicallyDetachBodyPart() +{ + ADD_MESSAGE("You haven't got any extra bodyparts."); +} diff --git a/Main/Source/charset.cpp b/Main/Source/charset.cpp new file mode 100644 index 0000000..bfe86aa --- /dev/null +++ b/Main/Source/charset.cpp @@ -0,0 +1,56 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + + #define __FILE_OF_STATIC_CHARACTER_PROTOTYPE_DEFINITIONS__ + +#include "proto.h" +#include "char.h" +#include "database.h" + +EXTENDED_SYSTEM_SPECIALIZATIONS(character)(0, 0, 0, "character"); + +#include "human.h" +#include "nonhuman.h" + +#undef __FILE_OF_STATIC_CHARACTER_PROTOTYPE_DEFINITIONS__ + +#include +#include +#include + +#include "team.h" +#include "error.h" +#include "game.h" +#include "message.h" +#include "save.h" +#include "stack.h" +#include "wsquare.h" +#include "actions.h" +#include "iconf.h" +#include "whandler.h" +#include "hscore.h" +#include "god.h" +#include "command.h" +#include "materias.h" +#include "room.h" +#include "felist.h" +#include "graphics.h" +#include "bitmap.h" +#include "rawbit.h" +#include "miscitem.h" +#include "confdef.h" +#include "traps.h" +#include "iloops.h" +#include "balance.h" + +#include "team.cpp" +#include "char.cpp" diff --git a/Main/Source/charsset.cpp b/Main/Source/charsset.cpp new file mode 100644 index 0000000..3b04982 --- /dev/null +++ b/Main/Source/charsset.cpp @@ -0,0 +1,40 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "stack.h" +#include "message.h" +#include "actions.h" +#include "wterras.h" +#include "rawbit.h" +#include "team.h" +#include "iconf.h" +#include "god.h" +#include "felist.h" +#include "miscitem.h" +#include "gear.h" +#include "confdef.h" +#include "room.h" +#include "game.h" +#include "graphics.h" +#include "materias.h" +#include "bitmap.h" +#include "lterras.h" +#include "save.h" +#include "traps.h" +#include "iloops.h" +#include "balance.h" +#include "human.h" +#include "database.h" +#include "nonhuman.h" + +#include "human.cpp" +#include "nonhuman.cpp" diff --git a/Main/Source/command.cpp b/Main/Source/command.cpp new file mode 100644 index 0000000..7e02719 --- /dev/null +++ b/Main/Source/command.cpp @@ -0,0 +1,1564 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "command.h" +#include "char.h" +#include "message.h" +#include "game.h" +#include "stack.h" +#include "room.h" +#include "god.h" +#include "felist.h" +#include "iconf.h" +#include "bitmap.h" +#include "actions.h" +#include "miscitem.h" +#include "worldmap.h" +#include "wsquare.h" +#include "wterras.h" +#include "materia.h" +#include "database.h" +#include "team.h" + +#ifdef WIZARD +#include "proto.h" +#endif + +command::command(truth (*LinkedFunction)(character*), cchar* Description, char Key1, char Key2, truth UsableInWilderness, truth WizardModeFunction) : LinkedFunction(LinkedFunction), Description(Description), Key1(Key1), Key2(Key2), UsableInWilderness(UsableInWilderness), WizardModeFunction(WizardModeFunction) { } + +char command::GetKey() const { return !ivanconfig::GetUseAlternativeKeys() ? Key1 : Key2; } + +command* commandsystem::Command[] = +{ + 0, + + /* Sort according to description */ + + new command(&Apply, "apply", 'a', 'a', false), + new command(&Talk, "chat", 'C', 'C', false), + new command(&Close, "close", 'c', 'c', false), + new command(&Dip, "dip", '!', '!', false), + new command(&Drink, "drink", 'D', 'D', true), + new command(&Drop, "drop", 'd', 'd', true), + new command(&Eat, "eat", 'e', 'e', true), + new command(&WhatToEngrave, "engrave", 'G', 'G', false), + new command(&EquipmentScreen, "equipment menu", 'E', 'E', true), + new command(&Go, "go", 'g', 'g', false), + new command(&GoDown, "go down/enter area", '>', '>', true), + new command(&GoUp, "go up", '<', '<', true), + new command(&IssueCommand, "issue command(s) to team member(s)", 'I', 'I', false), + new command(&Kick, "kick", 'k', 'K', false), + new command(&Look, "look", 'l', 'L', true), + new command(&AssignName, "name", 'n', 'n', false), + new command(&Offer, "offer", 'O', 'f', false), + new command(&Open, "open", 'o', 'O', false), + new command(&PickUp, "pick up", ',', ',', false), + new command(&Pray, "pray", 'p', 'p', false), + new command(&Quit, "quit", 'Q', 'Q', true), + new command(&Read, "read", 'r', 'r', false), + new command(&Rest, "rest/heal", 'h', 'h', true), + new command(&Save, "save game", 'S', 'S', true), + new command(&ScrollMessagesDown, "scroll messages down", '+', '+', true), + new command(&ScrollMessagesUp, "scroll messages up", '-', '-', true), + new command(&ShowConfigScreen, "show config screen", '\\', '\\', true), + new command(&ShowInventory, "show inventory", 'i', 'i', true), + new command(&ShowKeyLayout, "show key layout", '?', '?', true), + new command(&DrawMessageHistory, "show message history", 'M', 'M', true), + new command(&ShowWeaponSkills, "show weapon skills", '@', '@', true), + new command(&Search, "search", 's', 's', false), + new command(&Sit, "sit", '_', '_', false), + new command(&Throw, "throw", 't', 't', false), + new command(&ToggleRunning, "toggle running", 'u', 'U', true), + new command(&ForceVomit, "vomit", 'V', 'V', false), + new command(&NOP, "wait", '.', '.', true), + new command(&WieldInRightArm, "wield in right arm", 'w', 'w', true), + new command(&WieldInLeftArm, "wield in left arm", 'W', 'W', true), +#ifdef WIZARD + new command(&WizardMode, "wizard mode activation", 'X', 'X', true), +#endif + new command(&Zap, "zap", 'z', 'z', false), + +#ifdef WIZARD + + /* Sort according to key */ + + new command(&RaiseStats, "raise stats", '1', '1', true, true), + new command(&LowerStats, "lower stats", '2', '2', true, true), + new command(&SeeWholeMap, "see whole map", '3', '3', true, true), + new command(&WalkThroughWalls, "toggle walk through walls mode", '4', '4', true, true), + new command(&RaiseGodRelations, "raise your relations to the gods", '5', '5', true, true), + new command(&LowerGodRelations, "lower your relations to the gods", '6', '6', true, true), + new command(&GainDivineKnowledge, "gain knowledge of all gods", '\"', '\"', true, true), + new command(&GainAllItems, "gain all items", '$', '$', true, true), + new command(&SecretKnowledge, "reveal secret knowledge", '*', '*', true, false), + new command(&DetachBodyPart, "detach a limb", '0', '0', true, true), + new command(&SummonMonster, "summon monster", '&', '&', false, true), + new command(&LevelTeleport, "level teleport", '|', '|', false, true), + new command(&Possess, "possess creature", '{', '{', false, true), + new command(&Polymorph, "polymorph", '[', '[', true, true), + +#endif + + 0 +}; + +truth commandsystem::GoUp(character* Char) +{ + if(!Char->TryToUnStickTraps(ZERO_V2)) + return false; + + /*if(!game::IsInWilderness() && game::WizardModeIsActive() && game::GetCurrentLevelIndex() >= 1) + if(game::TryTravel(game::GetCurrentDungeonIndex(), game::GetCurrentLevelIndex() - 1, RANDOM, true)) + return true;*/ + + oterrain* Terrain = Char->GetSquareUnder()->GetOTerrain(); + + if(!Terrain) + { + if(game::IsInWilderness()) + { + if(!Char->IsFlying()) + ADD_MESSAGE("You jump into the air. For some reason you don't get too far above."); + else + ADD_MESSAGE("You fly around for some time."); + } + else + ADD_MESSAGE("You can't go up."); + + return false; + } + + if(Terrain->Enter(true)) + { + Char->EditExperience(LEG_STRENGTH, 150, 1 << 6); + Char->EditNP(-20); + Char->EditAP(-100000 / APBonus(Char->GetAttribute(AGILITY))); + return true; + } + else + return false; +} + +truth commandsystem::GoDown(character* Char) +{ + if(!Char->TryToUnStickTraps(ZERO_V2)) + return false; + + /*if(!game::IsInWilderness() && game::WizardModeIsActive() && game::GetCurrentLevelIndex() < game::GetLevels() - 1) + if(game::TryTravel(game::GetCurrentDungeonIndex(), game::GetCurrentLevelIndex() + 1, RANDOM, true)) + return true;*/ + + oterrain* Terrain = Char->GetSquareUnder()->GetOTerrain(); + + if(!Terrain) + { + if(game::IsInWilderness()) + ADD_MESSAGE("There seems to be nothing of interest here."); + else + ADD_MESSAGE("You can't go down."); + + return false; + } + + if(Terrain->Enter(false)) + { + Char->EditExperience(AGILITY, 150, 1 << 6); + Char->EditNP(-10); + Char->EditAP(-100000 / APBonus(Char->GetAttribute(AGILITY))); + return true; + } + else + return false; +} + +truth commandsystem::Open(character* Char) +{ + if(Char->CanOpen()) + { + int Key; + truth OpenableItems = Char->GetStack()->SortedItems(Char, &item::IsOpenable); + + if(OpenableItems) + Key = game::AskForKeyPress(CONST_S("What do you wish to open? [press a direction key, space or i]")); + else + Key = game::AskForKeyPress(CONST_S("What do you wish to open? [press a direction key or space]")); + + if(Key == 'i' && OpenableItems) + { + item* Item = Char->GetStack()->DrawContents(Char, CONST_S("What do you want to open?"), 0, &item::IsOpenable); + return Item && Item->Open(Char); + } + + v2 DirVect = game::GetDirectionVectorForKey(Key); + + if(DirVect != ERROR_V2 && Char->GetArea()->IsValidPos(Char->GetPos() + DirVect)) + return Char->GetNearLSquare(Char->GetPos() + DirVect)->Open(Char); + } + else + ADD_MESSAGE("This monster type cannot open anything."); + + return false; +} + +truth commandsystem::Close(character* Char) +{ + if(Char->CanOpen()) + { + int Dir = game::DirectionQuestion(CONST_S("What do you wish to close? [press a direction key]"), false); + + if(Dir != DIR_ERROR && Char->GetArea()->IsValidPos(Char->GetPos() + game::GetMoveVector(Dir))) + return Char->GetNearLSquare(Char->GetPos() + game::GetMoveVector(Dir))->Close(Char); + } + else + ADD_MESSAGE("This monster type cannot close anything."); + + return false; +} + +truth commandsystem::Drop(character* Char) +{ + if(!Char->GetStack()->GetItems()) + { + ADD_MESSAGE("You have nothing to drop!"); + return false; + } + + truth Success = false; + stack::SetSelected(0); + + for(;;) + { + itemvector ToDrop; + game::DrawEverythingNoBlit(); + Char->GetStack()->DrawContents(ToDrop, Char, CONST_S("What do you want to drop?"), REMEMBER_SELECTED); + + if(ToDrop.empty()) + break; + + if(game::IsInWilderness()) + { + for(uint c = 0; c < ToDrop.size(); ++c) + { + if(game::TruthQuestion(CONST_S("Are you sure? You will never see ") + ToDrop[c]->CHAR_NAME(DEFINITE) + + " again! [y/N]")) + { + + ADD_MESSAGE("You drop %s.", ToDrop[c]->CHAR_NAME(DEFINITE)); + ToDrop[c]->RemoveFromSlot(); + ToDrop[c]->SendToHell(); + } + } + } + else if(!Char->GetRoom() || Char->GetRoom()->DropItem(Char, ToDrop[0], ToDrop.size())) + { + ADD_MESSAGE("%s dropped.", ToDrop[0]->GetName(INDEFINITE, ToDrop.size()).CStr()); + for(uint c = 0; c < ToDrop.size(); ++c) + { + ToDrop[c]->MoveTo(Char->GetStackUnder()); + } + Success = true; + } + } + + if(Success) + { + Char->DexterityAction(2); + return true; + } + + return false; +} + +truth commandsystem::Eat(character* Char) +{ + if(!Char->CheckConsume(CONST_S("eat"))) + return false; + + lsquare* Square = Char->GetLSquareUnder(); + + if(!game::IsInWilderness() && Square->GetOLTerrain() && Square->GetOLTerrain()->HasEatEffect()) + { + if(Square->GetOLTerrain()->Eat(Char)) + return true; + } + + return Consume(Char, "eat", &item::IsEatable); +} + +truth commandsystem::Drink(character* Char) +{ + if(!Char->CheckConsume(CONST_S("drink"))) + return false; + + lsquare* Square = Char->GetLSquareUnder(); + + if(!game::IsInWilderness() && Square->GetOLTerrain() && Square->GetOLTerrain()->HasDrinkEffect()) + { + if(Square->GetOLTerrain()->Drink(Char)) + return true; + } + + return Consume(Char, "drink", &item::IsDrinkable); +} + +truth commandsystem::Consume(character* Char, cchar* ConsumeVerb, sorter Sorter) +{ + lsquare* Square = Char->GetLSquareUnder(); + stack* Inventory = Char->GetStack(); + stack* StackUnder = Square->GetStack(); + + if((game::IsInWilderness() || !StackUnder->SortedItems(Char, Sorter)) && !Inventory->SortedItems(Char, Sorter)) + { + ADD_MESSAGE("You have nothing to %s!", ConsumeVerb); + return false; + } + + itemvector Item; + festring Question = CONST_S("What do you wish to ") + ConsumeVerb + '?'; + + if(!game::IsInWilderness() && StackUnder->SortedItems(Char, Sorter)) + Inventory->DrawContents(Item, StackUnder, Char, Question, CONST_S("Items in your inventory"), CONST_S("Items on the ground"), CONST_S(""), 0, NO_MULTI_SELECT, Sorter); + else + Inventory->DrawContents(Item, Char, Question, NO_MULTI_SELECT, Sorter); + + return !Item.empty() ? Char->ConsumeItem(Item[0], ConsumeVerb + CONST_S("ing")) : false; +} + +truth commandsystem::ShowInventory(character* Char) +{ + festring Title("Your inventory (total weight: "); + Title << Char->GetStack()->GetWeight(); + Title << "g)"; + Char->GetStack()->DrawContents(Char, Title, NO_SELECT); + return false; +} + +truth commandsystem::PickUp(character* Char) +{ + if(!Char->GetStackUnder()->GetVisibleItems(Char)) + { + ADD_MESSAGE("There is nothing here to pick up!"); + return false; + } + + itemvectorvector PileVector; + Char->GetStackUnder()->Pile(PileVector, Char, CENTER); + + for(int c = 0; c < 4; ++c) + { + stack* Stack = Char->GetLSquareUnder()->GetStackOfAdjacentSquare(c); + + if(Stack) + Stack->Pile(PileVector, Char, 3 - c); + } + + if(PileVector.size() == 1) + if(PileVector[0][0]->CanBePickedUp()) + { + int Amount = PileVector[0].size(); + + if(Amount > 1) + Amount = game::ScrollBarQuestion(CONST_S("How many ") + PileVector[0][0]->GetName(PLURAL) + '?', Amount, 1, 0, Amount, 0, WHITE, LIGHT_GRAY, DARK_GRAY); + + if(!Amount) + return false; + + if((!PileVector[0][0]->GetRoom() + || PileVector[0][0]->GetRoom()->PickupItem(Char, PileVector[0][0], Amount)) + && PileVector[0][0]->CheckPickUpEffect(Char)) + { + for(int c = 0; c < Amount; ++c) + PileVector[0][c]->MoveTo(Char->GetStack()); + + ADD_MESSAGE("%s picked up.", PileVector[0][0]->GetName(INDEFINITE, Amount).CStr()); + Char->DexterityAction(2); + return true; + } + else + return false; + } + else + { + ADD_MESSAGE("%s too large to pick up!", PileVector[0].size() == 1 ? "It is" : "They are"); + return false; + } + + truth Success = false; + stack::SetSelected(0); + + for(;;) + { + itemvector ToPickup; + game::DrawEverythingNoBlit(); + Char->GetStackUnder()->DrawContents(ToPickup, Char, CONST_S("What do you want to pick up?"), REMEMBER_SELECTED); + + if(ToPickup.empty()) + break; + + if(ToPickup[0]->CanBePickedUp()) + { + if((!ToPickup[0]->GetRoom() + || ToPickup[0]->GetRoom()->PickupItem(Char, ToPickup[0], ToPickup.size())) + && ToPickup[0]->CheckPickUpEffect(Char)) + { + for(uint c = 0; c < ToPickup.size(); ++c) + ToPickup[c]->MoveTo(Char->GetStack()); + + ADD_MESSAGE("%s picked up.", ToPickup[0]->GetName(INDEFINITE, ToPickup.size()).CStr()); + Success = true; + } + } + else + ADD_MESSAGE("%s too large to pick up!", ToPickup.size() == 1 ? "It is" : "They are"); + } + + if(Success) + { + Char->DexterityAction(2); + return true; + } + + return false; +} + +truth commandsystem::Quit(character* Char) +{ + if(game::TruthQuestion(CONST_S("Your quest is not yet compeleted! Really quit? [y/N]"))) + { + Char->ShowAdventureInfo(); + festring Msg = CONST_S("cowardly quit the game"); + Char->AddScoreEntry(Msg, 0.75); + game::End(Msg, !game::WizardModeIsActive() || game::TruthQuestion(CONST_S("Remove saves? [y/N]"))); + return true; + } + else + return false; +} + +truth commandsystem::Talk(character* Char) +{ + if(!Char->CheckTalk()) + return false; + + character* ToTalk = 0; + int Characters = 0; + + for(int d = 0; d < 8; ++d) + { + lsquare* Square = Char->GetNaturalNeighbourLSquare(d); + + if(Square) + { + character* Dude = Square->GetCharacter(); + + if(Dude) + { + ToTalk = Dude; + ++Characters; + } + } + } + + if(!Characters) + { + ADD_MESSAGE("Find yourself someone to talk to first!"); + return false; + } + else if(Characters == 1) + return ToTalk->ChatMenu(); + else + { + int Dir = game::DirectionQuestion(CONST_S("To whom do you wish to talk to? [press a direction key]"), false, true); + + if(Dir == DIR_ERROR || !Char->GetArea()->IsValidPos(Char->GetPos() + game::GetMoveVector(Dir))) + return false; + + character* Dude = Char->GetNearSquare(Char->GetPos() + game::GetMoveVector(Dir))->GetCharacter(); + + if(Dude == Char) + { + ADD_MESSAGE("You talk to yourself for some time."); + Char->EditExperience(WISDOM, -50, 1 << 7); + Char->EditAP(-1000); + return true; + } + else if(Dude) + return Dude->ChatMenu(); + else + ADD_MESSAGE("You get no response."); + } + + return false; +} + +truth commandsystem::NOP(character* Char) +{ + Char->EditExperience(DEXTERITY, -25, 1 << 3); + Char->EditExperience(AGILITY, -25, 1 << 3); + Char->EditAP(-Char->GetStateAPGain(1000)); + return true; +} + +truth commandsystem::Save(character*) +{ + if(game::TruthQuestion(CONST_S("Do you truly wish to save and flee? [y/N]"))) + { + game::Save(); + game::End("", false); + return true; + } + else + return false; +} + +truth commandsystem::Read(character* Char) +{ + if(!Char->CanRead() && !game::GetSeeWholeMapCheatMode()) + { + ADD_MESSAGE("You can't read."); + return false; + } + + if(!Char->GetStack()->SortedItems(Char, &item::IsReadable)) + { + ADD_MESSAGE("You have nothing to read!"); + return false; + } + + if(Char->GetLSquareUnder()->IsDark() && !game::GetSeeWholeMapCheatMode()) + { + ADD_MESSAGE("It is too dark to read."); + return false; + } + + item* Item = Char->GetStack()->DrawContents(Char, CONST_S("What do you want to read?"), 0, &item::IsReadable); + return Item && Char->ReadItem(Item); +} + +truth commandsystem::Dip(character* Char) +{ + if(!Char->GetStack()->SortedItems(Char, &item::IsDippable) && !Char->EquipsSomething(&item::IsDippable)) + { + ADD_MESSAGE("You have nothing to dip!"); + return false; + } + + truth HasDipDestination = Char->PossessesItem(&item::IsDipDestination); + truth DipDestinationNear = false; + + for(int d = 0; d < 9; ++d) + { + lsquare* Square = Char->GetNaturalNeighbourLSquare(d); + + if(Square && Square->IsDipDestination()) + DipDestinationNear = true; + } + + if(!HasDipDestination && !DipDestinationNear) + { + ADD_MESSAGE("There is nothing to dip anything into."); + return false; + } + + item* Item = Char->SelectFromPossessions(CONST_S("What do you want to dip?"), &item::IsDippable); + + if(Item) + { + if(!HasDipDestination || (DipDestinationNear && game::TruthQuestion(CONST_S("Do you wish to dip in a nearby square? [y/N]")))) + { + int Dir = game::DirectionQuestion(CONST_S("Where do you want to dip ") + Item->GetName(DEFINITE) + "? [press a direction key or '.']", false, true); + v2 Pos = Char->GetPos() + game::GetMoveVector(Dir); + + if(Dir == DIR_ERROR || !Char->GetArea()->IsValidPos(Pos) || !Char->GetNearLSquare(Pos)->IsDipDestination()) + return false; + + return Char->GetNearLSquare(Pos)->DipInto(Item, Char); + } + else + { + item* DipTo = Char->SelectFromPossessions(CONST_S("Into what do you wish to dip it?"), &item::IsDipDestination); + + if(DipTo) + { + if(Item == DipTo) + { + ADD_MESSAGE("Very funny..."); + return false; + } + + Item->DipInto(DipTo->CreateDipLiquid(), Char); + return true; + } + } + } + + return false; +} + +truth commandsystem::ShowKeyLayout(character*) +{ + felist List(CONST_S("Keyboard Layout")); + List.AddDescription(CONST_S("")); + List.AddDescription(CONST_S("Key Description")); + festring Buffer; + + for(int c = 1; GetCommand(c); ++c) + if(!GetCommand(c)->IsWizardModeFunction()) + { + Buffer.Empty(); + Buffer << GetCommand(c)->GetKey(); + Buffer.Resize(10); + List.AddEntry(Buffer + GetCommand(c)->GetDescription(), LIGHT_GRAY); + } + + if(game::WizardModeIsActive()) + { + List.AddEntry(CONST_S(""), WHITE); + List.AddEntry(CONST_S("Wizard mode functions:"), WHITE); + List.AddEntry(CONST_S(""), WHITE); + + for(int c = 1; GetCommand(c); ++c) + if(GetCommand(c)->IsWizardModeFunction()) + { + Buffer.Empty(); + Buffer << GetCommand(c)->GetKey(); + Buffer.Resize(10); + List.AddEntry(Buffer + GetCommand(c)->GetDescription(), LIGHT_GRAY); + } + } + + game::SetStandardListAttributes(List); + List.Draw(); + return false; +} + +truth commandsystem::Look(character* Char) +{ + festring Msg; + if(!game::IsInWilderness()) + Char->GetLevel()->AddSpecialCursors(); + + if(!game::IsInWilderness()) + Msg = CONST_S("Direction keys move cursor, ESC exits, 'i' examines items, 'c' examines a character."); + else + Msg = CONST_S("Direction keys move cursor, ESC exits, 'c' examines a character."); + + game::PositionQuestion(Msg, Char->GetPos(), &game::LookHandler, &game::LookKeyHandler, ivanconfig::GetLookZoom()); + game::RemoveSpecialCursors(); + return false; +} + +truth commandsystem::WhatToEngrave(character* Char) +{ + if(!Char->CanRead()) + { + ADD_MESSAGE("You can't even read."); + return false; + } + + Char->GetNearLSquare(Char->GetPos())->Engrave(game::StringQuestion(CONST_S("What do you want to engrave here?"), WHITE, 0, 80, true)); + return false; +} + +truth commandsystem::Pray(character* Char) +{ + felist Panthenon(CONST_S("To Whom you want to address your prayers?")); + Panthenon.SetEntryDrawer(game::GodEntryDrawer); + int Known[GODS]; + int Index = 0; + int DivineMaster = Char->GetLSquareUnder()->GetDivineMaster(); + + if(DivineMaster == ATHEIST) + { + ADD_MESSAGE("Somehow you feel that no god will help you here."); + return false; + } + + if(!DivineMaster) + { + for(int c = 1; c <= GODS; ++c) + if(game::GetGod(c)->IsKnown()) + { + Panthenon.AddEntry(game::GetGod(c)->GetCompleteDescription(), LIGHT_GRAY, 20, c); + Known[Index++] = c; + } + } + else + if(game::GetGod(DivineMaster)->IsKnown()) + { + Panthenon.AddEntry(game::GetGod(DivineMaster)->GetCompleteDescription(), LIGHT_GRAY, 20, DivineMaster); + Known[0] = DivineMaster; + } + else + { + ADD_MESSAGE("Somehow you feel that no deity you know can hear your prayers from this place."); + return false; + } + + game::SetStandardListAttributes(Panthenon); + Panthenon.AddFlags(SELECTABLE); + int Select = Panthenon.Draw(); + + if(Select == LIST_WAS_EMPTY) + { + ADD_MESSAGE("You do not know any gods."); + return false; + } + + if(Select & FELIST_ERROR_BIT) + return false; + else + { + if(Char->GetLSquareUnder()->GetDivineMaster()) + { + if(!Select) + { + if(game::TruthQuestion(CONST_S("Do you really wish to pray to ") + game::GetGod(Char->GetLSquareUnder()->GetDivineMaster())->GetName() + "? [y/N]")) + game::GetGod(Char->GetLSquareUnder()->GetDivineMaster())->Pray(); + else + return false; + } + else + return false; + } + else + { + if(game::TruthQuestion(CONST_S("Do you really wish to pray to ") + game::GetGod(Known[Select])->GetName() + "? [y/N]")) + { + if(Char->StateIsActivated(CONFUSED) && !(RAND() & 7)) + { + int Index; + for(Index = 1 + RAND() % GODS; + Index == Known[Select]; + Index = 1 + RAND() % GODS); + + if(game::GetGod(Index)->IsKnown()) + ADD_MESSAGE("You feel something went wrong in the rituals. You have accidentally prayed to %s!", game::GetGod(Index)->GetName()); + else + ADD_MESSAGE("Your rituals were seriously incorrect. You have accidentally prayed to an unknown god, but have no idea how!"); + + game::GetGod(Index)->Pray(); + } + else + game::GetGod(Known[Select])->Pray(); + } + else + return false; + } + + Char->EditAP(-1000); + return true; + } +} + +truth commandsystem::Kick(character* Char) +{ + /** No multi-tile support */ + + if(!Char->CheckKick()) + return false; + + if(Char->GetBurdenState() == OVER_LOADED) + { + ADD_MESSAGE("You try to kick, but you collapse under your load."); + Char->EditAP(-100000 / APBonus(Char->GetAttribute(AGILITY))); + return true; + } + + int Dir = game::DirectionQuestion(CONST_S("In what direction do you wish to kick? [press a direction key]"), false); + + if(Dir == DIR_ERROR || !Char->GetArea()->IsValidPos(Char->GetPos() + game::GetMoveVector(Dir))) + return false; + + lsquare* Square = Char->GetNearLSquare(Char->GetPos() + game::GetMoveVector(Dir)); + + if(!Square->CheckKick(Char)) + return false; + + character* Enemy = Square->GetCharacter(); + + if(Enemy && !(Enemy->IsMasochist() && Char->GetRelation(Enemy) == FRIEND) && Char->GetRelation(Enemy) != HOSTILE && !game::TruthQuestion(CONST_S("This might cause a hostile reaction. Are you sure? [y/N]"))) + return false; + + /*if(Square->GetCharacter() && Char->GetRelation(Square->GetCharacter()) != HOSTILE) + if(!game::TruthQuestion(CONST_S("This might cause a hostile reaction. Are you sure? [y/N]"))) + return false; + else + */ + + Char->Hostility(Square->GetCharacter()); + Char->Kick(Square, Dir); + return true; +} + +truth commandsystem::Offer(character* Char) +{ + if(!Char->CheckOffer()) + return false; + + lsquare* Square = Char->GetLSquareUnder(); + + if(Square->GetOLTerrain() && Square->GetOLTerrain()->AcceptsOffers()) + { + if(!Char->GetStack()->GetItems()) + { + ADD_MESSAGE("You have nothing to offer!"); + return false; + } + + item* Item = Char->GetStack()->DrawContents(Char, CONST_S("What do you want to offer?")); + + if(Item) + { + if(game::GetGod(Square->GetDivineMaster())->ReceiveOffer(Item)) + { + Item->RemoveFromSlot(); + Item->SendToHell(); + Char->DexterityAction(5); /** C **/ + return true; + } + else + return false; + } + else + return false; + } + else + ADD_MESSAGE("There isn't any altar here!"); + + return false; +} + +truth commandsystem::DrawMessageHistory(character*) +{ + msgsystem::DrawMessageHistory(); + return false; +} + +truth commandsystem::Throw(character* Char) +{ + if(!Char->CheckThrow()) + return false; + + if(!Char->GetStack()->GetItems()) + { + ADD_MESSAGE("You have nothing to throw!"); + return false; + } + + item* Item = Char->GetStack()->DrawContents(Char, CONST_S("What do you want to throw?")); + + if(Item) + { + int Answer = game::DirectionQuestion(CONST_S("In what direction do you wish to throw? [press a direction key]"), false); + + if(Answer == DIR_ERROR) + return false; + + Char->ThrowItem(Answer, Item); + Char->EditExperience(ARM_STRENGTH, 75, 1 << 8); + Char->EditExperience(DEXTERITY, 75, 1 << 8); + Char->EditExperience(PERCEPTION, 75, 1 << 8); + Char->EditNP(-50); + Char->DexterityAction(5); + return true; + } + else + return false; +} + +truth commandsystem::Apply(character* Char) +{ + if(!Char->CanApply()) + { + ADD_MESSAGE("This monster type cannot apply."); + return false; + } + + if(!Char->CheckApply()) + return false; + + if(!Char->PossessesItem(&item::IsAppliable)) + { + ADD_MESSAGE("You have nothing to apply!"); + return false; + } + + item* Item = Char->SelectFromPossessions(CONST_S("What do you want to apply?"), &item::IsAppliable); + return Item && Item->Apply(Char); +} + +truth commandsystem::ForceVomit(character* Char) +{ + if(Char->CanForceVomit()) + { + int Dir = game::DirectionQuestion(CONST_S("Where do you wish to vomit? [press a direction key]"), false, true); + + if(Dir != DIR_ERROR) + { + v2 VomitPos = Char->GetPos() + game::GetMoveVector(Dir); + + if(Char->GetArea()->IsValidPos(VomitPos)) + { + ccharacter* Other = Char->GetArea()->GetSquare(VomitPos)->GetCharacter(); + + if(Other && Other->GetTeam() != Char->GetTeam() + && Other->GetRelation(Char) != HOSTILE + && Other->CanBeSeenBy(Char) + && !game::TruthQuestion("Do you really want to vomit at " + Other->GetObjectPronoun() + "? [y/N]")) + return false; + + ADD_MESSAGE(Char->GetForceVomitMessage().CStr()); + Char->Vomit(Char->GetPos() + game::GetMoveVector(Dir), 500 + RAND() % 500, false); + Char->EditAP(-1000); + return true; + } + } + } + else + ADD_MESSAGE("You can't vomit."); + + return false; +} + +truth commandsystem::Zap(character* Char) +{ + if(!Char->CheckZap()) + return false; + + if(!Char->PossessesItem(&item::IsZappable)) + { + ADD_MESSAGE("You have nothing to zap with, %s.", game::Insult()); + return false; + } + + item* Item = Char->SelectFromPossessions(CONST_S("What do you want to zap with?"), &item::IsZappable); + + if(Item) + { + int Answer = game::DirectionQuestion(CONST_S("In what direction do you wish to zap? [press a direction key or '.']"), false, true); + + if(Answer == DIR_ERROR) + return false; + + if(Item->Zap(Char, Char->GetPos(), Answer)) + { + Char->EditAP(-100000 / APBonus(Char->GetAttribute(PERCEPTION))); + return true; + } + else + return false; + } + else + return false; +} + +truth commandsystem::Rest(character* Char) +{ + if(Char->StateIsActivated(PANIC)) + { + ADD_MESSAGE("You are too scared to rest."); + return false; + } + + truth Error = false; + + if(Char->GetHP() == Char->GetMaxHP()) + { + ADD_MESSAGE("You HP is already at its maximum."); + Error = true; + } + else if(!Char->CanHeal()) + { + ADD_MESSAGE("You cannot heal."); + Error = true; + } + + if(Error) + { + long MinutesToRest = game::NumberQuestion(CONST_S("How many minutes to wait?"), WHITE, true); + + if(MinutesToRest > 0) + { + oterrain* Terrain = Char->GetSquareUnder()->GetOTerrain(); + + if(Terrain) + Terrain->ShowRestMessage(Char); + + rest* Rest = rest::Spawn(Char); + Rest->SetMinToStop(game::GetTotalMinutes() + MinutesToRest); + Rest->SetGoalHP(0); + Char->SetAction(Rest); + return true; + } + else + return false; + } + + long HPToRest = game::ScrollBarQuestion(CONST_S("How many hit points you desire?"), Char->GetMaxHP(), 1, 0, Char->GetMaxHP(), 0, WHITE, LIGHT_GRAY, DARK_GRAY); + + if(HPToRest <= Char->GetHP()) + { + if(HPToRest != 0) + ADD_MESSAGE("Your HP is already %d.", Char->GetHP()); + + return false; + } + + oterrain* Terrain = Char->GetSquareUnder()->GetOTerrain(); + + if(Terrain) + Terrain->ShowRestMessage(Char); + + rest* Rest = rest::Spawn(Char); + Rest->SetMinToStop(0); + Rest->SetGoalHP(HPToRest); + Char->SetAction(Rest); + return true; +} + +truth commandsystem::Sit(character* Char) +{ + lsquare* Square = Char->GetLSquareUnder(); + return (Square->GetOLTerrain() && Square->GetOLTerrain()->SitOn(Char)) || Square->GetGLTerrain()->SitOn(Char); +} + +truth commandsystem::Go(character* Char) +{ + int Dir = game::DirectionQuestion(CONST_S("In what direction do you want to go? [press a direction key]"), false); + + if(Dir == DIR_ERROR) + return false; + + go* Go = go::Spawn(Char); + Go->SetDirection(Dir); + int OKDirectionsCounter = 0; + + for(int d = 0; d < Char->GetNeighbourSquares(); ++d) + { + lsquare* Square = Char->GetNeighbourLSquare(d); + + if(Square && Char->CanMoveOn(Square)) + ++OKDirectionsCounter; + } + + Go->SetIsWalkingInOpen(OKDirectionsCounter > 2); + Char->SetAction(Go); + Char->EditAP(Char->GetStateAPGain(100)); // gum solution + Char->GoOn(Go, true); + return truth(Char->GetAction()); +} + +truth commandsystem::ShowConfigScreen(character*) +{ + ivanconfig::Show(); + return false; +} + +truth commandsystem::AssignName(character*) +{ + game::NameQuestion(); + return false; +} + +truth commandsystem::EquipmentScreen(character* Char) +{ + return Char->EquipmentScreen(Char->GetStack(), 0); +} + +truth commandsystem::ScrollMessagesDown(character*) +{ + msgsystem::ScrollDown(); + return false; +} + +truth commandsystem::ScrollMessagesUp(character*) +{ + msgsystem::ScrollUp(); + return false; +} + +truth commandsystem::ShowWeaponSkills(character* Char) +{ + felist List(CONST_S("Your experience in weapon categories")); + List.AddDescription(CONST_S("")); + List.AddDescription(CONST_S("Category name Level Points Needed Battle bonus")); + truth Something = false; + festring Buffer; + + for(int c = 0; c < Char->GetAllowedWeaponSkillCategories(); ++c) + { + cweaponskill* Skill = Char->GetCWeaponSkill(c); + + if(Skill->GetHits() / 100 || (Char->IsUsingWeaponOfCategory(c))) + { + Buffer = Skill->GetName(c); + Buffer.Resize(30); + Buffer << Skill->GetLevel(); + Buffer.Resize(40); + Buffer << Skill->GetHits() / 100; + Buffer.Resize(50); + + if(Skill->GetLevel() != 20) + Buffer << (Skill->GetLevelMap(Skill->GetLevel() + 1) - Skill->GetHits()) / 100; + else + Buffer << '-'; + + Buffer.Resize(60); + Buffer << '+' << (Skill->GetBonus() - 1000) / 10; + + if(Skill->GetBonus() % 10) + Buffer << '.' << Skill->GetBonus() % 10; + + Buffer << '%'; + + if(Char->IsUsingWeaponOfCategory(c)) + List.AddEntry(Buffer, WHITE); + else + List.AddEntry(Buffer, LIGHT_GRAY); + + Something = true; + } + } + + if(Char->AddSpecialSkillInfo(List)) + Something = true; + + if(Something) + { + game::SetStandardListAttributes(List); + List.Draw(); + } + else + ADD_MESSAGE("You are not experienced in any weapon skill yet!"); + + return false; +} + +truth commandsystem::WieldInRightArm(character* Char) +{ + if(!Char->CanUseEquipment()) + ADD_MESSAGE("You cannot wield anything."); + else if(Char->TryToChangeEquipment(Char->GetStack(), 0, RIGHT_WIELDED_INDEX)) + { + Char->DexterityAction(5); + return true; + } + + return false; +} + +truth commandsystem::WieldInLeftArm(character* Char) +{ + if(!Char->CanUseEquipment()) + ADD_MESSAGE("You cannot wield anything."); + else if(Char->TryToChangeEquipment(Char->GetStack(), 0, LEFT_WIELDED_INDEX)) + { + Char->DexterityAction(5); + return true; + } + + return false; +} + +truth commandsystem::Search(character* Char) +{ + Char->Search(Char->GetAttribute(PERCEPTION) << 2); + return true; +} + +#ifdef WIZARD + +truth commandsystem::WizardMode(character* Char) +{ + if(!game::WizardModeIsActive()) + { + if(game::TruthQuestion(CONST_S("Do you want to cheat, cheater? This action cannot be undone. [y/N]"))) + { + game::ActivateWizardMode(); + ADD_MESSAGE("Wizard mode activated."); + + if(game::IsInWilderness()) + { + v2 ElpuriCavePos = game::GetWorldMap()->GetEntryPos(0, ELPURI_CAVE); + game::GetWorldMap()->GetWSquare(ElpuriCavePos)->ChangeOWTerrain(elpuricave::Spawn()); + game::GetWorldMap()->RevealEnvironment(ElpuriCavePos, 1); + game::GetWorldMap()->SendNewDrawRequest(); + } + else + { + game::LoadWorldMap(); + v2 ElpuriCavePos = game::GetWorldMap()->GetEntryPos(0, ELPURI_CAVE); + game::GetWorldMap()->GetWSquare(ElpuriCavePos)->ChangeOWTerrain(elpuricave::Spawn()); + game::GetWorldMap()->RevealEnvironment(ElpuriCavePos, 1); + game::SaveWorldMap(); + } + + game::Save(); + game::Save(game::GetAutoSaveFileName()); + } + else + return false; + } + else + ADD_MESSAGE("Got some scrolls of wishing."); + + for(int c = 0; c < 5; ++c) + Char->GetStack()->AddItem(scrollofwishing::Spawn()); + + return false; +} + +truth commandsystem::RaiseStats(character* Char) +{ + Char->EditAllAttributes(1); + return false; +} + +truth commandsystem::LowerStats(character* Char) +{ + Char->EditAllAttributes(-1); + return false; +} + +truth commandsystem::GainAllItems(character* Char) +{ + itemvectorvector AllItems; + protosystem::CreateEveryItem(AllItems); + stack* Stack = game::IsInWilderness() ? Char->GetStack() : Char->GetStackUnder(); + + for(uint c = 0; c < AllItems.size(); ++c) + Stack->AddItem(AllItems[c][0]); + + return false; +} + +truth commandsystem::SeeWholeMap(character*) +{ + game::SeeWholeMap(); + return false; +} + +truth commandsystem::WalkThroughWalls(character*) +{ + game::GoThroughWalls(); + return false; +} + +truth commandsystem::RaiseGodRelations(character*) +{ + for(int c = 1; c <= GODS; ++c) + game::GetGod(c)->AdjustRelation(50); + + return false; +} + +truth commandsystem::LowerGodRelations(character*) +{ + for(int c = 1; c <= GODS; ++c) + game::GetGod(c)->AdjustRelation(-50); + + return false; +} + +truth commandsystem::GainDivineKnowledge(character*) +{ + for(int c = 1; c <= GODS; ++c) + game::LearnAbout(game::GetGod(c)); + + return false; +} + +truth commandsystem::SecretKnowledge(character* Char) +{ + felist List(CONST_S("Knowledge of the ancients")); + List.AddEntry(CONST_S("Character attributes"), LIGHT_GRAY); + List.AddEntry(CONST_S("Character attack info"), LIGHT_GRAY); + List.AddEntry(CONST_S("Character defence info"), LIGHT_GRAY); + List.AddEntry(CONST_S("Character danger values"), LIGHT_GRAY); + List.AddEntry(CONST_S("Item attack info"), LIGHT_GRAY); + List.AddEntry(CONST_S("Miscellaneous item info"), LIGHT_GRAY); + List.AddEntry(CONST_S("Material info"), LIGHT_GRAY); + game::SetStandardListAttributes(List); + List.AddFlags(SELECTABLE); + uint c, Chosen = List.Draw(); + festring Entry; + + if(Chosen & FELIST_ERROR_BIT) + return false; + + List.Empty(); + List.RemoveFlags(SELECTABLE); + + if(Chosen < 4) + { + charactervector& Character = game::GetCharacterDrawVector(); + int TeamSize = 0; + + for(std::list::const_iterator i = Char->GetTeam()->GetMember().begin(); + i != Char->GetTeam()->GetMember().end(); ++i) + if((*i)->IsEnabled()) + { + Character.push_back(*i); + ++TeamSize; + } + + protosystem::CreateEveryCharacter(Character); + List.SetEntryDrawer(game::CharacterEntryDrawer); + + switch(Chosen) + { + case 0: + List.AddDescription(CONST_S(" AS LS DX AG EN PE IN WI CH MA")); + + for(c = 0; c < Character.size(); ++c) + { + Entry.Empty(); + Character[c]->AddName(Entry, UNARTICLED); + Character[c]->AddAttributeInfo(Entry); + List.AddEntry(Entry, LIGHT_GRAY, 0, c); + } + + List.SetPageLength(15); + break; + case 1: + List.AddDescription(CONST_S(" BD THV APC")); + + for(c = 0; c < Character.size(); ++c) + { + Entry.Empty(); + Character[c]->AddName(Entry, UNARTICLED); + List.AddEntry(Entry, LIGHT_GRAY, 0, c); + Character[c]->AddAttackInfo(List); + } + + List.SetPageLength(20); + break; + case 2: + List.AddDescription(CONST_S(" DV/BV HP AV/BS")); + + for(c = 0; c < Character.size(); ++c) + { + Entry.Empty(); + Character[c]->AddName(Entry, UNARTICLED); + Entry.Resize(47); + Entry << int(Character[c]->GetDodgeValue()); + Entry.Resize(57); + Entry << Character[c]->GetMaxHP(); + List.AddEntry(Entry, LIGHT_GRAY, 0, c); + Character[c]->AddDefenceInfo(List); + } + + List.SetPageLength(25); + break; + case 3: + List.AddDescription(CONST_S(" Danger NGM EGM")); + + for(c = 0; c < Character.size(); ++c) + { + Entry.Empty(); + Character[c]->AddName(Entry, UNARTICLED); + Entry.Resize(47); + Entry << int(Character[c]->GetRelativeDanger(Char, true) * 1000); + Entry.Resize(57); + const dangerid& DI = game::GetDangerMap().find(configid(Character[c]->GetType(), Character[c]->GetConfig()))->second; + Entry << int(DI.NakedDanger * 1000); + Entry.Resize(67); + Entry << int(DI.EquippedDanger * 1000); + List.AddEntry(Entry, LIGHT_GRAY, 0, c); + } + + List.SetPageLength(15); + break; + } + + List.Draw(); + + for(c = TeamSize; c < Character.size(); ++c) + delete Character[c]; + + game::ClearCharacterDrawVector(); + } + else if(Chosen < 6) + { + itemvectorvector& Item = game::GetItemDrawVector(); + protosystem::CreateEveryItem(Item); + List.SetEntryDrawer(game::ItemEntryDrawer); + List.SetPageLength(20); + + switch(Chosen) + { + case 4: + List.AddDescription(CONST_S(" Weight Size SR DAM")); + + for(c = 0; c < Item.size(); ++c) + { + Entry.Empty(); + Item[c][0]->AddName(Entry, UNARTICLED); + List.AddEntry(Entry, LIGHT_GRAY, 0, c, true); + Item[c][0]->AddAttackInfo(List); + } + + break; + case 5: + List.AddDescription(CONST_S(" \177 OV NV")); + + for(c = 0; c < Item.size(); ++c) + { + Entry.Empty(); + Item[c][0]->AddName(Entry, UNARTICLED); + List.AddEntry(Entry, LIGHT_GRAY, 0, c, true); + Item[c][0]->AddMiscellaneousInfo(List); + } + + break; + } + + List.Draw(); + + for(c = 0; c < Item.size(); ++c) + delete Item[c][0]; + + game::ClearItemDrawVector(); + } + else if(Chosen < 7) + { + std::vector Material; + protosystem::CreateEveryMaterial(Material); + List.SetPageLength(30); + List.AddDescription(CONST_S(" Strength Flexibility Density")); + + for(c = 0; c < Material.size(); ++c) + { + Entry.Empty(); + Material[c]->AddName(Entry, false, false); + Entry.Resize(40); + Entry << Material[c]->GetStrengthValue(); + Entry.Resize(55); + Entry << int(Material[c]->GetFlexibility()); + Entry.Resize(70); + Entry << int(Material[c]->GetDensity()); + List.AddEntry(Entry, Material[c]->GetColor()); + } + + List.Draw(); + + for(c = 0; c < Material.size(); ++c) + delete Material[c]; + } + + List.PrintToFile(game::GetHomeDir() + "secret" + Chosen + ".txt"); + ADD_MESSAGE("Info written also to %ssecret%d.txt.", game::GetHomeDir().CStr(), Chosen); + return false; +} + +truth commandsystem::DetachBodyPart(character* Char) +{ + Char->DetachBodyPart(); + return false; +} + +truth commandsystem::SummonMonster(character* Char) +{ + character* Summoned = 0; + + while(!Summoned) + { + festring Temp = game::DefaultQuestion(CONST_S("Summon which monster?"), + game::GetDefaultSummonMonster()); + Summoned = protosystem::CreateMonster(Temp); + } + + Summoned->SetTeam(game::GetTeam(MONSTER_TEAM)); + Summoned->PutNear(Char->GetPos()); + return false; +} + +truth commandsystem::LevelTeleport(character*) +{ + long Level = game::NumberQuestion(CONST_S("To which level?"), WHITE); + + if(Level <= 0 || Level > game::GetLevels()) + { + ADD_MESSAGE("There is no level %ld in this dungeon, %s!", Level, game::Insult()); + return false; + } + + if(Level == game::GetCurrentLevelIndex() + 1) + { + ADD_MESSAGE("You are already here, %s!", game::Insult()); + return false; + } + + return game::TryTravel(game::GetCurrentDungeonIndex(), Level - 1, RANDOM, true); +} + +truth commandsystem::Possess(character* Char) +{ + int Dir = game::DirectionQuestion(CONST_S("Choose creature to possess. [press a direction key]"), false); + + if(Dir == DIR_ERROR || !Char->GetArea()->IsValidPos(Char->GetPos() + game::GetMoveVector(Dir))) + return false; + + character* ToPossess = Char->GetNearLSquare(Char->GetPos() + game::GetMoveVector(Dir))->GetCharacter(); + + if(ToPossess) + { + Char->RemoveFlags(C_PLAYER); + game::SetPlayer(ToPossess); + } + else + ADD_MESSAGE("There's no one to possess, %s!", game::Insult()); + + return true; // The old player's turn must end +} + +truth commandsystem::Polymorph(character* Char) +{ + character* NewForm; + + if(!Char->GetNewFormForPolymorphWithControl(NewForm)) + return true; + + Char->Polymorph(NewForm, game::NumberQuestion(CONST_S("For how long?"), WHITE)); + return true; +} + +#endif + +truth commandsystem::ToggleRunning(character* Char) +{ + if(game::PlayerIsRunning() + && PLAYER->StateIsActivated(PANIC) + && PLAYER->GetTirednessState() != FAINTING) + { + ADD_MESSAGE("You are too scared to move at normal pace."); + return false; + } + + if(!Char->CanMove()) + { + ADD_MESSAGE("Well, well, aren't we funny today, eh?"); + return false; + } + + game::SetPlayerIsRunning(!game::PlayerIsRunning()); + return false; +} + +truth commandsystem::IssueCommand(character* Char) +{ + if(!Char->CheckTalk()) + return false; + + return game::CommandQuestion(); +} diff --git a/Main/Source/cont.cpp b/Main/Source/cont.cpp new file mode 100644 index 0000000..46bac98 --- /dev/null +++ b/Main/Source/cont.cpp @@ -0,0 +1,110 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through wmapset.cpp */ + +uchar** continent::TypeBuffer; +short** continent::AltitudeBuffer; +uchar** continent::ContinentBuffer; + +continent::continent() { } +continent::continent(int Index) : Index(Index) { } +long continent::GetSize() const { return Member.size(); } +int continent::GetGTerrainAmount(int Type) const { return GTerrainAmount[Type]; } +v2 continent::GetMember(int I) const { return Member[I]; } + +void continent::Save(outputfile& SaveFile) const +{ + SaveFile << Name << Member << Index; +} + +void continent::Load(inputfile& SaveFile) +{ + SaveFile >> Name >> Member >> Index; +} + +void continent::AttachTo(continent* Continent) +{ + for(ulong c = 0; c < Member.size(); ++c) + ContinentBuffer[Member[c].X][Member[c].Y] = Continent->Index; + + if(!Continent->Member.size()) + Continent->Member.swap(Member); + else + { + Continent->Member.insert(Continent->Member.end(), Member.begin(), Member.end()); + Member.clear(); + } +} + +void continent::GenerateInfo() +{ + GTerrainAmount.resize(protocontainer::GetSize() + 1); + + for(ulong c = 0; c < Member.size(); ++c) + ++GTerrainAmount[TypeBuffer[Member[c].X][Member[c].Y]]; + + Name = CONST_S("number "); + Name << Index; +} + +v2 continent::GetRandomMember(int Type) +{ + if(!GTerrainAmount[Type]) + ABORT("Shortage of terrain!"); + + v2* TypeContainer = new v2[Member.size()]; + long Index = 0; + + for(ulong c = 0; c < Member.size(); ++c) + if(TypeBuffer[Member[c].X][Member[c].Y] == Type) + { + TypeContainer[Index++] = Member[c]; + + if(Index == GetGTerrainAmount(Type)) + break; + } + + v2 Return = TypeContainer[RAND() % Index]; + delete [] TypeContainer; + return Return; +} + +outputfile& operator<<(outputfile& SaveFile, const continent* Continent) +{ + if(Continent) + { + SaveFile.Put(1); + Continent->Save(SaveFile); + } + else + SaveFile.Put(0); + + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, continent*& Continent) +{ + if(SaveFile.Get()) + { + Continent = new continent; + Continent->Load(SaveFile); + } + + return SaveFile; +} + +void continent::Add(v2 Pos) +{ + Member.push_back(Pos); + ContinentBuffer[Pos.X][Pos.Y] = Index; +} diff --git a/Main/Source/coreset.cpp b/Main/Source/coreset.cpp new file mode 100644 index 0000000..287783a --- /dev/null +++ b/Main/Source/coreset.cpp @@ -0,0 +1,18 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "entity.h" +#include "pool.h" +#include "v2.h" + +#include "entity.cpp" +#include "pool.cpp" diff --git a/Main/Source/database.cpp b/Main/Source/database.cpp new file mode 100644 index 0000000..50bb01c --- /dev/null +++ b/Main/Source/database.cpp @@ -0,0 +1,820 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through dataset.cpp */ + +int CreateConfigTable(databasebase*** ConfigTable, databasebase*** TempTable, databasebase** ConfigArray, long* TempTableInfo, int Type, int Configs, int TempTables) +{ + memset(ConfigTable, 0, CONFIG_TABLE_SIZE * sizeof(databasebase**)); + + for(int c = 0; c < Configs; ++c) + { + int Config = ConfigArray[c]->Config; + int Hash = Config >> 8 ^ Config & 0xFF; + + if((TempTableInfo[Hash] & 0xFFFF) != Type) + { + TempTable[0][Hash] = ConfigArray[c]; + TempTableInfo[Hash] = Type | 0x10000; + } + else + { + int Conflicts = (TempTableInfo[Hash] & 0xFFFF0000) >> 16; + + if(Conflicts == TempTables) + TempTable[TempTables++] = new databasebase*[CONFIG_TABLE_SIZE]; + + TempTable[Conflicts][Hash] = ConfigArray[c]; + TempTableInfo[Hash] += 0x10000; + } + } + + for(int c1 = 0; c1 < CONFIG_TABLE_SIZE; ++c1) + if((TempTableInfo[c1] & 0xFFFF) == Type) + { + int Entries = (TempTableInfo[c1] & 0xFFFF0000) >> 16; + ConfigTable[c1] = new databasebase*[Entries + 1]; + + for(int c2 = 0; c2 < Entries; ++c2) + ConfigTable[c1][c2] = TempTable[c2][c1]; + + ConfigTable[c1][Entries] = 0; + } + + return TempTables; +} + +template void databasecreator::ReadFrom(inputfile& SaveFile) +{ + typedef typename type::prototype prototype; + festring Word; + database* TempConfig[1024]; + databasebase** TempTable[1024]; + long TempTableInfo[CONFIG_TABLE_SIZE]; + int TempTables = 1; + databasebase* FirstTempTable[CONFIG_TABLE_SIZE]; + TempTable[0] = FirstTempTable; + memset(TempTableInfo, 0, CONFIG_TABLE_SIZE * sizeof(long)); + CreateDataBaseMemberMap(); + + for(SaveFile.ReadWord(Word, false); !SaveFile.Eof(); SaveFile.ReadWord(Word, false)) + { + int Type = protocontainer::SearchCodeName(Word); + + if(!Type) + ABORT("Odd term %s encountered in %s datafile line %ld!", Word.CStr(), protocontainer::GetMainClassID(), SaveFile.TellLine()); + + prototype* Proto = protocontainer::GetProtoData()[Type]; + database* DataBase = Proto->Base ? new database(**Proto->Base->ConfigData) : new database; + DataBase->InitDefaults(Proto, 0); + TempConfig[0] = DataBase; + int Configs = 1; + + if(SaveFile.ReadWord() != "{") + ABORT("Bracket missing in %s datafile line %ld!", protocontainer::GetMainClassID(), SaveFile.TellLine()); + + for(SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) + { + if(Word == "Config") + { + int ConfigNumber = SaveFile.ReadNumber(); + database* ConfigDataBase = new database(*Proto->ChooseBaseForConfig(TempConfig, Configs, ConfigNumber)); + ConfigDataBase->InitDefaults(Proto, ConfigNumber); + TempConfig[Configs++] = ConfigDataBase; + + if(SaveFile.ReadWord() != "{") + ABORT("Bracket missing in %s datafile line %ld!", protocontainer::GetMainClassID(), SaveFile.TellLine()); + + for(SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) + if(!AnalyzeData(SaveFile, Word, *ConfigDataBase)) + ABORT("Illegal datavalue %s found while building up %s config #%d, line %ld!", Word.CStr(), Proto->GetClassID(), ConfigNumber, SaveFile.TellLine()); + + ConfigDataBase->PostProcess(); + continue; + } + + if(!AnalyzeData(SaveFile, Word, *DataBase)) + ABORT("Illegal datavalue %s found while building up %s, line %ld!", Word.CStr(), Proto->GetClassID(), SaveFile.TellLine()); + } + + DataBase->PostProcess(); + //Configs = Proto->CreateSpecialConfigurations(TempConfig, Configs); + Proto->ConfigData = new database*[Configs]; + Proto->ConfigSize = Configs; + memcpy(Proto->ConfigData, TempConfig, Configs * sizeof(database*)); + } + + int c1; + + for(c1 = 0; c1 < SPECIAL_CONFIGURATION_GENERATION_LEVELS; ++c1) + for(int c2 = 1; c2 < protocontainer::GetSize(); ++c2) + { + prototype* Proto = protocontainer::GetProtoData()[c2]; + int Configs = Proto->ConfigSize; + memcpy(TempConfig, Proto->ConfigData, Configs * sizeof(database*)); + Configs = Proto->CreateSpecialConfigurations(TempConfig, Configs, c1); + + if(Proto->ConfigSize != Configs) + { + delete [] Proto->ConfigData; + Proto->ConfigData = new database*[Configs]; + Proto->ConfigSize = Configs; + memcpy(Proto->ConfigData, TempConfig, Configs * sizeof(database*)); + } + } + + for(c1 = 1; c1 < protocontainer::GetSize(); ++c1) + { + prototype* Proto = protocontainer::GetProtoData()[c1]; + + TempTables = + CreateConfigTable(reinterpret_cast(Proto->ConfigTable), + TempTable, + reinterpret_cast(Proto->ConfigData), + TempTableInfo, c1, Proto->ConfigSize, TempTables); + } + + for(c1 = 1; c1 < TempTables; ++c1) + delete [] TempTable[c1]; + + GetDataBaseMemberMap().clear(); +} + +template int databasecreator::CreateDivineConfigurations(const prototype* Proto, database** TempConfig, int Configs) +{ + int OldConfigs = Configs; + + for(int c1 = 1; c1 < protocontainer::GetSize(); ++c1) + { + database* ConfigDataBase = 0; + int c2; + + for(c2 = 1; c2 < OldConfigs; ++c2) + { + ConfigDataBase = TempConfig[c2]; + + if(ConfigDataBase->Config == c1) + break; + } + + truth Created = false; + + if(c2 == OldConfigs) + { + ConfigDataBase = new database(**TempConfig); + ConfigDataBase->InitDefaults(Proto, c1); + Created = true; + } + + ConfigDataBase->AttachedGod = c1; + ConfigDataBase->PostFix << "of " << festring(protocontainer::GetProto(c1)->GetClassID()).CapitalizeCopy(); + + if(Created) + TempConfig[Configs++] = ConfigDataBase; + } + + return Configs; +} + +template int databasecreator::CreateDivineConfigurations(const prototype*, database**, int); +template int databasecreator::CreateDivineConfigurations(const prototype*, database**, int); +template int databasecreator::CreateDivineConfigurations(const prototype*, database**, int); + +template struct databasemember : public databasememberbase +{ + databasemember(member Member) : Member(Member) { } + virtual void ReadData(database& DataBase, inputfile& SaveFile) { ::ReadData(DataBase.*Member, SaveFile); } + member Member; +}; + +template void AddMember(std::map*>& Map, cchar* Str, member Member) +{ + Map.insert(std::pair*>(Str, new databasemember(Member))); +} + +/* Explicit instantiations seem to increase compile speed greatly here... */ + +#define INST_ADD_MEMBER(type, member)\ +template void AddMember(std::map*>&, cchar*, member type##database::*) + +INST_ADD_MEMBER(character, int); +INST_ADD_MEMBER(character, long); +INST_ADD_MEMBER(character, packcol16); +INST_ADD_MEMBER(character, col24); +INST_ADD_MEMBER(character, packv2); +INST_ADD_MEMBER(character, festring); +INST_ADD_MEMBER(character, fearray); +INST_ADD_MEMBER(character, fearray); +INST_ADD_MEMBER(character, contentscript); + +INST_ADD_MEMBER(item, int); +INST_ADD_MEMBER(item, long); +INST_ADD_MEMBER(item, col24); +INST_ADD_MEMBER(item, v2); +INST_ADD_MEMBER(item, festring); +INST_ADD_MEMBER(item, fearray); +INST_ADD_MEMBER(item, fearray); + +INST_ADD_MEMBER(glterrain, int); +INST_ADD_MEMBER(glterrain, long); +INST_ADD_MEMBER(glterrain, v2); +INST_ADD_MEMBER(glterrain, festring); +INST_ADD_MEMBER(glterrain, fearray); + +INST_ADD_MEMBER(olterrain, int); +INST_ADD_MEMBER(olterrain, long); +INST_ADD_MEMBER(olterrain, v2); +INST_ADD_MEMBER(olterrain, festring); +INST_ADD_MEMBER(olterrain, fearray); +INST_ADD_MEMBER(olterrain, fearray >); + +INST_ADD_MEMBER(material, int); +INST_ADD_MEMBER(material, long); +INST_ADD_MEMBER(material, col24); +INST_ADD_MEMBER(material, festring); +INST_ADD_MEMBER(material, contentscript); + +#define ADD_MEMBER(data) AddMember(Map, #data, &database::data); + +template<> void databasecreator::CreateDataBaseMemberMap() +{ + databasemembermap& Map = GetDataBaseMemberMap(); + ADD_MEMBER(DefaultArmStrength); + ADD_MEMBER(DefaultLegStrength); + ADD_MEMBER(DefaultDexterity); + ADD_MEMBER(DefaultAgility); + ADD_MEMBER(DefaultEndurance); + ADD_MEMBER(DefaultPerception); + ADD_MEMBER(DefaultIntelligence); + ADD_MEMBER(DefaultWisdom); + ADD_MEMBER(DefaultWillPower); + ADD_MEMBER(DefaultCharisma); + ADD_MEMBER(DefaultMana); + ADD_MEMBER(DefaultMoney); + ADD_MEMBER(TotalSize); + ADD_MEMBER(CanRead); + ADD_MEMBER(Sex); + ADD_MEMBER(CanBeGenerated); + ADD_MEMBER(CriticalModifier); + ADD_MEMBER(StandVerb); + ADD_MEMBER(CanOpen); + ADD_MEMBER(Frequency); + ADD_MEMBER(EnergyResistance); + ADD_MEMBER(FireResistance); + ADD_MEMBER(PoisonResistance); + ADD_MEMBER(ElectricityResistance); + ADD_MEMBER(AcidResistance); + ADD_MEMBER(IsUnique); + ADD_MEMBER(ConsumeFlags); + ADD_MEMBER(TotalVolume); + ADD_MEMBER(HeadBitmapPos); + ADD_MEMBER(TorsoBitmapPos); + ADD_MEMBER(ArmBitmapPos); + ADD_MEMBER(LegBitmapPos); + ADD_MEMBER(RightArmBitmapPos); + ADD_MEMBER(LeftArmBitmapPos); + ADD_MEMBER(RightLegBitmapPos); + ADD_MEMBER(LeftLegBitmapPos); + ADD_MEMBER(GroinBitmapPos); + ADD_MEMBER(ClothColor); + ADD_MEMBER(SkinColor); + ADD_MEMBER(CapColor); + ADD_MEMBER(HairColor); + ADD_MEMBER(EyeColor); + ADD_MEMBER(TorsoMainColor); + ADD_MEMBER(BeltColor); + ADD_MEMBER(BootColor); + ADD_MEMBER(TorsoSpecialColor); + ADD_MEMBER(ArmMainColor); + ADD_MEMBER(GauntletColor); + ADD_MEMBER(ArmSpecialColor); + ADD_MEMBER(LegMainColor); + ADD_MEMBER(LegSpecialColor); + ADD_MEMBER(IsNameable); + ADD_MEMBER(BaseEmitation); + ADD_MEMBER(UsesLongArticle); + ADD_MEMBER(Adjective); + ADD_MEMBER(UsesLongAdjectiveArticle); + ADD_MEMBER(NameSingular); + ADD_MEMBER(NamePlural); + ADD_MEMBER(PostFix); + ADD_MEMBER(ArticleMode); + ADD_MEMBER(IsAbstract); + ADD_MEMBER(IsPolymorphable); + ADD_MEMBER(BaseUnarmedStrength); + ADD_MEMBER(BaseBiteStrength); + ADD_MEMBER(BaseKickStrength); + ADD_MEMBER(AttackStyle); + ADD_MEMBER(CanUseEquipment); + ADD_MEMBER(CanKick); + ADD_MEMBER(CanTalk); + ADD_MEMBER(ClassStates); + ADD_MEMBER(CanBeWished); + ADD_MEMBER(Alias); + ADD_MEMBER(CreateDivineConfigurations); + ADD_MEMBER(CreateGolemMaterialConfigurations); + ADD_MEMBER(Helmet); + ADD_MEMBER(Amulet); + ADD_MEMBER(Cloak); + ADD_MEMBER(BodyArmor); + ADD_MEMBER(Belt); + ADD_MEMBER(RightWielded); + ADD_MEMBER(LeftWielded); + ADD_MEMBER(RightRing); + ADD_MEMBER(LeftRing); + ADD_MEMBER(RightGauntlet); + ADD_MEMBER(LeftGauntlet); + ADD_MEMBER(RightBoot); + ADD_MEMBER(LeftBoot); + ADD_MEMBER(AttributeBonus); + ADD_MEMBER(KnownCWeaponSkills); + ADD_MEMBER(CWeaponSkillHits); + ADD_MEMBER(RightSWeaponSkillHits); + ADD_MEMBER(LeftSWeaponSkillHits); + ADD_MEMBER(PanicLevel); + ADD_MEMBER(CanBeCloned); + ADD_MEMBER(Inventory); + ADD_MEMBER(DangerModifier); + ADD_MEMBER(DefaultName); + ADD_MEMBER(FriendlyReplies); + ADD_MEMBER(HostileReplies); + ADD_MEMBER(CanZap); + ADD_MEMBER(FleshMaterial); + ADD_MEMBER(HasALeg); + ADD_MEMBER(DeathMessage); + ADD_MEMBER(IgnoreDanger); + ADD_MEMBER(HPRequirementForGeneration); + ADD_MEMBER(DayRequirementForGeneration); + ADD_MEMBER(IsExtraCoward); + ADD_MEMBER(SpillsBlood); + ADD_MEMBER(HasEyes); + ADD_MEMBER(HasHead); + ADD_MEMBER(CanThrow); + ADD_MEMBER(UsesNutrition); + ADD_MEMBER(AttackWisdomLimit); + ADD_MEMBER(AttachedGod); + ADD_MEMBER(BodyPartsDisappearWhenSevered); + ADD_MEMBER(CanBeConfused); + ADD_MEMBER(CanApply); + ADD_MEMBER(WieldedPosition); + ADD_MEMBER(NaturalSparkleFlags); + ADD_MEMBER(BiteCapturesBodyPart); + ADD_MEMBER(IsPlant); + ADD_MEMBER(MoveType); + ADD_MEMBER(DestroysWalls); + ADD_MEMBER(IsRooted); + ADD_MEMBER(BloodMaterial); + ADD_MEMBER(VomitMaterial); + ADD_MEMBER(HasSecondaryMaterial); + ADD_MEMBER(IsImmuneToLeprosy); + ADD_MEMBER(PolymorphIntelligenceRequirement); + ADD_MEMBER(AutomaticallySeen); + ADD_MEMBER(CanHear); + ADD_MEMBER(DefaultCommandFlags); + ADD_MEMBER(ConstantCommandFlags); + ADD_MEMBER(WillCarryItems); + ADD_MEMBER(ForceVomitMessage); + ADD_MEMBER(DefaultSweatMaterial); + ADD_MEMBER(Sweats); + ADD_MEMBER(IsImmuneToItemTeleport); + ADD_MEMBER(AlwaysUseMaterialAttributes); + ADD_MEMBER(IsEnormous); + ADD_MEMBER(ScienceTalkAdjectiveAttribute); + ADD_MEMBER(ScienceTalkSubstantiveAttribute); + ADD_MEMBER(ScienceTalkPrefix); + ADD_MEMBER(ScienceTalkName); + ADD_MEMBER(ScienceTalkPossibility); + ADD_MEMBER(ScienceTalkIntelligenceModifier); + ADD_MEMBER(ScienceTalkWisdomModifier); + ADD_MEMBER(ScienceTalkCharismaModifier); + ADD_MEMBER(ScienceTalkIntelligenceRequirement); + ADD_MEMBER(ScienceTalkWisdomRequirement); + ADD_MEMBER(ScienceTalkCharismaRequirement); + ADD_MEMBER(IsExtraFragile); + ADD_MEMBER(AllowUnconsciousness); + ADD_MEMBER(CanChoke); + ADD_MEMBER(IsImmuneToStickiness); + ADD_MEMBER(DisplacePriority); + ADD_MEMBER(RunDescriptionLineOne); + ADD_MEMBER(RunDescriptionLineTwo); + ADD_MEMBER(ForceCustomStandVerb); + ADD_MEMBER(VomittingIsUnhealthy); + ADD_MEMBER(AllowPlayerToChangeEquipment); + ADD_MEMBER(TamingDifficulty); + ADD_MEMBER(IsMasochist); + ADD_MEMBER(IsSadist); + ADD_MEMBER(IsCatacombCreature); + ADD_MEMBER(CreateUndeadConfigurations); + ADD_MEMBER(UndeadVersions); + ADD_MEMBER(UndeadAttributeModifier); + ADD_MEMBER(UndeadVolumeModifier); + ADD_MEMBER(UndeadCopyMaterials); + ADD_MEMBER(CanBeGeneratedOnlyInTheCatacombs); + ADD_MEMBER(IsAlcoholic); + ADD_MEMBER(IsImmuneToWhipOfThievery); + ADD_MEMBER(IsRangedAttacker); + ADD_MEMBER(WhatCategoryToThrow); + ADD_MEMBER(WhatWeaponConfigToThrow); + ADD_MEMBER(WhatThrowItemTypesToThrow); +} + +template<> void databasecreator::CreateDataBaseMemberMap() +{ + databasemembermap& Map = GetDataBaseMemberMap(); + ADD_MEMBER(Possibility); + ADD_MEMBER(IsDestroyable); + ADD_MEMBER(CanBeWished); + ADD_MEMBER(IsMaterialChangeable); + ADD_MEMBER(WeaponCategory); + ADD_MEMBER(IsPolymorphSpawnable); + ADD_MEMBER(IsAutoInitializable); + ADD_MEMBER(Category); + ADD_MEMBER(FireResistance); + ADD_MEMBER(PoisonResistance); + ADD_MEMBER(ElectricityResistance); + ADD_MEMBER(AcidResistance); + ADD_MEMBER(StrengthModifier); + ADD_MEMBER(FormModifier); + ADD_MEMBER(DefaultSize); + ADD_MEMBER(DefaultMainVolume); + ADD_MEMBER(DefaultSecondaryVolume); + ADD_MEMBER(BitmapPos); + ADD_MEMBER(Price); + ADD_MEMBER(BaseEmitation); + ADD_MEMBER(UsesLongArticle); + ADD_MEMBER(Adjective); + ADD_MEMBER(UsesLongAdjectiveArticle); + ADD_MEMBER(NameSingular); + ADD_MEMBER(NamePlural); + ADD_MEMBER(PostFix); + ADD_MEMBER(ArticleMode); + ADD_MEMBER(MainMaterialConfig); + ADD_MEMBER(SecondaryMaterialConfig); + ADD_MEMBER(MaterialConfigChances); + ADD_MEMBER(IsAbstract); + ADD_MEMBER(IsPolymorphable); + ADD_MEMBER(Alias); + ADD_MEMBER(OKVisualEffects); + ADD_MEMBER(CanBeGeneratedInContainer); + ADD_MEMBER(ForcedVisualEffects); + ADD_MEMBER(Roundness); + ADD_MEMBER(GearStates); + ADD_MEMBER(IsTwoHanded); + ADD_MEMBER(CreateDivineConfigurations); + ADD_MEMBER(CanBeCloned); + ADD_MEMBER(CanBeMirrored); + ADD_MEMBER(BeamRange); + ADD_MEMBER(CanBeBroken); + ADD_MEMBER(WallBitmapPos); + ADD_MEMBER(FlexibleNameSingular); + ADD_MEMBER(MaxCharges); + ADD_MEMBER(MinCharges); + ADD_MEMBER(CanBePiled); + ADD_MEMBER(StorageVolume); + ADD_MEMBER(MaxGeneratedContainedItems); + ADD_MEMBER(AffectsArmStrength); + ADD_MEMBER(AffectsLegStrength); + ADD_MEMBER(AffectsDexterity); + ADD_MEMBER(AffectsAgility); + ADD_MEMBER(AffectsEndurance); + ADD_MEMBER(AffectsPerception); + ADD_MEMBER(AffectsIntelligence); + ADD_MEMBER(AffectsWisdom); + ADD_MEMBER(AffectsWillPower); + ADD_MEMBER(AffectsCharisma); + ADD_MEMBER(AffectsMana); + ADD_MEMBER(BaseEnchantment); + ADD_MEMBER(PriceIsProportionalToEnchantment); + ADD_MEMBER(InElasticityPenaltyModifier); + ADD_MEMBER(CanBeUsedBySmith); + ADD_MEMBER(AffectsCarryingCapacity); + ADD_MEMBER(DamageDivider); + ADD_MEMBER(HandleInPairs); + ADD_MEMBER(CanBeEnchanted); + ADD_MEMBER(BeamColor); + ADD_MEMBER(BeamEffect); + ADD_MEMBER(BeamStyle); + ADD_MEMBER(WearWisdomLimit); + ADD_MEMBER(AttachedGod); + ADD_MEMBER(BreakEffectRangeSquare); + ADD_MEMBER(WieldedBitmapPos); + ADD_MEMBER(IsQuestItem); + ADD_MEMBER(IsGoodWithPlants); + ADD_MEMBER(CreateLockConfigurations); + ADD_MEMBER(CanBePickedUp); + ADD_MEMBER(CoverPercentile); + ADD_MEMBER(TorsoArmorBitmapPos); + ADD_MEMBER(ArmArmorBitmapPos); + ADD_MEMBER(AthleteArmArmorBitmapPos); + ADD_MEMBER(LegArmorBitmapPos); + ADD_MEMBER(HelmetBitmapPos); + ADD_MEMBER(CloakBitmapPos); + ADD_MEMBER(BeltBitmapPos); + ADD_MEMBER(GauntletBitmapPos); + ADD_MEMBER(BootBitmapPos); + ADD_MEMBER(HasSecondaryMaterial); + ADD_MEMBER(AllowEquip); + ADD_MEMBER(ReadDifficulty); + ADD_MEMBER(IsValuable); + ADD_MEMBER(EnchantmentMinusChance); + ADD_MEMBER(EnchantmentPlusChance); + ADD_MEMBER(TeleportPriority); + ADD_MEMBER(HasNormalPictureDirection); + ADD_MEMBER(DamageFlags); + ADD_MEMBER(IsKamikazeWeapon); + ADD_MEMBER(FlexibilityIsEssential); + ADD_MEMBER(BreakMsg); + ADD_MEMBER(IsSadistWeapon); + ADD_MEMBER(IsThrowingWeapon); + ADD_MEMBER(ThrowItemTypes); +} + +template +void databasecreator::CreateLTerrainDataBaseMemberMap() +{ + databasemembermap& Map = GetDataBaseMemberMap(); + ADD_MEMBER(BitmapPos); + ADD_MEMBER(UsesLongArticle); + ADD_MEMBER(Adjective); + ADD_MEMBER(UsesLongAdjectiveArticle); + ADD_MEMBER(NameSingular); + ADD_MEMBER(NamePlural); + ADD_MEMBER(PostFix); + ADD_MEMBER(ArticleMode); + ADD_MEMBER(MainMaterialConfig); + ADD_MEMBER(SecondaryMaterialConfig); + ADD_MEMBER(MaterialConfigChances); + ADD_MEMBER(IsAbstract); + ADD_MEMBER(OKVisualEffects); + ADD_MEMBER(MaterialColorB); + ADD_MEMBER(MaterialColorC); + ADD_MEMBER(MaterialColorD); + ADD_MEMBER(SitMessage); + ADD_MEMBER(ShowMaterial); + ADD_MEMBER(AttachedGod); + ADD_MEMBER(Walkability); + ADD_MEMBER(HasSecondaryMaterial); + ADD_MEMBER(UseBorderTiles); + ADD_MEMBER(BorderTilePriority); +} + +template<> void databasecreator::CreateDataBaseMemberMap() +{ + CreateLTerrainDataBaseMemberMap(); +} + +template<> void databasecreator::CreateDataBaseMemberMap() +{ + CreateLTerrainDataBaseMemberMap(); + databasemembermap& Map = GetDataBaseMemberMap(); + ADD_MEMBER(CreateDivineConfigurations); + ADD_MEMBER(DigMessage); + ADD_MEMBER(CanBeDestroyed); + ADD_MEMBER(RestModifier); + ADD_MEMBER(RestMessage); + ADD_MEMBER(IsUpLink); + ADD_MEMBER(StorageVolume); + ADD_MEMBER(HPModifier); + ADD_MEMBER(IsSafeToCreateDoor); + ADD_MEMBER(OpenBitmapPos); + ADD_MEMBER(CreateLockConfigurations); + ADD_MEMBER(IsAlwaysTransparent); + ADD_MEMBER(CreateWindowConfigurations); + ADD_MEMBER(WindowBitmapPos); + ADD_MEMBER(ShowThingsUnder); + ADD_MEMBER(LeftOverItems); + ADD_MEMBER(IsWall); +} + +template<> void databasecreator::CreateDataBaseMemberMap() +{ + databasemembermap& Map = GetDataBaseMemberMap(); + ADD_MEMBER(CommonFlags); + ADD_MEMBER(NameFlags); + ADD_MEMBER(CategoryFlags); + ADD_MEMBER(BodyFlags); + ADD_MEMBER(InteractionFlags); + ADD_MEMBER(StrengthValue); + ADD_MEMBER(ConsumeType); + ADD_MEMBER(Density); + ADD_MEMBER(Color); + ADD_MEMBER(RainColor); + ADD_MEMBER(PriceModifier); + ADD_MEMBER(Emitation); + ADD_MEMBER(NutritionValue); + ADD_MEMBER(NameStem); + ADD_MEMBER(AdjectiveStem); + ADD_MEMBER(Effect); + ADD_MEMBER(ConsumeEndMessage); + ADD_MEMBER(HitMessage); + ADD_MEMBER(ExplosivePower); + ADD_MEMBER(Alpha); + ADD_MEMBER(Flexibility); + ADD_MEMBER(SpoilModifier); + ADD_MEMBER(EffectStrength); + ADD_MEMBER(DigProductMaterial); + ADD_MEMBER(ConsumeWisdomLimit); + ADD_MEMBER(AttachedGod); + ADD_MEMBER(BreatheMessage); + ADD_MEMBER(StepInWisdomLimit); + ADD_MEMBER(RustModifier); + ADD_MEMBER(Acidicity); + ADD_MEMBER(NaturalForm); + ADD_MEMBER(HardenedMaterial); + ADD_MEMBER(SoftenedMaterial); + ADD_MEMBER(IntelligenceRequirement); + ADD_MEMBER(Stickiness); + ADD_MEMBER(DisablesPanicWhenConsumed); +} + +#define ADD_BASE_VALUE(name)\ +if(Word == #name)\ + game::GetGlobalValueMap()[CONST_S("Base")]\ + = DataBase.*static_cast*>(Data)->Member; + +template +void databasecreator::SetBaseValue(cfestring&, databasememberbase*, database&) { } + +template<> void databasecreator::SetBaseValue(cfestring& Word, databasememberbase* Data, materialdatabase& DataBase) +{ + ADD_BASE_VALUE(CommonFlags); + ADD_BASE_VALUE(NameFlags); + ADD_BASE_VALUE(CategoryFlags); + ADD_BASE_VALUE(BodyFlags); + ADD_BASE_VALUE(InteractionFlags); +} + +template +truth databasecreator::AnalyzeData(inputfile& SaveFile, cfestring& Word, database& DataBase) +{ + typename databasemembermap::iterator i = GetDataBaseMemberMap().find(Word); + + if(i != GetDataBaseMemberMap().end()) + { + SetBaseValue(Word, i->second, DataBase); + i->second->ReadData(DataBase, SaveFile); + CheckDefaults(Word, DataBase); + return true; + } + else + return false; +} + +template<> void databasecreator::CheckDefaults(cfestring& Word, character::database& DataBase) +{ + if(Word == "ArmBitmapPos") + DataBase.RightArmBitmapPos = + DataBase.LeftArmBitmapPos = + DataBase.ArmBitmapPos; + else if(Word == "LegBitmapPos") + DataBase.GroinBitmapPos = + DataBase.RightLegBitmapPos = + DataBase.LeftLegBitmapPos = + DataBase.LegBitmapPos; + else if(Word == "ClothColor") + DataBase.CapColor = + DataBase.TorsoMainColor = + DataBase.ArmMainColor = + DataBase.GauntletColor = + DataBase.LegMainColor = + DataBase.ClothColor; + else if(Word == "NameSingular") + DataBase.NamePlural = DataBase.NameSingular + 's'; + else if(Word == "BaseUnarmedStrength") + { + DataBase.BaseBiteStrength = DataBase.BaseUnarmedStrength >> 1; + DataBase.BaseKickStrength = DataBase.BaseUnarmedStrength << 1; + } + else if(Word == "RightGauntlet") + DataBase.LeftGauntlet = DataBase.RightGauntlet; + else if(Word == "RightBoot") + DataBase.LeftBoot = DataBase.RightBoot; + else if(Word == "DefaultName") + DataBase.Alias.Add(DataBase.DefaultName); + else if(Word == "IsUnique") + DataBase.CanBeWished = !DataBase.IsUnique; +} + +template<> void databasecreator::CheckDefaults(cfestring& Word, item::database& DataBase) +{ + if(Word == "NameSingular") + { + DataBase.NamePlural = DataBase.NameSingular + 's'; + DataBase.FlexibleNameSingular = DataBase.NameSingular; + } + else if(Word == "BitmapPos") + DataBase.WallBitmapPos = DataBase.BitmapPos; + else if(Word == "MaterialConfigChances") + DataBase.MaterialConfigChanceSum = femath::SumArray(DataBase.MaterialConfigChances); + else if(Word == "CanBeCloned") + DataBase.CanBeMirrored = DataBase.CanBeCloned; +} + +template<> void databasecreator::CheckDefaults(cfestring& Word, glterrain::database& DataBase) +{ + if(Word == "NameSingular") + DataBase.NamePlural = DataBase.NameSingular + 's'; + else if(Word == "MaterialConfigChances") + DataBase.MaterialConfigChanceSum = femath::SumArray(DataBase.MaterialConfigChances); +} + +template<> void databasecreator::CheckDefaults(cfestring& Word, olterrain::database& DataBase) +{ + if(Word == "NameSingular") + DataBase.NamePlural = DataBase.NameSingular + 's'; + else if(Word == "MaterialConfigChances") + DataBase.MaterialConfigChanceSum = femath::SumArray(DataBase.MaterialConfigChances); +} + +template<> void databasecreator::CheckDefaults(cfestring& Word, material::database& DataBase) +{ + if(Word == "NameStem") + DataBase.AdjectiveStem = DataBase.NameStem; + else if(Word == "Color") + DataBase.RainColor = DataBase.Color; +} + +void databasesystem::Initialize() +{ + { + /* Must be before character */ + + inputfile ScriptFile(game::GetGameDir() + "Script/material.dat", &game::GetGlobalValueMap()); + databasecreator::ReadFrom(ScriptFile); + } + + { + inputfile ScriptFile(game::GetGameDir() + "Script/char.dat", &game::GetGlobalValueMap()); + databasecreator::ReadFrom(ScriptFile); + } + + { + /* Must be before olterrain */ + + inputfile ScriptFile(game::GetGameDir() + "Script/item.dat", &game::GetGlobalValueMap()); + databasecreator::ReadFrom(ScriptFile); + } + + { + inputfile ScriptFile(game::GetGameDir() + "Script/glterra.dat", &game::GetGlobalValueMap()); + databasecreator::ReadFrom(ScriptFile); + } + + { + inputfile ScriptFile(game::GetGameDir() + "Script/olterra.dat", &game::GetGlobalValueMap()); + databasecreator::ReadFrom(ScriptFile); + } +} + +template inline void databasecreator::FindDataBase(const database*& DataBase, const prototype* Proto, int Config) +{ + database** Table = Proto->ConfigTable[Config >> 8 ^ Config & 0xFF]; + + if(Table) + { + if((*Table)->Config == Config) + { + DataBase = *Table; + return; + } + else + for(++Table; *Table; ++Table) + if((*Table)->Config == Config) + { + DataBase = *Table; + return; + } + } + + DataBase = 0; +} + +template void databasecreator::FindDataBase(const database*&, const prototype*, int); +template void databasecreator::FindDataBase(const database*&, const prototype*, int); + +template void databasecreator::InstallDataBase(type* Instance, int Config) +{ + const prototype* Proto = Instance->FindProtoType(); + FindDataBase(Instance->DataBase, Proto, Config); + + if(!Instance->DataBase) + ABORT("Undefined %s configuration #%d sought!", Proto->GetClassID(), Config); +} + +#define INST_INSTALL_DATABASE(type)\ +template void databasecreator::InstallDataBase(type*, int) + +INST_INSTALL_DATABASE(material); +INST_INSTALL_DATABASE(character); +INST_INSTALL_DATABASE(item); +INST_INSTALL_DATABASE(glterrain); +INST_INSTALL_DATABASE(olterrain); diff --git a/Main/Source/dataset.cpp b/Main/Source/dataset.cpp new file mode 100644 index 0000000..a5a3919 --- /dev/null +++ b/Main/Source/dataset.cpp @@ -0,0 +1,23 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "char.h" +#include "proto.h" +#include "game.h" +#include "materia.h" +#include "message.h" +#include "database.h" +#include "save.h" +#include "god.h" + +#include "proto.cpp" +#include "database.cpp" diff --git a/Main/Source/dungeon.cpp b/Main/Source/dungeon.cpp new file mode 100644 index 0000000..eea0e6f --- /dev/null +++ b/Main/Source/dungeon.cpp @@ -0,0 +1,240 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "dungeon.h" +#include "level.h" +#include "script.h" +#include "error.h" +#include "game.h" +#include "save.h" +#include "femath.h" +#include "bitmap.h" + +dungeon::dungeon() { } + +dungeon::dungeon(int Index) : Index(Index) +{ + Initialize(); + + for(int c = 0; c < GetLevels(); ++c) + Generated[c] = false; +} + +dungeon::~dungeon() +{ + for(int c = 0; c < GetLevels(); ++c) + delete Level[c]; + + delete [] Level; + delete [] Generated; +} + +void dungeon::Initialize() +{ + std::map::const_iterator DungeonIterator = game::GetGameScript()->GetDungeon().find(Index); + + if(DungeonIterator != game::GetGameScript()->GetDungeon().end()) + DungeonScript = &DungeonIterator->second; + else + { + ABORT("Unknown dungeon #%d requested!", int(Index)); + return; + } + + Level = new level*[GetLevels()]; + Generated = new truth[GetLevels()]; + + for(int c = 0; c < GetLevels(); ++c) + Level[c] = 0; +} + +const levelscript* dungeon::GetLevelScript(int I) +{ + std::map::const_iterator LevelIterator = DungeonScript->GetLevel().find(I); + + const levelscript* LevelScript; + + if(LevelIterator != DungeonScript->GetLevel().end()) + LevelScript = &LevelIterator->second; + else + LevelScript = DungeonScript->GetLevelDefault(); + + return LevelScript; +} + +/* Returns whether the level has been visited before */ + +truth dungeon::PrepareLevel(int Index, truth Visual) +{ + if(Generated[Index]) + { + level* NewLevel = LoadLevel(game::SaveName(), Index); + game::SetCurrentArea(NewLevel); + game::SetCurrentLevel(NewLevel); + game::SetCurrentLSquareMap(NewLevel->GetMap()); + return true; + } + else + { + level* NewLevel = Level[Index] = new level; + NewLevel->SetDungeon(this); + NewLevel->SetIndex(Index); + const levelscript* LevelScript = GetLevelScript(Index); + NewLevel->SetLevelScript(LevelScript); + + if(Visual) + { + if(LevelScript->GetEnterImage()) + { + cbitmap* EnterImage = new bitmap(game::GetGameDir() + "Graphics/" + *LevelScript->GetEnterImage()); + game::SetEnterImage(EnterImage); + v2 Displacement = *LevelScript->GetEnterTextDisplacement(); + game::SetEnterTextDisplacement(Displacement); + game::TextScreen(CONST_S("Entering ") + GetLevelDescription(Index) + CONST_S("...\n\nThis may take some time, please wait."), Displacement, WHITE, false, true, &game::BusyAnimation); + game::TextScreen(CONST_S("Entering ") + GetLevelDescription(Index) + CONST_S("...\n\nPress any key to continue."), Displacement, WHITE, true, false, &game::BusyAnimation); + game::SetEnterImage(0); + delete EnterImage; + } + else + game::TextScreen(CONST_S("Entering ") + GetLevelDescription(Index) + CONST_S("...\n\nThis may take some time, please wait."), ZERO_V2, WHITE, false, true, &game::BusyAnimation); + } + + NewLevel->Generate(Index); + game::SetCurrentLSquareMap(NewLevel->GetMap()); + Generated[Index] = true; + game::BusyAnimation(); + + if(*NewLevel->GetLevelScript()->GenerateMonsters()) + NewLevel->GenerateNewMonsters(NewLevel->GetIdealPopulation(), false); + + return false; + } +} + +void dungeon::SaveLevel(cfestring& SaveName, int Number, truth DeleteAfterwards) +{ + outputfile SaveFile(SaveName + '.' + Index + Number); + SaveFile << Level[Number]; + + if(DeleteAfterwards) + { + delete Level[Number]; + Level[Number] = 0; + } +} + +level* dungeon::LoadLevel(cfestring& SaveName, int Number) +{ + inputfile SaveFile(SaveName + '.' + Index + Number); + return LoadLevel(SaveFile, Number); +} + +void dungeon::Save(outputfile& SaveFile) const +{ + SaveFile << Index << WorldMapPos; + + for(int c = 0; c < GetLevels(); ++c) + SaveFile << Generated[c]; +} + +void dungeon::Load(inputfile& SaveFile) +{ + SaveFile >> Index >> WorldMapPos; + Initialize(); + + for(int c = 0; c < GetLevels(); ++c) + SaveFile >> Generated[c]; +} + +int dungeon::GetLevels() const +{ + return *DungeonScript->GetLevels(); +} + +festring dungeon::GetLevelDescription(int I) +{ + if(GetLevel(I)->GetLevelScript()->GetDescription()) + return *GetLevel(I)->GetLevelScript()->GetDescription(); + else + return *DungeonScript->GetDescription() + " level " + (I + 1); +} + +festring dungeon::GetShortLevelDescription(int I) +{ + if(GetLevel(I)->GetLevelScript()->GetShortDescription()) + return *GetLevel(I)->GetLevelScript()->GetShortDescription(); + else + return *DungeonScript->GetShortDescription() + " level " + (I + 1); +} + +outputfile& operator<<(outputfile& SaveFile, const dungeon* Dungeon) +{ + if(Dungeon) + { + SaveFile.Put(1); + Dungeon->Save(SaveFile); + } + else + SaveFile.Put(0); + + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, dungeon*& Dungeon) +{ + if(SaveFile.Get()) + { + Dungeon = new dungeon; + Dungeon->Load(SaveFile); + } + + return SaveFile; +} + +level* dungeon::LoadLevel(inputfile& SaveFile, int Number) +{ + SaveFile >> Level[Number]; + Level[Number]->SetDungeon(this); + Level[Number]->SetIndex(Number); + Level[Number]->SetLevelScript(GetLevelScript(Number)); + return Level[Number]; +} + +int dungeon::GetLevelTeleportDestination(int From) const +{ + int To; + + if(Index == ELPURI_CAVE) + { + if(RAND_2) + { + To = From + RAND_2 + RAND_2 + RAND_2 + RAND_2 + 1; + + if(To > DARK_LEVEL) + To = From; + } + else + { + To = From - RAND_2 - RAND_2 - RAND_2 - RAND_2 - 1; + + if(To < 0) + To = 0; + } + + return To; + } + + if(Index == UNDER_WATER_TUNNEL) + return RAND_N(3); + + return From; +} diff --git a/Main/Source/entity.cpp b/Main/Source/entity.cpp new file mode 100644 index 0000000..790c7b8 --- /dev/null +++ b/Main/Source/entity.cpp @@ -0,0 +1,75 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through coreset.cpp */ + +truth entity::TryToUnStick(character*, v2) { return false; } + +entity::entity(const entity& Entity) : Emitation(Entity.Emitation), Flags(Entity.Flags) +{ + if(Flags & HAS_BE) + pool::Add(this); +} + +entity::entity(int Flags) : Emitation(0), Flags(Flags|EXISTS) +{ + if(Flags & HAS_BE) + pool::Add(this); +} + +entity::~entity() +{ + if(Flags & HAS_BE) + pool::Remove(this); +} + +/* Calling SendToHell() marks the entity dead, + * so that it will be removed by hell::Burn() at the end of the tick. + * In general, you should never delete an entity instead of calling this, + * because pool::Be() will crash if the entity currently Be()ing is deleted. */ + +void entity::SendToHell() +{ + if(Flags & EXISTS) + { + if(Flags & HAS_BE) + { + pool::Remove(this); + Flags ^= HAS_BE; + } + + pool::AddToHell(this); + Flags ^= EXISTS; + } +} + +/* These functions tell the poolsystem whether the Be() function of the entity + * ought to be called during each tick, thus allowing it to alter itself independently. + * If the entity is stabile, use Disable(), since it speeds up the game. */ + +void entity::Enable() +{ + if(!(Flags & HAS_BE)) + { + pool::Add(this); + Flags |= HAS_BE; + } +} + +void entity::Disable() +{ + if(Flags & HAS_BE) + { + pool::Remove(this); + Flags ^= HAS_BE; + } +} diff --git a/Main/Source/fluid.cpp b/Main/Source/fluid.cpp new file mode 100644 index 0000000..6f06933 --- /dev/null +++ b/Main/Source/fluid.cpp @@ -0,0 +1,751 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through materset.cpp */ + +/* Used to determine how pixels are distributed when fluid over bodyarmors + is shown */ + +const long fluid::BodyArmorPartPixels[] = { 34, 7, 7, 8, 6, 6 }; + +fluid::fluid() : entity(HAS_BE), Next(0), MotherItem(0), GearImage(0) { } + +fluid::fluid(liquid* Liquid, lsquare* LSquareUnder) : entity(HAS_BE), Next(0), Liquid(Liquid), LSquareUnder(LSquareUnder), MotherItem(0), Image(false), GearImage(0), Flags(0) +{ + TrapData.TrapID = game::CreateNewTrapID(this); + TrapData.VictimID = 0; + Image.ShadowPos = ZERO_V2; + Liquid->SetMotherEntity(this); + Emitation = Liquid->GetEmitation(); + AddLiquid(Liquid->GetVolume()); +} + +fluid::fluid(liquid* Liquid, item* MotherItem, cfestring& LocationName, truth IsInside) : entity(HAS_BE), Next(0), Liquid(Liquid), LSquareUnder(0), MotherItem(MotherItem), Image(false), GearImage(0), Flags(0), LocationName(LocationName) +{ + TrapData.TrapID = 0; + + if(UseImage()) + { + Image.Picture->InitRandMap(); + Image.Picture->InitPriorityMap(AVERAGE_PRIORITY); + Image.ShadowPos = MotherItem->GetBitmapPos(0); + Image.SpecialFlags = MotherItem->GetSpecialFlags(); + } + + if(IsInside) + Flags |= FLUID_INSIDE; + + Liquid->SetMotherEntity(this); + Emitation = Liquid->GetEmitation(); + AddLiquid(Liquid->GetVolume()); +} + +fluid::~fluid() +{ + game::RemoveTrapID(TrapData.TrapID); + delete Liquid; + delete [] GearImage; +} + +void fluid::AddLiquid(long Volume) +{ + long Pixels = Volume >> 2; + + if(Pixels && UseImage()) + { + col16 Col = Liquid->GetColor(); + + if(MotherItem) + { + pixelpredicate Pred = MotherItem->GetFluidPixelAllowedPredicate(); + Image.AddLiquidToPicture(MotherItem->GetRawPicture(), Pixels, 225, Col, Pred); + + if(GearImage) + if(Flags & HAS_BODY_ARMOR_PICTURES) + for(int c = 0; c < BODY_ARMOR_PARTS; ++c) + GearImage[c].AddLiquidToPicture(igraph::GetHumanoidRawGraphic(), Pixels * BodyArmorPartPixels[c] / HUMAN_BODY_ARMOR_PIXELS, Image.AlphaAverage, Col, Pred); + else + GearImage->AddLiquidToPicture(igraph::GetHumanoidRawGraphic(), Pixels, Image.AlphaAverage, Col, Pred); + } + else + Image.AddLiquidToPicture(0, Pixels, 225, Col, 0); + } +} + +void fluid::AddLiquidAndVolume(long Volume) +{ + AddLiquid(Volume); + Liquid->SetVolumeNoSignals(Liquid->GetVolume() + Volume); +} + +void fluid::Be() +{ + long Rand = RAND(); + + if(!(Rand & 7)) + if(MotherItem) + { + if(MotherItem->Exists() && MotherItem->AllowFluidBe()) + Liquid->TouchEffect(MotherItem, LocationName); + } + else + { + Liquid->TouchEffect(LSquareUnder->GetGLTerrain()); + + if(LSquareUnder->GetOLTerrain()) + Liquid->TouchEffect(LSquareUnder->GetOLTerrain()); + + if(LSquareUnder->GetCharacter()) + LSquareUnder->GetCharacter()->StayOn(Liquid); + } + + if(MotherItem ? !(Rand & 15) && MotherItem->Exists() && MotherItem->AllowFluidBe() : !(Rand & 63)) + { + long OldVolume = Liquid->GetVolume(); + long NewVolume = ((OldVolume << 6) - OldVolume) >> 6; + Liquid->SetVolumeNoSignals(NewVolume); + + if(UseImage()) + while(NewVolume < Image.AlphaSum >> 6 && FadePictures()); + + if(!MotherItem) + { + LSquareUnder->SendNewDrawRequest(); + LSquareUnder->SendMemorizedUpdateRequest(); + } + + if(!Liquid->GetVolume()) + Destroy(); + } +} + +void fluid::Save(outputfile& SaveFile) const +{ + SaveFile << TrapData << Liquid; + SaveFile << LocationName << Flags; + Image.Save(SaveFile); + + if(GearImage) + { + SaveFile.Put(true); + int Images = Flags & HAS_BODY_ARMOR_PICTURES ? BODY_ARMOR_PARTS : 1; + + for(int c = 0; c < Images; ++c) + GearImage[c].Save(SaveFile); + } + else + SaveFile.Put(false); +} + +void fluid::Load(inputfile& SaveFile) +{ + LSquareUnder = static_cast(game::GetSquareInLoad()); + SaveFile >> TrapData; + game::AddTrapID(this, TrapData.TrapID); + Liquid = static_cast(ReadType(SaveFile)); + Liquid->SetMotherEntity(this); + Emitation = Liquid->GetEmitation(); + SaveFile >> LocationName >> Flags; + Image.Load(SaveFile); + + if(SaveFile.Get()) + { + int Images = Flags & HAS_BODY_ARMOR_PICTURES ? BODY_ARMOR_PARTS : 1; + GearImage = new imagedata[Images]; + + for(int c = 0; c < Images; ++c) + { + GearImage[c].Load(SaveFile); + GearImage[c].Picture->InitRandMap(); + GearImage[c].Picture->CalculateRandMap(); + } + } +} + +void fluid::SimpleDraw(blitdata& BlitData) const +{ + Image.Picture->AlphaLuminanceBlit(BlitData); +} + +void fluid::Draw(blitdata& BlitData) const +{ + if(!UseImage()) + return; + + bitmap* Picture = Image.Picture; + int SpecialFlags = MotherItem ? MotherItem->GetSpecialFlags() : 0; + + if(SpecialFlags & 0x7) + { + /* Priority Bug!!! */ + Picture->BlitAndCopyAlpha(igraph::GetFlagBuffer(), SpecialFlags); + igraph::GetFlagBuffer()->AlphaLuminanceBlit(BlitData); + } + else + Picture->AlphaPriorityBlit(BlitData); + + if(MotherItem && BlitData.CustomData & ALLOW_ANIMATE) + Image.Animate(BlitData, SpecialFlags); +} + +outputfile& operator<<(outputfile& SaveFile, const fluid* Fluid) +{ + Fluid->Save(SaveFile); + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, fluid*& Fluid) +{ + Fluid = new fluid; + Fluid->Load(SaveFile); + return SaveFile; +} + +/* If fluid has decreased, fade, otherwise add new pixels */ + +void fluid::SignalVolumeAndWeightChange() +{ + long Volume = Liquid->GetVolume(); + + if(UseImage()) + if(Volume < Image.AlphaSum >> 6) + while(FadePictures() && Volume < Image.AlphaSum >> 6); + else + AddLiquid(Volume - (Image.AlphaSum >> 6)); +} + +truth fluid::FadePictures() +{ + truth Change = Image.Fade(); + + if(GearImage) + { + int Images = Flags & HAS_BODY_ARMOR_PICTURES ? BODY_ARMOR_PARTS : 1; + + for(int c = 0; c < Images; ++c) + GearImage[c].Fade(); + } + + return Change; +} + +/* Used by lookmode, item descriptions etc. If there are 1-3 members in + Fluid, list them all, otherwise say "a lot of liquids". If there are + several types of blood in the list, they are counted as one. */ + +void fluid::AddFluidInfo(const fluid* Fluid, festring& String) +{ + liquid* LiquidStack[4]; + liquid** Show = LiquidStack + 1; + int Index = 0; + truth Blood = false, OneBlood = true; + + for(; Fluid; Fluid = Fluid->Next) + { + liquid* Liquid = Fluid->GetLiquid(); + truth LiquidBlood = Liquid->GetCategoryFlags() & IS_BLOOD; + + if(!LiquidBlood || !Blood) + { + if(Index < 3) + { + if(LiquidBlood) + { + --Show; + Show[0] = Liquid; + ++Index; + } + else + Show[Index++] = Liquid; + } + else + { + ++Index; + break; + } + } + + if(LiquidBlood) + { + if(Blood) + OneBlood = false; + else + Blood = true; + } + } + + if(Index <= 3) + { + if(!Blood || OneBlood) + String << Show[0]->GetName(false, false); + else + String << "different types of blood"; + + if(Index == 2) + String << " and " << Show[1]->GetName(false, false); + else if(Index == 3) + String << ", " << Show[1]->GetName(false, false) << " and " << Show[2]->GetName(false, false); + } + else + String << "a lot of liquids"; +} + +/* Used only when loading fluids. Correcting RandMap here is somewhat a gum + solution. */ + +void fluid::SetMotherItem(item* What) +{ + MotherItem = What; + + if(UseImage()) + { + Image.Picture->InitRandMap(); + Image.Picture->CalculateRandMap(); + } +} + +/* Ensures the gear pictures are correct after this. ShadowPos is the armor's + or weapon's BitmapPos in humanoid.pcx. SpecialFlags should include drawing + information of the bodypart in question (ST_RIGHT_ARM etc). BodyArmor should + be true iff the picture is part of a body armor, for instance armor covering + one's shoulder. */ + +void fluid::CheckGearPicture(v2 ShadowPos, int SpecialFlags, truth BodyArmor) +{ + if(!UseImage()) + return; + + if(BodyArmor && !(Flags & HAS_BODY_ARMOR_PICTURES)) + { + Flags |= HAS_BODY_ARMOR_PICTURES; + delete [] GearImage; + GearImage = 0; + } + else if(!BodyArmor && Flags & HAS_BODY_ARMOR_PICTURES) + { + Flags &= ~HAS_BODY_ARMOR_PICTURES; + delete [] GearImage; + GearImage = 0; + } + + imagedata* ImagePtr; + long Pixels; + + if(BodyArmor) + { + int Index = (SpecialFlags & 0x38) >> 3; + + if(Index >= BODY_ARMOR_PARTS) + Index = 0; + + if(GearImage) + if(GearImage[Index].ShadowPos != ShadowPos) + GearImage[Index].Clear(false); + else + return; // the picture already exists and is correct + else + { + GearImage = new imagedata[BODY_ARMOR_PARTS]; + + for(int c = 0; c < BODY_ARMOR_PARTS; ++c) + new(&GearImage[c]) imagedata(false); + } + + ImagePtr = &GearImage[Index]; + Pixels = (Image.AlphaSum * BodyArmorPartPixels[Index] / HUMAN_BODY_ARMOR_PIXELS) >> 10; + } + else + { + if(GearImage) + if(GearImage->ShadowPos != ShadowPos) + GearImage->Clear(false); + else + return; // the picture already exists and is correct + else + { + GearImage = new imagedata[1]; + new(GearImage) imagedata(false); + } + + ImagePtr = GearImage; + Pixels = Image.AlphaSum >> 10; + } + + ImagePtr->ShadowPos = ShadowPos; + ImagePtr->SpecialFlags = SpecialFlags; + ImagePtr->Picture->InitRandMap(); + ImagePtr->Picture->InitPriorityMap(AVERAGE_PRIORITY); + + if(Pixels) + ImagePtr->AddLiquidToPicture(igraph::GetHumanoidRawGraphic(), + Pixels, + Image.AlphaAverage, + Liquid->GetColor(), + MotherItem->GetFluidPixelAllowedPredicate()); +} + +void fluid::DrawGearPicture(blitdata& BlitData, int SpecialFlags) const +{ + if(!UseImage()) + return; + + bitmap* Picture = GearImage->Picture; + + if(SpecialFlags & 0x7) + { + Picture->BlitAndCopyAlpha(igraph::GetFlagBuffer(), SpecialFlags); + igraph::GetFlagBuffer()->AlphaPriorityBlit(BlitData); + } + else + GearImage->Picture->AlphaPriorityBlit(BlitData); + + if(BlitData.CustomData & ALLOW_ANIMATE) + GearImage->Animate(BlitData, SpecialFlags); +} + +void fluid::DrawBodyArmorPicture(blitdata& BlitData, int SpecialFlags) const +{ + if(!UseImage()) + return; + + /* We suppose body armor pictures are never rotated */ + + int Index = (SpecialFlags & 0x38) >> 3; + + if(Index >= BODY_ARMOR_PARTS) + Index = 0; + + GearImage[Index].Picture->AlphaPriorityBlit(BlitData); + + if(BlitData.CustomData & ALLOW_ANIMATE) + GearImage[Index].Animate(BlitData, 0); +} + +truth fluid::imagedata::Fade() +{ + return ShadowPos != ERROR_V2 ? Picture->Fade(AlphaSum, AlphaAverage, 1) : false; +} + +/* Let two pixels fall every now and then over the picture. CurrentFlags + should include rotation info. */ + +void fluid::imagedata::Animate(blitdata& BlitData, int CurrentFlags) const +{ + if(!AlphaSum) + return; + + if(!DripTimer) + { + DripPos = Picture->RandomizePixel(); + + /* DripPos != ERROR_V2 since AlphaSum != 0 */ + + if(DripPos.Y <= 12) + { + DripColor = Picture->GetPixel(DripPos); + DripAlpha = Picture->GetAlpha(DripPos); + } + else + ++DripTimer; + } + + if(DripTimer <= 0) + { + v2 TrueDripPos = DripPos; + Rotate(TrueDripPos, 16, CurrentFlags); + TrueDripPos.Y -= DripTimer; + + if(TrueDripPos.Y < 16) + BlitData.Bitmap->AlphaPutPixel(TrueDripPos + BlitData.Dest, DripColor, BlitData.Luminance, DripAlpha); + + if(TrueDripPos.Y < 15) + { + ++TrueDripPos.Y; + BlitData.Bitmap->AlphaPutPixel(TrueDripPos + BlitData.Dest, DripColor, BlitData.Luminance, DripAlpha); + } + else + DripTimer = Min(RAND() % (500000 / AlphaSum), 25000); + } + + --DripTimer; +} + +fluid::imagedata::imagedata(truth Load) : Picture(0), DripTimer(0), AlphaSum(0), ShadowPos(ERROR_V2) +{ + if(!Load) + { + Picture = new bitmap(TILE_V2, TRANSPARENT_COLOR); + Picture->ActivateFastFlag(); + Picture->CreateAlphaMap(0); + } +} + +fluid::imagedata::~imagedata() +{ + delete Picture; +} + +void fluid::imagedata::Save(outputfile& SaveFile) const +{ + SaveFile << Picture << AlphaSum << ShadowPos << (int)SpecialFlags; +} + +void fluid::imagedata::Load(inputfile& SaveFile) +{ + SaveFile >> Picture >> AlphaSum >> ShadowPos >> (int&)SpecialFlags; +} + +/* Shadow and this->ShadowPos specify the location of the raw image of + MotherItem of which shape the pixels are bound. If the image is meant + to be drawn on the ground, Shadow should be zero. The alphas of the + pixels will be on average AlphaSuggestion. PixelPredicate is used + to determine whether pixels of the Shadow are allowed to be covered + by the fluid. It is not used if Shadow == 0. */ + +void fluid::imagedata::AddLiquidToPicture(const rawbitmap* Shadow, long Pixels, long AlphaSuggestion, col16 Color, pixelpredicate PixelPredicate) +{ + if(ShadowPos == ERROR_V2) + return; + + DripTimer = 0; + cint* ValidityMap = igraph::GetBodyBitmapValidityMap(SpecialFlags); + v2 PixelAllowed[256]; + int PixelsAllowed = 0; + + if(Shadow) + { + for(int x = 1; x < 14; ++x) + for(int y = 1; y < 14; ++y) + if(ValidityMap[x] & (1 << y) + && !(Shadow->*PixelPredicate)(ShadowPos + v2(x, y))) + PixelAllowed[PixelsAllowed++] = v2(x, y); + + if(!PixelsAllowed) + return; + } + + long Lumps = Pixels - (Pixels << 3) / 9; // ceil[Pixels/9] + long RoomForPixels = (Lumps << 3) + Lumps; + int Red = GetRed16(Color); + int Green = GetGreen16(Color); + int Blue = GetBlue16(Color); + + if(AlphaSuggestion < 25) + AlphaSuggestion = 25; + + for(long c = 0; c < Lumps; ++c) + { + v2 Cords; + + if(Shadow) + Cords = PixelAllowed[RAND() % PixelsAllowed]; + else + Cords = v2(1 + RAND() % 14, 1 + RAND() % 14); + + Picture->PutPixel(Cords, Color); + long Alpha = Limit(AlphaSuggestion - 25 + RAND() % 50, 0, 0xFF); + AlphaSum += Alpha - Picture->GetAlpha(Cords); + Picture->SetAlpha(Cords, Alpha); + Picture->SafeUpdateRandMap(Cords, true); + --Pixels; + --RoomForPixels; + + for(int d = 0; d < 8; ++d) + { + if(Pixels > RAND() % RoomForPixels) + { + v2 Pos = Cords + game::GetMoveVector(d); + + if(!Shadow || (!(Shadow->*PixelPredicate)(ShadowPos + Pos) + && ValidityMap[Pos.X] & (1 << Pos.Y))) + { + --Pixels; + Picture->PutPixel(Pos, MakeRGB16(Limit(Red - 25 + RAND() % 51, 0, 0xFF), + Limit(Green - 25 + RAND() % 51, 0, 0xFF), + Limit(Blue - 25 + RAND() % 51, 0, 0xFF))); + + long Alpha = Limit(AlphaSuggestion - 25 + RAND() % 50, 0, 0xFF); + AlphaSum += Alpha - Picture->GetAlpha(Pos); + Picture->SetAlpha(Pos, Alpha); + Picture->SafeUpdateRandMap(Pos, true); + + if(!Pixels) // implies c + 1 == Lumps + break; + } + } + + --RoomForPixels; + } + } + + AlphaAverage = Picture->CalculateAlphaAverage(); +} + +/* Remakes all images. Usually decreases, and never increases, the liquid's + volume */ + +void fluid::Redistribute() +{ + if(!UseImage()) + return; + + truth InitRandMap = truth(MotherItem); + Image.Clear(InitRandMap); + + if(GearImage) + if(Flags & HAS_BODY_ARMOR_PICTURES) + for(int c = 0; c < BODY_ARMOR_PARTS; ++c) + GearImage[c].Clear(InitRandMap); + else + GearImage->Clear(InitRandMap); + + AddLiquid(Liquid->GetVolume()); +} + +void fluid::imagedata::Clear(truth InitRandMap) +{ + Picture->ClearToColor(TRANSPARENT_COLOR); + Picture->FillAlpha(0); + AlphaSum = 0; + + if(InitRandMap) + Picture->InitRandMap(); +} + +material* fluid::RemoveMaterial(material*) +{ + Destroy(); + return 0; +} + +void fluid::Destroy() +{ + if(MotherItem) + { + if(!MotherItem->Exists()) + return; + + MotherItem->RemoveFluid(this); + } + else + { + character* Char = game::SearchCharacter(GetVictimID()); + + if(Char) + Char->RemoveTrap(GetTrapID()); + + TrapData.VictimID = 0; + LSquareUnder->RemoveFluid(this); + } + + SendToHell(); +} + +truth fluid::UseImage() const +{ + return !(Flags & FLUID_INSIDE) + && (!MotherItem || MotherItem->ShowFluids()); +} + +void fluid::AddTrapName(festring& String, int) const +{ + Liquid->AddName(String, false, false); +} + +truth fluid::TryToUnStick(character* Victim, v2) +{ + ulong TrapID = GetTrapID(); + int Sum = Victim->GetAttribute(ARM_STRENGTH) + Victim->GetAttribute(LEG_STRENGTH) + Victim->GetAttribute(DEXTERITY) + Victim->GetAttribute(AGILITY); + int Modifier = Liquid->GetStickiness() * Liquid->GetVolume() / (Max(Sum, 1) * 500); + + if(!RAND_N(Max(Modifier, 2))) + { + Victim->RemoveTrap(TrapID); + TrapData.VictimID = 0; + + if(Victim->IsPlayer()) + ADD_MESSAGE("You manage to unstick yourself from the %s.", Liquid->GetName(false, false).CStr()); + else if(Victim->CanBeSeenByPlayer()) + ADD_MESSAGE("%s manages to unstick %sself from the %s.", Victim->CHAR_NAME(DEFINITE), Victim->CHAR_OBJECT_PRONOUN, Liquid->GetName(false, false).CStr()); + + Victim->EditAP(-250); + return true; + } + + Modifier = Sum * 10000 / (Liquid->GetStickiness() * Liquid->GetVolume()); + + if(!RAND_N(Max(Modifier, 2))) + { + int VictimBodyPart = Victim->RandomizeTryToUnStickBodyPart(ALL_BODYPART_FLAGS&~TrapData.BodyParts); + + if(VictimBodyPart != NONE_INDEX) + { + TrapData.BodyParts |= 1 << VictimBodyPart; + Victim->AddTrap(GetTrapID(), 1 << VictimBodyPart); + + if(Victim->IsPlayer()) + ADD_MESSAGE("You fail to free yourself from the %s and your %s is stuck in it in the attempt.", Liquid->GetName(false, false).CStr(), Victim->GetBodyPartName(VictimBodyPart).CStr()); + else if(Victim->CanBeSeenByPlayer()) + ADD_MESSAGE("%s tries to free %sself from the %s but is stuck more tightly in it in the attempt.", Victim->CHAR_NAME(DEFINITE), Victim->CHAR_OBJECT_PRONOUN, Liquid->GetName(false, false).CStr()); + + Victim->EditAP(-1000); + return true; + } + } + + if(Victim->IsPlayer()) + ADD_MESSAGE("You are unable to unstick yourself from %s.", Liquid->GetName(false, false).CStr()); + + Victim->EditAP(-1000); + return false; +} + +void fluid::StepOnEffect(character* Stepper) +{ + if(!Liquid->GetStickiness() || Stepper->IsImmuneToStickiness()) + return; + + int StepperBodyPart = Stepper->GetRandomStepperBodyPart(); + + if(StepperBodyPart == NONE_INDEX) + return; + + TrapData.VictimID = Stepper->GetID(); + TrapData.BodyParts = 1 << StepperBodyPart; + Stepper->AddTrap(GetTrapID(), 1 << StepperBodyPart); + + if(Stepper->IsPlayer()) + ADD_MESSAGE("Your %s is stuck to %s.", Stepper->GetBodyPartName(StepperBodyPart).CStr(), Liquid->GetName(false, false).CStr()); + else if(Stepper->CanBeSeenByPlayer()) + ADD_MESSAGE("%s gets stuck to %s.", Stepper->CHAR_NAME(DEFINITE), Liquid->GetName(false, false).CStr()); +} + +void fluid::PreProcessForBone() +{ + game::RemoveTrapID(TrapData.TrapID); + TrapData.TrapID = 0; +} + +void fluid::PostProcessForBone() +{ + TrapData.TrapID = game::CreateNewTrapID(this); +} + +truth fluid::IsStuckTo(ccharacter* Char) const +{ + return TrapData.VictimID == Char->GetID(); +} + +truth fluid::IsDangerous(ccharacter* Char) const +{ + return Char->GetAttribute(WISDOM) >= Liquid->GetStepInWisdomLimit(); +} diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp new file mode 100644 index 0000000..7e8d37f --- /dev/null +++ b/Main/Source/game.cpp @@ -0,0 +1,3715 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include +#include + +#if defined(LINUX) || defined(__DJGPP__) +#include +#endif + +#ifdef WIN32 +#include +#endif + +#include "whandler.h" +#include "hscore.h" +#include "rawbit.h" +#include "message.h" +#include "feio.h" +#include "team.h" +#include "iconf.h" +#include "allocate.h" +#include "pool.h" +#include "god.h" +#include "proto.h" +#include "stack.h" +#include "felist.h" +#include "human.h" +#include "nonhuman.h" +#include "wsquare.h" +#include "game.h" +#include "graphics.h" +#include "bitmap.h" +#include "save.h" +#include "miscitem.h" +#include "room.h" +#include "materias.h" +#include "rain.h" +#include "gear.h" +#include "fetime.h" +#include "balance.h" +#include "confdef.h" + +#define SAVE_FILE_VERSION 119 // Increment this if changes make savefiles incompatible +#define BONE_FILE_VERSION 106 // Increment this if changes make bonefiles incompatible + +#define LOADED 0 +#define NEW_GAME 1 +#define BACK 2 + +int game::CurrentLevelIndex; +truth game::InWilderness = false; +worldmap* game::WorldMap; +area* game::AreaInLoad; +square* game::SquareInLoad; +dungeon** game::Dungeon; +int game::CurrentDungeonIndex; +ulong game::NextCharacterID = 1; +ulong game::NextItemID = 1; +ulong game::NextTrapID = 1; +team** game::Team; +ulong game::LOSTick; +v2 game::CursorPos(-1, -1); +truth game::Zoom; +truth game::Generating = false; +double game::AveragePlayerArmStrengthExperience; +double game::AveragePlayerLegStrengthExperience; +double game::AveragePlayerDexterityExperience; +double game::AveragePlayerAgilityExperience; +int game::Teams; +int game::Dungeons; +int game::StoryState; +int game::MondedrPass; +int game::RingOfThieves; +int game::Masamune; +int game::Muramasa; +int game::LoricatusHammer; +int game::OmmelBloodMission; +int game::RegiiTalkState; +massacremap game::PlayerMassacreMap; +massacremap game::PetMassacreMap; +massacremap game::MiscMassacreMap; +long game::PlayerMassacreAmount = 0; +long game::PetMassacreAmount = 0; +long game::MiscMassacreAmount = 0; +boneidmap game::BoneItemIDMap; +boneidmap game::BoneCharacterIDMap; +truth game::TooGreatDangerFoundTruth; +itemvectorvector game::ItemDrawVector; +charactervector game::CharacterDrawVector; +truth game::SumoWrestling; +liquid* game::GlobalRainLiquid; +v2 game::GlobalRainSpeed; +long game::GlobalRainTimeModifier; +truth game::PlayerSumoChampion; +truth game::PlayerSolicitusChampion; +ulong game::SquarePartEmitationTick = 0; +long game::Turn; +truth game::PlayerRunning; +character* game::LastPetUnderCursor; +charactervector game::PetVector; +double game::DangerFound; +int game::OldAttribute[ATTRIBUTES]; +int game::NewAttribute[ATTRIBUTES]; +int game::LastAttributeChangeTick[ATTRIBUTES]; +int game::NecroCounter; +int game::CursorData; +truth game::CausePanicFlag; + +truth game::Loading = false; +truth game::JumpToPlayerBe = false; +truth game::InGetCommand = false; +character* game::Petrus = 0; +time_t game::TimePlayedBeforeLastLoad; +time_t game::LastLoad; +time_t game::GameBegan; +truth game::PlayerHasReceivedAllGodsKnownBonus; + +festring game::AutoSaveFileName = game::GetSaveDir() + "AutoSave"; +cchar* const game::Alignment[] = { "L++", "L+", "L", "L-", "N+", "N=", "N-", "C+", "C", "C-", "C--" }; +god** game::God; + +cint game::MoveNormalCommandKey[] = { KEY_HOME, KEY_UP, KEY_PAGE_UP, KEY_LEFT, KEY_RIGHT, KEY_END, KEY_DOWN, KEY_PAGE_DOWN, '.' }; +cint game::MoveAbnormalCommandKey[] = { '7','8','9','u','o','j','k','l','.' }; + +cv2 game::MoveVector[] = { v2(-1, -1), v2(0, -1), v2(1, -1), v2(-1, 0), v2(1, 0), v2(-1, 1), v2(0, 1), v2(1, 1), v2(0, 0) }; +cv2 game::RelativeMoveVector[] = { v2(-1, -1), v2(1, 0), v2(1, 0), v2(-2, 1), v2(2, 0), v2(-2, 1), v2(1, 0), v2(1, 0), v2(-1, -1) }; +cv2 game::BasicMoveVector[] = { v2(-1, 0), v2(1, 0), v2(0, -1), v2(0, 1) }; +cv2 game::LargeMoveVector[] = { v2(-1, -1), v2(0, -1), v2(1, -1), v2(2, -1), v2(-1, 0), v2(2, 0), v2(-1, 1), v2(2, 1), v2(-1, 2), v2(0, 2), v2(1, 2), v2(2, 2), v2(0, 0), v2(1, 0), v2(0, 1), v2(1, 1) }; +cint game::LargeMoveDirection[] = { 0, 1, 1, 2, 3, 4, 3, 4, 5, 6, 6, 7, 8, 8, 8, 8 }; + +truth game::LOSUpdateRequested = false; +uchar*** game::LuxTable = 0; +truth game::Running; +character* game::Player; +v2 game::Camera(0, 0); +ulong game::Tick; +gamescript* game::GameScript = 0; +valuemap game::GlobalValueMap; +dangermap game::DangerMap; +int game::NextDangerIDType; +int game::NextDangerIDConfigIndex; +characteridmap game::CharacterIDMap; +itemidmap game::ItemIDMap; +trapidmap game::TrapIDMap; +truth game::PlayerHurtByExplosion; +area* game::CurrentArea; +level* game::CurrentLevel; +wsquare*** game::CurrentWSquareMap; +lsquare*** game::CurrentLSquareMap; +festring game::DefaultPolymorphTo; +festring game::DefaultSummonMonster; +festring game::DefaultWish; +festring game::DefaultChangeMaterial; +festring game::DefaultDetectMaterial; +truth game::WizardMode; +int game::SeeWholeMapCheatMode; +truth game::GoThroughWallsCheat; +int game::QuestMonstersFound; +bitmap* game::BusyAnimationCache[32]; +festring game::PlayerName; +ulong game::EquipmentMemory[MAX_EQUIPMENT_SLOTS]; +olterrain* game::MonsterPortal; +std::vector game::SpecialCursorPos; +std::vector game::SpecialCursorData; +cbitmap* game::EnterImage; +v2 game::EnterTextDisplacement; + +void game::AddCharacterID(character* Char, ulong ID) { + if(CharacterIDMap.find(ID) != CharacterIDMap.end()) + int esko = esko = 2; + CharacterIDMap.insert(std::make_pair(ID, Char)); +} +void game::RemoveCharacterID(ulong ID) { + if(CharacterIDMap.find(ID) == CharacterIDMap.end()) + int esko = esko = 2; + CharacterIDMap.erase(CharacterIDMap.find(ID)); +} +void game::AddItemID(item* Item, ulong ID) { + if(ItemIDMap.find(ID) != ItemIDMap.end()) + int esko = esko = 2; + ItemIDMap.insert(std::make_pair(ID, Item)); +} +void game::RemoveItemID(ulong ID) +{ + if(ID && ItemIDMap.find(ID) == ItemIDMap.end()) + int esko = esko = 2; + + if(ID) ItemIDMap.erase(ItemIDMap.find(ID)); +} +void game::UpdateItemID(item* Item, ulong ID) { + if(ItemIDMap.find(ID) == ItemIDMap.end()) + int esko = esko = 2; + ItemIDMap.find(ID)->second = Item; +} +void game::AddTrapID(entity* Trap, ulong ID) { + if(TrapIDMap.find(ID) != TrapIDMap.end()) + int esko = esko = 2; + + if(ID) + TrapIDMap.insert(std::make_pair(ID, Trap)); +} +void game::RemoveTrapID(ulong ID) +{ + if(ID && TrapIDMap.find(ID) == TrapIDMap.end()) + int esko = esko = 2; + + if(ID) TrapIDMap.erase(TrapIDMap.find(ID)); +} +void game::UpdateTrapID(entity* Trap, ulong ID) { + if(TrapIDMap.find(ID) == TrapIDMap.end()) + int esko = esko = 2; + TrapIDMap.find(ID)->second = Trap; +} +const dangermap& game::GetDangerMap() { return DangerMap; } +void game::ClearItemDrawVector() { ItemDrawVector.clear(); } +void game::ClearCharacterDrawVector() { CharacterDrawVector.clear(); } + +void game::InitScript() +{ + inputfile ScriptFile(GetGameDir() + "Script/dungeon.dat", &GlobalValueMap); + GameScript = new gamescript; + GameScript->ReadFrom(ScriptFile); + GameScript->RandomizeLevels(); +} + +truth game::Init(cfestring& Name) +{ + if(Name.IsEmpty()) + if(ivanconfig::GetDefaultName().IsEmpty()) + { + PlayerName.Empty(); + + if(iosystem::StringQuestion(PlayerName, CONST_S("What is your name? (1-20 letters)"), v2(30, 46), WHITE, 1, 20, true, true) == ABORTED || PlayerName.IsEmpty()) + return false; + } + else + PlayerName = ivanconfig::GetDefaultName(); + else + PlayerName = Name; + +#ifdef WIN32 + _mkdir("Save"); + _mkdir("Bones"); +#endif + +#ifdef __DJGPP__ + mkdir("Save", S_IWUSR); + mkdir("Bones", S_IWUSR); +#endif + +#ifdef LINUX + mkdir(GetSaveDir().CStr(), S_IRWXU|S_IRWXG); +#endif + + LOSTick = 2; + DangerFound = 0; + CausePanicFlag = false; + + switch(Load(SaveName(PlayerName))) + { + case LOADED: + { + globalwindowhandler::InstallControlLoop(AnimationController); + SetIsRunning(true); + SetForceJumpToPlayerBe(true); + GetCurrentArea()->SendNewDrawRequest(); + SendLOSUpdateRequest(); + ADD_MESSAGE("Game loaded successfully."); + return true; + } + case NEW_GAME: + { + iosystem::TextScreen(CONST_S( "You couldn't possibly have guessed this day would differ from any other.\n" + "It began just as always. You woke up at dawn and drove off the giant spider\n" + "resting on your face. On your way to work you had serious trouble avoiding\n" + "the lions and pythons roaming wild around the village. After getting kicked\n" + "by colony masters for being late you performed your twelve-hour routine of\n" + "climbing trees, gathering bananas, climbing trees, gathering bananas, chasing\n" + "monkeys that stole the first gathered bananas, carrying bananas to the village\n" + "and trying to look happy when real food was distributed.\n\n" + "Finally you were about to enjoy your free time by taking a quick dip in the\n" + "nearby crocodile bay. However, at this point something unusual happened.\n" + "You were summoned to the mansion of Richel Decos, the viceroy of the\n" + "colony, and were led directly to him.")); + + iosystem::TextScreen(CONST_S( "\"I have a task for you, citizen\", said the viceroy picking his golden\n" + "teeth, \"The market price of bananas has taken a deep dive and yet the\n" + "central government is about to raise taxes. I have sent appeals to high\n" + "priest Petrus but received no response. I fear my enemies in Attnam are\n" + "plotting against me and intercepting my messages before they reach him!\"\n\n" + "\"That is why you must travel to Attnam with a letter I'll give you and\n" + "deliver it to Petrus directly. Alas, you somehow have to cross the sea\n" + "between. Because it's winter, all Attnamese ships are trapped by ice and\n" + "I have none. Therefore you must venture through the small underwater tunnel\n" + "connecting our islands. It is infested with monsters, but since you have\n" + "stayed alive here so long, the trip will surely cause you no trouble.\"\n\n" + "You have never been so happy! According to the mansion's traveling\n" + "brochures, Attnam is a peaceful but bustling world city on a beautiful\n" + "snowy fell surrounded by frozen lakes glittering in the arctic sun just\n" + "like the diamonds of the imperial treasury. Not that you would believe a\n" + "word. The point is that tomorrow you can finally forget your home and\n" + "face the untold adventures ahead.")); + + globalwindowhandler::InstallControlLoop(AnimationController); + SetIsRunning(true); + InWilderness = true; + iosystem::TextScreen(CONST_S("Generating game...\n\nThis may take some time, please wait."), ZERO_V2, WHITE, false, true, &BusyAnimation); + igraph::CreateBackGround(GRAY_FRACTAL); + NextCharacterID = 1; + NextItemID = 1; + NextTrapID = 1; + InitScript(); + CreateTeams(); + CreateGods(); + SetPlayer(playerkind::Spawn()); + Player->SetAssignedName(PlayerName); + Player->SetTeam(GetTeam(0)); + Player->SetNP(SATIATED_LEVEL); + + for(int c = 0; c < ATTRIBUTES; ++c) + { + if(c != ENDURANCE) + Player->EditAttribute(c, (RAND() & 1) - (RAND() & 1)); + + Player->EditExperience(c, 500, 1 << 11); + } + + Player->SetMoney(Player->GetMoney() + RAND() % 11); + GetTeam(0)->SetLeader(Player); + InitDangerMap(); + Petrus = 0; + InitDungeons(); + SetCurrentArea(WorldMap = new worldmap(128, 128)); + CurrentWSquareMap = WorldMap->GetMap(); + WorldMap->Generate(); + UpdateCamera(); + SendLOSUpdateRequest(); + Tick = 0; + Turn = 0; + InitPlayerAttributeAverage(); + StoryState = 0; + MondedrPass = 0; + RingOfThieves = 0; + Masamune = 0; + Muramasa = 0; + LoricatusHammer = 0; + OmmelBloodMission = 0; + RegiiTalkState = 0; + PlayerMassacreMap.clear(); + PetMassacreMap.clear(); + MiscMassacreMap.clear(); + PlayerMassacreAmount = PetMassacreAmount = MiscMassacreAmount = 0; + DefaultPolymorphTo.Empty(); + DefaultSummonMonster.Empty(); + DefaultWish.Empty(); + DefaultChangeMaterial.Empty(); + DefaultDetectMaterial.Empty(); + Player->GetStack()->AddItem(encryptedscroll::Spawn()); + character* Doggie = dog::Spawn(); + Doggie->SetTeam(GetTeam(0)); + GetWorldMap()->GetPlayerGroup().push_back(Doggie); + Doggie->SetAssignedName(ivanconfig::GetDefaultPetName()); + WizardMode = false; + SeeWholeMapCheatMode = MAP_HIDDEN; + GoThroughWallsCheat = false; + SumoWrestling = false; + GlobalRainTimeModifier = 2048 - (RAND() & 4095); + PlayerSumoChampion = false; + PlayerSolicitusChampion = false; + protosystem::InitCharacterDataBaseFlags(); + memset(EquipmentMemory, 0, sizeof(EquipmentMemory)); + PlayerRunning = false; + InitAttributeMemory(); + NecroCounter = 0; + GameBegan = time(0); + LastLoad = time(0); + TimePlayedBeforeLastLoad = time::GetZeroTime(); + bool PlayerHasReceivedAllGodsKnownBonus = false; + ADD_MESSAGE("You commence your journey to Attnam. Use direction keys to move, '>' to enter an area and '?' to view other commands."); + + if(IsXMas()) + { + item* Present = banana::Spawn(); + Player->GetStack()->AddItem(Present); + ADD_MESSAGE("Atavus is happy today! He gives you %s.", Present->CHAR_NAME(INDEFINITE)); + } + + return true; + } + default: + return false; + } +} + +void game::DeInit() +{ + delete WorldMap; + WorldMap = 0; + int c; + + for(c = 1; c < Dungeons; ++c) + delete Dungeon[c]; + + delete [] Dungeon; + + for(c = 1; c <= GODS; ++c) + delete God[c]; // sorry, Valpuri! + + delete [] God; + pool::BurnHell(); + + for(c = 0; c < Teams; ++c) + delete Team[c]; + + delete [] Team; + delete GameScript; + msgsystem::Format(); + DangerMap.clear(); +} + +void game::Run() +{ + for(;;) + { + if(!InWilderness) + { + /* Temporary places */ + static int Counter = 0; + + if(++Counter == 10) + { + CurrentLevel->GenerateMonsters(); + Counter = 0; + } + + if(CurrentDungeonIndex == ELPURI_CAVE + && CurrentLevelIndex == ZOMBIE_LEVEL + && !RAND_N(1000 + NecroCounter)) + { + character* Char = necromancer::Spawn(RAND_N(4) ? APPRENTICE_NECROMANCER : MASTER_NECROMANCER); + v2 Pos; + + for(int c2 = 0; c2 < 30; ++c2) + { + Pos = GetCurrentLevel()->GetRandomSquare(Char); + + if(abs(int(Pos.X) - Player->GetPos().X) > 20 + || abs(int(Pos.Y) - Player->GetPos().Y) > 20) + break; + } + + if(Pos != ERROR_V2) + { + Char->SetTeam(GetTeam(MONSTER_TEAM)); + Char->PutTo(Pos); + Char->SetGenerationDanger(GetCurrentLevel()->GetDifficulty()); + Char->SignalGeneration(); + Char->SignalNaturalGeneration(); + ivantime Time; + GetTime(Time); + int Modifier = Time.Day - EDIT_ATTRIBUTE_DAY_MIN; + + if(Modifier > 0) + Char->EditAllAttributes(Modifier >> EDIT_ATTRIBUTE_DAY_SHIFT); + + NecroCounter += 50; + } + else + delete Char; + } + + if(!(GetTick() % 1000)) + CurrentLevel->CheckSunLight(); + + if((CurrentDungeonIndex == NEW_ATTNAM + || CurrentDungeonIndex == ATTNAM) + && CurrentLevelIndex == 0) + { + long OldVolume = GlobalRainLiquid->GetVolume(); + long NewVolume = Max(long(sin((Tick + GlobalRainTimeModifier) * 0.0003) * 300 - 150), 0L); + + if(NewVolume && !OldVolume) + CurrentLevel->EnableGlobalRain(); + else if(!NewVolume && OldVolume) + CurrentLevel->DisableGlobalRain(); + + GlobalRainLiquid->SetVolumeNoSignals(NewVolume); + + /*{ + item* Item; + + if(!RAND_N(2)) + Item = wand::Spawn(1 + RAND_N(12)); + else if(!RAND_N(2)) + { + Item = beartrap::Spawn(); + Item->SetIsActive(true); + Item->SetTeam(MONSTER_TEAM); + } + else if(!RAND_N(2)) + { + Item = mine::Spawn(); + Item->SetIsActive(true); + Item->SetTeam(MONSTER_TEAM); + } + else + Item = holybanana::Spawn(); + + CurrentLevel->GetLSquare(CurrentLevel->GetRandomSquare())->AddItem(Item); + } + + if(!RAND_N(10)) + { + character* Char = protosystem::CreateMonster(0, 1000000); + Char->ChangeTeam(GetTeam(RAND() % Teams)); + Char->PutTo(CurrentLevel->GetRandomSquare(Char)); + } + + if(!RAND_N(5)) + { + character* Char; + if(!RAND_N(5)) + Char = spider::Spawn(GIANT); + else if(!RAND_N(5)) + Char = darkmage::Spawn(1 + RAND_N(4)); + else if(!RAND_N(5)) + Char = necromancer::Spawn(1 + RAND_N(2)); + else if(!RAND_N(5)) + Char = chameleon::Spawn(); + else if(!RAND_N(5)) + Char = kamikazedwarf::Spawn(1 + RAND_N(GODS)); + else if(!RAND_N(5)) + Char = mommo::Spawn(1 + RAND_N(2)); + else if(!RAND_N(3)) + Char = bunny::Spawn(RAND_2 ? ADULT_MALE : ADULT_FEMALE); + else if(!RAND_N(3)) + Char = eddy::Spawn(); + else if(!RAND_N(3)) + Char = magicmushroom::Spawn(); + else if(!RAND_N(5)) + Char = mushroom::Spawn(); + else if(!RAND_N(3)) + Char = blinkdog::Spawn(); + else if(!RAND_N(5)) + Char = tourist::Spawn(1 + RAND_N(3)); + else if(!RAND_N(5)) + Char = hattifattener::Spawn(); + else if(!RAND_N(5)) + Char = genetrixvesana::Spawn(); + else if(!RAND_N(5)) + Char = skunk::Spawn(); + else if(!RAND_N(5)) + Char = ennerbeast::Spawn(); + else if(!RAND_N(5)) + Char = werewolfhuman::Spawn(); + else if(!RAND_N(5)) + Char = unicorn::Spawn(1 + RAND_N(3)); + else if(!RAND_N(5)) + Char = floatingeye::Spawn(); + else if(!RAND_N(5)) + Char = zombie::Spawn(); + else if(!RAND_N(5)) + Char = magpie::Spawn(); + else if(!RAND_N(5)) + Char = elpuri::Spawn(); + else if(!RAND_N(5)) + Char = vladimir::Spawn(); + else if(!RAND_N(5)) + Char = billswill::Spawn(); + else if(!RAND_N(5)) + Char = ghost::Spawn(); + else if(!RAND_N(5)) + Char = dolphin::Spawn(); + else if(!RAND_N(5)) + Char = cossack::Spawn(); + else + Char = invisiblestalker::Spawn(); + + Char->SetTeam(GetTeam(RAND() % Teams)); + Char->PutTo(CurrentLevel->GetRandomSquare(Char)); + }*/ + } + } + + try + { + pool::Be(); + pool::BurnHell(); + IncreaseTick(); + ApplyDivineTick(); + } + catch(quitrequest) + { + break; + } + catch(areachangerequest) + { + } + } +} + +void game::InitLuxTable() +{ + if(!LuxTable) + { + Alloc3D(LuxTable, 256, 33, 33); + + for(int c = 0; c < 0x100; ++c) + for(int x = 0; x < 33; ++x) + for(int y = 0; y < 33; ++y) + { + int X = x - 16, Y = y - 16; + LuxTable[c][x][y] = int(c / (double(X * X + Y * Y) / 128 + 1)); + } + + atexit(DeInitLuxTable); + } +} + +void game::DeInitLuxTable() +{ + delete [] LuxTable; + LuxTable = 0; +} + +void game::UpdateCameraX() +{ + UpdateCameraX(Player->GetPos().X); +} + +void game::UpdateCameraY() +{ + UpdateCameraY(Player->GetPos().Y); +} + +void game::UpdateCameraX(int X) +{ + UpdateCameraCoordinate(Camera.X, X, GetCurrentArea()->GetXSize(), GetScreenXSize()); +} + +void game::UpdateCameraY(int Y) +{ + UpdateCameraCoordinate(Camera.Y, Y, GetCurrentArea()->GetYSize(), GetScreenYSize()); +} + +void game::UpdateCameraCoordinate(int& Coordinate, int Center, int Size, int ScreenSize) +{ + int OldCoordinate = Coordinate; + + if(Size < ScreenSize) + Coordinate = (Size - ScreenSize) >> 1; + else if(Center < ScreenSize >> 1) + Coordinate = 0; + else if(Center > Size - (ScreenSize >> 1)) + Coordinate = Size - ScreenSize; + else + Coordinate = Center - (ScreenSize >> 1); + + if(Coordinate != OldCoordinate) + GetCurrentArea()->SendNewDrawRequest(); +} + +cchar* game::Insult() // convert to array +{ + switch(RAND_N(18)) + { + case 0 : return "moron"; + case 1 : return "silly"; + case 2 : return "idiot"; + case 3 : return "airhead"; + case 4 : return "jerk"; + case 5 : return "dork"; + case 6 : return "Mr. Mole"; + case 7 : return "navastater"; + case 8 : return "potatoes-for-eyes"; + case 9 : return "lamer"; + case 10 : return "mommo-for-brains"; + case 11 : return "pinhead"; + case 12 : return "stupid-headed person"; + case 13 : return "software abuser"; + case 14 : return "loser"; + case 15 : return "peaballs"; + case 16 : return "person-with-problems"; + case 17 : return "unimportant user"; + default : return "hugger-mugger"; + } +} + +/* DefaultAnswer = REQUIRES_ANSWER the question requires an answer */ + +truth game::TruthQuestion(cfestring& String, int DefaultAnswer, int OtherKeyForTrue) +{ + if(DefaultAnswer == NO) + DefaultAnswer = 'n'; + else if(DefaultAnswer == YES) + DefaultAnswer = 'y'; + else if(DefaultAnswer != REQUIRES_ANSWER) + ABORT("Illegal TruthQuestion DefaultAnswer send!"); + + int FromKeyQuestion = KeyQuestion(String, DefaultAnswer, 5, 'y', 'Y', 'n', 'N', OtherKeyForTrue); + return FromKeyQuestion == 'y' || FromKeyQuestion == 'Y' || FromKeyQuestion == OtherKeyForTrue; +} + +void game::DrawEverything() +{ + DrawEverythingNoBlit(); + graphics::BlitDBToScreen(); +} + +truth game::OnScreen(v2 Pos) +{ + return Pos.X >= 0 && Pos.Y >= 0 && Pos.X >= Camera.X && Pos.Y >= Camera.Y && Pos.X < GetCamera().X + GetScreenXSize() && Pos.Y < GetCamera().Y + GetScreenYSize(); +} + +void game::DrawEverythingNoBlit(truth AnimationDraw) +{ + if(LOSUpdateRequested && Player->IsEnabled()) + if(!IsInWilderness()) + GetCurrentLevel()->UpdateLOS(); + else + GetWorldMap()->UpdateLOS(); + + if(OnScreen(CursorPos)) + if(!IsInWilderness() || CurrentWSquareMap[CursorPos.X][CursorPos.Y]->GetLastSeen() || GetSeeWholeMapCheatMode()) + CurrentArea->GetSquare(CursorPos)->SendStrongNewDrawRequest(); + else + DOUBLE_BUFFER->Fill(CalculateScreenCoordinates(CursorPos), TILE_V2, 0); + + int c; + + for(c = 0; c < SpecialCursorPos.size(); ++c) + if(OnScreen(SpecialCursorPos[c])) + CurrentArea->GetSquare(SpecialCursorPos[c])->SendStrongNewDrawRequest(); + + globalwindowhandler::UpdateTick(); + GetCurrentArea()->Draw(AnimationDraw); + Player->DrawPanel(AnimationDraw); + + if(!AnimationDraw) + msgsystem::Draw(); + + if(OnScreen(CursorPos)) + { + v2 ScreenCoord = CalculateScreenCoordinates(CursorPos); + blitdata B = { DOUBLE_BUFFER, + { 0, 0 }, + { ScreenCoord.X, ScreenCoord.Y }, + { TILE_SIZE, TILE_SIZE }, + { 0 }, + TRANSPARENT_COLOR, + ALLOW_ANIMATE|ALLOW_ALPHA }; + + if(!IsInWilderness() && !GetSeeWholeMapCheatMode()) + { + lsquare* Square = CurrentLSquareMap[CursorPos.X][CursorPos.Y]; + + if(Square->GetLastSeen() != GetLOSTick()) + Square->DrawMemorized(B); + } + + if(DoZoom()) + { + B.Src = B.Dest; + B.Dest.X = RES.X - 96; + B.Dest.Y = RES.Y - 96; + B.Stretch = 5; + DOUBLE_BUFFER->StretchBlit(B); + } + + igraph::DrawCursor(ScreenCoord, CursorData); + } + + if(Player->IsEnabled()) + if(Player->IsSmall()) + { + v2 Pos = Player->GetPos(); + + if(OnScreen(Pos)) + { + v2 ScreenCoord = CalculateScreenCoordinates(Pos); + igraph::DrawCursor(ScreenCoord, Player->GetCursorData()); + } + } + else + { + for(c = 0; c < Player->GetSquaresUnder(); ++c) + { + v2 Pos = Player->GetPos(c); + + if(OnScreen(Pos)) + { + v2 ScreenCoord = CalculateScreenCoordinates(Pos); + igraph::DrawCursor(ScreenCoord, Player->GetCursorData()|CURSOR_BIG, c); + } + } + } + + for(c = 0; c < SpecialCursorPos.size(); ++c) + if(OnScreen(SpecialCursorPos[c])) + { + v2 ScreenCoord = CalculateScreenCoordinates(SpecialCursorPos[c]); + igraph::DrawCursor(ScreenCoord, SpecialCursorData[c]); + GetCurrentArea()->GetSquare(SpecialCursorPos[c])->SendStrongNewDrawRequest(); + } +} + +truth game::Save(cfestring& SaveName) +{ + outputfile SaveFile(SaveName + ".sav"); + SaveFile << int(SAVE_FILE_VERSION); + SaveFile << GameScript << CurrentDungeonIndex << CurrentLevelIndex << Camera; + SaveFile << WizardMode << SeeWholeMapCheatMode << GoThroughWallsCheat; + SaveFile << Tick << Turn << InWilderness << NextCharacterID << NextItemID << NextTrapID << NecroCounter; + SaveFile << SumoWrestling << PlayerSumoChampion << GlobalRainTimeModifier; + SaveFile << PlayerSolicitusChampion; + long Seed = RAND(); + femath::SetSeed(Seed); + SaveFile << Seed; + SaveFile << AveragePlayerArmStrengthExperience; + SaveFile << AveragePlayerLegStrengthExperience; + SaveFile << AveragePlayerDexterityExperience; + SaveFile << AveragePlayerAgilityExperience; + SaveFile << Teams << Dungeons << StoryState << MondedrPass << RingOfThieves << Masamune << Muramasa << LoricatusHammer << OmmelBloodMission << RegiiTalkState << PlayerRunning; + SaveFile << PlayerMassacreMap << PetMassacreMap << MiscMassacreMap; + SaveFile << PlayerMassacreAmount << PetMassacreAmount << MiscMassacreAmount; + SaveArray(SaveFile, EquipmentMemory, MAX_EQUIPMENT_SLOTS); + int c; + + for(c = 0; c < ATTRIBUTES; ++c) + SaveFile << OldAttribute[c] << NewAttribute[c] << LastAttributeChangeTick[c]; + + for(c = 1; c < Dungeons; ++c) + SaveFile << Dungeon[c]; + + for(c = 1; c <= GODS; ++c) + SaveFile << God[c]; + + for(c = 0; c < Teams; ++c) + SaveFile << Team[c]; + + if(InWilderness) + SaveWorldMap(SaveName, false); + else + GetCurrentDungeon()->SaveLevel(SaveName, CurrentLevelIndex, false); + + SaveFile << Player->GetPos() << PlayerName; + msgsystem::Save(SaveFile); + SaveFile << DangerMap << NextDangerIDType << NextDangerIDConfigIndex; + SaveFile << DefaultPolymorphTo << DefaultSummonMonster; + SaveFile << DefaultWish << DefaultChangeMaterial << DefaultDetectMaterial; + SaveFile << GetTimeSpent(); + /* or in more readable format: time() - LastLoad + TimeAtLastLoad */ + + SaveFile << PlayerHasReceivedAllGodsKnownBonus; + protosystem::SaveCharacterDataBaseFlags(SaveFile); + return true; +} + +int game::Load(cfestring& SaveName) +{ + inputfile SaveFile(SaveName + ".sav", 0, false); + + if(!SaveFile.IsOpen()) + return NEW_GAME; + + int Version; + SaveFile >> Version; + + if(Version != SAVE_FILE_VERSION) + { + if(!iosystem::Menu(0, v2(RES.X >> 1, RES.Y >> 1), CONST_S("Sorry, this save is incompatible with the new version.\rStart new game?\r"), CONST_S("Yes\rNo\r"), LIGHT_GRAY)) + return NEW_GAME; + else + return BACK; + } + + SaveFile >> GameScript >> CurrentDungeonIndex >> CurrentLevelIndex >> Camera; + SaveFile >> WizardMode >> SeeWholeMapCheatMode >> GoThroughWallsCheat; + SaveFile >> Tick >> Turn >> InWilderness >> NextCharacterID >> NextItemID >> NextTrapID >> NecroCounter; + SaveFile >> SumoWrestling >> PlayerSumoChampion >> GlobalRainTimeModifier; + SaveFile >> PlayerSolicitusChampion; + femath::SetSeed(ReadType(SaveFile)); + SaveFile >> AveragePlayerArmStrengthExperience; + SaveFile >> AveragePlayerLegStrengthExperience; + SaveFile >> AveragePlayerDexterityExperience; + SaveFile >> AveragePlayerAgilityExperience; + SaveFile >> Teams >> Dungeons >> StoryState >> MondedrPass >> RingOfThieves >> Masamune >> Muramasa >> LoricatusHammer >> OmmelBloodMission >> RegiiTalkState >> PlayerRunning; + SaveFile >> PlayerMassacreMap >> PetMassacreMap >> MiscMassacreMap; + SaveFile >> PlayerMassacreAmount >> PetMassacreAmount >> MiscMassacreAmount; + LoadArray(SaveFile, EquipmentMemory, MAX_EQUIPMENT_SLOTS); + int c; + + for(c = 0; c < ATTRIBUTES; ++c) + SaveFile >> OldAttribute[c] >> NewAttribute[c] >> LastAttributeChangeTick[c]; + + Dungeon = new dungeon*[Dungeons]; + Dungeon[0] = 0; + + for(c = 1; c < Dungeons; ++c) + SaveFile >> Dungeon[c]; + + God = new god*[GODS + 1]; + God[0] = 0; + + for(c = 1; c <= GODS; ++c) + SaveFile >> God[c]; + + Team = new team*[Teams]; + + for(c = 0; c < Teams; ++c) + SaveFile >> Team[c]; + + if(InWilderness) + { + SetCurrentArea(LoadWorldMap(SaveName)); + CurrentWSquareMap = WorldMap->GetMap(); + igraph::CreateBackGround(GRAY_FRACTAL); + } + else + { + SetCurrentArea(CurrentLevel = GetCurrentDungeon()->LoadLevel(SaveName, CurrentLevelIndex)); + CurrentLSquareMap = CurrentLevel->GetMap(); + igraph::CreateBackGround(*CurrentLevel->GetLevelScript()->GetBackGroundType()); + } + + v2 Pos; + SaveFile >> Pos >> PlayerName; + SetPlayer(GetCurrentArea()->GetSquare(Pos)->GetCharacter()); + msgsystem::Load(SaveFile); + SaveFile >> DangerMap >> NextDangerIDType >> NextDangerIDConfigIndex; + SaveFile >> DefaultPolymorphTo >> DefaultSummonMonster; + SaveFile >> DefaultWish >> DefaultChangeMaterial >> DefaultDetectMaterial; + SaveFile >> TimePlayedBeforeLastLoad; + SaveFile >> PlayerHasReceivedAllGodsKnownBonus; + LastLoad = time(0); + protosystem::LoadCharacterDataBaseFlags(SaveFile); + return LOADED; +} + +festring game::SaveName(cfestring& Base) +{ + festring SaveName = GetSaveDir(); + + if(!Base.GetSize()) + SaveName << PlayerName; + else + SaveName << Base; + + for(festring::sizetype c = 0; c < SaveName.GetSize(); ++c) + if(SaveName[c] == ' ') + SaveName[c] = '_'; + +#if defined(WIN32) || defined(__DJGPP__) + if(SaveName.GetSize() > 13) + SaveName.Resize(13); +#endif + + return SaveName; +} + +int game::GetMoveCommandKeyBetweenPoints(v2 A, v2 B) +{ + for(int c = 0; c < EXTENDED_DIRECTION_COMMAND_KEYS; ++c) + if((A + GetMoveVector(c)) == B) + return GetMoveCommandKey(c); + + return DIR_ERROR; +} + +void game::ApplyDivineTick() +{ + for(int c = 1; c <= GODS; ++c) + GetGod(c)->ApplyDivineTick(); +} + +void game::ApplyDivineAlignmentBonuses(god* CompareTarget, int Multiplier, truth Good) +{ + for(int c = 1; c <= GODS; ++c) + if(GetGod(c) != CompareTarget) + GetGod(c)->AdjustRelation(CompareTarget, Multiplier, Good); +} + +v2 game::GetDirectionVectorForKey(int Key) +{ + if(Key == KEY_NUMPAD_5) + return ZERO_V2; + + for(int c = 0; c < EXTENDED_DIRECTION_COMMAND_KEYS; ++c) + if(Key == GetMoveCommandKey(c)) + return GetMoveVector(c); + + return ERROR_V2; +} + +double game::GetMinDifficulty() +{ + double Base = CurrentLevel->GetDifficulty() * 0.2; + long MultiplierExponent = 0; + ivantime Time; + GetTime(Time); + int Modifier = Time.Day - DANGER_PLUS_DAY_MIN; + + if(Modifier > 0) + Base += DANGER_PLUS_MULTIPLIER * Modifier; + + for(;;) + { + int Dice = RAND() % 25; + + if(Dice < 5 && MultiplierExponent > -3) + { + Base /= 3; + --MultiplierExponent; + continue; + } + + if(Dice >= 20 && MultiplierExponent < 3) + { + Base *= 3; + ++MultiplierExponent; + continue; + } + + return Base; + } +} + +void game::ShowLevelMessage() +{ + if(CurrentLevel->GetLevelMessage().GetSize()) + ADD_MESSAGE(CurrentLevel->GetLevelMessage().CStr()); + + CurrentLevel->SetLevelMessage(""); +} + +int game::DirectionQuestion(cfestring& Topic, truth RequireAnswer, truth AcceptYourself) +{ + for(;;) + { + int Key = AskForKeyPress(Topic); + + if(AcceptYourself && Key == '.') + return YOURSELF; + + for(int c = 0; c < DIRECTION_COMMAND_KEYS; ++c) + if(Key == GetMoveCommandKey(c)) + return c; + + if(!RequireAnswer) + return DIR_ERROR; + } +} + +void game::RemoveSaves(truth RealSavesAlso) +{ + if(RealSavesAlso) + { + remove(festring(SaveName() + ".sav").CStr()); + remove(festring(SaveName() + ".wm").CStr()); + } + + remove(festring(AutoSaveFileName + ".sav").CStr()); + remove(festring(AutoSaveFileName + ".wm").CStr()); + festring File; + + for(int i = 1; i < Dungeons; ++i) + for(int c = 0; c < GetDungeon(i)->GetLevels(); ++c) + { + /* This looks very odd. And it is very odd. + * Indeed, gcc is very odd to not compile this correctly with -O3 + * if it is written in a less odd way. */ + + File = SaveName() + '.' + i; + File << c; + + if(RealSavesAlso) + remove(File.CStr()); + + File = AutoSaveFileName + '.' + i; + File << c; + + remove(File.CStr()); + } +} + +void game::SetPlayer(character* NP) +{ + Player = NP; + + if(Player) + Player->AddFlags(C_PLAYER); +} + +void game::InitDungeons() +{ + Dungeons = *GetGameScript()->GetDungeons() + 1; + Dungeon = new dungeon*[Dungeons]; + Dungeon[0] = 0; + + for(int c = 1; c < Dungeons; ++c) + { + Dungeon[c] = new dungeon(c); + Dungeon[c]->SetIndex(c); + } +} + +void game::DoEvilDeed(int Amount) +{ + if(!Amount) + return; + + for(int c = 1; c <= GODS; ++c) + { + int Change = Amount - Amount * GetGod(c)->GetAlignment() / 5; + + if(!IsInWilderness() && Player->GetLSquareUnder()->GetDivineMaster() == c) + if(GetGod(c)->GetRelation() - (Change << 1) < -750) + { + if(GetGod(c)->GetRelation() > -750) + GetGod(c)->SetRelation(-750); + } + else if(GetGod(c)->GetRelation() - (Change << 1) > 750) + { + if(GetGod(c)->GetRelation() < 750) + GetGod(c)->SetRelation(750); + } + else + GetGod(c)->SetRelation(GetGod(c)->GetRelation() - (Change << 1)); + else + if(GetGod(c)->GetRelation() - Change < -500) + { + if(GetGod(c)->GetRelation() > -500) + GetGod(c)->SetRelation(-500); + } + else if(GetGod(c)->GetRelation() - Change > 500) + { + if(GetGod(c)->GetRelation() < 500) + GetGod(c)->SetRelation(500); + } + else + GetGod(c)->SetRelation(GetGod(c)->GetRelation() - Change); + } +} + +void game::SaveWorldMap(cfestring& SaveName, truth DeleteAfterwards) +{ + outputfile SaveFile(SaveName + ".wm"); + SaveFile << WorldMap; + + if(DeleteAfterwards) + { + delete WorldMap; + WorldMap = 0; + } +} + +worldmap* game::LoadWorldMap(cfestring& SaveName) +{ + inputfile SaveFile(SaveName + ".wm"); + SaveFile >> WorldMap; + return WorldMap; +} + +void game::Hostility(team* Attacker, team* Defender) +{ + for(int c = 0; c < Teams; ++c) + if(GetTeam(c) != Attacker && GetTeam(c) != Defender + && GetTeam(c)->GetRelation(Defender) == FRIEND + && c != NEW_ATTNAM_TEAM + && c != TOURIST_GUIDE_TEAM) // gum solution + GetTeam(c)->SetRelation(Attacker, HOSTILE); +} + +void game::CreateTeams() +{ + Teams = *GetGameScript()->GetTeams(); + Team = new team*[Teams]; + int c; + + for(c = 0; c < Teams; ++c) + { + Team[c] = new team(c); + + for(int i = 0; i < c; ++i) + Team[i]->SetRelation(Team[c], UNCARING); + } + + for(c = 0; c < Teams; ++c) + if(c != 1) + Team[1]->SetRelation(Team[c], HOSTILE); + + const std::list >& TeamScript = GetGameScript()->GetTeam(); + + for(std::list >::const_iterator i = TeamScript.begin(); i != TeamScript.end(); ++i) + { + for(uint c = 0; c < i->second.GetRelation().size(); ++c) + GetTeam(i->second.GetRelation()[c].first)->SetRelation(GetTeam(i->first), i->second.GetRelation()[c].second); + + cint* KillEvilness = i->second.GetKillEvilness(); + + if(KillEvilness) + GetTeam(i->first)->SetKillEvilness(*KillEvilness); + } +} + +/* v2 Pos should be removed from xxxQuestion()s? */ + +festring game::StringQuestion(cfestring& Topic, col16 Color, festring::sizetype MinLetters, festring::sizetype MaxLetters, truth AllowExit, stringkeyhandler KeyHandler) +{ + DrawEverythingNoBlit(); + igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23)); // pos may be incorrect! + festring Return; + iosystem::StringQuestion(Return, Topic, v2(16, 6), Color, MinLetters, MaxLetters, false, AllowExit, KeyHandler); + igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23)); + return Return; +} + +long game::NumberQuestion(cfestring& Topic, col16 Color, truth ReturnZeroOnEsc) +{ + DrawEverythingNoBlit(); + igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23)); + long Return = iosystem::NumberQuestion(Topic, v2(16, 6), Color, false, ReturnZeroOnEsc); + igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23)); + return Return; +} + +long game::ScrollBarQuestion(cfestring& Topic, long BeginValue, long Step, long Min, long Max, long AbortValue, col16 TopicColor, col16 Color1, col16 Color2, void (*Handler)(long)) +{ + DrawEverythingNoBlit(); + igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23)); + long Return = iosystem::ScrollBarQuestion(Topic, v2(16, 6), BeginValue, Step, Min, Max, AbortValue, TopicColor, Color1, Color2, GetMoveCommandKey(KEY_LEFT_INDEX), GetMoveCommandKey(KEY_RIGHT_INDEX), false, Handler); + igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23)); + return Return; +} + +ulong game::IncreaseLOSTick() +{ + if(LOSTick != 0xFE) + return LOSTick += 2; + else + { + CurrentLevel->InitLastSeen(); + return LOSTick = 4; + } +} + +void game::UpdateCamera() +{ + UpdateCameraX(); + UpdateCameraY(); +} + +truth game::HandleQuitMessage() +{ +#ifdef USE_SDL + + if(IsRunning()) + { + if(IsInGetCommand()) + { + switch(Menu(0, v2(RES.X >> 1, RES.Y >> 1), CONST_S("Do you want to save your game before quitting?\r"), CONST_S("Yes\rNo\rCancel\r"), LIGHT_GRAY)) + { + case 0: + Save(); + RemoveSaves(false); + break; + case 2: + GetCurrentArea()->SendNewDrawRequest(); + DrawEverything(); + return false; + default: + festring Msg = CONST_S("cowardly quit the game"); + Player->AddScoreEntry(Msg, 0.75); + End(Msg, true, false); + break; + } + } + else + if(!Menu(0, v2(RES.X >> 1, RES.Y >> 1), CONST_S("You can't save at this point. Are you sure you still want to do this?\r"), CONST_S("Yes\rNo\r"), LIGHT_GRAY)) + RemoveSaves(); + else + { + GetCurrentArea()->SendNewDrawRequest(); + DrawEverything(); + return false; + } + } + +#endif /* USE_SDL */ + + return true; +} + +int game::GetDirectionForVector(v2 Vector) +{ + for(int c = 0; c < DIRECTION_COMMAND_KEYS; ++c) + if(Vector == GetMoveVector(c)) + return c; + + return DIR_ERROR; +} + +cchar* game::GetVerbalPlayerAlignment() +{ + long Sum = 0; + + for(int c = 1; c <= GODS; ++c) + { + if(GetGod(c)->GetRelation() > 0) + Sum += GetGod(c)->GetRelation() * (5 - GetGod(c)->GetAlignment()); + } + + if(Sum > 15000) + return "extremely lawful"; + if(Sum > 10000) + return "very lawful"; + if(Sum > 5000) + return "lawful"; + if(Sum > 1000) + return "mildly lawful"; + if(Sum > -1000) + return "neutral"; + if(Sum > -5000) + return "mildly chaotic"; + if(Sum > -10000) + return "chaotic"; + if(Sum > -15000) + return "very chaotic"; + + return "extremely chaotic"; +} + +void game::CreateGods() +{ + God = new god*[GODS + 1]; + God[0] = 0; + + for(int c = 1; c < protocontainer::GetSize(); ++c) + God[c] = protocontainer::GetProto(c)->Spawn(); +} + +void game::BusyAnimation() +{ + BusyAnimation(DOUBLE_BUFFER, false); +} + +void game::BusyAnimation(bitmap* Buffer, truth ForceDraw) +{ + static clock_t LastTime = 0; + static int Frame = 0; + static blitdata B1 = { 0, + { 0, 0 }, + { 0, 0 }, + { RES.X, RES.Y }, + { 0 }, + 0, + 0 }; + static blitdata B2 = { 0, + { 0, 0 }, + { (RES.X >> 1) - 100, (RES.Y << 1) / 3 - 100 }, + { 200, 200 }, + { 0 }, + 0, + 0 }; + + if(ForceDraw || clock() - LastTime > CLOCKS_PER_SEC / 25) + { + B2.Bitmap = Buffer; + B2.Dest.X = (RES.X >> 1) - 100 + EnterTextDisplacement.X; + B2.Dest.Y = (RES.Y << 1) / 3 - 100 + EnterTextDisplacement.Y; + + if(EnterImage) + { + B1.Bitmap = Buffer; + EnterImage->NormalMaskedBlit(B1); + } + + BusyAnimationCache[Frame]->NormalBlit(B2); + + if(Buffer == DOUBLE_BUFFER) + graphics::BlitDBToScreen(); + + if(++Frame == 32) + Frame = 0; + + LastTime = clock(); + } +} + +void game::CreateBusyAnimationCache() +{ + bitmap Elpuri(TILE_V2, TRANSPARENT_COLOR); + Elpuri.ActivateFastFlag(); + packcol16 Color = MakeRGB16(60, 60, 60); + igraph::GetCharacterRawGraphic()->MaskedBlit(&Elpuri, v2(64, 0), ZERO_V2, TILE_V2, &Color); + bitmap Circle(v2(200, 200), TRANSPARENT_COLOR); + Circle.ActivateFastFlag(); + + for(int x = 0; x < 4; ++x) + Circle.DrawPolygon(100, 100, 95 + x, 50, MakeRGB16(255 - 12 * x, 0, 0)); + + blitdata B1 = { 0, + { 0, 0 }, + { 92, 92 }, + { TILE_SIZE, TILE_SIZE }, + { 0 }, + TRANSPARENT_COLOR, + 0 }; + + blitdata B2 = { 0, + { 0, 0 }, + { 0, 0 }, + { 200, 200 }, + { 0 }, + TRANSPARENT_COLOR, + 0 }; + + for(int c = 0; c < 32; ++c) + { + B1.Bitmap = B2.Bitmap = BusyAnimationCache[c] = new bitmap(v2(200, 200), 0); + B1.Bitmap->ActivateFastFlag(); + Elpuri.NormalMaskedBlit(B1); + double Rotation = 0.3 + c * FPI / 80; + + for(int x = 0; x < 10; ++x) + B1.Bitmap->DrawPolygon(100, 100, 95, 5, MakeRGB16(5 + 25 * x, 0, 0), false, true, Rotation + double(x) / 50); + + Circle.NormalMaskedBlit(B2); + } +} + +int game::AskForKeyPress(cfestring& Topic) +{ + DrawEverythingNoBlit(); + FONT->Printf(DOUBLE_BUFFER, v2(16, 8), WHITE, "%s", Topic.CapitalizeCopy().CStr()); + graphics::BlitDBToScreen(); + int Key = GET_KEY(); + igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23)); + return Key; +} + +/* Handler is called when the key has been identified as a movement key + * KeyHandler is called when the key has NOT been identified as a movement key + * Both can be deactivated by passing 0 as parameter */ + +v2 game::PositionQuestion(cfestring& Topic, v2 CursorPos, void (*Handler)(v2), positionkeyhandler KeyHandler, truth Zoom) +{ + int Key = 0; + SetDoZoom(Zoom); + v2 Return; + CursorData = RED_CURSOR; + + if(Handler) + Handler(CursorPos); + + for(;;) + { + square* Square = GetCurrentArea()->GetSquare(CursorPos); + + if(!Square->HasBeenSeen() && (!Square->GetCharacter() || !Square->GetCharacter()->CanBeSeenByPlayer()) && !GetSeeWholeMapCheatMode()) + DOUBLE_BUFFER->Fill(CalculateScreenCoordinates(CursorPos), TILE_V2, BLACK); + else + GetCurrentArea()->GetSquare(CursorPos)->SendStrongNewDrawRequest(); + + if(Key == ' ' || Key == '.') + { + Return = CursorPos; + break; + } + + if(Key == KEY_ESC) + { + Return = ERROR_V2; + break; + } + + v2 DirectionVector = GetDirectionVectorForKey(Key); + + if(DirectionVector != ERROR_V2) + { + CursorPos += DirectionVector; + + if(CursorPos.X > GetCurrentArea()->GetXSize() - 1) CursorPos.X = 0; + if(CursorPos.X < 0) CursorPos.X = GetCurrentArea()->GetXSize() - 1; + if(CursorPos.Y > GetCurrentArea()->GetYSize() - 1) CursorPos.Y = 0; + if(CursorPos.Y < 0) CursorPos.Y = GetCurrentArea()->GetYSize() - 1; + + if(Handler) + Handler(CursorPos); + } + else if(KeyHandler) + { + CursorPos = KeyHandler(CursorPos, Key); + + if(CursorPos == ERROR_V2 || CursorPos == ABORT_V2) + { + Return = CursorPos; + break; + } + } + + if(CursorPos.X < GetCamera().X + 3 || CursorPos.X >= GetCamera().X + GetScreenXSize() - 3) + UpdateCameraX(CursorPos.X); + + if(CursorPos.Y < GetCamera().Y + 3 || CursorPos.Y >= GetCamera().Y + GetScreenYSize() - 3) + UpdateCameraY(CursorPos.Y); + + FONT->Printf(DOUBLE_BUFFER, v2(16, 8), WHITE, "%s", Topic.CStr()); + SetCursorPos(CursorPos); + DrawEverything(); + Key = GET_KEY(); + } + + igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23)); + igraph::BlitBackGround(v2(RES.X - 96, RES.Y - 96), v2(80, 80)); + SetDoZoom(false); + SetCursorPos(v2(-1, -1)); + return Return; +} + +void game::LookHandler(v2 CursorPos) +{ + square* Square = GetCurrentArea()->GetSquare(CursorPos); + festring OldMemory; + + if(GetSeeWholeMapCheatMode()) + { + OldMemory = Square->GetMemorizedDescription(); + + if(IsInWilderness()) + GetWorldMap()->GetWSquare(CursorPos)->UpdateMemorizedDescription(true); + else + GetCurrentLevel()->GetLSquare(CursorPos)->UpdateMemorizedDescription(true); + } + + festring Msg; + + if(Square->HasBeenSeen() || GetSeeWholeMapCheatMode()) + { + if(!IsInWilderness() && !Square->CanBeSeenByPlayer() && GetCurrentLevel()->GetLSquare(CursorPos)->CanBeFeltByPlayer()) + Msg = CONST_S("You feel here "); + else if(Square->CanBeSeenByPlayer(true) || GetSeeWholeMapCheatMode()) + Msg = CONST_S("You see here "); + else + Msg = CONST_S("You remember here "); + + Msg << Square->GetMemorizedDescription() << '.'; + + if(!IsInWilderness() && (Square->CanBeSeenByPlayer() || GetSeeWholeMapCheatMode())) + { + lsquare* LSquare = GetCurrentLevel()->GetLSquare(CursorPos); + LSquare->DisplaySmokeInfo(Msg); + + if(LSquare->HasEngravings() && LSquare->IsTransparent()) + if(LSquare->EngravingsCanBeReadByPlayer() || GetSeeWholeMapCheatMode()) + LSquare->DisplayEngravedInfo(Msg); + else + Msg << " Something has been engraved here."; + } + } + else + Msg = CONST_S("You have never been here."); + + character* Character = Square->GetCharacter(); + + if(Character && (Character->CanBeSeenByPlayer() || GetSeeWholeMapCheatMode())) + Character->DisplayInfo(Msg); + + if(!(RAND() % 10000) && (Square->CanBeSeenByPlayer() || GetSeeWholeMapCheatMode())) + Msg << " You see here a frog eating a magnolia."; + + ADD_MESSAGE("%s", Msg.CStr()); + + if(GetSeeWholeMapCheatMode()) + Square->SetMemorizedDescription(OldMemory); +} + +truth game::AnimationController() +{ + DrawEverythingNoBlit(true); + return true; +} + +void game::InitGlobalValueMap() +{ + inputfile SaveFile(GetGameDir() + "Script/define.dat", &GlobalValueMap); + festring Word; + + for(SaveFile.ReadWord(Word, false); !SaveFile.Eof(); SaveFile.ReadWord(Word, false)) + { + if(Word != "#" || SaveFile.ReadWord() != "define") + ABORT("Illegal datafile define on line %ld!", SaveFile.TellLine()); + + SaveFile.ReadWord(Word); + GlobalValueMap.insert(std::make_pair(Word, SaveFile.ReadNumber())); + } +} + +void game::TextScreen(cfestring& Text, v2 Displacement, col16 Color, truth GKey, truth Fade, bitmapeditor BitmapEditor) +{ + globalwindowhandler::DisableControlLoops(); + iosystem::TextScreen(Text, Displacement, Color, GKey, Fade, BitmapEditor); + globalwindowhandler::EnableControlLoops(); +} + +/* ... all the keys that are acceptable + DefaultAnswer = REQUIRES_ANSWER if this question requires an answer + Not surprisingly KeyNumber is the number of keys at ... +*/ + +int game::KeyQuestion(cfestring& Message, int DefaultAnswer, int KeyNumber, ...) +{ + int* Key = new int[KeyNumber]; + va_list Arguments; + va_start(Arguments, KeyNumber); + + for(int c = 0; c < KeyNumber; ++c) + Key[c] = va_arg(Arguments, int); + + va_end(Arguments); + DrawEverythingNoBlit(); + FONT->Printf(DOUBLE_BUFFER, v2(16, 8), WHITE, "%s", Message.CStr()); + graphics::BlitDBToScreen(); + int Return = 0; + + while(!Return) + { + int k = GET_KEY(); + + for(int c = 0; c < KeyNumber; ++c) + if(Key[c] == k) + { + Return = k; + break; + } + + if(!Return && DefaultAnswer != REQUIRES_ANSWER) + Return = DefaultAnswer; + } + + delete [] Key; + igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23)); + return Return; +} + +v2 game::LookKeyHandler(v2 CursorPos, int Key) +{ + square* Square = GetCurrentArea()->GetSquare(CursorPos); + + switch(Key) + { + case 'i': + if(!IsInWilderness()) + if(Square->CanBeSeenByPlayer() || CursorPos == Player->GetPos() || GetSeeWholeMapCheatMode()) + { + lsquare* LSquare = GetCurrentLevel()->GetLSquare(CursorPos); + stack* Stack = LSquare->GetStack(); + + if(LSquare->IsTransparent() && Stack->GetVisibleItems(Player)) + Stack->DrawContents(Player, "Items here", NO_SELECT|(GetSeeWholeMapCheatMode() ? 0 : NO_SPECIAL_INFO)); + else + ADD_MESSAGE("You see no items here."); + } + else + ADD_MESSAGE("You should perhaps move a bit closer."); + + break; + case 'c': + if(Square->CanBeSeenByPlayer() || CursorPos == Player->GetPos() || GetSeeWholeMapCheatMode()) + { + character* Char = Square->GetCharacter(); + + if(Char && (Char->CanBeSeenByPlayer() || Char->IsPlayer() || GetSeeWholeMapCheatMode())) + Char->PrintInfo(); + else + ADD_MESSAGE("You see no one here."); + } + else + ADD_MESSAGE("You should perhaps move a bit closer."); + + break; + } + + return CursorPos; +} + +v2 game::NameKeyHandler(v2 CursorPos, int Key) +{ + if(SelectPet(Key)) + return LastPetUnderCursor->GetPos(); + + if(Key == 'n' || Key == 'N') + { + character* Char = GetCurrentArea()->GetSquare(CursorPos)->GetCharacter(); + + if(Char && Char->CanBeSeenByPlayer()) + Char->TryToName(); + else + ADD_MESSAGE("You don't see anyone here to name."); + } + + return CursorPos; +} + +void game::End(festring DeathMessage, truth Permanently, truth AndGoToMenu) +{ + globalwindowhandler::DeInstallControlLoop(AnimationController); + SetIsRunning(false); + + if(Permanently || !WizardModeIsReallyActive()) + RemoveSaves(Permanently); + + if(Permanently && !WizardModeIsReallyActive()) + { + highscore HScore; + + if(HScore.LastAddFailed()) + { + iosystem::TextScreen(CONST_S("You didn't manage to get onto the high score list.\n\n\n\n") + + GetPlayerName() + ", " + DeathMessage + "\nRIP"); + } + else + HScore.Draw(); + } + + if(AndGoToMenu) + { + /* This prevents monster movement etc. after death. */ + + throw quitrequest(); + } +} + +int game::CalculateRoughDirection(v2 Vector) +{ + if(!Vector.X && !Vector.Y) + return YOURSELF; + + double Angle = femath::CalculateAngle(Vector); + + if(Angle < FPI / 8) + return 4; + else if(Angle < 3 * FPI / 8) + return 7; + else if(Angle < 5 * FPI / 8) + return 6; + else if(Angle < 7 * FPI / 8) + return 5; + else if(Angle < 9 * FPI / 8) + return 3; + else if(Angle < 11 * FPI / 8) + return 0; + else if(Angle < 13 * FPI / 8) + return 1; + else if(Angle < 15 * FPI / 8) + return 2; + else + return 4; +} + +int game::Menu(bitmap* BackGround, v2 Pos, cfestring& Topic, cfestring& sMS, col16 Color, cfestring& SmallText1, cfestring& SmallText2) +{ + globalwindowhandler::DisableControlLoops(); + int Return = iosystem::Menu(BackGround, Pos, Topic, sMS, Color, SmallText1, SmallText2); + globalwindowhandler::EnableControlLoops(); + return Return; +} + +void game::InitDangerMap() +{ + truth First = true; + + for(int c1 = 1; c1 < protocontainer::GetSize(); ++c1) + { + BusyAnimation(); + const character::prototype* Proto = protocontainer::GetProto(c1); + const character::database*const* ConfigData = Proto->GetConfigData(); + int ConfigSize = Proto->GetConfigSize(); + + for(int c2 = 0; c2 < ConfigSize; ++c2) + if(!ConfigData[c2]->IsAbstract) + { + int Config = ConfigData[c2]->Config; + + if(First) + { + NextDangerIDType = c1; + NextDangerIDConfigIndex = c2; + First = false; + } + + character* Char = Proto->Spawn(Config, NO_EQUIPMENT|NO_PIC_UPDATE|NO_EQUIPMENT_PIC_UPDATE); + double NakedDanger = Char->GetRelativeDanger(Player, true); + delete Char; + Char = Proto->Spawn(Config, NO_PIC_UPDATE|NO_EQUIPMENT_PIC_UPDATE); + double EquippedDanger = Char->GetRelativeDanger(Player, true); + delete Char; + DangerMap[configid(c1, Config)] = dangerid(NakedDanger, EquippedDanger); + } + } +} + +void game::CalculateNextDanger() +{ + if(IsInWilderness() || !*CurrentLevel->GetLevelScript()->GenerateMonsters()) + return; + + const character::prototype* Proto = protocontainer::GetProto(NextDangerIDType); + const character::database*const* ConfigData = Proto->GetConfigData(); + const character::database* DataBase = ConfigData[NextDangerIDConfigIndex]; + dangermap::iterator DangerIterator = DangerMap.find(configid(NextDangerIDType, DataBase->Config)); + team* Team = GetTeam(PLAYER_TEAM); + + if(DataBase && DangerIterator != DangerMap.end()) + { + character* Char = Proto->Spawn(DataBase->Config, NO_EQUIPMENT|NO_PIC_UPDATE|NO_EQUIPMENT_PIC_UPDATE); + std::list::const_iterator i; + double DangerSum = Player->GetRelativeDanger(Char, true); + + for(i = Team->GetMember().begin(); i != Team->GetMember().end(); ++i) + if((*i)->IsEnabled() && !(*i)->IsTemporary() && !RAND_N(10)) + DangerSum += (*i)->GetRelativeDanger(Char, true) / 4; + + double CurrentDanger = 1 / DangerSum; + double NakedDanger = DangerIterator->second.NakedDanger; + delete Char; + + if(NakedDanger > CurrentDanger) + DangerIterator->second.NakedDanger = (NakedDanger * 9 + CurrentDanger) / 10; + + Char = Proto->Spawn(DataBase->Config, NO_PIC_UPDATE|NO_EQUIPMENT_PIC_UPDATE); + DangerSum = Player->GetRelativeDanger(Char, true); + + for(i = Team->GetMember().begin(); i != Team->GetMember().end(); ++i) + if((*i)->IsEnabled() && !(*i)->IsTemporary() && !RAND_N(10)) + DangerSum += (*i)->GetRelativeDanger(Char, true) / 4; + + CurrentDanger = 1 / DangerSum; + double EquippedDanger = DangerIterator->second.EquippedDanger; + delete Char; + + if(EquippedDanger > CurrentDanger) + DangerIterator->second.EquippedDanger = (EquippedDanger * 9 + CurrentDanger) / 10; + + if(++NextDangerIDConfigIndex < Proto->GetConfigSize()) + return; + + for(;;) + { + if(++NextDangerIDType >= protocontainer::GetSize()) + NextDangerIDType = 1; + + Proto = protocontainer::GetProto(NextDangerIDType); + ConfigData = Proto->GetConfigData(); + int ConfigSize = Proto->GetConfigSize(); + + for(int c = 0; c < ConfigSize; ++c) + if(!ConfigData[c]->IsAbstract) + { + NextDangerIDConfigIndex = c; + return; + } + } + } + else + ABORT("It is dangerous to go ice fishing in the summer."); +} + +truth game::TryTravel(int Dungeon, int Area, int EntryIndex, truth AllowHostiles, truth AlliesFollow) +{ + charactervector Group; + + if(LeaveArea(Group, AllowHostiles, AlliesFollow)) + { + CurrentDungeonIndex = Dungeon; + EnterArea(Group, Area, EntryIndex); + return true; + } + else + return false; +} + +truth game::LeaveArea(charactervector& Group, truth AllowHostiles, truth AlliesFollow) +{ + if(!IsInWilderness()) + { + if(AlliesFollow && !GetCurrentLevel()->CollectCreatures(Group, Player, AllowHostiles)) + return false; + + Player->Remove(); + GetCurrentDungeon()->SaveLevel(SaveName(), CurrentLevelIndex); + } + else + { + Player->Remove(); + GetWorldMap()->GetPlayerGroup().swap(Group); + SaveWorldMap(); + } + + return true; +} + +/* Used always when the player enters an area. */ + +void game::EnterArea(charactervector& Group, int Area, int EntryIndex) +{ + if(Area != WORLD_MAP) + { + Generating = true; + SetIsInWilderness(false); + CurrentLevelIndex = Area; + truth New = !PrepareRandomBone(Area) && !GetCurrentDungeon()->PrepareLevel(Area); + igraph::CreateBackGround(*CurrentLevel->GetLevelScript()->GetBackGroundType()); + GetCurrentArea()->SendNewDrawRequest(); + v2 Pos = GetCurrentLevel()->GetEntryPos(Player, EntryIndex); + + if(Player) + { + GetCurrentLevel()->GetLSquare(Pos)->KickAnyoneStandingHereAway(); + Player->PutToOrNear(Pos); + } + else + SetPlayer(GetCurrentLevel()->GetLSquare(Pos)->GetCharacter()); + + uint c; + + for(c = 0; c < Group.size(); ++c) + { + v2 NPCPos = GetCurrentLevel()->GetNearestFreeSquare(Group[c], Pos); + + if(NPCPos == ERROR_V2) + NPCPos = GetCurrentLevel()->GetRandomSquare(Group[c]); + + Group[c]->PutTo(NPCPos); + } + + GetCurrentLevel()->FiatLux(); + ctruth* AutoReveal = GetCurrentLevel()->GetLevelScript()->AutoReveal(); + + if(New && AutoReveal && *AutoReveal) + GetCurrentLevel()->Reveal(); + + ShowLevelMessage(); + SendLOSUpdateRequest(); + UpdateCamera(); + + /* Gum solution! */ + + if(New && CurrentDungeonIndex == ATTNAM && Area == 0) + { + GlobalRainLiquid = powder::Spawn(SNOW); + GlobalRainSpeed = v2(-64, 128); + CurrentLevel->CreateGlobalRain(GlobalRainLiquid, GlobalRainSpeed); + } + + if(New && CurrentDungeonIndex == NEW_ATTNAM && Area == 0) + { + GlobalRainLiquid = liquid::Spawn(WATER); + GlobalRainSpeed = v2(256, 512); + CurrentLevel->CreateGlobalRain(GlobalRainLiquid, GlobalRainSpeed); + } + + if(New && CurrentDungeonIndex == ELPURI_CAVE && Area == OREE_LAIR) + { + GlobalRainLiquid = liquid::Spawn(BLOOD); + GlobalRainSpeed = v2(256, 512); + CurrentLevel->CreateGlobalRain(GlobalRainLiquid, GlobalRainSpeed); + GlobalRainLiquid->SetVolumeNoSignals(200); + CurrentLevel->EnableGlobalRain(); + } + + if(New && CurrentDungeonIndex == MUNTUO && Area == 0) + { + GlobalRainLiquid = liquid::Spawn(WATER); + GlobalRainSpeed = v2(-64, 1024); + CurrentLevel->CreateGlobalRain(GlobalRainLiquid, GlobalRainSpeed); + } + + Generating = false; + GetCurrentLevel()->UpdateLOS(); + Player->SignalStepFrom(0); + + for(c = 0; c < Group.size(); ++c) + Group[c]->SignalStepFrom(0); + + if(ivanconfig::GetAutoSaveInterval()) + Save(GetAutoSaveFileName().CStr()); + } + else + { + igraph::CreateBackGround(GRAY_FRACTAL); + SetIsInWilderness(true); + LoadWorldMap(); + SetCurrentArea(WorldMap); + CurrentWSquareMap = WorldMap->GetMap(); + GetWorldMap()->GetPlayerGroup().swap(Group); + Player->PutTo(GetWorldMap()->GetEntryPos(Player, EntryIndex)); + SendLOSUpdateRequest(); + UpdateCamera(); + GetWorldMap()->UpdateLOS(); + + if(ivanconfig::GetAutoSaveInterval()) + Save(GetAutoSaveFileName().CStr()); + } +} + +int game::CompareLightToInt(col24 L, col24 Int) +{ + if((L & 0xFF0000) > Int || (L & 0xFF00) > Int || (L & 0xFF) > Int) + return 1; + else if((L & 0xFF0000) == Int || (L & 0xFF00) == Int || (L & 0xFF) == Int) + return 0; + else + return -1; +} + +void game::SetStandardListAttributes(felist& List) +{ + List.SetPos(v2(26, 42)); + List.SetWidth(652); + List.SetFlags(DRAW_BACKGROUND_AFTERWARDS); + List.SetUpKey(GetMoveCommandKey(KEY_UP_INDEX)); + List.SetDownKey(GetMoveCommandKey(KEY_DOWN_INDEX)); +} + +void game::InitPlayerAttributeAverage() +{ + AveragePlayerArmStrengthExperience + = AveragePlayerLegStrengthExperience + = AveragePlayerDexterityExperience + = AveragePlayerAgilityExperience + = 0; + + if(!Player->IsHumanoid()) + return; + + humanoid* Player = static_cast(GetPlayer()); + int Arms = 0; + int Legs = 0; + arm* RightArm = Player->GetRightArm(); + + if(RightArm && !RightArm->UseMaterialAttributes()) + { + AveragePlayerArmStrengthExperience += RightArm->GetStrengthExperience(); + AveragePlayerDexterityExperience += RightArm->GetDexterityExperience(); + ++Arms; + } + + arm* LeftArm = Player->GetLeftArm(); + + if(LeftArm && !LeftArm->UseMaterialAttributes()) + { + AveragePlayerArmStrengthExperience += LeftArm->GetStrengthExperience(); + AveragePlayerDexterityExperience += LeftArm->GetDexterityExperience(); + ++Arms; + } + + leg* RightLeg = Player->GetRightLeg(); + + if(RightLeg && !RightLeg->UseMaterialAttributes()) + { + AveragePlayerLegStrengthExperience += RightLeg->GetStrengthExperience(); + AveragePlayerAgilityExperience += RightLeg->GetAgilityExperience(); + ++Legs; + } + + leg* LeftLeg = Player->GetLeftLeg(); + + if(LeftLeg && !LeftLeg->UseMaterialAttributes()) + { + AveragePlayerLegStrengthExperience += LeftLeg->GetStrengthExperience(); + AveragePlayerAgilityExperience += LeftLeg->GetAgilityExperience(); + ++Legs; + } + + if(Arms) + { + AveragePlayerArmStrengthExperience /= Arms; + AveragePlayerDexterityExperience /= Arms; + } + + if(Legs) + { + AveragePlayerLegStrengthExperience /= Legs; + AveragePlayerAgilityExperience /= Legs; + } +} + +void game::UpdatePlayerAttributeAverage() +{ + if(!Player->IsHumanoid()) + return; + + humanoid* Player = static_cast(GetPlayer()); + double PlayerArmStrengthExperience = 0; + double PlayerLegStrengthExperience = 0; + double PlayerDexterityExperience = 0; + double PlayerAgilityExperience = 0; + int Arms = 0; + int Legs = 0; + arm* RightArm = Player->GetRightArm(); + + if(RightArm && !RightArm->UseMaterialAttributes()) + { + PlayerArmStrengthExperience += RightArm->GetStrengthExperience(); + PlayerDexterityExperience += RightArm->GetDexterityExperience(); + ++Arms; + } + + arm* LeftArm = Player->GetLeftArm(); + + if(LeftArm && !LeftArm->UseMaterialAttributes()) + { + PlayerArmStrengthExperience += LeftArm->GetStrengthExperience(); + PlayerDexterityExperience += LeftArm->GetDexterityExperience(); + ++Arms; + } + + leg* RightLeg = Player->GetRightLeg(); + + if(RightLeg && !RightLeg->UseMaterialAttributes()) + { + PlayerLegStrengthExperience += RightLeg->GetStrengthExperience(); + PlayerAgilityExperience += RightLeg->GetAgilityExperience(); + ++Legs; + } + + leg* LeftLeg = Player->GetLeftLeg(); + + if(LeftLeg && !LeftLeg->UseMaterialAttributes()) + { + PlayerLegStrengthExperience += LeftLeg->GetStrengthExperience(); + PlayerAgilityExperience += LeftLeg->GetAgilityExperience(); + ++Legs; + } + + if(Arms) + { + AveragePlayerArmStrengthExperience = (49 * AveragePlayerArmStrengthExperience + PlayerArmStrengthExperience / Arms) / 50; + AveragePlayerDexterityExperience = (49 * AveragePlayerDexterityExperience + PlayerDexterityExperience / Arms) / 50; + } + + if(Legs) + { + AveragePlayerLegStrengthExperience = (49 * AveragePlayerLegStrengthExperience + PlayerLegStrengthExperience / Legs) / 50; + AveragePlayerAgilityExperience = (49 * AveragePlayerAgilityExperience + PlayerAgilityExperience / Legs) / 50; + } +} + +void game::CallForAttention(v2 Pos, int RangeSquare) +{ + for(int c = 0; c < GetTeams(); ++c) + { + if(GetTeam(c)->HasEnemy()) + for(std::list::const_iterator i = GetTeam(c)->GetMember().begin(); i != GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled()) + { + long ThisDistance = HypotSquare(long((*i)->GetPos().X) - Pos.X, long((*i)->GetPos().Y) - Pos.Y); + + if(ThisDistance <= RangeSquare && !(*i)->IsGoingSomeWhere()) + (*i)->SetGoingTo(Pos); + } + } +} + +outputfile& operator<<(outputfile& SaveFile, const homedata* HomeData) +{ + if(HomeData) + { + SaveFile.Put(1); + SaveFile << HomeData->Pos << HomeData->Dungeon << HomeData->Level << HomeData->Room; + } + else + SaveFile.Put(0); + + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, homedata*& HomeData) +{ + if(SaveFile.Get()) + { + HomeData = new homedata; + SaveFile >> HomeData->Pos >> HomeData->Dungeon >> HomeData->Level >> HomeData->Room; + } + + return SaveFile; +} + +ulong game::CreateNewCharacterID(character* NewChar) +{ + ulong ID = NextCharacterID++; + + if(CharacterIDMap.find(ID) != CharacterIDMap.end()) + int esko = esko = 2; + + CharacterIDMap.insert(std::make_pair(ID, NewChar)); + return ID; +} + +ulong game::CreateNewItemID(item* NewItem) +{ + ulong ID = NextItemID++; + + if(ItemIDMap.find(ID) != ItemIDMap.end()) + int esko = esko = 2; + + if(NewItem) + ItemIDMap.insert(std::make_pair(ID, NewItem)); + + return ID; +} + +ulong game::CreateNewTrapID(entity* NewTrap) +{ + ulong ID = NextTrapID++; + + if(TrapIDMap.find(ID) != TrapIDMap.end()) + int esko = esko = 2; + + if(NewTrap) + TrapIDMap.insert(std::make_pair(ID, NewTrap)); + + return ID; +} + +character* game::SearchCharacter(ulong ID) +{ + characteridmap::iterator Iterator = CharacterIDMap.find(ID); + return Iterator != CharacterIDMap.end() ? Iterator->second : 0; +} + +item* game::SearchItem(ulong ID) +{ + itemidmap::iterator Iterator = ItemIDMap.find(ID); + return Iterator != ItemIDMap.end() ? Iterator->second : 0; +} + +entity* game::SearchTrap(ulong ID) +{ + trapidmap::iterator Iterator = TrapIDMap.find(ID); + return Iterator != TrapIDMap.end() ? Iterator->second : 0; +} + +outputfile& operator<<(outputfile& SaveFile, const configid& Value) +{ + SaveFile.Write(reinterpret_cast(&Value), sizeof(Value)); + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, configid& Value) +{ + SaveFile.Read(reinterpret_cast(&Value), sizeof(Value)); + return SaveFile; +} + +outputfile& operator<<(outputfile& SaveFile, const dangerid& Value) +{ + SaveFile << Value.NakedDanger << Value.EquippedDanger; + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, dangerid& Value) +{ + SaveFile >> Value.NakedDanger >> Value.EquippedDanger; + return SaveFile; +} + +/* The program can only create directories to the deepness of one, no more... */ + +festring game::GetHomeDir() +{ +#ifdef LINUX + festring Dir; + Dir << getenv("HOME") << '/'; + return Dir; +#endif + +#if defined(WIN32) || defined(__DJGPP__) + return ""; +#endif +} + +festring game::GetSaveDir() +{ +#ifdef LINUX + festring Dir; + Dir << getenv("HOME") << "/IvanSave/"; + return Dir; +#endif + +#if defined(WIN32) || defined(__DJGPP__) + return "Save/"; +#endif +} + +festring game::GetGameDir() +{ +#ifdef LINUX + return DATADIR "/ivan/"; +#endif + +#if defined(WIN32) || defined(__DJGPP__) + return ""; +#endif +} + +festring game::GetBoneDir() +{ +#ifdef LINUX + return LOCAL_STATE_DIR "/Bones/"; +#endif + +#if defined(WIN32) || defined(__DJGPP__) + return "Bones/"; +#endif +} + +level* game::GetLevel(int I) +{ + return GetCurrentDungeon()->GetLevel(I); +} + +int game::GetLevels() +{ + return GetCurrentDungeon()->GetLevels(); +} + +void game::SignalDeath(ccharacter* Ghost, ccharacter* Murderer, festring DeathMsg) +{ + if(InWilderness) + DeathMsg << " in the world map"; + else + DeathMsg << " in " << GetCurrentDungeon()->GetLevelDescription(CurrentLevelIndex); + + massacremap* MassacreMap; + + if(!Murderer) + { + ++MiscMassacreAmount; + MassacreMap = &MiscMassacreMap; + } + else if(Murderer->IsPlayer()) + { + ++PlayerMassacreAmount; + MassacreMap = &PlayerMassacreMap; + } + else if(Murderer->IsPet()) + { + ++PetMassacreAmount; + MassacreMap = &PetMassacreMap; + } + else + { + ++MiscMassacreAmount; + MassacreMap = &MiscMassacreMap; + } + + massacreid MI(Ghost->GetType(), Ghost->GetConfig(), Ghost->GetAssignedName()); + massacremap::iterator i = MassacreMap->find(MI); + + if(i == MassacreMap->end()) + { + i = MassacreMap->insert(std::make_pair(MI, killdata(1, Ghost->GetGenerationDanger()))).first; + i->second.Reason.push_back(killreason(DeathMsg, 1)); + } + else + { + ++i->second.Amount; + i->second.DangerSum += Ghost->GetGenerationDanger(); + std::vector& Reason = i->second.Reason; + uint c; + + for(c = 0; c < Reason.size(); ++c) + if(Reason[c].String == DeathMsg) + { + ++Reason[c].Amount; + break; + } + + if(c == Reason.size()) + Reason.push_back(killreason(DeathMsg, 1)); + } +} + +void game::DisplayMassacreLists() +{ + DisplayMassacreList(PlayerMassacreMap, "directly by you.", PlayerMassacreAmount); + DisplayMassacreList(PetMassacreMap, "by your allies.", PetMassacreAmount); + DisplayMassacreList(MiscMassacreMap, "by some other reason.", MiscMassacreAmount); +} + +struct massacresetentry +{ + bool operator<(const massacresetentry& MSE) const + { + return festring::IgnoreCaseCompare(Key, MSE.Key); + } + festring Key; + festring String; + std::vector Details; + int ImageKey; +}; + +void game::DisplayMassacreList(const massacremap& MassacreMap, cchar* Reason, long Amount) +{ + std::set MassacreSet; + festring FirstPronoun; + truth First = true; + charactervector GraveYard; + + for(massacremap::const_iterator i1 = MassacreMap.begin(); i1 != MassacreMap.end(); ++i1) + { + character* Victim = protocontainer::GetProto(i1->first.Type)->Spawn(i1->first.Config); + Victim->SetAssignedName(i1->first.Name); + massacresetentry Entry; + GraveYard.push_back(Victim); + Entry.ImageKey = AddToCharacterDrawVector(Victim); + + if(i1->second.Amount == 1) + { + Victim->AddName(Entry.Key, UNARTICLED); + Victim->AddName(Entry.String, INDEFINITE); + } + else + { + Victim->AddName(Entry.Key, PLURAL); + Entry.String << i1->second.Amount << ' ' << Entry.Key; + } + + if(First) + { + FirstPronoun = Victim->GetSex() == UNDEFINED ? "it" : Victim->GetSex() == MALE ? "he" : "she"; + First = false; + } + + const std::vector& Reason = i1->second.Reason; + std::vector& Details = Entry.Details; + + if(Reason.size() == 1) + { + festring Begin; + + if(Reason[0].Amount == 1) + Begin = ""; + else if(Reason[0].Amount == 2) + Begin = "both "; + else + Begin = "all "; + + Details.push_back(Begin + Reason[0].String); + } + else + { + for(uint c = 0; c < Reason.size(); ++c) + Details.push_back(CONST_S("") + Reason[c].Amount + ' ' + Reason[c].String); + + std::sort(Details.begin(), Details.end(), ignorecaseorderer()); + } + + MassacreSet.insert(Entry); + } + + long Total = PlayerMassacreAmount + PetMassacreAmount + MiscMassacreAmount; + festring MainTopic; + + if(Total == 1) + MainTopic << "One creature perished during your adventure."; + else + MainTopic << Total << " creatures perished during your adventure."; + + felist List(MainTopic); + SetStandardListAttributes(List); + List.SetPageLength(15); + List.AddFlags(SELECTABLE); + List.SetEntryDrawer(CharacterEntryDrawer); + List.AddDescription(CONST_S("")); + festring SideTopic; + + if(Amount != Total) + { + SideTopic = CONST_S("The following "); + + if(Amount == 1) + SideTopic << "one was killed " << Reason; + else + SideTopic << Amount << " were killed " << Reason; + } + else + { + if(Amount == 1) + { + FirstPronoun.Capitalize(); + SideTopic << FirstPronoun << " was killed " << Reason; + } + else + SideTopic << "They were all killed " << Reason; + } + + List.AddDescription(SideTopic); + List.AddDescription(CONST_S("")); + List.AddDescription("Choose a type of creatures to browse death details."); + std::set::const_iterator i2; + + for(i2 = MassacreSet.begin(); i2 != MassacreSet.end(); ++i2) + List.AddEntry(i2->String, LIGHT_GRAY, 0, i2->ImageKey); + + for(;;) + { + int Chosen = List.Draw(); + + if(Chosen & FELIST_ERROR_BIT) + break; + + felist SubList(CONST_S("Massacre details")); + SetStandardListAttributes(SubList); + SubList.SetPageLength(20); + int Counter = 0; + + for(i2 = MassacreSet.begin(); i2 != MassacreSet.end(); ++i2, ++Counter) + if(Counter == Chosen) + { + for(uint c = 0; c < i2->Details.size(); ++c) + SubList.AddEntry(i2->Details[c], LIGHT_GRAY); + + break; + } + + SubList.Draw(); + } + + ClearCharacterDrawVector(); + + for(uint c = 0; c < GraveYard.size(); ++c) + delete GraveYard[c]; +} + +truth game::MassacreListsEmpty() +{ + return PlayerMassacreMap.empty() && PetMassacreMap.empty() && MiscMassacreMap.empty(); +} + +#ifdef WIZARD + +void game::SeeWholeMap() +{ + if(SeeWholeMapCheatMode < 2) + ++SeeWholeMapCheatMode; + else + SeeWholeMapCheatMode = 0; + + GetCurrentArea()->SendNewDrawRequest(); +} + +#endif + +void game::CreateBone() +{ + if(!WizardModeIsActive() && !IsInWilderness() && RAND() & 3 && GetCurrentLevel()->PreProcessForBone()) + { + int BoneIndex; + festring BoneName; + + for(BoneIndex = 0; BoneIndex < 1000; ++BoneIndex) + { + BoneName = GetBoneDir() + "bon" + CurrentDungeonIndex + CurrentLevelIndex + BoneIndex; + inputfile BoneFile(BoneName, 0, false); + + if(!BoneFile.IsOpen()) + break; + } + + if(BoneIndex != 1000) + { + festring BoneName = GetBoneDir() + "bon" + CurrentDungeonIndex + CurrentLevelIndex + BoneIndex; + outputfile BoneFile(BoneName); + BoneFile << int(BONE_FILE_VERSION) << PlayerName << CurrentLevel; + } + } +} + +truth game::PrepareRandomBone(int LevelIndex) +{ + if(WizardModeIsActive() || GetCurrentDungeon()->IsGenerated(LevelIndex) || !*GetCurrentDungeon()->GetLevelScript(LevelIndex)->CanGenerateBone()) + return false; + + int BoneIndex; + festring BoneName; + + for(BoneIndex = 0; BoneIndex < 1000; ++BoneIndex) + { + BoneName = GetBoneDir() + "bon" + CurrentDungeonIndex + LevelIndex + BoneIndex; + inputfile BoneFile(BoneName, 0, false); + + if(BoneFile.IsOpen() && !(RAND() & 7)) + { + if(ReadType(BoneFile) != BONE_FILE_VERSION) + { + BoneFile.Close(); + remove(BoneName.CStr()); + continue; + } + + festring Name; + BoneFile >> Name; + level* NewLevel = GetCurrentDungeon()->LoadLevel(BoneFile, LevelIndex); + + if(!NewLevel->PostProcessForBone()) + { + delete NewLevel; + GetBoneItemIDMap().clear(); + GetBoneCharacterIDMap().clear(); + continue; + } + + NewLevel->FinalProcessForBone(); + GetBoneItemIDMap().clear(); + GetBoneCharacterIDMap().clear(); + SetCurrentArea(NewLevel); + CurrentLevel = NewLevel; + CurrentLSquareMap = NewLevel->GetMap(); + GetCurrentDungeon()->SetIsGenerated(LevelIndex, true); + + if(Name == PlayerName) + ADD_MESSAGE("This place is oddly familiar. Like you had been here in one of your past lives."); + else + ADD_MESSAGE("You smell the stench of death."); + + break; + } + } + + Generating = true; + + if(BoneIndex != 1000) + { + remove(BoneName.CStr()); + return true; + } + else + return false; +} + +double game::CalculateAverageDanger(const charactervector& EnemyVector, character* Char) +{ + double DangerSum = 0; + int Enemies = 0; + + for(uint c = 0; c < EnemyVector.size(); ++c) + { + DangerSum += EnemyVector[c]->GetRelativeDanger(Char, true); + ++Enemies; + } + + return DangerSum / Enemies; +} + +double game::CalculateAverageDangerOfAllNormalEnemies() +{ + double DangerSum = 0; + int Enemies = 0; + + for(int c1 = 1; c1 < protocontainer::GetSize(); ++c1) + { + const character::prototype* Proto = protocontainer::GetProto(c1); + const character::database*const* ConfigData = Proto->GetConfigData(); + int ConfigSize = Proto->GetConfigSize(); + + for(int c2 = 0; c2 < ConfigSize; ++c2) + if(!ConfigData[c2]->IsAbstract + && !ConfigData[c2]->IsUnique + && ConfigData[c2]->CanBeGenerated) + { + DangerSum += DangerMap.find(configid(c1, ConfigData[c2]->Config))->second.EquippedDanger; + ++Enemies; + } + } + + return DangerSum / Enemies; +} + +character* game::CreateGhost() +{ + double AverageDanger = CalculateAverageDangerOfAllNormalEnemies(); + charactervector EnemyVector; + protosystem::CreateEveryNormalEnemy(EnemyVector); + ghost* Ghost = ghost::Spawn(); + Ghost->SetTeam(GetTeam(MONSTER_TEAM)); + Ghost->SetGenerationDanger(CurrentLevel->GetDifficulty()); + Ghost->SetOwnerSoul(PlayerName); + Ghost->SetIsActive(false); + Ghost->EditAllAttributes(-4); + Player->SetSoulID(Ghost->GetID()); + while(CalculateAverageDanger(EnemyVector, Ghost) > AverageDanger && Ghost->EditAllAttributes(1)); + + for(uint c = 0; c < EnemyVector.size(); ++c) + delete EnemyVector[c]; + + return Ghost; +} + +int game::GetMoveCommandKey(int I) +{ + if(!ivanconfig::GetUseAlternativeKeys()) + return MoveNormalCommandKey[I]; + else + return MoveAbnormalCommandKey[I]; +} + +long game::GetScore() +{ + double Counter = 0; + massacremap::const_iterator i; + massacremap SumMap = PlayerMassacreMap; + + for(i = PetMassacreMap.begin(); i != PetMassacreMap.end(); ++i) + { + killdata& KillData = SumMap[i->first]; + KillData.Amount += i->second.Amount; + KillData.DangerSum += i->second.DangerSum; + } + + for(i = SumMap.begin(); i != SumMap.end(); ++i) + { + character* Char = protocontainer::GetProto(i->first.Type)->Spawn(i->first.Config); + int SumOfAttributes = Char->GetSumOfAttributes(); + Counter += sqrt(i->second.DangerSum / DEFAULT_GENERATION_DANGER) * SumOfAttributes * SumOfAttributes; + delete Char; + } + + return long(0.01 * Counter); +} + +/* Only works if New Attnam is loaded */ + +truth game::TweraifIsFree() +{ + for(std::list::const_iterator i = GetTeam(COLONIST_TEAM)->GetMember().begin(); i != GetTeam(COLONIST_TEAM)->GetMember().end(); ++i) + if((*i)->IsEnabled()) + return false; + + return true; +} + +truth game::IsXMas() // returns true if date is christmaseve or day +{ + time_t Time = time(0); + struct tm* TM = localtime(&Time); + return (TM->tm_mon == 11 && (TM->tm_mday == 24 || TM->tm_mday == 25)); +} + +int game::AddToItemDrawVector(const itemvector& What) +{ + ItemDrawVector.push_back(What); + return ItemDrawVector.size() - 1; +} + +v2 ItemDisplacement[3][3] = +{ + { v2(0, 0), ERROR_V2, ERROR_V2 }, + { v2(-2, -2), v2(2, 2), ERROR_V2 }, + { v2(-4, -4), v2(0, 0), v2(4, 4) } +}; + +void game::ItemEntryDrawer(bitmap* Bitmap, v2 Pos, uint I) +{ + blitdata B = { Bitmap, + { 0, 0 }, + { 0, 0 }, + { TILE_SIZE, TILE_SIZE }, + { NORMAL_LUMINANCE }, + TRANSPARENT_COLOR, + ALLOW_ANIMATE }; + + itemvector ItemVector = ItemDrawVector[I]; + int Amount = Min(ItemVector.size(), 3); + + for(int c = 0; c < Amount; ++c) + { + v2 Displacement = ItemDisplacement[Amount - 1][c]; + + if(!ItemVector[0]->HasNormalPictureDirection()) + Displacement.X = -Displacement.X; + + B.Dest = Pos + Displacement; + + if(ItemVector[c]->AllowAlphaEverywhere()) + B.CustomData |= ALLOW_ALPHA; + + ItemVector[c]->Draw(B); + B.CustomData &= ~ALLOW_ALPHA; + } + + if(ItemVector.size() > 3) + { + B.Src.X = 0; + B.Src.Y = 16; + B.Dest = ItemVector[0]->HasNormalPictureDirection() ? Pos + v2(11, -2) : Pos + v2(-2, -2); + B.Flags = 0; + igraph::GetSymbolGraphic()->NormalMaskedBlit(B); + } +} + +int game::AddToCharacterDrawVector(character* What) +{ + CharacterDrawVector.push_back(What); + return CharacterDrawVector.size() - 1; +} + +void game::CharacterEntryDrawer(bitmap* Bitmap, v2 Pos, uint I) +{ + if(CharacterDrawVector[I]) + { + blitdata B = { Bitmap, + { 0, 0 }, + { Pos.X, Pos.Y }, + { TILE_SIZE, TILE_SIZE }, + { NORMAL_LUMINANCE }, + TRANSPARENT_COLOR, + ALLOW_ANIMATE|ALLOW_ALPHA }; + + CharacterDrawVector[I]->DrawBodyParts(B); + } +} + +void game::GodEntryDrawer(bitmap* Bitmap, v2 Pos, uint I) +{ + blitdata B = { Bitmap, + { I << 4, 0 }, + { Pos.X, Pos.Y }, + { TILE_SIZE, TILE_SIZE }, + { 0 }, + TRANSPARENT_COLOR, + 0 }; + + igraph::GetSymbolGraphic()->NormalMaskedBlit(B); +} + +character* game::GetSumo() +{ + return GetCurrentLevel()->GetLSquare(SUMO_ROOM_POS)->GetRoom()->GetMaster(); +} + +truth game::TryToEnterSumoArena() +{ + character* Sumo = GetSumo(); + + if(!Sumo || !Sumo->IsEnabled() || Sumo->GetRelation(Player) == HOSTILE || !Player->CanBeSeenBy(Sumo)) + return true; + + if(TweraifIsFree()) + { + ADD_MESSAGE("\"You started this stupid revolution, after which I've been constantly hungry. Get lost!\""); + return false; + } + + if(PlayerIsSumoChampion()) + { + ADD_MESSAGE("\"I don't really enjoy losing, especially many times to the same guy. Go away.\""); + return false; + } + + if(Player->IsPolymorphed()) + { + ADD_MESSAGE("\"Don't try to cheat. Come back when you're normal again.\""); + return false; + } + + if(Player->GetHungerState() < SATIATED) + { + ADD_MESSAGE("\"Your figure is too slender for this sport. Eat a lot more and come back.\""); + return false; + } + + if(Player->GetHungerState() < BLOATED) + { + ADD_MESSAGE("\"You're still somewhat too thin. Eat some more and we'll compete.\""); + return false; + } + + ADD_MESSAGE("\"So you want to compete? Okay, I'll explain the rules. First, I'll make a mirror image out of us both. We'll enter the arena and fight till one is knocked out. Use of any equipment is not allowed. Note that we will not gain experience from fighting as a mirror image, but won't get really hurt, either. However, controlling the image is exhausting and you can get hungry very quickly.\""); + + if(!TruthQuestion("Do you want to challenge him? [y/N]")) + return false; + + SumoWrestling = true; + character* MirrorPlayer = Player->Duplicate(IGNORE_PROHIBITIONS); + character* MirrorSumo = Sumo->Duplicate(IGNORE_PROHIBITIONS); + SetPlayer(MirrorPlayer); + charactervector Spectators; + + if(Player->GetTeam()->GetRelation(GetTeam(TOURIST_GUIDE_TEAM)) != HOSTILE + && Player->GetTeam()->GetRelation(GetTeam(TOURIST_TEAM)) != HOSTILE) + { + GetTeam(TOURIST_GUIDE_TEAM)->MoveMembersTo(Spectators); + GetTeam(TOURIST_TEAM)->MoveMembersTo(Spectators); + } + + GetCurrentDungeon()->SaveLevel(SaveName(), 0); + charactervector test; + EnterArea(test, 1, STAIRS_UP); + MirrorSumo->PutTo(SUMO_ARENA_POS + v2(6, 5)); + MirrorSumo->ChangeTeam(GetTeam(SUMO_TEAM)); + GetCurrentLevel()->GetLSquare(SUMO_ARENA_POS)->GetRoom()->SetMasterID(MirrorSumo->GetID()); + + for(uint c = 0; c < Spectators.size(); ++c) + Spectators[c]->PutToOrNear(SUMO_ARENA_POS + v2(6, 10)); + + throw areachangerequest(); + return true; +} + +truth game::TryToExitSumoArena() +{ + if(GetTeam(PLAYER_TEAM)->GetRelation(GetTeam(NEW_ATTNAM_TEAM)) == HOSTILE) + return true; + + itemvector IVector; + charactervector CVector; + + if(IsSumoWrestling()) + { + if(TruthQuestion("Do you really wish to give up? [y/N]")) + return EndSumoWrestling(LOST); + else + return false; + } + else + { + Player->Remove(); + GetCurrentLevel()->CollectEverything(IVector, CVector); + GetCurrentDungeon()->SaveLevel(SaveName(), 1); + std::vector test; + EnterArea(test, 0, STAIRS_DOWN); + Player->GetStackUnder()->AddItems(IVector); + + if(!IVector.empty()) + { + character* Sumo = GetSumo(); + + if(Sumo && Sumo->GetRelation(Player) != HOSTILE && Player->CanBeSeenBy(Sumo)) + ADD_MESSAGE("\"Don't leave anything there, please.\""); + } + + v2 PlayerPos = Player->GetPos(); + + for(uint c = 0; c < CVector.size(); ++c) + CVector[c]->PutNear(PlayerPos); + + throw areachangerequest(); + return true; + } +} + +truth game::EndSumoWrestling(int Result) +{ + msgsystem::LeaveBigMessageMode(); + + if(Result == LOST) + AskForKeyPress("You lose. [press any key to continue]"); + else if(Result == WON) + AskForKeyPress("You win! [press any key to continue]"); + else if(Result == DISQUALIFIED) + AskForKeyPress("You are disqualified! [press any key to continue]"); + + character* Sumo = GetCurrentLevel()->GetLSquare(SUMO_ARENA_POS)->GetRoom()->GetMaster(); + + /* We'll make a throw soon so deletes are allowed */ + + if(Sumo) + { + Sumo->Remove(); + delete Sumo; + } + + Player->Remove(); + delete Player; + SetPlayer(0); + itemvector IVector; + charactervector CVector; + GetCurrentLevel()->CollectEverything(IVector, CVector); + GetCurrentDungeon()->SaveLevel(SaveName(), 1); + charactervector test; + EnterArea(test, 0, STAIRS_DOWN); + SumoWrestling = false; + Player->GetStackUnder()->AddItems(IVector); + v2 PlayerPos = Player->GetPos(); + + for(uint c = 0; c < CVector.size(); ++c) + CVector[c]->PutNear(PlayerPos); + + if(Result == LOST) + ADD_MESSAGE("\"I hope you've learned your lesson now!\""); + else if(Result == DISQUALIFIED) + ADD_MESSAGE("\"Don't do that again or I'll be really angry!\""); + else + { + PlayerSumoChampion = true; + character* Sumo = GetSumo(); + festring Msg = Sumo->GetName(DEFINITE) + " seems humbler than before. \"Darn. You bested me.\n"; + Msg << "Here's a little something as a reward\", " << Sumo->GetPersonalPronoun() << " says and hands you a belt of levitation.\n\""; + (belt::Spawn(BELT_OF_LEVITATION))->MoveTo(Player->GetStack()); + Msg << "Allow me to also teach you a few nasty martial art tricks the years have taught me.\""; + Player->GetCWeaponSkill(UNARMED)->AddHit(100000); + Player->GetCWeaponSkill(KICK)->AddHit(100000); + character* Imperialist = GetCurrentLevel()->GetLSquare(5, 5)->GetRoom()->GetMaster(); + + if(Imperialist && Imperialist->GetRelation(Player) != HOSTILE) + { + v2 Pos = Player->GetPos() + v2(0, 1); + GetCurrentLevel()->GetLSquare(Pos)->KickAnyoneStandingHereAway(); + Imperialist->Remove(); + Imperialist->PutTo(Pos); + Msg << "\n\nSuddenly you notice " << Imperialist->GetName(DEFINITE) << " has also entered.\n" + "\"I see we have a promising fighter among us. I had already heard of your\n" + "adventures outside the village, but hardly could I believe that one day you\n" + "would defeat even the mighty Huang Ming Pong! A hero such as you is bound\n" + "to become world famous, and can earn a fortune if wealthy sponsors are behind\n" + "him. May I therefore propose a mutually profitable contract: I'll give you this\n" + "nice shirt with my company's ad, and you'll wear it as you journey bravely to\n" + "the unknown and fight epic battles against the limitless minions of evil. I'll\n" + "reward you well when you return, depending on how much you have used it.\""; + Player->GetStack()->AddItem(decosadshirt::Spawn()); + } + + TextScreen(Msg); + GetCurrentArea()->SendNewDrawRequest(); + DrawEverything(); + } + + Player->EditNP(-25000); + Player->CheckStarvationDeath(CONST_S("exhausted after controlling a mirror image for too long")); + throw areachangerequest(); + return true; +} + +rain* game::ConstructGlobalRain() +{ + return new rain(GlobalRainLiquid, static_cast(GetSquareInLoad()), GlobalRainSpeed, MONSTER_TEAM, false); +} + +v2 game::GetSunLightDirectionVector() +{ + int Index = Tick % 48000 / 1000; + + /* Should have the same sign as sin(PI * Index / 24) and XTable[Index] / + YTable[Index] should equal roughly -tan(PI * Index / 24). Also, vector + (XTable[Index], YTable[Index]) + P should not be a valid position of + any possible level L for any P belonging to L. */ + + static int XTable[48] = { 0, 1000, 1000, 1000, 1000, 1000, + 1000, 1303, 1732, 2414, 3732, 7596, + 1000, 7596, 3732, 2414, 1732, 1303, + 1000, 1000, 1000, 1000, 1000, 1000, + 0, -1000, -1000, -1000, -1000, -1000, + -1000, -1303, -1732, -2414, -3732, -7596, + -1000, -7596, -3732, -2414, -1732, -1303, + -1000, -1000, -1000, -1000, -1000, -1000 }; + + /* Should have the same sign as -cos(PI * Index / 24) */ + + static int YTable[48] = { -1000, -7596, -3732, -2414, -1732, -1303, + -1000, -1000, -1000, -1000, -1000, -1000, + 0, 1000, 1000, 1000, 1000, 1000, + 1000, 1303, 1732, 2414, 3732, 7596, + 1000, 7596, 3732, 2414, 1732, 1303, + 1000, 1000, 1000, 1000, 1000, 1000, + 0, -1000, -1000, -1000, -1000, -1000, + -1000, -1303, -1732, -2414, -3732, -7596 }; + + return v2(XTable[Index], YTable[Index]); +} + +int game::CalculateMinimumEmitationRadius(col24 E) +{ + int MaxElement = Max(GetRed24(E), GetGreen24(E), GetBlue24(E)); + return int(sqrt(double(MaxElement << 7) / LIGHT_BORDER - 120.)); +} + +ulong game::IncreaseSquarePartEmitationTicks() +{ + if((SquarePartEmitationTick += 2) == 0x100) + { + CurrentLevel->InitSquarePartEmitationTicks(); + SquarePartEmitationTick = 2; + } + + return SquarePartEmitationTick; +} + +void game::Wish(character* Wisher, cchar* MsgSingle, cchar* MsgPair) +{ + for(;;) + { + festring Temp = DefaultQuestion(CONST_S("What do you want to wish for?"), + DefaultWish); + item* TempItem = protosystem::CreateItem(Temp, Wisher->IsPlayer()); + + if(TempItem) + { + Wisher->GetStack()->AddItem(TempItem); + TempItem->SpecialGenerationHandler(); + + if(TempItem->HandleInPairs()) + ADD_MESSAGE(MsgPair, TempItem->CHAR_NAME(PLURAL)); + else + ADD_MESSAGE(MsgSingle, TempItem->CHAR_NAME(INDEFINITE)); + + return; + } + } +} + +festring game::DefaultQuestion(festring Topic, festring& Default, stringkeyhandler KeyHandler) +{ + festring ShortDefault = Default; + + if(Default.GetSize() > 29) + { + ShortDefault.Resize(27); + ShortDefault = ShortDefault << CONST_S("..."); + } + + if(!Default.IsEmpty()) + Topic << " [" << ShortDefault << ']'; + + festring Answer = StringQuestion(Topic, WHITE, 0, 80, false, KeyHandler); + + if(Answer.IsEmpty()) + Answer = Default; + + return Default = Answer; +} + +void game::GetTime(ivantime& Time) +{ + Time.Hour = 12 + Tick / 2000; + Time.Day = Time.Hour / 24 + 1; + Time.Hour %= 24; + Time.Min = Tick % 2000 * 60 / 2000; +} + +truth NameOrderer(character* C1, character* C2) +{ + return festring::IgnoreCaseCompare(C1->GetName(UNARTICLED), C2->GetName(UNARTICLED)); +} + +truth game::PolymorphControlKeyHandler(int Key, festring& String) +{ + if(Key == '?') + { + felist List(CONST_S("List of known creatures and their intelligence requirements")); + SetStandardListAttributes(List); + List.SetPageLength(15); + List.AddFlags(SELECTABLE); + protosystem::CreateEverySeenCharacter(CharacterDrawVector); + std::sort(CharacterDrawVector.begin(), CharacterDrawVector.end(), NameOrderer); + List.SetEntryDrawer(CharacterEntryDrawer); + std::vector StringVector; + uint c; + + for(c = 0; c < CharacterDrawVector.size(); ++c) + { + character* Char = CharacterDrawVector[c]; + + if(Char->CanBeWished()) + { + festring Entry; + Char->AddName(Entry, UNARTICLED); + StringVector.push_back(Entry); + int Req = Char->GetPolymorphIntelligenceRequirement(); + + if(Char->IsSameAs(Player) + || (Player->GetPolymorphBackup() + && Player->GetPolymorphBackup()->IsSameAs(Char))) + Req = 0; + + Entry << " (" << Req << ')'; + int Int = Player->GetAttribute(INTELLIGENCE); + List.AddEntry(Entry, Req > Int ? RED : LIGHT_GRAY, 0, c); + } + } + + int Chosen = List.Draw(); + + for(c = 0; c < CharacterDrawVector.size(); ++c) + delete CharacterDrawVector[c]; + + if(!(Chosen & FELIST_ERROR_BIT)) + String = StringVector[Chosen]; + + CharacterDrawVector.clear(); + return true; + } + + return false; +} + +outputfile& operator<<(outputfile& SaveFile, const killdata& Value) +{ + SaveFile << Value.Amount << Value.DangerSum << Value.Reason; + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, killdata& Value) +{ + SaveFile >> Value.Amount >> Value.DangerSum >> Value.Reason; + return SaveFile; +} + +outputfile& operator<<(outputfile& SaveFile, const killreason& Value) +{ + SaveFile << Value.Amount << Value.String; + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, killreason& Value) +{ + SaveFile >> Value.Amount >> Value.String; + return SaveFile; +} + +truth DistanceOrderer(character* C1, character* C2) +{ + v2 PlayerPos = PLAYER->GetPos(); + v2 Pos1 = C1->GetPos(); + v2 Pos2 = C2->GetPos(); + int D1 = Max(abs(Pos1.X - PlayerPos.X), abs(Pos1.Y - PlayerPos.Y)); + int D2 = Max(abs(Pos2.X - PlayerPos.X), abs(Pos2.Y - PlayerPos.Y)); + + if(D1 != D2) + return D1 < D2; + else if(Pos1.Y != Pos2.Y) + return Pos1.Y < Pos2.Y; + else + return Pos1.X < Pos2.X; +} + +truth game::FillPetVector(cchar* Verb) +{ + PetVector.clear(); + team* Team = GetTeam(PLAYER_TEAM); + + for(std::list::const_iterator i = Team->GetMember().begin(); + i != Team->GetMember().end(); ++i) + if((*i)->IsEnabled() && !(*i)->IsPlayer() && (*i)->CanBeSeenByPlayer()) + PetVector.push_back(*i); + + if(PetVector.empty()) + { + ADD_MESSAGE("You don't detect any friends to %s.", Verb); + return false; + } + + std::sort(PetVector.begin(), PetVector.end(), DistanceOrderer); + LastPetUnderCursor = PetVector[0]; + return true; +} + +truth game::CommandQuestion() +{ + if(!FillPetVector("command")) + return false; + + character* Char; + + if(PetVector.size() == 1) + Char = PetVector[0]; + else + { + v2 Pos = PetVector[0]->GetPos(); + Pos = PositionQuestion(CONST_S("Whom do you wish to command? [direction keys/'+'/'-'/'a'll/space/esc]"), Pos, &PetHandler, &CommandKeyHandler); + + if(Pos == ERROR_V2) + return false; + + if(Pos == ABORT_V2) + return true; + + Char = CurrentArea->GetSquare(Pos)->GetCharacter(); + + if(!Char || !Char->CanBeSeenByPlayer()) + { + ADD_MESSAGE("You don't see anyone here to command."); + return false; + } + + if(Char->IsPlayer()) + { + ADD_MESSAGE("You do that all the time."); + return false; + } + + if(!Char->IsPet()) + { + ADD_MESSAGE("%s refuses to be commanded by you.", Char->CHAR_NAME(DEFINITE)); + return false; + } + } + + return Char->IssuePetCommands(); +} + +void game::NameQuestion() +{ + if(!FillPetVector("name")) + return; + + if(PetVector.size() == 1) + PetVector[0]->TryToName(); + else + PositionQuestion(CONST_S("Who do you want to name? [direction keys/'+'/'-'/'n'ame/esc]"), PetVector[0]->GetPos(), &PetHandler, &NameKeyHandler); +} + +void game::PetHandler(v2 CursorPos) +{ + character* Char = CurrentArea->GetSquare(CursorPos)->GetCharacter(); + + if(Char && Char->CanBeSeenByPlayer() && Char->IsPet() && !Char->IsPlayer()) + CursorData = RED_CURSOR|CURSOR_TARGET; + else + CursorData = RED_CURSOR; + + if(Char && !Char->IsPlayer() && Char->IsPet()) + LastPetUnderCursor = Char; +} + +v2 game::CommandKeyHandler(v2 CursorPos, int Key) +{ + if(SelectPet(Key)) + return LastPetUnderCursor->GetPos(); + else if(Key == 'a' || Key == 'A') + return CommandAll() ? ABORT_V2 : ERROR_V2; + + return CursorPos; +} + +truth game::SelectPet(int Key) +{ + if(Key == '+') + { + for(uint c = 0; c < PetVector.size(); ++c) + if(PetVector[c] == LastPetUnderCursor) + { + if(++c == PetVector.size()) + c = 0; + + LastPetUnderCursor = PetVector[c]; + return true; + } + } + else if(Key == '-') + { + for(uint c = 0; c < PetVector.size(); ++c) + if(PetVector[c] == LastPetUnderCursor) + { + if(!c) + c = PetVector.size(); + + LastPetUnderCursor = PetVector[--c]; + return true; + } + } + + return false; +} + +void game::CommandScreen(cfestring& Topic, ulong PossibleFlags, ulong ConstantFlags, ulong& VaryFlags, ulong& Flags) +{ + static cchar* CommandDescription[COMMAND_FLAGS] = + { + "Follow me", + "Flee from enemies", + "Don't change your equipment", + "Don't consume anything valuable" + }; + + felist List(Topic); + SetStandardListAttributes(List); + List.AddFlags(SELECTABLE); + List.AddDescription(CONST_S("")); + List.AddDescription(CONST_S("Command Active?")); + + for(;;) + { + int c, i; + + for(c = 0; c < COMMAND_FLAGS; ++c) + if(1 << c & PossibleFlags) + { + truth Changeable = !(1 << c & ConstantFlags); + festring Entry; + + if(Changeable) + { + Entry = CommandDescription[c]; + Entry.Resize(60); + } + else + { + Entry << " " << CommandDescription[c]; + Entry.Resize(63); + } + + if(1 << c & VaryFlags) + Entry << "varies"; + else + Entry << (1 << c & Flags ? "yes" : "no"); + + List.AddEntry(Entry, Changeable ? LIGHT_GRAY : DARK_GRAY, 0, NO_IMAGE, Changeable); + } + + int Chosen = List.Draw(); + + if(Chosen & FELIST_ERROR_BIT) + return; + + for(c = 0, i = 0; c < COMMAND_FLAGS; ++c) + if(1 << c & PossibleFlags && !(1 << c & ConstantFlags) && i++ == Chosen) + { + if(1 << c & VaryFlags) + { + VaryFlags &= ~(1 << c); + Flags |= 1 << c; + } + else + Flags ^= 1 << c; + + break; + } + + List.Empty(); + DrawEverythingNoBlit(); + } +} + +truth game::CommandAll() +{ + ulong PossibleFlags = 0, ConstantFlags = ALL_COMMAND_FLAGS, VaryFlags = 0, OldFlags = 0; + uint c1, c2; + + for(c1 = 0; c1 < PetVector.size(); ++c1) + { + ConstantFlags &= PetVector[c1]->GetConstantCommandFlags(); + ulong C = PetVector[c1]->GetCommandFlags(); + ulong ThisPossible = PetVector[c1]->GetPossibleCommandFlags(); + + for(c2 = 0; c2 < COMMAND_FLAGS; ++c2) + if(1 << c2 & PossibleFlags & ThisPossible + && (1 << c2 & C) != (1 << c2 & OldFlags)) + VaryFlags |= 1 << c2; + + PossibleFlags |= ThisPossible; + OldFlags |= C & ThisPossible; + } + + if(!PossibleFlags) + { + ADD_MESSAGE("Not a single creature in your visible team can be commanded."); + return false; + } + + ulong NewFlags = OldFlags; + CommandScreen(CONST_S("Issue commands to whole visible team"), PossibleFlags, ConstantFlags, VaryFlags, NewFlags); + truth Change = false; + + for(c1 = 0; c1 < PetVector.size(); ++c1) + { + character* Char = PetVector[c1]; + + if(!Char->IsConscious()) + continue; + + ulong OldC = Char->GetCommandFlags(); + ulong ConstC = Char->GetConstantCommandFlags(); + ulong ThisC = NewFlags + & Char->GetPossibleCommandFlags() + & ~(ConstC|VaryFlags) + | (OldC & (ConstC|VaryFlags)); + + if(ThisC != OldC) + Change = true; + + Char->SetCommandFlags(ThisC); + } + + if(!Change) + return false; + + Player->EditAP(-500); + Player->EditExperience(CHARISMA, 50, 1 << 7); + return true; +} + +col16 game::GetAttributeColor(int I) +{ + int Delta = GetTick() - LastAttributeChangeTick[I]; + + if(OldAttribute[I] == NewAttribute[I] || Delta >= 510) + return WHITE; + else if(OldAttribute[I] < NewAttribute[I]) + return MakeRGB16(255, 255, Delta >> 1); + else + return MakeRGB16(255, Delta >> 1, Delta >> 1); +} + +void game::UpdateAttributeMemory() +{ + for(int c = 0; c < ATTRIBUTES; ++c) + { + int A = Player->GetAttribute(c); + + if(A != NewAttribute[c]) + { + OldAttribute[c] = NewAttribute[c]; + NewAttribute[c] = A; + LastAttributeChangeTick[c] = GetTick(); + } + } +} + +void game::InitAttributeMemory() +{ + for(int c = 0; c < ATTRIBUTES; ++c) + OldAttribute[c] = NewAttribute[c] = Player->GetAttribute(c); +} + +void game::TeleportHandler(v2 CursorPos) +{ + if((CursorPos - Player->GetPos()).GetLengthSquare() > Player->GetTeleportRangeSquare()) + CursorData = BLUE_CURSOR|CURSOR_TARGET; + else + CursorData = RED_CURSOR|CURSOR_TARGET; +} + +double game::GetGameSituationDanger() +{ + double SituationDanger = 0; + character* Player = GetPlayer(); + truth PlayerStuck = Player->IsStuck(); + v2 PlayerPos = Player->GetPos(); + character* TruePlayer = Player; + + if(PlayerStuck) + (Player = Player->Duplicate(IGNORE_PROHIBITIONS))->ChangeTeam(0); + + for(int c1 = 0; c1 < GetTeams(); ++c1) + if(GetTeam(c1)->GetRelation(GetTeam(PLAYER_TEAM)) == HOSTILE) + for(std::list::const_iterator i1 = GetTeam(c1)->GetMember().begin(); + i1 != GetTeam(c1)->GetMember().end(); ++i1) + { + character* Enemy = *i1; + + if(Enemy->IsEnabled() && Enemy->CanAttack() + && (Enemy->CanMove() || Enemy->GetPos().IsAdjacent(PlayerPos))) + { + truth EnemyStuck = Enemy->IsStuck(); + v2 EnemyPos = Enemy->GetPos(); + truth Sees = TruePlayer->CanBeSeenBy(Enemy); + character* TrueEnemy = Enemy; + + if(EnemyStuck) + Enemy = Enemy->Duplicate(IGNORE_PROHIBITIONS); + + double PlayerTeamDanger = 1 / Enemy->GetSituationDanger(Player, EnemyPos, PlayerPos, Sees); + + for(int c2 = 0; c2 < GetTeams(); ++c2) + if(GetTeam(c2)->GetRelation(GetTeam(c1)) == HOSTILE) + for(std::list::const_iterator i2 = GetTeam(c2)->GetMember().begin(); + i2 != GetTeam(c2)->GetMember().end(); ++i2) + { + character* Friend = *i2; + + if(Friend->IsEnabled() && !Friend->IsPlayer() && Friend->CanAttack() + && (Friend->CanMove() || Friend->GetPos().IsAdjacent(EnemyPos))) + { + v2 FriendPos = Friend->GetPos(); + truth Sees = TrueEnemy->CanBeSeenBy(Friend); + + if(Friend->IsStuck()) + { + Friend = Friend->Duplicate(IGNORE_PROHIBITIONS); + PlayerTeamDanger += Friend->GetSituationDanger(Enemy, FriendPos, EnemyPos, Sees) * .2; + delete Friend; + } + else + PlayerTeamDanger += Friend->GetSituationDanger(Enemy, FriendPos, EnemyPos, Sees); + } + } + + if(EnemyStuck) + { + PlayerTeamDanger *= 5; + delete Enemy; + } + + SituationDanger += 1 / PlayerTeamDanger; + } + } + + Player->ModifySituationDanger(SituationDanger); + + if(PlayerStuck) + { + SituationDanger *= 2; + delete Player; + } + + return SituationDanger; +} + +long game::GetTimeSpent() +{ + return time::TimeAdd(time::TimeDifference(time(0),LastLoad), TimePlayedBeforeLastLoad); +} + +outputfile& operator<<(outputfile& SaveFile, const massacreid& MI) +{ + SaveFile << MI.Type << MI.Config << MI.Name; + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, massacreid& MI) +{ + SaveFile >> MI.Type >> MI.Config >> MI.Name; + return SaveFile; +} + +truth game::PlayerIsRunning() +{ + return PlayerRunning && Player->CanMove(); +} + +void game::AddSpecialCursor(v2 Pos, int Data) +{ + SpecialCursorPos.push_back(Pos); + SpecialCursorData.push_back(Data); +} + +void game::RemoveSpecialCursors() +{ + SpecialCursorPos.clear(); + SpecialCursorData.clear(); +} + +void game::LearnAbout(god* Who) +{ + Who->SetIsKnown(true); + + /* slightly slow, but doesn't matter since + this is run so rarely */ + + if(PlayerKnowsAllGods() && !game::PlayerHasReceivedAllGodsKnownBonus) + { + GetPlayer()->ApplyAllGodsKnownBonus(); + game::PlayerHasReceivedAllGodsKnownBonus = true; + } +} + +truth game::PlayerKnowsAllGods() +{ + for(int c = 1; c <= GODS; ++c) + if(!GetGod(c)->IsKnown()) + return false; + + return true; +} + +void game::AdjustRelationsToAllGods(int Amount) +{ + for(int c = 1; c <= GODS; ++c) + GetGod(c)->AdjustRelation(Amount); +} + +void game::SetRelationsToAllGods(int Amount) +{ + for(int c = 1; c <= GODS; ++c) + GetGod(c)->SetRelation(Amount); +} + +void game::ShowDeathSmiley(bitmap* Buffer, truth) +{ + static blitdata B = { 0, + { 0, 0 }, + { (RES.X >> 1) - 24, RES.Y * 4 / 7 - 24 }, + { 48, 48 }, + { 0 }, + TRANSPARENT_COLOR, + 0 }; + + int Tick = globalwindowhandler::UpdateTick(); + + if(((Tick >> 1) & 31) == 1) + B.Src.X = 48; + else if(((Tick >> 1) & 31) == 2) + B.Src.X = 96; + else + B.Src.X = 0; + + B.Bitmap = Buffer; + igraph::GetSmileyGraphic()->NormalBlit(B); + + if(Buffer == DOUBLE_BUFFER) + graphics::BlitDBToScreen(); +} diff --git a/Main/Source/gear.cpp b/Main/Source/gear.cpp new file mode 100644 index 0000000..29fa8e3 --- /dev/null +++ b/Main/Source/gear.cpp @@ -0,0 +1,1737 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through itemset.cpp */ + +void meleeweapon::SetSecondaryMaterial(material* What, int SpecialFlags) { SetMaterial(SecondaryMaterial, What, GetDefaultSecondaryVolume(), SpecialFlags); } +void meleeweapon::ChangeSecondaryMaterial(material* What, int SpecialFlags) { ChangeMaterial(SecondaryMaterial, What, GetDefaultSecondaryVolume(), SpecialFlags); } +void meleeweapon::InitMaterials(material* M1, material* M2, truth CUP) { ObjectInitMaterials(MainMaterial, M1, GetDefaultMainVolume(), SecondaryMaterial, M2, GetDefaultSecondaryVolume(), CUP); } +double meleeweapon::GetTHVBonus() const { return Enchantment * .5; } +double meleeweapon::GetDamageBonus() const { return Enchantment; } +col16 meleeweapon::GetDripColor() const { return Fluid[0]->GetLiquid()->GetColor(); } +truth meleeweapon::IsDippable(ccharacter*) const { return !Fluid; } +truth meleeweapon::AllowRegularColors() const { return SecondaryMaterial->GetVolume(); } +v2 meleeweapon::GetWieldedBitmapPos(int I) const { return SecondaryMaterial->GetVolume() ? item::GetWieldedBitmapPos(I) : v2(160, 128); } +void meleeweapon::InitMaterials(const materialscript* M, const materialscript* S, truth CUP) { InitMaterials(M->Instantiate(), S->Instantiate(), CUP); } + +col16 justifier::GetOutlineColor(int) const { return MakeRGB16(0, 255, 0); } + +col16 neercseulb::GetOutlineColor(int) const { return MakeRGB16(255, 0, 0); } + +int flamingsword::GetSpecialFlags() const { return meleeweapon::GetSpecialFlags()|ST_FLAME_1; } + +col16 gorovitsweapon::GetOutlineColor(int) const { return MakeRGB16(255, 0, 0); } + +int thunderhammer::GetSpecialFlags() const { return !IsBroken() ? meleeweapon::GetSpecialFlags()|ST_LIGHTNING : meleeweapon::GetSpecialFlags(); } + +col16 belderiver::GetOutlineColor(int) const { return MakeRGB16(180, 50, 50); } + +col16 loricatushammer::GetOutlineColor(int) const { return MakeRGB16(0, 0, 255); } + +col16 demonhead::GetOutlineColor(int) const { return MakeRGB16(255, 0, 0); } + +col16 goldenjaguarshirt::GetOutlineColor(int) const { return MakeRGB16(255, 255, 128); } + +col16 muramasa::GetOutlineColor(int) const { return MakeRGB16(255, 0, 0); } + +col16 masamune::GetOutlineColor(int) const { return MakeRGB16(0, 0, 255); } + +int smite::GetSpecialFlags() const { return !IsBroken() ? meleeweapon::GetSpecialFlags()|ST_LIGHTNING : meleeweapon::GetSpecialFlags(); } + +int armor::GetCarryingBonus() const { return Enchantment << 1; } +double armor::GetTHVBonus() const { return Enchantment * .5; } +double armor::GetDamageBonus() const { return Enchantment; } + +long bodyarmor::GetPrice() const { return (armor::GetPrice() << 3) + GetEnchantedPrice(Enchantment); } +truth bodyarmor::IsInCorrectSlot(int I) const { return I == BODY_ARMOR_INDEX; } +cfestring& bodyarmor::GetNameSingular() const { return GetMainMaterial()->GetFlexibility() >= 5 ? item::GetFlexibleNameSingular() : item::GetNameSingular(); } +cchar* bodyarmor::GetBreakVerb() const { return GetMainMaterial()->GetFlexibility() >= 5 ? "is torn apart" : "breaks"; } + +col16 goldeneagleshirt::GetOutlineColor(int) const { return MakeRGB16(0, 255, 255); } + +long cloak::GetPrice() const { return armor::GetPrice() * 10 + GetEnchantedPrice(Enchantment); } +truth cloak::IsInCorrectSlot(int I) const { return I == CLOAK_INDEX; } +col16 cloak::GetMaterialColorB(int) const { return MakeRGB16(111, 64, 37); } +cchar* cloak::GetBreakVerb() const { return GetMainMaterial()->GetFlexibility() >= 5 ? "is torn apart" : "breaks"; } +truth cloak::ReceiveDamage(character* Damager, int Damage, int Type, int Dir) { return armor::ReceiveDamage(Damager, Damage >> 1, Type, Dir); } +int cloak::GetSpecialFlags() const { return ST_CLOAK; } + +long boot::GetPrice() const { return armor::GetPrice() / 5 + GetEnchantedPrice(Enchantment); } +truth boot::IsInCorrectSlot(int I) const { return I == RIGHT_BOOT_INDEX || I == LEFT_BOOT_INDEX; } + +long gauntlet::GetPrice() const { return armor::GetPrice() / 3 + GetEnchantedPrice(Enchantment); } +truth gauntlet::IsInCorrectSlot(int I) const { return I == RIGHT_GAUNTLET_INDEX || I == LEFT_GAUNTLET_INDEX; } + +long belt::GetPrice() const { return armor::GetPrice() * 5 + GetEnchantedPrice(Enchantment); } +truth belt::IsInCorrectSlot(int I) const { return I == BELT_INDEX; } + +truth ring::IsInCorrectSlot(int I) const { return I == RIGHT_RING_INDEX || I == LEFT_RING_INDEX; } +col16 ring::GetMaterialColorB(int) const { return MakeRGB16(200, 200, 200); } + +truth amulet::IsInCorrectSlot(int I) const { return I == AMULET_INDEX; } +col16 amulet::GetMaterialColorB(int) const { return MakeRGB16(111, 64, 37); } + +truth helmet::IsGorovitsFamilyRelic() const { return GetConfig() == GOROVITS_FAMILY_GAS_MASK; } +long helmet::GetPrice() const { return armor::GetPrice() + GetEnchantedPrice(Enchantment); } +truth helmet::IsInCorrectSlot(int I) const { return I == HELMET_INDEX; } +col16 helmet::GetMaterialColorB(int) const { return GetConfig() != GOROVITS_FAMILY_GAS_MASK ? (GetConfig() & ~BROKEN) ? MakeRGB16(140, 70, 70) : MakeRGB16(111, 64, 37) : MakeRGB16(0, 40, 0); } +col16 helmet::GetMaterialColorC(int) const { return MakeRGB16(180, 200, 180); } + +int wondersmellstaff::GetClassAnimationFrames() const { return !IsBroken() ? 128 : 1; } + +int eptyron::GetClassAnimationFrames() const { return !IsBroken() ? 128 : 1; } + +int taiaha::GetClassAnimationFrames() const { return !IsBroken() ? 128 : 1; } + +truth meleeweapon::HitEffect(character* Enemy, character*, v2, int BodyPartIndex, int, truth BlockedByArmour) +{ + if(!BlockedByArmour && Fluid) + { + truth Success = false; + fluidvector FluidVector; + FillFluidVector(FluidVector); + + for(uint c = 0; c < FluidVector.size(); ++c) + if(FluidVector[c]->Exists() + && FluidVector[c]->GetLiquid()->HitEffect(Enemy, Enemy->GetBodyPart(BodyPartIndex))) + Success = true; + + return Success; + } + else + return false; +} + +void meleeweapon::DipInto(liquid* Liquid, character* Dipper) +{ + if(Dipper->IsPlayer()) + ADD_MESSAGE("%s is now covered with %s.", CHAR_NAME(DEFINITE), Liquid->GetName(false, false).CStr()); + + SpillFluid(Dipper, Liquid); + Dipper->DexterityAction(10); +} + +truth pickaxe::Apply(character* User) +{ + if(IsBroken()) + { + ADD_MESSAGE("%s is totally broken.",CHAR_NAME(DEFINITE)); + return false; + } + + int Dir = game::DirectionQuestion(CONST_S("What direction do you want to dig? [press a direction key]"), false); + + v2 Temp = game::GetMoveVector(Dir); + v2 ToBeDug = User->GetPos() + Temp; + if(Dir == DIR_ERROR || !GetArea()->IsValidPos(ToBeDug)) + return false; + + lsquare* Square = GetNearLSquare(ToBeDug); + olterrain* Terrain = Square->GetOLTerrain(); + + if(!Terrain) + { + ADD_MESSAGE("Nothing to dig there!"); + return false; + } + + if(Square->CanBeDug()) + { + if(Terrain->CanBeDestroyed()) + if(Terrain->GetMainMaterial()->CanBeDug(GetMainMaterial())) + { + int RoomNumber = Square->GetRoomIndex(); + + if(!RoomNumber || Square->GetLevel()->GetRoom(RoomNumber)->CheckDestroyTerrain(User)) + { + User->SwitchToDig(this, ToBeDug); + User->DexterityAction(5); + return true; + } + else + return false; + } + else + ADD_MESSAGE("%s is too hard to dig with %s.", Square->GetOLTerrain()->CHAR_NAME(DEFINITE), CHAR_NAME(INDEFINITE)); + else + ADD_MESSAGE(Terrain->GetDigMessage().CStr()); + } + + return false; +} + +long meleeweapon::GetPrice() const +{ + double WeaponStrengthModifier = GetFormModifier() * GetMainMaterial()->GetStrengthValue(); + WeaponStrengthModifier *= WeaponStrengthModifier; + WeaponStrengthModifier *= GetMainMaterial()->GetWeight(); + WeaponStrengthModifier *= Max((10 + Enchantment) * 0.1, 0.1); + return long(WeaponStrengthModifier / (20000000.0 * sqrt(GetWeight()))) + + GetEnchantedPrice(Enchantment); +} + +int whip::GetFormModifier() const +{ + return item::GetFormModifier() * GetMainMaterial()->GetFlexibility(); +} + +truth pickaxe::IsAppliable(ccharacter* Who) const +{ + return Who->CanWield(); +} + +void meleeweapon::Save(outputfile& SaveFile) const +{ + item::Save(SaveFile); + SaveFile << Enchantment; + SaveFile << SecondaryMaterial; +} + +void meleeweapon::Load(inputfile& SaveFile) +{ + item::Load(SaveFile); + SaveFile >> Enchantment; + LoadMaterial(SaveFile, SecondaryMaterial); +} + +material* meleeweapon::GetMaterial(int I) const +{ + return !I ? MainMaterial : SecondaryMaterial; +} + +col16 meleeweapon::GetMaterialColorB(int) const +{ + return SecondaryMaterial->GetVolume() ? SecondaryMaterial->GetColor() : TRANSPARENT_COLOR; +} + +alpha meleeweapon::GetAlphaB(int) const +{ + return SecondaryMaterial->GetAlpha(); +} + +truth flamingsword::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(Enemy->IsEnabled() && RAND() & 1) + { + if(Hitter) + { + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("%s sword burns %s.", Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_DESCRIPTION(DEFINITE)); + } + else + { + if(Enemy->IsPlayer() || Enemy->CanBeSeenByPlayer()) + ADD_MESSAGE("The sword burns %s.", Enemy->CHAR_DESCRIPTION(DEFINITE)); + } + + return Enemy->ReceiveBodyPartDamage(Hitter, 3 + (RAND() & 3), FIRE, BodyPartIndex, Direction) || BaseSuccess; + } + else + return BaseSuccess; +} + +truth mjolak::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(!IsBroken() && Enemy->IsEnabled() && !(RAND() % 3)) + { + if(Hitter) + { + if(Hitter->IsPlayer()) + game::DoEvilDeed(10); + + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("A burst of %s Mjolak's unholy energy fries %s.", Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_DESCRIPTION(DEFINITE)); + } + else + { + if(Enemy->IsPlayer() || Enemy->CanBeSeenByPlayer()) + ADD_MESSAGE("A burst of Mjolak's unholy energy fries %s.", Enemy->CHAR_DESCRIPTION(DEFINITE)); + } + + return Enemy->ReceiveBodyPartDamage(Hitter, 5 + (RAND() % 6), ENERGY, BodyPartIndex, Direction) || BaseSuccess; + } + else + return BaseSuccess; +} + +truth vacuumblade::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(!IsBroken() && Enemy->IsEnabled() && !(RAND() % 2)) + { + if(Hitter) + { + + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("%s blade creates a wind vortex cutting %s.", Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_DESCRIPTION(DEFINITE)); + } + else + { + if(Enemy->IsPlayer() || Enemy->CanBeSeenByPlayer()) + ADD_MESSAGE("The blade creates a wind vortex cutting %s.", Enemy->CHAR_DESCRIPTION(DEFINITE)); + } + + return Enemy->ReceiveBodyPartDamage(Hitter, 6 + (RAND() % 6), ENERGY, BodyPartIndex, Direction) || BaseSuccess; + } + else + return BaseSuccess; +} + +truth zulfiqar::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(!IsBroken() && Enemy->IsEnabled() && !(RAND() % 3)) + { + + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("%s Zulfiqar's slash deeply cuts %s.", Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_DESCRIPTION(DEFINITE)); + + return Enemy->ReceiveBodyPartDamage(Hitter, 4 + (RAND() % 5), PHYSICAL_DAMAGE, BodyPartIndex, Direction) || BaseSuccess; + } + else + return BaseSuccess; +} + +truth tipswordofpenetration::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(!IsBroken() && Enemy->IsEnabled() && !(RAND() % 3)) + { + + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("%s tip sword's slash penetrates %s.", Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_DESCRIPTION(DEFINITE)); + + return Enemy->ReceiveBodyPartDamage(Hitter, 2 + (RAND() % 5), PHYSICAL_DAMAGE, BodyPartIndex, Direction) || BaseSuccess; + } + else + return BaseSuccess; +} + +truth masamune::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(!IsBroken() && Enemy->IsEnabled() && !(RAND() % 4)) + { + + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("%s Masamune's slash cuts %s so deep you thought it was cut in half for a moment.", Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_DESCRIPTION(DEFINITE)); + + return Enemy->ReceiveBodyPartDamage(Hitter, 4 + (RAND() % 4), PHYSICAL_DAMAGE, BodyPartIndex, Direction) || BaseSuccess; + } + else + return BaseSuccess; +} + +truth vermis::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(!IsBroken() && Enemy->IsEnabled() && !(RAND() % 5)) + { + if(Hitter) + { + if(Enemy->IsPlayer() || Enemy->CanBeSeenByPlayer()) + ADD_MESSAGE("%s Vermis sends %s on a sudden journey.", Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_DESCRIPTION(DEFINITE)); + } + else + { + if(Enemy->IsPlayer() || Enemy->CanBeSeenByPlayer()) + ADD_MESSAGE("Vermis sends %s on a sudden journey.", Enemy->CHAR_DESCRIPTION(DEFINITE)); + } + + Enemy->TeleportRandomly(); + return true; + } + else + return BaseSuccess; +} + +truth turox::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(!IsBroken() && Enemy->IsEnabled() && !(RAND() % 5)) + { + if(Hitter) + { + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("%s smash%s %s with the full force of Turox.", Hitter->CHAR_PERSONAL_PRONOUN, Hitter->IsPlayer() ? "" : "es", Enemy->CHAR_DESCRIPTION(DEFINITE)); + } + else + { + if(Enemy->IsPlayer() || Enemy->CanBeSeenByPlayer()) + ADD_MESSAGE("Turox is smashed against %s with full force.", Enemy->CHAR_DESCRIPTION(DEFINITE)); + } + + if(GetSquareUnder()->CanBeSeenByPlayer(true)) + ADD_MESSAGE("A magical explosion is triggered!"); + + Enemy->GetLevel()->Explosion(Hitter, CONST_S("burned @bkp Turox's explosion"), HitPos, 10 + RAND() % 100); + return true; + } + else + return BaseSuccess; +} + +//Entropyaxe code begins + +/** Whatever is not commented below, is stuff that works */ + +truth eptyron::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + int c, Index = 0; + + + if(!IsBroken() && Enemy->IsEnabled() && Enemy->IsHumanoid() /*&& !(RAND() % 2)*/) + { + bodypart* BodyPartThatWasHit = Enemy -> GetBodyPart(BodyPartIndex); + item* MainArmor = 0; + + switch(BodyPartIndex) + { + case TORSO_INDEX: + MainArmor = Enemy->GetEquipment(BODY_ARMOR_INDEX); + break; + case HEAD_INDEX: + MainArmor = Enemy->GetEquipment(HELMET_INDEX); + break; + case RIGHT_ARM_INDEX: + MainArmor = Enemy->GetEquipment(RIGHT_GAUNTLET_INDEX); + //BodyPartThatWasHit = Enemy -> GetBodyPart(BodyPartIndex); + break; + case LEFT_ARM_INDEX: + MainArmor = Enemy->GetEquipment(LEFT_GAUNTLET_INDEX); + //BodyPartThatWasHit = Enemy -> GetBodyPart(BodyPartIndex); + break; + case GROIN_INDEX: + MainArmor = Enemy->GetEquipment(BELT_INDEX); + break; + case RIGHT_LEG_INDEX: + MainArmor = Enemy->GetEquipment(RIGHT_BOOT_INDEX); + //BodyPartThatWasHit = Enemy -> GetBodyPart(BodyPartIndex); + break; + case LEFT_LEG_INDEX: + MainArmor = Enemy->GetEquipment(LEFT_BOOT_INDEX); + //BodyPartThatWasHit = Enemy -> GetBodyPart(BodyPartIndex); + break; + } + + if(MainArmor) + { + material* OldMaterial = MainArmor->GetMainMaterial(); + int OldModifier = OldMaterial->GetHardenModifier(MainArmor); + materialvector MaterialVector; + protosystem::CreateEveryMaterial(MaterialVector); + truth Changed = false; + int * CandidateMaterialIndex; + CandidateMaterialIndex = new int[MaterialVector.size()]; + + festring Desc; + MainArmor->AddName(Desc, UNARTICLED); + + for(c = 0; c < MaterialVector.size(); ++c) + { + if((MaterialVector[c]->GetCommonFlags() & CAN_BE_WISHED) + && (MaterialVector[c]->GetConsumeType() & OldMaterial->GetConsumeType()) ) + { + material* Material = MaterialVector[c]; + + if(Material->GetHardenModifier(MainArmor) < OldModifier + /*&& Material->GetRawPrice() < OldMaterial->GetRawPrice()*/ + && ((Material->GetCommonFlags() & IS_VALUABLE) == (OldMaterial->GetCommonFlags() & IS_VALUABLE)) //Hint: ie only valuable can entropy to valuable, and non-valuable to non-valuable + && ( 10*(1 + OldMaterial->GetIntelligenceRequirement() - Material->GetIntelligenceRequirement()) < (Hitter->GetAttribute(INTELLIGENCE))*(Hitter->GetSWeaponSkillLevel(this)) ) ) + { + CandidateMaterialIndex[Index++] = c; + } + } + } + if(Index) + { + int Chosen = CandidateMaterialIndex[(RAND() % Index)]; + material* ChosenMaterial = MaterialVector[Chosen]; + MainArmor->ChangeMainMaterial(ChosenMaterial->SpawnMore()); + if(ChosenMaterial != NONE) + { + if(Hitter->IsPlayer()) + { + ADD_MESSAGE("%s's %s softens into %s!", Enemy->CHAR_DESCRIPTION(DEFINITE), Desc.CStr() , ChosenMaterial->GetName(false, false).CStr()); + } + else + ADD_MESSAGE("Your %s softens into %s!", Desc.CStr(), ChosenMaterial->GetName(false, false).CStr()); + Changed = true; + } + else + { + Changed = false; + } + } + delete[] CandidateMaterialIndex; + + for(c = 0; c < MaterialVector.size(); ++c) + delete MaterialVector[c]; + if(!Changed) + if(Hitter->IsPlayer()) + { + ADD_MESSAGE("%s's %s vibrates slightly but remains unchanged.", Enemy->CHAR_DESCRIPTION(DEFINITE), MainArmor->CHAR_NAME(UNARTICLED) ); + } + else + ADD_MESSAGE("Your %s vibrates slightly but remains unchanged.", MainArmor->CHAR_NAME(UNARTICLED) ); + + return Changed; + } + + /*this bit softens enemy limbs!!! but pictures do not update :( */ + //if(BodyPartThatWasHit && !(Enemy -> BodyPartIsVital(BodyPartIndex)) ) + //{ + // material* OldMaterial = BodyPartThatWasHit->GetMainMaterial(); + // int OldModifier = OldMaterial->GetHardenModifier(BodyPartThatWasHit); + // materialvector MaterialVector; + // protosystem::CreateEveryMaterial(MaterialVector); + // truth Changed = false; + +//// for(c = 0; c < MaterialVector.size(); ++c) + // { + // if(MaterialVector[c]->GetCommonFlags() & CAN_BE_WISHED) + // { + // material* Material = MaterialVector[c]; + +//// if(Material->GetHardenModifier(BodyPartThatWasHit) < OldModifier + // /*&& !RAND_N(Max(Material->GetIntelligenceRequirement() - 15, 1))*/) + // { + // BodyPartThatWasHit->ChangeMainMaterial(Material->SpawnMore()); + // if(Hitter->IsPlayer()) + // { + // ADD_MESSAGE("%s %s softens into %s!", Enemy->CHAR_POSSESSIVE_PRONOUN, BodyPartThatWasHit->GetBodyPartName().CStr() , Material->GetName(false, false).CStr()); + // } + // else + // ADD_MESSAGE("Your %s softens into %s!", BodyPartThatWasHit->GetBodyPartName().CStr(), Material->GetName(false, false).CStr()); + // Changed = true; + // break; + // } + // } + // } + // for(c = 0; c < MaterialVector.size(); ++c) + // delete MaterialVector[c]; + // if(!Changed) + // if(Hitter->IsPlayer()) + // { + // ADD_MESSAGE("%s %s vibrates slightly but remains unchanged.", Enemy->CHAR_POSSESSIVE_PRONOUN, BodyPartThatWasHit->GetBodyPartName().CStr() ); + // } + // else + // ADD_MESSAGE("Your %s vibrates slightly but remains unchanged.", BodyPartThatWasHit->GetBodyPartName().CStr() ); + // + // return Changed; + //} + + + ////if(BlockedByArmour) + ////{ + // if(MainArmor) + // { + // if(MainArmor->IsMaterialChangeable()) + // { + // festring Desc; + // MainArmor->AddName(Desc, UNARTICLED); + // Desc << " is transformed"; + // + // switch(RAND() % 3) + // { + // case 0: + // MainArmor->ChangeMainMaterial(MAKE_MATERIAL(CLOTH)); + // break; + // case 1: + // MainArmor->ChangeMainMaterial(MAKE_MATERIAL(PLANT_FIBER)); + // break; + // case 2: + // MainArmor->ChangeMainMaterial(MAKE_MATERIAL(PARCHMENT)); + // break; + // } + // if(Hitter->IsPlayer()) + // { + // ADD_MESSAGE("%s's %s", Enemy->CHAR_DESCRIPTION(DEFINITE), Desc.CStr()); + // } + // else + // ADD_MESSAGE("Your %s", Desc.CStr()); + // } + // else + // ADD_MESSAGE("%s's %s emits a strange light but remain unchanged.", Enemy->CHAR_DESCRIPTION(DEFINITE), MainArmor->CHAR_NAME(DEFINITE)); + // } + // return true; + ////} + // + //if(BodyPartThatWasHit && !(Enemy -> BodyPartIsVital(BodyPartIndex)) ) + //{ + // if(BodyPartThatWasHit->IsMaterialChangeable()) + // { + // if(Hitter->IsPlayer()) + // { + // ADD_MESSAGE("%s's %s softens into banana flesh.", Enemy->CHAR_DESCRIPTION(DEFINITE), BodyPartThatWasHit->GetBodyPartName().CStr() ); + // } + // else + // ADD_MESSAGE("Your %s softens into banana flesh.", BodyPartThatWasHit->GetBodyPartName().CStr() ); + // + // BodyPartThatWasHit->ChangeMainMaterial(MAKE_MATERIAL(BANANA_FLESH)); + // } + // else + // ADD_MESSAGE("%s's %s emits a strange light but remains unchanged.", Enemy->CHAR_DESCRIPTION(DEFINITE), BodyPartThatWasHit->GetBodyPartName().CStr() ); + // } + //return true; + } + return BaseSuccess; +} + +void eptyron::BlockEffect(character* Blocker, character* Attacker, item* Weapon, int Type) +{ + int c, Index = 0; + + if(!IsBroken()) + { + if(Weapon && !(Weapon->IsWhip())) + { + material* OldMaterial = Weapon->GetMainMaterial(); + int OldModifier = OldMaterial->GetHardenModifier(Weapon); + materialvector MaterialVector; + protosystem::CreateEveryMaterial(MaterialVector); + truth Changed = false; + int * CandidateMaterialIndex; + CandidateMaterialIndex = new int[MaterialVector.size()]; + + festring Desc; + Weapon->AddName(Desc, UNARTICLED); + + for(c = 0; c < MaterialVector.size(); ++c) + { + if((MaterialVector[c]->GetCommonFlags() & CAN_BE_WISHED) + && (MaterialVector[c]->GetConsumeType() & OldMaterial->GetConsumeType()) ) + { + material* Material = MaterialVector[c]; + + if(Material->GetHardenModifier(Weapon) < OldModifier + && Material->GetRawPrice() < OldMaterial->GetRawPrice() + && ((Material->GetCommonFlags() & IS_VALUABLE) == (OldMaterial->GetCommonFlags() & IS_VALUABLE)) //ie only valuable can entropy to valuable, and non-valuable to non-valuable + && ( 10*(1 + OldMaterial->GetIntelligenceRequirement() - Material->GetIntelligenceRequirement()) < (Blocker->GetAttribute(INTELLIGENCE))*(Blocker->GetSWeaponSkillLevel(this)) ) ) + { + CandidateMaterialIndex[Index++] = c; + } + } + } + if(Index) + { + int Chosen = CandidateMaterialIndex[(RAND() % Index)]; + material* ChosenMaterial = MaterialVector[Chosen]; + Weapon->ChangeMainMaterial(ChosenMaterial->SpawnMore()); + if(ChosenMaterial != NONE) + { + if(Blocker->IsPlayer()) + { + ADD_MESSAGE("%s's %s softens into %s!", Attacker->CHAR_DESCRIPTION(DEFINITE), Desc.CStr() , ChosenMaterial->GetName(false, false).CStr()); + } + else + ADD_MESSAGE("Your %s softens into %s!", Desc.CStr(), ChosenMaterial->GetName(false, false).CStr()); + Changed = true; + } + else + { + Changed = false; + } + } + delete[] CandidateMaterialIndex; + + for(c = 0; c < MaterialVector.size(); ++c) + delete MaterialVector[c]; + if(!Changed) + if(Blocker->IsPlayer()) + { + ADD_MESSAGE("%s's %s vibrates slightly but remains unchanged.", Attacker->CHAR_DESCRIPTION(DEFINITE), Weapon->CHAR_NAME(UNARTICLED) ); + } + else + ADD_MESSAGE("Your %s vibrates slightly but remains unchanged.", Weapon->CHAR_NAME(UNARTICLED) ); + + return; + } + } + return; +} + +//Entropyaxe code ends + +//begin taiaha + +truth taiaha::Zap(character* Zapper, v2, int Direction) +{ + if(Charges <= TimesUsed) + { + ADD_MESSAGE("Nothing happens."); + return true; + } + + Zapper->EditExperience(PERCEPTION, 150, 1 << 10); + int TaiahaBeamEffect = RAND() & 3; + + beamdata Beam // Just hard-code this + ( + Zapper, + CONST_S("killed by ") + GetName(INDEFINITE) + " zapped @bk", + Zapper->GetPos(), + GREEN, //was GetBeamColor() + TaiahaBeamEffect ? ((RAND() & 2) ? BEAM_FIRE_BALL : BEAM_STRIKE ) : BEAM_LIGHTNING, //was GetBeamEffect() + Direction, + 15, // 10 is the lowest beamrange out of the three + 0 //was GetSpecialParameters() + ); + + (GetLevel()->*level::GetBeam(!TaiahaBeamEffect))(Beam); // BeamStyle = !TaiahaBeamEffect; + ++TimesUsed; + return true; +} + +void taiaha::AddInventoryEntry(ccharacter* Viewer, festring& Entry, int, truth ShowSpecialInfo) const // never piled +{ + AddName(Entry, INDEFINITE); + + if(ShowSpecialInfo) + { + Entry << " [" << GetWeight() << "g, DAM " << GetBaseMinDamage() << '-' << GetBaseMaxDamage(); + Entry << ", " << GetBaseToHitValueDescription(); + + if(!IsBroken() && !IsWhip()) + Entry << ", " << GetStrengthValueDescription(); + + int CWeaponSkillLevel = Viewer->GetCWeaponSkillLevel(this); + int SWeaponSkillLevel = Viewer->GetSWeaponSkillLevel(this); + + if(CWeaponSkillLevel || SWeaponSkillLevel) + Entry << ", skill " << CWeaponSkillLevel << '/' << SWeaponSkillLevel; + + if(TimesUsed == 1) + Entry << ", zapped 1 time]"; + else if(TimesUsed) + Entry << ", zapped " << TimesUsed << " times]"; + else + Entry << "]"; + } +} + +void taiaha::BreakEffect(character* Terrorist, cfestring& DeathMsg) +{ + v2 Pos = GetPos(); + level* Level = GetLevel(); + RemoveFromSlot(); + ulong StackSize = Level->AddRadiusToSquareStack(Pos, 2); //hardcode, default is 2 for most wands, but zero for fireballs + lsquare** SquareStack = Level->GetSquareStack(); + ulong c; + + for(c = 0; c < StackSize; ++c) + SquareStack[c]->RemoveFlags(IN_SQUARE_STACK); + + fearray Stack(SquareStack, StackSize); + (Level->*level::GetBeamEffectVisualizer(PARTICLE_BEAM))(Stack, YELLOW); //beamstyle + + beamdata Beam + ( + Terrorist, + DeathMsg, + YOURSELF, + 0 //was GetSpecialParameters() + ); + + for(c = 0; c < Stack.Size; ++c) + (Stack[c]->*lsquare::GetBeamEffect(BEAM_FIRE_BALL))(Beam); // beam effect + + SendToHell(); //removes the taiaha from existence +} + +void taiaha::Save(outputfile& SaveFile) const +{ + item::Save(SaveFile); + SaveFile << TimesUsed << Charges; + SaveFile << Enchantment; + SaveFile << SecondaryMaterial; + //meleeweapon::Save(SaveFile); +} + +void taiaha::Load(inputfile& SaveFile) +{ + item::Load(SaveFile); + SaveFile >> TimesUsed >> Charges; + SaveFile >> Enchantment; + LoadMaterial(SaveFile, SecondaryMaterial); + //meleeweapon::Load(SaveFile); +} + +truth taiaha::ReceiveDamage(character* Damager, int Damage, int Type, int) +{ + if(Type & (FIRE|ENERGY|PHYSICAL_DAMAGE) && Damage && (Damage > 125 || !(RAND() % (250 / Damage)))) + { + festring DeathMsg = CONST_S("killed by an explosion of "); + AddName(DeathMsg, INDEFINITE); + + if(Damager) + DeathMsg << " caused @bk"; + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s %s.", GetExtendedDescription().CStr(), GetBreakMsg().CStr()); + + BreakEffect(Damager, DeathMsg); + return true; + } + + return false; +} + +void taiaha::PostConstruct() +{ + Charges = GetMinCharges() + RAND() % (GetMaxCharges() - GetMinCharges() + 1); + TimesUsed = 0; + meleeweapon::PostConstruct(); +} + +alpha taiaha::GetOutlineAlpha(int Frame) const +{ + if(!IsBroken()) + { + Frame &= 31; + return Frame * (31 - Frame) >> 1; + } + else + return 255; +} + +col16 taiaha::GetOutlineColor(int Frame) const +{ + if(!IsBroken()) + switch((Frame&127) >> 5) + { + case 0: return BLUE; + case 1: return GREEN; + case 2: return RED; + case 3: return YELLOW; + } + + return TRANSPARENT_COLOR; +} + +//end taiaha + +truth whipofthievery::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(Enemy->IsEnabled() && Hitter && CleptiaHelps(Enemy, Hitter)) + { + if(Hitter->IsPlayer()) + { + game::DoEvilDeed(10); + game::GetGod(CLEPTIA)->AdjustRelation(10); + } + + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("%s whip asks for the help of Cleptia as it steals %s %s.", Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_POSSESSIVE_PRONOUN, Enemy->GetMainWielded()->CHAR_NAME(UNARTICLED)); + + Enemy->GetMainWielded()->MoveTo(GetLSquareUnder()->GetStack()); + return true; + } + else + return BaseSuccess; +} + +meleeweapon::~meleeweapon() +{ + delete SecondaryMaterial; +} + +meleeweapon::meleeweapon(const meleeweapon& MW) : mybase(MW), Enchantment(MW.Enchantment) +{ + CopyMaterial(MW.SecondaryMaterial, SecondaryMaterial); +} + +truth whipofthievery::CleptiaHelps(ccharacter* Enemy, ccharacter* Hitter) const +{ + if(Enemy->IsImmuneToWhipOfThievery() || !Enemy->GetMainWielded() || GetMainMaterial()->GetFlexibility() <= 5) + return false; + + if(Hitter->IsPlayer()) + { + if(game::GetGod(CLEPTIA)->GetRelation() < 0) + return false; + else + return !RAND_N(10 - game::GetGod(CLEPTIA)->GetRelation() / 200); + } + else + return !RAND_N(10); +} + +void meleeweapon::AddInventoryEntry(ccharacter* Viewer, festring& Entry, int, truth ShowSpecialInfo) const // never piled +{ + AddName(Entry, INDEFINITE); + + if(ShowSpecialInfo) + { + Entry << " [" << GetWeight() << "g, DAM " << GetBaseMinDamage() << '-' << GetBaseMaxDamage(); + Entry << ", " << GetBaseToHitValueDescription(); + + if(!IsBroken() && !IsWhip()) + Entry << ", " << GetStrengthValueDescription(); + + int CWeaponSkillLevel = Viewer->GetCWeaponSkillLevel(this); + int SWeaponSkillLevel = Viewer->GetSWeaponSkillLevel(this); + + if(CWeaponSkillLevel || SWeaponSkillLevel) + Entry << ", skill " << CWeaponSkillLevel << '/' << SWeaponSkillLevel; + + Entry << ']'; + } +} + +void meleeweapon::SignalSpoil(material* Material) +{ + if(!Exists()) + return; + + if(Material == MainMaterial) + { + if(CanBeSeenByPlayer()) + if(SecondaryMaterial->GetVolume()) + ADD_MESSAGE("The edge of %s spoils.", GetExtendedDescription().CStr()); + else + ADD_MESSAGE("%s spoils.", GetExtendedDescription().CStr()); + + RemoveMainMaterial(); + } + else + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("The handle of %s spoils", GetExtendedDescription().CStr()); + + delete RemoveSecondaryMaterial(); + } +} + +void meleeweapon::AddPostFix(festring& String, int Case) const +{ + item::AddPostFix(String, Case); + + if(Fluid) + { + String << " covered with "; + fluid::AddFluidInfo(Fluid[0], String); + } + + if(Enchantment > 0) + String << " +" << Enchantment; + else if(Enchantment < 0) + String << ' ' << Enchantment; +} + +void meleeweapon::Be() +{ + item::Be(); + + if(Exists() && SecondaryMaterial->GetVolume()) + SecondaryMaterial->Be(ItemFlags); +} + +long whipofthievery::GetPrice() const +{ + /* If intact but not flexible enough to work, special thievery bonus must be removed */ + + return GetMainMaterial()->GetFlexibility() > 5 || IsBroken() ? whip::GetPrice() : whip::GetPrice() - item::GetPrice(); +} + +int meleeweapon::GetSparkleFlags() const +{ + return (MainMaterial->IsSparkling() ? SPARKLING_A|(SecondaryMaterial->GetVolume() ? SPARKLING_C : 0) : 0) + | (SecondaryMaterial->IsSparkling() ? SPARKLING_B : 0); +} + +void meleeweapon::SetEnchantment(int Amount) +{ + Enchantment = Amount; + SignalEnchantmentChange(); +} + +void meleeweapon::EditEnchantment(int Amount) +{ + Enchantment += Amount; + SignalEnchantmentChange(); +} + +int meleeweapon::GetStrengthValue() const +{ + return Max(long(GetStrengthModifier()) * GetMainMaterial()->GetStrengthValue() / 2000 + Enchantment, 0); +} + +void meleeweapon::PostConstruct() +{ + Enchantment = GetBaseEnchantment(); +} + +int meleeweapon::GetSpoilLevel() const +{ + int MainSpoilLevel = MainMaterial->GetSpoilLevel(); + + if(SecondaryMaterial->GetVolume()) + return Max(MainSpoilLevel, SecondaryMaterial->GetSpoilLevel()); + else + return MainSpoilLevel; +} + +truth neercseulb::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(!IsBroken() && Enemy->IsEnabled() && !(RAND() % 5)) + { + if(Hitter->IsPlayer()) + game::DoEvilDeed(10); + + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("%s Neerc Se-ulb's life-draining energies swallow %s!", Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_DESCRIPTION(DEFINITE)); + + return Enemy->ReceiveBodyPartDamage(Hitter, 10 + (RAND() % 11), DRAIN, BodyPartIndex, Direction) || BaseSuccess; + } + else + return BaseSuccess; +} + +truth muramasa::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(!IsBroken() && Enemy->IsEnabled() && !(RAND() % 5)) + { + if(Hitter->IsPlayer()) + game::DoEvilDeed(10); + + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("%s Muramasa's life-draining energies swallow %s!", Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_DESCRIPTION(DEFINITE)); + + return Enemy->ReceiveBodyPartDamage(Hitter, 10 + (RAND() % 11), DRAIN, BodyPartIndex, Direction) || BaseSuccess; + } + else + return BaseSuccess; +} + +truth thunderhammer::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(!IsBroken() && Enemy->IsEnabled() && !(RAND() % 5)) + { + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("%s weapon shoots a lightning bolt at %s!", Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_DESCRIPTION(DEFINITE)); + + beamdata Beam + ( + Hitter, + CONST_S("electrocuted @bkp thunder hammer"), + Hitter->GetPos(), + WHITE, + BEAM_LIGHTNING, + Direction, + 4, + 0 + ); + + GetLevel()->LightningBeam(Beam); + return true; + } + else + return BaseSuccess; +} + +truth smite::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(!IsBroken() && Enemy->IsEnabled() && !(RAND() % 5)) + { + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("%s mace named Smite shoots a lightning bolt at %s!", Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_DESCRIPTION(DEFINITE)); + + beamdata Beam + ( + Hitter, + CONST_S("electrocuted @bkp mace named Smite"), + Hitter->GetPos(), + WHITE, + BEAM_LIGHTNING, + Direction, + 5, + 0 + ); + + GetLevel()->LightningBeam(Beam); + return true; + } + else + return BaseSuccess; +} + +truth smite::ReceiveDamage(character* Damager, int Damage, int Type, int Dir) +{ + return Type & ELECTRICITY ? false : meleeweapon::ReceiveDamage(Damager, Damage, Type, Dir); +} + +truth thunderhammer::ReceiveDamage(character* Damager, int Damage, int Type, int Dir) +{ + return Type & ELECTRICITY ? false : meleeweapon::ReceiveDamage(Damager, Damage, Type, Dir); +} + +long armor::GetPrice() const +{ + double StrengthValue = GetStrengthValue(); + return long(StrengthValue * StrengthValue * StrengthValue * 20 / sqrt(GetWeight())); +} + +int belt::GetFormModifier() const +{ + return item::GetFormModifier() * GetMainMaterial()->GetFlexibility(); +} + +void armor::AddInventoryEntry(ccharacter*, festring& Entry, int Amount, truth ShowSpecialInfo) const +{ + if(Amount == 1) + AddName(Entry, INDEFINITE); + else + { + Entry << Amount << ' '; + AddName(Entry, PLURAL); + } + + if(ShowSpecialInfo) + Entry << " [" << GetWeight() * Amount << "g, AV " << GetStrengthValue() << ']'; +} + +void shield::AddInventoryEntry(ccharacter* Viewer, festring& Entry, int, truth ShowSpecialInfo) const // never piled +{ + AddName(Entry, INDEFINITE); + + if(ShowSpecialInfo) + { + Entry << " [" << GetWeight() << "g, " << GetBaseBlockValueDescription(); + + if(!IsBroken()) + Entry << ", " << GetStrengthValueDescription(); + + int CWeaponSkillLevel = Viewer->GetCWeaponSkillLevel(this); + int SWeaponSkillLevel = Viewer->GetSWeaponSkillLevel(this); + + if(CWeaponSkillLevel || SWeaponSkillLevel) + Entry << ", skill " << CWeaponSkillLevel << '/' << SWeaponSkillLevel; + + Entry << ']'; + } +} + +truth armor::CanBePiledWith(citem* Item, ccharacter* Viewer) const +{ + return item::CanBePiledWith(Item, Viewer) && Enchantment == static_cast(Item)->Enchantment; +} + +long shield::GetPrice() const /* temporary... */ +{ + double StrengthValue = GetStrengthValue(); + return long(sqrt(GetBaseBlockValue()) * StrengthValue * StrengthValue) + item::GetPrice(); +} + +void armor::Save(outputfile& SaveFile) const +{ + item::Save(SaveFile); + SaveFile << Enchantment; +} + +void armor::Load(inputfile& SaveFile) +{ + item::Load(SaveFile); + SaveFile >> Enchantment; +} + +void armor::AddPostFix(festring& String, int Case) const +{ + item::AddPostFix(String, Case); + + if(Fluid) + { + String << " covered with "; + fluid::AddFluidInfo(Fluid[0], String); + } + + if(Enchantment > 0) + String << " +" << Enchantment; + else if(Enchantment < 0) + String << ' ' << Enchantment; +} + +void armor::SetEnchantment(int Amount) +{ + Enchantment = Amount; + SignalEnchantmentChange(); +} + +void armor::EditEnchantment(int Amount) +{ + Enchantment += Amount; + SignalEnchantmentChange(); +} + +int armor::GetStrengthValue() const +{ + return Max(long(GetStrengthModifier()) * GetMainMaterial()->GetStrengthValue() / 2000 + Enchantment, 0); +} + +void armor::PostConstruct() +{ + Enchantment = GetBaseEnchantment(); +} + +int armor::GetInElasticityPenalty(int Attribute) const +{ + return Attribute * GetInElasticityPenaltyModifier() / (GetMainMaterial()->GetFlexibility() * 100); +} + +void meleeweapon::GenerateMaterials() +{ + int Chosen = RandomizeMaterialConfiguration(); + const fearray& MMC = GetMainMaterialConfig(); + InitMaterial(MainMaterial, + MAKE_MATERIAL(MMC.Data[MMC.Size == 1 ? 0 : Chosen]), + GetDefaultMainVolume()); + const fearray& SMC = GetSecondaryMaterialConfig(); + InitMaterial(SecondaryMaterial, + MAKE_MATERIAL(SMC.Data[SMC.Size == 1 ? 0 : Chosen]), + GetDefaultSecondaryVolume()); +} + +truth chameleonwhip::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(!IsBroken() && Enemy->IsEnabled() && ScabiesHelps(Enemy, Hitter)) + { + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("%s whip asks for the help of Scabies as it polymorphs %s.", Hitter->CHAR_PERSONAL_PRONOUN, Enemy->CHAR_DESCRIPTION(DEFINITE)); + + if(Hitter->IsPlayer()) + { + game::DoEvilDeed(20); + game::GetGod(SCABIES)->AdjustRelation(10); + } + + int CurrentDanger = int(Enemy->GetRelativeDanger(PLAYER) * 1000); + Enemy->PolymorphRandomly(CurrentDanger / 4, Min(CurrentDanger, 999999), 100 + RAND() % 400); + return true; + } + else + return BaseSuccess; +} + +truth chameleonwhip::ScabiesHelps(ccharacter* Enemy, ccharacter* Hitter) const +{ + if(!Enemy->IsPolymorphable()) + return false; + + if(Hitter->IsPlayer()) + { + if(game::GetGod(SCABIES)->GetRelation() < 0) + return false; + else + return !(RAND() % (20 - game::GetGod(SCABIES)->GetRelation() / 150)); + } + else + return !(RAND() % 20); +} + +alpha justifier::GetOutlineAlpha(int Frame) const +{ + Frame &= 31; + return 50 + (Frame * (31 - Frame) >> 1); +} + +alpha belderiver::GetOutlineAlpha(int Frame) const +{ + Frame &= 31; + return 50 + (Frame * (31 - Frame) >> 1); +} + +alpha loricatushammer::GetOutlineAlpha(int Frame) const +{ + Frame &= 31; + return 50 + (Frame * (31 - Frame) >> 1); +} + +alpha demonhead::GetOutlineAlpha(int Frame) const +{ + Frame &= 31; + return 50 + (Frame * (31 - Frame) >> 1); +} + +alpha masamune::GetOutlineAlpha(int Frame) const +{ + Frame &= 31; + return 50 + (Frame * (31 - Frame) >> 1); +} + +alpha muramasa::GetOutlineAlpha(int Frame) const +{ + Frame &= 31; + return 50 + (Frame * (31 - Frame) >> 1); +} + +alpha goldenjaguarshirt::GetOutlineAlpha(int Frame) const +{ + Frame &= 31; + return 50 + (Frame * (31 - Frame) >> 1); +} + +alpha neercseulb::GetOutlineAlpha(int Frame) const +{ + Frame &= 31; + return 50 + (Frame * (31 - Frame) >> 1); +} + +alpha gorovitsweapon::GetOutlineAlpha(int Frame) const +{ + Frame &= 31; + return 50 + (Frame * (31 - Frame) >> 1); +} + +alpha goldeneagleshirt::GetOutlineAlpha(int Frame) const +{ + Frame &= 31; + return 50 + (Frame * (31 - Frame) >> 1); +} + +alpha wondersmellstaff::GetOutlineAlpha(int Frame) const +{ + if(!IsBroken()) + { + Frame &= 31; + return Frame * (31 - Frame) >> 1; + } + else + return 255; +} + +col16 wondersmellstaff::GetOutlineColor(int Frame) const +{ + if(!IsBroken()) + switch((Frame&127) >> 5) + { + case 0: return BLUE; + case 1: return GREEN; + case 2: return RED; + case 3: return YELLOW; + } + + return TRANSPARENT_COLOR; +} + +alpha eptyron::GetOutlineAlpha(int Frame) const +{ + if(!IsBroken()) + { + Frame &= 31; + return Frame * (31 - Frame) >> 1; + } + else + return 255; +} + +col16 eptyron::GetOutlineColor(int Frame) const +{ + if(!IsBroken()) + switch((Frame&127) >> 5) + { + case 0: return BLUE; + case 1: return GREEN; + case 2: return RED; + case 3: return YELLOW; + } + + return TRANSPARENT_COLOR; +} + +truth wondersmellstaff::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(!IsBroken() && Enemy->IsEnabled() && !(RAND() % 5)) + { + if(RAND() & 3) + { + truth Seen = false; + int Amount = 250 / Enemy->GetSquaresUnder(); + + for(int c = 0; c < Enemy->GetSquaresUnder(); ++c) + { + lsquare* Square = Enemy->GetLSquareUnder(c); + + if(Square->IsFlyable()) + { + Square->AddSmoke(gas::Spawn(EVIL_WONDER_STAFF_VAPOUR, Amount)); + + if(!Seen && Square->CanBeSeenByPlayer()) + Seen = true; + } + } + + if(Seen) + ADD_MESSAGE("Strange red smoke billows out of %s staff.", Hitter->CHAR_POSSESSIVE_PRONOUN); + } + else + { + /* Can a multitiled creature ever be the hitter? */ + + lsquare* Square = Hitter->GetLSquareUnder(); + + if(Square->IsFlyable()) + { + if(Square->CanBeSeenByPlayer()) + ADD_MESSAGE("Strange blue smoke billows out of %s staff.", Hitter->CHAR_POSSESSIVE_PRONOUN); + + Square->AddSmoke(gas::Spawn(GOOD_WONDER_STAFF_VAPOUR, 100)); + } + } + + return true; + } + else + return BaseSuccess; +} + +truth bodyarmor::AddAdjective(festring& String, truth Articled) const +{ + if(IsBroken()) + { + if(Articled) + String << "a "; + + if(GetMainMaterial()->GetFlexibility() >= 5) + String << "torn"; + else + String << "broken"; + + String << ' '; + return true; + } + else + return false; +} + +truth cloak::AddAdjective(festring& String, truth Articled) const +{ + if(IsBroken()) + { + if(Articled) + String << "a "; + + if(GetMainMaterial()->GetFlexibility() >= 5) + String << "torn"; + else + String << "broken"; + + String << ' '; + return true; + } + else + return false; +} + +int meleeweapon::GetRustDataB() const +{ + return SecondaryMaterial->GetRustData(); +} + +void meleeweapon::TryToRust(long LiquidModifier) +{ + item::TryToRust(LiquidModifier); + + if(SecondaryMaterial->GetVolume() && SecondaryMaterial->TryToRust(LiquidModifier)) + SecondaryMaterial->SetRustLevel(SecondaryMaterial->GetRustLevel() + 1); +} + +material* meleeweapon::GetConsumeMaterial(ccharacter* Consumer, materialpredicate Predicate) const +{ + if((SecondaryMaterial->*Predicate)() + && SecondaryMaterial->GetVolume() + && Consumer->CanConsume(SecondaryMaterial)) + return SecondaryMaterial; + else + return item::GetConsumeMaterial(Consumer, Predicate); +} + +material* meleeweapon::RemoveMaterial(material* Material) +{ + if(Material == MainMaterial) + return RemoveMainMaterial(); + else + return RemoveSecondaryMaterial(); +} + +material* meleeweapon::RemoveMainMaterial() +{ + truth Equipped = PLAYER->Equips(this); + + if(SecondaryMaterial->GetVolume()) + { + item* Lump = SecondaryMaterial->CreateNaturalForm(SecondaryMaterial->GetVolume()); + DonateFluidsTo(Lump); + DonateIDTo(Lump); + DonateSlotTo(Lump); + } + else + RemoveFromSlot(); + + if(Equipped) + game::AskForKeyPress(CONST_S("Equipment destroyed! [press any key to continue]")); + + SendToHell(); + return 0; +} + +material* meleeweapon::RemoveSecondaryMaterial() +{ + SecondaryMaterial->SetVolume(0); + + if(!IsBroken()) + Break(0); + else + { + RedistributeFluids(); + UpdatePictures(); + SendNewDrawAndMemorizedUpdateRequest(); + } + + return 0; +} + +pixelpredicate meleeweapon::GetFluidPixelAllowedPredicate() const +{ + if(SecondaryMaterial->GetVolume()) + return &rawbitmap::IsTransparent; + else + return &rawbitmap::IsMaterialColor1; +} + +void meleeweapon::CalculateEmitation() +{ + Emitation = GetBaseEmitation(); + + if(MainMaterial) + game::CombineLights(Emitation, MainMaterial->GetEmitation()); + + if(SecondaryMaterial->GetVolume()) + game::CombineLights(Emitation, SecondaryMaterial->GetEmitation()); +} + +truth meleeweapon::CalculateHasBe() const +{ + return LifeExpectancy + || (MainMaterial && MainMaterial->HasBe()) + || (SecondaryMaterial + && SecondaryMaterial->GetVolume() + && SecondaryMaterial->HasBe()); +} + +void decosadshirt::Be() +{ + if(PLAYER->Equips(this)) + ++EquippedTicks; + + bodyarmor::Be(); +} + +decosadshirt::decosadshirt() : EquippedTicks(0) +{ + Enable(); +} + +void decosadshirt::Save(outputfile& SaveFile) const +{ + bodyarmor::Save(SaveFile); + SaveFile << EquippedTicks; +} + +void decosadshirt::Load(inputfile& SaveFile) +{ + bodyarmor::Load(SaveFile); + SaveFile >> EquippedTicks; + Enable(); +} + +item* meleeweapon::Fix() +{ + SecondaryMaterial->SetVolumeNoSignals(GetDefaultSecondaryVolume()); + return item::Fix(); +} + +long meleeweapon::GetMaterialPrice() const +{ + return MainMaterial->GetRawPrice() + SecondaryMaterial->GetRawPrice(); +} + +void meleeweapon::CalculateEnchantment() +{ + Enchantment -= femath::LoopRoll(game::GetCurrentLevel()->GetEnchantmentMinusChance(), 5); + Enchantment += femath::LoopRoll(game::GetCurrentLevel()->GetEnchantmentPlusChance(), 5); + Enchantment -= femath::LoopRoll(GetEnchantmentMinusChance(), 5); + Enchantment += femath::LoopRoll(GetEnchantmentPlusChance(), 5); +} + +void armor::CalculateEnchantment() +{ + Enchantment -= femath::LoopRoll(game::GetCurrentLevel()->GetEnchantmentMinusChance(), 5); + Enchantment += femath::LoopRoll(game::GetCurrentLevel()->GetEnchantmentPlusChance(), 5); + Enchantment -= femath::LoopRoll(GetEnchantmentMinusChance(), 5); + Enchantment += femath::LoopRoll(GetEnchantmentPlusChance(), 5); +} + +col16 meleeweapon::GetMaterialColorC(int Frame) const +{ + return SecondaryMaterial->GetVolume() ? GetMaterialColorA(Frame) : TRANSPARENT_COLOR; +} + +void daggerofvenom::Be() +{ + meleeweapon::Be(); + + if(Exists() && !IsBroken() && (*Slot)->IsGearSlot() && !RAND_N(10)) + { + fluidvector FluidVector; + FillFluidVector(FluidVector); + uint Volume = 0; + + for(uint c = 0; c < FluidVector.size(); ++c) + { + liquid* L = FluidVector[c]->GetLiquid(); + Volume += L->GetVolume(); //I imagine that there is a function I don't know to do this... + } + + if(Volume < 90) + SpillFluid(0, liquid::Spawn(POISON_LIQUID, 10)); + } +} + +truth weepblade::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = meleeweapon::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(Enemy->IsEnabled() && !(RAND_N(3))) + { + if(Enemy->IsPlayer() || Hitter->IsPlayer() + || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("%s weeping blade spills acid on %s.", + Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_DESCRIPTION(DEFINITE)); + Enemy->SpillFluid(PLAYER, liquid::Spawn(SULPHURIC_ACID, 25+RAND()%25)); + return BaseSuccess; + } + else + return BaseSuccess; +} + +void acidshield::BlockEffect(character* Blocker, character* Attacker, item* Weapon, int Type) +{ + int CheckAttackType = 0; + if(!IsBroken()) + { + if(!RAND_N(400)) + { + if(Weapon) + { + Weapon->SpillFluid(Blocker, liquid::Spawn(SULPHURIC_ACID, 200 + RAND() % 51)); + ADD_MESSAGE("%s is completely doused in sulphuric acid!", Attacker->CHAR_DESCRIPTION(DEFINITE)); + return; + } + } + + if(RAND_2 && Weapon) + { + Weapon->SpillFluid(Blocker, liquid::Spawn(SULPHURIC_ACID, 20 + RAND() % 41)); + ADD_MESSAGE("%s weapon is splashed with acid from the shield!", Attacker->CHAR_POSSESSIVE_PRONOUN); + } + + if(!RAND_N(5)) + { + Attacker->SpillFluid(Blocker, liquid::Spawn(SULPHURIC_ACID, 5 + RAND() % 11)); + ADD_MESSAGE("%s is splashed with acid!", Attacker->CHAR_DESCRIPTION(DEFINITE)); + return; + } + + if(RAND_2) + { + Attacker->SpillFluid(Blocker, liquid::Spawn(SULPHURIC_ACID, 25 + RAND() % 26)); + ADD_MESSAGE("%s is splashed with acid from the shield!", Attacker->CHAR_DESCRIPTION(DEFINITE)); + } + } +} + + +void wondersmellstaff::Break(character* Who, int Much) +{ + material* GasMaterial = GetSecondaryMaterial(); + GetLevel()->GasExplosion(gas::Spawn(GOOD_WONDER_STAFF_VAPOUR, 100), GetLSquareUnder()); + + if(CanBeSeenByPlayer()) + { + ADD_MESSAGE("%s unleashes a puff of a wonderous gas.", CHAR_NAME(DEFINITE)); + + } + meleeweapon::Break(Who,Much); +} + +truth thievesgirdle::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = item::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(Enemy->IsEnabled() && Hitter && CleptiaHelps(Enemy, Hitter)) + { + if(Hitter->IsPlayer()) + { + game::DoEvilDeed(10); + game::GetGod(CLEPTIA)->AdjustRelation(10); + } + + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("%s whip asks for the help of Cleptia as it steals %s %s.", Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_POSSESSIVE_PRONOUN, Enemy->GetMainWielded()->CHAR_NAME(UNARTICLED)); + + Enemy->GetMainWielded()->MoveTo(GetLSquareUnder()->GetStack()); + return true; + } + else + return BaseSuccess; +} + +truth thievesgirdle::CleptiaHelps(ccharacter* Enemy, ccharacter* Hitter) const +{ + if(Enemy->IsImmuneToWhipOfThievery() || !Enemy->GetMainWielded() || GetMainMaterial()->GetFlexibility() <= 5) + return false; + + if(Hitter->IsPlayer()) + { + if(game::GetGod(CLEPTIA)->GetRelation() < 0) + return false; + else + return !RAND_N(10 - game::GetGod(CLEPTIA)->GetRelation() / 200); + } + else + return !RAND_N(10); +} diff --git a/Main/Source/god.cpp b/Main/Source/god.cpp new file mode 100644 index 0000000..a77c6b4 --- /dev/null +++ b/Main/Source/god.cpp @@ -0,0 +1,536 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through godset.cpp */ + +godprototype::godprototype(godspawner Spawner, cchar* ClassID) : Spawner(Spawner), ClassID(ClassID) { Index = protocontainer::Add(this); } + +god::god() : Relation(0), Timer(0), Known(false), LastPray(-1) { } +int god::GetBasicAlignment() const { return NEUTRAL; } + +void god::Pray() +{ + LastPray = 0; + if(!Timer) + if(Relation >= -RAND_N(500)) + { + ADD_MESSAGE("You feel %s is pleased.", GetName()); + + if(!TryToAttachBodyPart(PLAYER) && !TryToHardenBodyPart(PLAYER)) + PrayGoodEffect(); + + AdjustTimer(5000); + AdjustRelation(50); + game::ApplyDivineAlignmentBonuses(this, 10, true); + PLAYER->EditExperience(WISDOM, 200, 1 << 10); + + if(Relation > 250 && !(RAND() % 20)) + { + character* Angel = CreateAngel(PLAYER->GetTeam(), 10000); + + if(Angel) + ADD_MESSAGE("%s seems to be very friendly towards you.", Angel->CHAR_DESCRIPTION(DEFINITE)); + } + else if(Relation > 100 && !(RAND() % 20)) + { + long Category = RAND() & ANY_CATEGORY; + + if(!Category) + Category = ANY_CATEGORY; + + item* Gift = protosystem::BalancedCreateItem(Relation / 2, Relation * 2, Category, 0, 0, GetType()); + + if(Gift) + { + Gift->CalculateEnchantment(); + PLAYER->GetStack()->AddItem(Gift); + + if(Gift->IsBroken()) + Gift = Gift->Fix(); + + if(Relation == 1000) + Gift->EditEnchantment(3); + else if(Relation >= 500) + Gift->EditEnchantment(2); + else + Gift->EditEnchantment(1); + + ADD_MESSAGE("You notice %s in your inventory which you don't recall picking up anywhere.", Gift->CHAR_NAME(INDEFINITE)); + } + } + } + else + { + ADD_MESSAGE("You feel %s is displeased today.", GetName()); + PrayBadEffect(); + AdjustTimer(10000); + AdjustRelation(-50); + game::ApplyDivineAlignmentBonuses(this, 10, false); + PLAYER->EditExperience(WISDOM, -50, 1 << 10); + } + else + if(Relation > RAND_N(500) && Timer < RAND_N(500000)) + { + ADD_MESSAGE("You feel %s is displeased, but tries to help you anyway.", GetName()); + + if(!TryToAttachBodyPart(PLAYER) && !TryToHardenBodyPart(PLAYER)) + PrayGoodEffect(); + + AdjustTimer(25000); + AdjustRelation(-75); + game::ApplyDivineAlignmentBonuses(this, 15, false); + PLAYER->EditExperience(WISDOM, -50, 1 << 10); + } + else + { + ADD_MESSAGE("You feel %s is angry.", GetName()); + PrayBadEffect(); + AdjustTimer(50000); + AdjustRelation(-100); + game::ApplyDivineAlignmentBonuses(this, 20, false); + PLAYER->EditExperience(WISDOM, -100, 1 << 11); + + if(Relation < -250 && !(RAND() % 5)) + { + character* Angel = CreateAngel(game::GetTeam(4), 10000); + + if(Angel) + ADD_MESSAGE("%s seems to be hostile.", Angel->CHAR_DESCRIPTION(DEFINITE)); + } + } +} + + +festring god::GetCompleteDescription() const +{ + festring Desc(game::GetAlignment(GetAlignment())); + Desc.Resize(4); + Desc << GetName(); + Desc.Resize(20); + + if(game::WizardModeIsActive()) { + Desc << "Timer: " << Timer << " Relation: " << Relation; + return Desc; + } + else + Desc << "You have "; + if (LastPray>-1) + { + int Hour = LastPray / 2000; + int Day = Hour / 24; + Hour %= 24; + int Min = LastPray % 2000 * 60 / 2000; + Desc << "last prayed "; + if (Day>=7) + Desc << "over a week ago."; + else + { + if (Day>1) + Desc << Day << " days, "; + else if (Day) + Desc << "one day, "; + if (Hour>1) + Desc << Hour << " hours, "; + else if (Hour) + Desc << "one hour, "; + if (Day || Hour) + Desc << "and " << Min << " minutes ago."; + else + Desc << Min << " minutes ago."; + } + } + else + Desc << "never prayed to this god."; + return Desc; +} + + +void god::AdjustRelation(god* Competitor, int Multiplier, truth Good) +{ + int Adjustment = (Multiplier << 1) - abs(GetAlignment() - Competitor->GetAlignment()) * Multiplier; + + if(!Good && Adjustment > 0) + Adjustment = -Adjustment; + + AdjustRelation(Adjustment); +} + +void god::AdjustRelation(int Amount) +{ + if(Amount < 0) + Amount = Amount * 100 / (100 + PLAYER->GetAttribute(WISDOM)); + else + Amount = Amount * (100 + PLAYER->GetAttribute(WISDOM)) / 100; + + Relation += Amount; + + if(Relation < -1000) + Relation = -1000; + + if(Relation > 1000) + Relation = 1000; +} + +void god::AdjustTimer(long Amount) +{ + Timer += Amount; + + if(Timer < 0) + Timer = 0; + + if(Timer > 1000000000) + Timer = 1000000000; +} + +truth god::PlayerVomitedOnAltar(liquid* Liquid) +{ + if(PLAYER->GetVirtualHead()) + { + ADD_MESSAGE("The vomit drops on the altar, but then suddenly gravity changes its direction. The vomit lands on your face."); + PLAYER->GetVirtualHead()->SpillFluid(0, Liquid); + PLAYER->ReceiveDamage(0, 1 + (RAND() & 1), ACID, HEAD); + } + else + { + ADD_MESSAGE("The vomit drops on the altar, but then suddenly gravity changes its direction. The vomit lands all over you."); + PLAYER->SpillFluid(0, Liquid); + PLAYER->ReceiveDamage(0, 1 + (RAND() & 1), ACID); + } + + AdjustRelation(-200); + PLAYER->CheckDeath(CONST_S("killed by a flying lump of vomit"), 0); + + if(!(RAND() % 5)) + { + character* Angel = CreateAngel(game::GetTeam(4), 10000); + + if(Angel) + ADD_MESSAGE("%s seems to be hostile.", Angel->CHAR_DESCRIPTION(DEFINITE)); + } + + return true; +} + +character* god::CreateAngel(team* Team, int LifeBase) +{ + v2 TryToCreate; + + for(int c = 0; c < 100; ++c) + { + TryToCreate = PLAYER->GetPos() + game::GetMoveVector(RAND() % DIRECTION_COMMAND_KEYS); + + if(game::GetCurrentArea()->IsValidPos(TryToCreate)) + { + angel* Angel; + + if(LifeBase && GetType() != VALPURUS + && (!(RAND() % 5) || abs(Relation) == 1000)) + { + Angel = archangel::Spawn(GetType()); + Angel->SetLifeExpectancy(LifeBase / 10, 0); + } + else + { + Angel = angel::Spawn(GetType()); + + if(LifeBase) + Angel->SetLifeExpectancy(LifeBase, 0); + } + + lsquare* Square = game::GetCurrentLevel()->GetLSquare(TryToCreate); + + if(Angel->CanMoveOn(Square) && Angel->IsFreeForMe(Square)) + { + Angel->SetTeam(Team); + Angel->SetGenerationDanger(ANGEL_GENERATION_DANGER); + Angel->PutTo(TryToCreate); + ADD_MESSAGE("Suddenly %s appears!", Angel->CHAR_DESCRIPTION(INDEFINITE)); + return Angel; + } + else + Angel->SendToHell(); + } + } + + return 0; +} + +void god::PrintRelation() const +{ + cchar* VerbalRelation; + + if(GetRelation() == 1000) + VerbalRelation = "greets you as a Champion of the Cause!"; + else if(GetRelation() > 750) + VerbalRelation = "is extremely pleased."; + else if(GetRelation() > 250) + VerbalRelation = "is very pleased."; + else if(GetRelation() > 50) + VerbalRelation = "is pleased."; + else if(GetRelation() > -50) + VerbalRelation = "is content."; + else if(GetRelation() > -250) + VerbalRelation = "is angry."; + else if(GetRelation() > -750) + VerbalRelation = "is very angry."; + else if(GetRelation() > -1000) + VerbalRelation = "is extremely angry."; + else VerbalRelation = "hates you more than any other mortal."; + + ADD_MESSAGE("%s %s", GetName(), VerbalRelation); +} + +truth god::ReceiveOffer(item* Sacrifice) +{ + if(Sacrifice->SpecialOfferEffect(GetType())) + { + return true; + } + + int OfferValue = Sacrifice->GetOfferValue(GetType()); + + if(OfferValue) + { + if(!Sacrifice->IsDestroyable(PLAYER)) + { + ADD_MESSAGE("%s is too important for you to be sacrificed.", Sacrifice->CHAR_NAME(DEFINITE)); + return false; + } + + AdjustRelation(OfferValue); + + if(OfferValue > 0) + game::ApplyDivineAlignmentBonuses(this, OfferValue / 5, true); + else + game::ApplyDivineAlignmentBonuses(this, -OfferValue / 5, false); + + if(OfferValue > 0) + PLAYER->EditExperience(WISDOM, 150, 1 << 7); + else + PLAYER->EditExperience(WISDOM, -100, 1 << 7); + + if(OfferValue > 0) + { + if(Sacrifice->GetAttachedGod() == GetType()) + ADD_MESSAGE("%s appreciates your generous offer truly.", GetName()); + else + ADD_MESSAGE("%s thanks you for your gift.", GetName()); + } + else + ADD_MESSAGE("%s seems not to appreciate your gift at all.", GetName()); + + PrintRelation(); + int RandModifier = Sacrifice->GetAttachedGod() == GetType() ? 50 : 100; + + if(OfferValue > 0 && Relation > 250 && !(RAND() % RandModifier)) + { + character* Angel = CreateAngel(PLAYER->GetTeam()); + + if(Angel) + ADD_MESSAGE("%s seems to be very friendly towards you.", Angel->CHAR_DESCRIPTION(DEFINITE)); + } + + return true; + } + else + { + ADD_MESSAGE("Nothing happens."); + return false; + } +} + + +god* godprototype::SpawnAndLoad(inputfile& SaveFile) const +{ + god* God = Spawner(); + God->Load(SaveFile); + return God; +} + +truth god::LikesMaterial(const materialdatabase* MDB, ccharacter*) const +{ + return MDB->AttachedGod == GetType(); +} + +struct materialsorter +{ + materialsorter(citem* Item) : Item(Item) { } + truth operator()(cmaterial* M1, cmaterial* M2) const + { + return M1->GetHardenModifier(Item) > M2->GetHardenModifier(Item); + } + citem* Item; +}; + +truth god::TryToAttachBodyPart(character* Char) +{ + msgsystem::EnterBigMessageMode(); + + if(!Char->HasAllBodyParts()) + { + bodypart* BodyPart = Char->FindRandomOwnBodyPart(true); + + if(BodyPart && LikesMaterial(BodyPart->GetMainMaterial()->GetDataBase(), Char)) + { + BodyPart->RemoveFromSlot(); + Char->AttachBodyPart(BodyPart); + ADD_MESSAGE("%s attaches your old %s back.", GetName(), BodyPart->GetBodyPartName().CStr()); + } + else + { + BodyPart = 0; + materialvector MaterialVector; + protosystem::CreateEveryMaterial(MaterialVector, this, Char); + std::sort(MaterialVector.begin(), MaterialVector.end(), materialsorter(0)); + uint c; + + for(c = 0; c < MaterialVector.size(); ++c) + if(ForceGiveBodyPart() + || (MaterialVector[c]->GetCommonFlags() & CAN_BE_WISHED + && !RAND_N(6000 / (GetRelation() + 2000)) + && !RAND_N(Max(MaterialVector[c]->GetIntelligenceRequirement() - 10, 1)))) + { + BodyPart = Char->GenerateRandomBodyPart(); + BodyPart->ChangeMainMaterial(MaterialVector[c]->SpawnMore()); + Char->UpdatePictures(); + festring MadeOf; + + if(!MaterialVector[c]->IsSameAs(Char->GetTorso()->GetMainMaterial())) + { + MadeOf << " made of "; + MaterialVector[c]->AddName(MadeOf, false, false); + } + + ADD_MESSAGE("%s gives you a new %s%s.", GetName(), BodyPart->GetBodyPartName().CStr(), MadeOf.CStr()); + break; + } + + for(c = 0; c < MaterialVector.size(); ++c) + delete MaterialVector[c]; + } + + if(BodyPart) + { + if(MutatesBodyParts()) + { + BodyPart->Mutate(); + ADD_MESSAGE("It seems somehow different."); + } + + truth Heal = !BodyPart->CanRegenerate() || HealRegeneratingBodyParts(); + BodyPart->SetHP(Heal ? BodyPart->GetMaxHP() : 1); + msgsystem::LeaveBigMessageMode(); + return true; + } + } + + msgsystem::LeaveBigMessageMode(); + return false; +} + +truth god::TryToHardenBodyPart(character* Char) +{ + bodypart* PossibleBodyPart[MAX_BODYPARTS]; + uint c, Index = 0; + + for(c = 1; c < uint(Char->GetBodyParts()); ++c) // annoying :( + { + bodypart* BodyPart = Char->GetBodyPart(c); + + if(BodyPart && LikesMaterial(BodyPart->GetMainMaterial()->GetDataBase(), Char)) + PossibleBodyPart[Index++] = BodyPart; + } + + if(!Index) + return false; + + bodypart* BodyPart = PossibleBodyPart[RAND_N(Index)]; + material* OldMaterial = BodyPart->GetMainMaterial(); + int OldModifier = OldMaterial->GetHardenModifier(BodyPart); + materialvector MaterialVector; + protosystem::CreateEveryMaterial(MaterialVector, this, Char); + std::sort(MaterialVector.begin(), MaterialVector.end(), materialsorter(BodyPart)); + truth Changed = false; + + for(c = 0; c < MaterialVector.size(); ++c) + if(MaterialVector[c]->GetCommonFlags() & CAN_BE_WISHED) + { + material* Material = MaterialVector[c]; + + if(Material->GetHardenModifier(BodyPart) > OldModifier + && !RAND_N(12000 / (GetRelation() + 2000)) + && !RAND_N(Max(Material->GetIntelligenceRequirement() - 15, 1))) + { + BodyPart->ChangeMainMaterial(Material->SpawnMore()); + ADD_MESSAGE("%s changes your %s to %s.", GetName(), BodyPart->GetBodyPartName().CStr(), Material->GetName(false, false).CStr()); + Changed = true; + break; + } + } + + for(c = 0; c < MaterialVector.size(); ++c) + delete MaterialVector[c]; + + return Changed; +} + +cchar* god::GetPersonalPronoun() const +{ + return GetSex() == MALE ? "He" : "She"; +} + +cchar* god::GetObjectPronoun() const +{ + return GetSex() == MALE ? "Him" : "Her"; +} + +void god::SignalRandomAltarGeneration(const std::vector& RoomSquare) +{ + int Times = 1 + femath::LoopRoll(50, 4); + + for(int c = 0; c < Times; ++c) + { + long Category = RAND() & ANY_CATEGORY; + + if(!Category) + Category = ANY_CATEGORY; + + long MaxPrice = 250 + femath::LoopRoll(95, 500) * 10; + item* Item = protosystem::BalancedCreateItem(0, MaxPrice, Category, 0, 0, GetType()); + + if(Item) + { + Item->CalculateEnchantment(); + game::GetCurrentLevel()->GetLSquare(RoomSquare[RAND_N(RoomSquare.size())])->AddItem(Item); + } + } +} + +void god::Save(outputfile& SaveFile) const +{ + SaveFile << (ushort)GetType(); + SaveFile << Relation << Timer << Known << LastPray; +} + +void god::Load(inputfile& SaveFile) +{ + SaveFile >> Relation >> Timer >> Known >> LastPray; +} + + +void god::ApplyDivineTick() +{ + if(Timer) + --Timer; + if(LastPray > -1 && LastPray < 336000) + ++LastPray; +} diff --git a/Main/Source/gods.cpp b/Main/Source/gods.cpp new file mode 100644 index 0000000..c4d8e70 --- /dev/null +++ b/Main/Source/gods.cpp @@ -0,0 +1,1131 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through godset.cpp */ + +#define LAWFUL_BASIC_COLOR MakeRGB16(160, 160, 0) +#define LAWFUL_ELITE_COLOR MakeRGB16(220, 220, 220) + +#define NEUTRAL_BASIC_COLOR MakeRGB16(20, 120, 200) +#define NEUTRAL_ELITE_COLOR MakeRGB16(120, 120, 120) + +#define CHAOS_BASIC_COLOR MakeRGB16(200, 0, 0) +#define CHAOS_ELITE_COLOR MakeRGB16(40, 40, 40) + +cchar* valpurus::GetName() const { return "Valpurus"; } +cchar* valpurus::GetDescription() const { return "King of Gods"; } +int valpurus::GetAlignment() const { return ALPP; } +int valpurus::GetBasicAlignment() const { return GOOD; } +col16 valpurus::GetColor() const { return LAWFUL_BASIC_COLOR; } +col16 valpurus::GetEliteColor() const { return LAWFUL_ELITE_COLOR; } + +cchar* legifer::GetName() const { return "Legifer"; } +cchar* legifer::GetDescription() const { return "god of law and order"; } +int legifer::GetAlignment() const { return ALP; } +int legifer::GetBasicAlignment() const { return GOOD; } +col16 legifer::GetColor() const { return LAWFUL_BASIC_COLOR; } +col16 legifer::GetEliteColor() const { return LAWFUL_ELITE_COLOR; } + +cchar* atavus::GetName() const { return "Atavus"; } +cchar* atavus::GetDescription() const { return "god of charity and munificence"; } +int atavus::GetAlignment() const { return ALP; } +int atavus::GetBasicAlignment() const { return GOOD; } +col16 atavus::GetColor() const { return LAWFUL_BASIC_COLOR; } +col16 atavus::GetEliteColor() const { return LAWFUL_ELITE_COLOR; } + +cchar* dulcis::GetName() const { return "Dulcis"; } +cchar* dulcis::GetDescription() const { return "goddess of love and art"; } +int dulcis::GetAlignment() const { return AL; } +int dulcis::GetBasicAlignment() const { return GOOD; } +col16 dulcis::GetColor() const { return LAWFUL_BASIC_COLOR; } +col16 dulcis::GetEliteColor() const { return LAWFUL_ELITE_COLOR; } + +cchar* seges::GetName() const { return "Seges"; } +cchar* seges::GetDescription() const { return "goddess of health and nutrition"; } +int seges::GetAlignment() const { return AL; } +int seges::GetBasicAlignment() const { return GOOD; } +col16 seges::GetColor() const { return LAWFUL_BASIC_COLOR; } +col16 seges::GetEliteColor() const { return LAWFUL_ELITE_COLOR; } + +cchar* sophos::GetName() const { return "Sophos"; } +cchar* sophos::GetDescription() const { return "god of knowledge, magic and handicrafts"; } +int sophos::GetAlignment() const { return ALM; } +int sophos::GetBasicAlignment() const { return GOOD; } +col16 sophos::GetColor() const { return LAWFUL_BASIC_COLOR; } +col16 sophos::GetEliteColor() const { return LAWFUL_ELITE_COLOR; } + +cchar* silva::GetName() const { return "Silva"; } +cchar* silva::GetDescription() const { return "goddess of nature"; } +int silva::GetAlignment() const { return ANP; } +int silva::GetBasicAlignment() const { return NEUTRAL; } +col16 silva::GetColor() const { return NEUTRAL_BASIC_COLOR; } +col16 silva::GetEliteColor() const { return NEUTRAL_ELITE_COLOR; } + +cchar* loricatus::GetName() const { return "Loricatus"; } +cchar* loricatus::GetDescription() const { return "god of fire, machines and weaponry"; } +int loricatus::GetAlignment() const { return AN; } +int loricatus::GetBasicAlignment() const { return NEUTRAL; } +col16 loricatus::GetColor() const { return NEUTRAL_BASIC_COLOR; } +col16 loricatus::GetEliteColor() const { return NEUTRAL_ELITE_COLOR; } + +cchar* mellis::GetName() const { return "Mellis"; } +cchar* mellis::GetDescription() const { return "god of money, trade and politics"; } +int mellis::GetAlignment() const { return ANM; } +int mellis::GetBasicAlignment() const { return NEUTRAL; } +col16 mellis::GetColor() const { return NEUTRAL_BASIC_COLOR; } +col16 mellis::GetEliteColor() const { return NEUTRAL_ELITE_COLOR; } + +cchar* cleptia::GetName() const { return "Cleptia"; } +cchar* cleptia::GetDescription() const { return "goddess of assassins and thieves"; } +int cleptia::GetAlignment() const { return ACP; } +int cleptia::GetBasicAlignment() const { return EVIL; } +col16 cleptia::GetColor() const { return CHAOS_BASIC_COLOR; } +col16 cleptia::GetEliteColor() const { return CHAOS_ELITE_COLOR; } + +cchar* nefas::GetName() const { return "Nefas"; } +cchar* nefas::GetDescription() const { return "goddess of forbidden pleasures"; } +int nefas::GetAlignment() const { return AC; } +int nefas::GetBasicAlignment() const { return EVIL; } +col16 nefas::GetColor() const { return CHAOS_BASIC_COLOR; } +col16 nefas::GetEliteColor() const { return CHAOS_ELITE_COLOR; } + +cchar* scabies::GetName() const { return "Scabies"; } +cchar* scabies::GetDescription() const { return "goddess of mutations, disease and famine"; } +int scabies::GetAlignment() const { return AC; } +int scabies::GetBasicAlignment() const { return EVIL; } +col16 scabies::GetColor() const { return CHAOS_BASIC_COLOR; } +col16 scabies::GetEliteColor() const { return CHAOS_ELITE_COLOR; } + +cchar* infuscor::GetName() const { return "Infuscor"; } +cchar* infuscor::GetDescription() const { return "goddess of wrong knowledge and vile magic"; } +int infuscor::GetAlignment() const { return ACM; } +int infuscor::GetBasicAlignment() const { return EVIL; } +col16 infuscor::GetColor() const { return CHAOS_BASIC_COLOR; } +col16 infuscor::GetEliteColor() const { return CHAOS_ELITE_COLOR; } + +cchar* cruentus::GetName() const { return "Cruentus"; } +cchar* cruentus::GetDescription() const { return "god of war and blood"; } +int cruentus::GetAlignment() const { return ACM; } +int cruentus::GetBasicAlignment() const { return EVIL; } +col16 cruentus::GetColor() const { return CHAOS_BASIC_COLOR; } +col16 cruentus::GetEliteColor() const { return CHAOS_ELITE_COLOR; } + +cchar* mortifer::GetName() const { return "Mortifer"; } +cchar* mortifer::GetDescription() const { return "Destroyer of Worlds"; } +int mortifer::GetAlignment() const { return ACMM; } +int mortifer::GetBasicAlignment() const { return EVIL; } +col16 mortifer::GetColor() const { return CHAOS_BASIC_COLOR; } +col16 mortifer::GetEliteColor() const { return CHAOS_ELITE_COLOR; } + +/* +cchar* solicitu::GetName() const { return "Solicitus"; } +cchar* solicitu::GetDescription() const { return "god of stress and hopeless situations"; } +int solicitu::GetAlignment() const { return AN; } +int solicitu::GetBasicAlignment() const { return TOPPLED; } +col16 solicitu::GetColor() const { return NEUTRAL_BASIC_COLOR; } +col16 solicitu::GetEliteColor() const { return NEUTRAL_ELITE_COLOR; } +*/ + +void sophos::PrayGoodEffect() +{ + ADD_MESSAGE("Suddenly, the fabric of space experiences an unnaturally powerful quantum displacement! You teleport away!"); + PLAYER->Move(game::GetCurrentLevel()->GetRandomSquare(PLAYER), true); +} + +void sophos::PrayBadEffect() +{ + ADD_MESSAGE("Suddenly, the fabric of space experiences an unnaturally powerful quantum displacement!"); + PLAYER->TeleportSomePartsAway(1 + (RAND() & 1)); + PLAYER->CheckDeath(CONST_S("shattered to pieces by the wrath of ") + GetName(), 0); +} + +void valpurus::PrayGoodEffect() +{ + if(RAND() & 1) + { + ADD_MESSAGE("You hear booming voice: \"THESE WILL PROTECT THEE FROM MORTIFER, DEFEAT HIM, MY PALADIN!\" A sword and shield glittering with holy might appears from nothing."); + shield* Shield = shield::Spawn(); + Shield->InitMaterials(MAKE_MATERIAL(VALPURIUM)); + PLAYER->GetGiftStack()->AddItem(Shield); + meleeweapon* Weapon = meleeweapon::Spawn(TWO_HANDED_SWORD); + Weapon->InitMaterials(MAKE_MATERIAL(VALPURIUM), MAKE_MATERIAL(VALPURIUM), true); + PLAYER->GetGiftStack()->AddItem(Weapon); + } +} + +void valpurus::PrayBadEffect() +{ + ADD_MESSAGE("Valpurus smites you with a small hammer."); + PLAYER->ReceiveDamage(0, 10, PHYSICAL_DAMAGE, HEAD, RAND() & 7); + PLAYER->CheckDeath(CONST_S("faced the hammer of Justice from the hand of ") + GetName(), 0); +} + +void legifer::PrayGoodEffect() +{ + if(!game::GetMasamune()) + { + if(GetRelation() == 1000) + { + ADD_MESSAGE("You hear a booming voice: \"Thou have mastered the arts of law, order, and honor. I grant thee the most sacred symbol of mastery, the Masamune!\" A weapon glowing in power appears from nothing."); + PLAYER->GetGiftStack()->AddItem(masamune::Spawn()); + game::SetMasamune(1); + } + } + + ADD_MESSAGE("A booming voice echoes: \"Inlux! Inlux! Save us!\" A huge firestorm engulfs everything around you."); + game::GetCurrentLevel()->Explosion(PLAYER, CONST_S("killed by the holy fire of ") + GetName(), PLAYER->GetPos(), (300 + Max(GetRelation(), 0)) >> 3, false); +} + +void legifer::PrayBadEffect() +{ + ADD_MESSAGE("%s casts a beam of horrible, yet righteous, fire on you.", GetName()); + PLAYER->ReceiveDamage(0, 50 + RAND() % 50, FIRE, ALL); + PLAYER->CheckDeath(CONST_S("burned to death by the wrath of ") + GetName(), 0); +} + +void dulcis::PrayGoodEffect() +{ + ADD_MESSAGE("A beautiful melody echoes around you."); + + for(int d = 0; d < PLAYER->GetNeighbourSquares(); ++d) + { + square* Square = PLAYER->GetNeighbourSquare(d); + + if(Square) + { + character* Char = Square->GetCharacter(); + + if(Char) + if(Char->CanHear()) + if(Char->CanTameWithDulcis(PLAYER)) + { + if(Char->GetTeam() == PLAYER->GetTeam()) + ADD_MESSAGE("%s seems to be very happy.", Char->CHAR_DESCRIPTION(DEFINITE)); + else if(Char->GetRelation(PLAYER) == HOSTILE) + ADD_MESSAGE("%s stops fighting.", Char->CHAR_DESCRIPTION(DEFINITE)); + else + ADD_MESSAGE("%s seems to be very friendly towards you.", Char->CHAR_DESCRIPTION(DEFINITE)); + + Char->ChangeTeam(PLAYER->GetTeam()); + } + else + ADD_MESSAGE("%s resists its charming call.", Char->CHAR_DESCRIPTION(DEFINITE)); + else + ADD_MESSAGE("%s seems not affected.", Char->CHAR_DESCRIPTION(DEFINITE)); + } + } +} + +void dulcis::PrayBadEffect() +{ + ADD_MESSAGE("%s plays a horrible tune that rots your brain.", GetName()); + PLAYER->ReceiveDamage(0, 1 + RAND() % 9, SOUND, HEAD); + PLAYER->CheckDeath(CONST_S("became insane by listening ") + GetName() + " too much", 0); +} + +void seges::PrayGoodEffect() +{ + if(PLAYER->IsInBadCondition()) + { + ADD_MESSAGE("%s cures your wounds.", GetName()); + PLAYER->RestoreLivingHP(); + return; + } + + if(PLAYER->TemporaryStateIsActivated(POISONED)) + { + ADD_MESSAGE("%s removes the foul liquid in your veins.", GetName()); + PLAYER->DeActivateTemporaryState(POISONED); + return; + } + + if(PLAYER->StateIsActivated(LEPROSY)) + { + ADD_MESSAGE("%s cures your leprosy.", GetName()); + PLAYER->DeActivateTemporaryState(LEPROSY); + return; + } + + if(PLAYER->GetNP() < SATIATED_LEVEL) + { + ADD_MESSAGE("Your stomach feels full again."); + PLAYER->SetNP(BLOATED_LEVEL); + return; + } + + if(PLAYER->GetStamina() < PLAYER->GetMaxStamina() >> 1) + { + ADD_MESSAGE("You don't feel a bit tired anymore."); + PLAYER->RestoreStamina(); + return; + } +} + +void seges::PrayBadEffect() +{ + if(PLAYER->UsesNutrition()) + { + ADD_MESSAGE("You feel Seges altering the contents of your stomach in an eerie way."); + PLAYER->EditNP(-10000); + PLAYER->CheckStarvationDeath(CONST_S("starved by ") + GetName()); + } + else + ADD_MESSAGE("Seges tries to alter the contents of your stomach, but fails."); +} + +void atavus::PrayGoodEffect() +{ + if(!Timer && Relation > 500 + RAND_N(500)) + { + item* Reward = bodyarmor::Spawn(PLATE_MAIL, NO_MATERIALS); + Reward->InitMaterials(MAKE_MATERIAL(ARCANITE)); + ADD_MESSAGE("%s materializes before you.", Reward->CHAR_NAME(INDEFINITE)); + PLAYER->GetGiftStack()->AddItem(Reward); + AdjustTimer(45000); + AdjustRelation(-300); + } + else + ADD_MESSAGE("Nothing happens."); +} + +void atavus::PrayBadEffect() +{ + ADD_MESSAGE("You have not been good the whole year."); + + if(PLAYER->GetStack()->GetItems()) + { + int ToBeDeleted = RAND() % PLAYER->GetStack()->GetItems(); + item* Disappearing = PLAYER->GetStack()->GetItem(ToBeDeleted); + + if(Disappearing->IsDestroyable(0)) + { + ADD_MESSAGE("Your %s disappears.", Disappearing->CHAR_NAME(UNARTICLED)); + Disappearing->RemoveFromSlot(); + Disappearing->SendToHell(); + } + else + { + ADD_MESSAGE("%s tries to remove your %s, but fails. You feel you are not so gifted anymore.", GetName(), Disappearing->CHAR_NAME(UNARTICLED)); + PLAYER->EditAttribute(AGILITY, -1); + PLAYER->EditAttribute(ARM_STRENGTH, -1); + PLAYER->EditAttribute(ENDURANCE, -1); + } + } + else + { + ADD_MESSAGE("You feel you are not so gifted anymore."); + PLAYER->EditAttribute(AGILITY, -1); + PLAYER->EditAttribute(ARM_STRENGTH, -1); + PLAYER->EditAttribute(ENDURANCE, -1); + } + + PLAYER->CheckDeath(CONST_S("killed by Atavus's humour")); +} + +void silva::PrayGoodEffect() +{ + if(PLAYER->GetNP() < HUNGER_LEVEL) + { + ADD_MESSAGE("Your stomach feels full again."); + PLAYER->SetNP(SATIATED_LEVEL); + } + + if(!game::GetCurrentLevel()->IsOnGround()) + { + ADD_MESSAGE("Suddenly a horrible earthquake shakes the level."); + int c, Tunnels = 2 + RAND() % 3; + + for(c = 0; c < Tunnels; ++c) + game::GetCurrentLevel()->AttachPos(game::GetCurrentLevel()->GetRandomSquare(0, NOT_WALKABLE|ATTACHABLE)); + + int ToEmpty = 10 + RAND() % 11; + + for(c = 0; c < ToEmpty; ++c) + for(int i = 0; i < 50; ++i) + { + v2 Pos = game::GetCurrentLevel()->GetRandomSquare(0, NOT_WALKABLE); + truth Correct = false; + + for(int d = 0; d < 8; ++d) + { + lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos)->GetNeighbourLSquare(d); + + if(Square && Square->IsFlyable()) + { + Correct = true; + break; + } + } + + if(Correct) + { + game::GetCurrentLevel()->GetLSquare(Pos)->ChangeOLTerrainAndUpdateLights(0); + break; + } + } + + int ToGround = 20 + RAND() % 21; + + for(c = 0; c < ToGround; ++c) + for(int i = 0; i < 50; ++i) + { + v2 Pos = game::GetCurrentLevel()->GetRandomSquare(0, RAND() & 1 ? 0 : HAS_CHARACTER); + + if(Pos == ERROR_V2) + continue; + + lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos); + character* Char = Square->GetCharacter(); + + if(Square->GetOLTerrain() || (Char && (Char->IsPlayer() || PLAYER->GetRelation(Char) != HOSTILE))) + continue; + + int Walkables = 0; + + for(int d = 0; d < 8; ++d) + { + lsquare* NearSquare = game::GetCurrentLevel()->GetLSquare(Pos)->GetNeighbourLSquare(d); + + if(NearSquare && NearSquare->IsFlyable()) + ++Walkables; + } + + if(Walkables > 6) + { + Square->ChangeOLTerrainAndUpdateLights(earth::Spawn()); + + if(Char) + { + if(Char->CanBeSeenByPlayer()) + ADD_MESSAGE("%s is hit by a brick of earth falling from the roof!", Char->CHAR_NAME(DEFINITE)); + + Char->ReceiveDamage(0, 20 + RAND() % 21, PHYSICAL_DAMAGE, HEAD|TORSO, 8, true); + Char->CheckDeath(CONST_S("killed by an earthquake"), 0); + } + + Square->KickAnyoneStandingHereAway(); + Square->GetStack()->ReceiveDamage(0, 10 + RAND() % 41, PHYSICAL_DAMAGE); + break; + } + } + + // Generate a few boulders in the level + + int BoulderNumber = 10 + RAND() % 10; + + for(c = 0; c < BoulderNumber; ++c) + { + v2 Pos = game::GetCurrentLevel()->GetRandomSquare(); + lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos); + character* MonsterHere = Square->GetCharacter(); + + if(!Square->GetOLTerrain() && (!MonsterHere || MonsterHere->GetRelation(PLAYER) == HOSTILE)) + { + Square->ChangeOLTerrainAndUpdateLights(boulder::Spawn(1 + (RAND() & 1))); + + if(MonsterHere) + MonsterHere->ReceiveDamage(0, 10 + RAND() % 10, PHYSICAL_DAMAGE, HEAD|TORSO, 8, true); + + Square->GetStack()->ReceiveDamage(0, 10 + RAND() % 10, PHYSICAL_DAMAGE); + } + } + + // Damage to items in the level + + for(int x = 0; x < game::GetCurrentLevel()->GetXSize(); ++x) + for(int y = 0; y < game::GetCurrentLevel()->GetYSize(); ++y) + game::GetCurrentLevel()->GetLSquare(x, y)->ReceiveEarthQuakeDamage(); + } + else + { + int TryToCreate = 1 + RAND() % 7; + int Created = 0; + + for(; Created < TryToCreate; ++Created) + { + wolf* Wolf = wolf::Spawn(); + v2 Pos = game::GetCurrentLevel()->GetNearestFreeSquare(Wolf, PLAYER->GetPos()); + + if(Pos == ERROR_V2) + { + delete Wolf; + break; + } + + Wolf->SetTeam(PLAYER->GetTeam()); + Wolf->PutTo(Pos); + } + + if(!Created && PLAYER->CanHear()) + ADD_MESSAGE("You hear a sad howling of a wolf imprisoned in the earth."); + + if(Created == 1) + ADD_MESSAGE("Suddenly a tame wolf materializes beside you."); + + if(Created > 1) + ADD_MESSAGE("Suddenly some tame wolves materialize around you."); + } +} + +void silva::PrayBadEffect() +{ + switch(RAND() % 3) + { + case 0: + PLAYER->Polymorph(largerat::Spawn(), 1000 + RAND() % 1000); + break; + case 1: + PLAYER->Polymorph(ass::Spawn(), 1000 + RAND() % 1000); + break; + case 2: + PLAYER->Polymorph(jackal::Spawn(), 1000 + RAND() % 1000); + break; + } +} + +void loricatus::PrayGoodEffect() +{ + + if(!game::GetLoricatusHammer()) + + if(GetRelation() == 1000) + { + { + ADD_MESSAGE("You hear a booming voice: \"Thou have succeeded the arts of blacksmithing, I aid ye well with a divine hammer\" A hammer appears from nothing."); + thunderhammer* Weapon = thunderhammer::Spawn(HAMMER); + Weapon->InitMaterials(MAKE_MATERIAL(ILLITHIUM), MAKE_MATERIAL(SAPPHIRE), true); + PLAYER->GetGiftStack()->AddItem(Weapon); + game::SetLoricatusHammer(1); + } + } + item* MainWielded = PLAYER->GetMainWielded(); + + if(MainWielded) + { + if(MainWielded->IsMaterialChangeable() && MainWielded->GetMainMaterial()->GetAttachedGod() == GetType()) + { + int Config = MainWielded->GetMainMaterial()->GetHardenedMaterial(MainWielded); + + if(Config) + { + int IR = material::GetDataBase(Config)->IntelligenceRequirement - GetRelation() / 50; + + if(IR <= 1 || !RAND_N(IR)) + { + festring Desc; + item* SecondaryWielded; + + if(MainWielded->HandleInPairs() && (SecondaryWielded = PLAYER->GetSecondaryWielded()) && SecondaryWielded->CanBePiledWith(MainWielded, PLAYER)) + { + MainWielded->AddName(Desc, PLURAL); + Desc << " glow and sparkle like they were"; + + if(SecondaryWielded->GetSecondaryMaterial() && SecondaryWielded->GetSecondaryMaterial()->IsSameAs(MainWielded->GetMainMaterial())) + SecondaryWielded->ChangeSecondaryMaterial(MAKE_MATERIAL(Config)); + + SecondaryWielded->ChangeMainMaterial(MAKE_MATERIAL(Config)); + } + else + { + MainWielded->AddName(Desc, UNARTICLED); + Desc << " glows and sparkles like it was"; + } + + if(MainWielded->GetSecondaryMaterial() && MainWielded->GetSecondaryMaterial()->IsSameAs(MainWielded->GetMainMaterial())) + MainWielded->ChangeSecondaryMaterial(MAKE_MATERIAL(Config)); + + MainWielded->ChangeMainMaterial(MAKE_MATERIAL(Config)); + ADD_MESSAGE("Your %s reforged by invisible hands.", Desc.CStr()); + return; + } + } + + if(!(RAND() % 10)) + { + item* Scroll = scrollofrepair::Spawn(); + ADD_MESSAGE("%s gives you %s.", GetName(), Scroll->CHAR_NAME(INDEFINITE)); + PLAYER->GetGiftStack()->AddItem(Scroll); + return; + } + else + ADD_MESSAGE("\"Mortal, thou art always my valiant knight!\""); + } + } + + for(int c = 0; c < PLAYER->GetEquipments(); ++c) + { + item* Equipment = PLAYER->GetEquipment(c); + + if(Equipment && Equipment->IsBroken()) + { + ADD_MESSAGE("%s fixes your %s.", GetName(), Equipment->CHAR_NAME(UNARTICLED)); + Equipment->Fix(); + return; + } + } + + if(PLAYER->GetUsableArms()) + ADD_MESSAGE("You feel a slight tingling in your hands."); + else + ADD_MESSAGE("You feel a slight tingle."); +} + +void loricatus::PrayBadEffect() +{ + item* MainWielded = PLAYER->GetMainWielded(); + + if(MainWielded) + if(MainWielded->IsMaterialChangeable()) + { + festring Desc; + item* SecondaryWielded; + + if(MainWielded->HandleInPairs() && (SecondaryWielded = PLAYER->GetSecondaryWielded()) && SecondaryWielded->CanBePiledWith(MainWielded, PLAYER)) + { + MainWielded->AddName(Desc, PLURAL); + Desc << " vibrate and soften"; + SecondaryWielded->ChangeMainMaterial(MAKE_MATERIAL(BANANA_FLESH)); + } + else + { + MainWielded->AddName(Desc, UNARTICLED); + Desc << " vibrates and softens"; + } + + MainWielded->ChangeMainMaterial(MAKE_MATERIAL(BANANA_FLESH)); + ADD_MESSAGE("Your %s.", Desc.CStr()); + } + else + ADD_MESSAGE("%s emits strange light but remain unchanged.", MainWielded->CHAR_NAME(DEFINITE)); + else + { + if(PLAYER->GetUsableArms()) + ADD_MESSAGE("You feel a slight tingling in your hands."); + else + ADD_MESSAGE("You feel a slight tingle."); + } +} + +void cleptia::PrayGoodEffect() +{ + PLAYER->RestoreStamina(); + + if(!PLAYER->StateIsActivated(HASTE)) + { + ADD_MESSAGE("%s gives you the talent for speed.", GetName()); + PLAYER->BeginTemporaryState(HASTE, 2500); + return; + } + + if(!PLAYER->StateIsActivated(INVISIBLE)) + { + ADD_MESSAGE("%s helps you to avoid your enemies by making you invisible.", GetName()); + PLAYER->BeginTemporaryState(INVISIBLE, 2500); + return; + } + + ADD_MESSAGE("Cleptia helps you, but you really don't know how."); + int StateToActivate = RAND() & 1 ? HASTE : INVISIBLE; + PLAYER->BeginTemporaryState(StateToActivate, 2500); +} + +void cleptia::PrayBadEffect() +{ + ADD_MESSAGE("%s slows you down.", GetName()); + PLAYER->BeginTemporaryState(SLOW, 250); +} + +void mortifer::PrayGoodEffect() +{ + ADD_MESSAGE("The air vibrates violently around you. A terrible undead voice echoes through the caverns: \"SlAvE! ThOu HaSt PlAeSeD mE! lIfT tHy ReWaRd, ChAmPiOn!\" A heavy weapon of pure corruption materializes before you."); + PLAYER->GetGiftStack()->AddItem(neercseulb::Spawn()); +} + +void mortifer::PrayBadEffect() +{ + ADD_MESSAGE("A dark, booming voice shakes the area: \"PuNy MoRtAl! ThOu ArT nOt WoRtHy! I sHaLl dEsTrOy ThEe LiKe EvErYoNe ElSe!\" A bolt of black energy hits you."); + PLAYER->ReceiveDamage(0, 1 + RAND() % 20, ENERGY, ALL); + PLAYER->EditAttribute(AGILITY, -1); + PLAYER->EditAttribute(ARM_STRENGTH, -1); + PLAYER->EditAttribute(ENDURANCE, -1); + PLAYER->CheckDeath(CONST_S("obliterated by the unholy power of ") + GetName(), 0); +} + +void mellis::PrayGoodEffect() +{ + truth Success = false; + itemvector OKItems; + + for(stackiterator i = PLAYER->GetStack()->GetBottom(); i.HasItem(); ++i) + { + if(!i->HasBetterVersion()) + continue; + + OKItems.push_back(*i); + Success = true; + } + + item* NewVersion; + + for(int c = 0; !OKItems.empty() && c < 5; ++c) + { + item* ToBeDeleted = OKItems[RAND() % OKItems.size()]; + NewVersion = ToBeDeleted->BetterVersion(); + ADD_MESSAGE("%s manages to trade %s into %s.", GetName(), ToBeDeleted->CHAR_NAME(DEFINITE), NewVersion->CHAR_NAME(INDEFINITE)); + PLAYER->GetStack()->AddItem(NewVersion); + ToBeDeleted->RemoveFromSlot(); + ToBeDeleted->SendToHell(); + OKItems.erase(std::find(OKItems.begin(), OKItems.end(), ToBeDeleted)); + } + + if((Success && !(RAND() % 5)) || (!Success && !(RAND() % 3))) + { + int Possible[GODS]; + int PossibleSize = 0; + + for(int c = 1; c <= GODS; ++c) + if(!game::GetGod(c)->IsKnown()) + Possible[PossibleSize++] = c; + + if(PossibleSize) + { + int NewKnownGod = Possible[RAND() % PossibleSize]; + game::LearnAbout(game::GetGod(NewKnownGod)); + ADD_MESSAGE("%s shares his knowledge of %s, the %s.", GetName(), game::GetGod(NewKnownGod)->GetName(), game::GetGod(NewKnownGod)->GetDescription()); + return; + } + } + + if(!Success) + ADD_MESSAGE("Nothing happens."); +} + +void mellis::PrayBadEffect() +{ + for(int c = 1; c <= GODS; ++c) + if(c != GetType()) + game::GetGod(c)->AdjustRelation(-100); + + ADD_MESSAGE("%s spreads bad rumours about you to other gods.", GetName()); +} + +void valpurus::Pray() +{ + if(!Timer && Relation == 1000) + { + ADD_MESSAGE("You feel %s is very pleased.", GetName()); + PrayGoodEffect(); + AdjustTimer(100000); + AdjustRelation(-500); + game::ApplyDivineAlignmentBonuses(this, 100, true); + PLAYER->EditExperience(WISDOM, 400, 1 << 11); + + if(Relation > 250 && !(RAND() % 2)) + { + character* Angel = CreateAngel(PLAYER->GetTeam()); + + if(Angel) + ADD_MESSAGE("%s seems to be very friendly towards you.", Angel->CHAR_DESCRIPTION(DEFINITE)); + } + } + else if(Relation < 0 || (!TryToAttachBodyPart(PLAYER) && !TryToHardenBodyPart(PLAYER))) + { + ADD_MESSAGE("You feel you are not yet worthy enough for %s.", GetName()); + PrayBadEffect(); + AdjustTimer(50000); + AdjustRelation(-100); + game::ApplyDivineAlignmentBonuses(this, 20, false); + PLAYER->EditExperience(WISDOM, -50, 1 << 10); + + if(Relation < -250 && !(RAND() % 3)) + { + character* Angel = CreateAngel(game::GetTeam(4), 10000); + + if(Angel) + ADD_MESSAGE("%s seems to be hostile.", Angel->CHAR_DESCRIPTION(DEFINITE)); + } + } +} + +void mortifer::Pray() +{ + if(!Timer && Relation == 1000) + { + ADD_MESSAGE("You feel %s is very pleased.", GetName()); + PrayGoodEffect(); + AdjustTimer(100000); + AdjustRelation(-500); + game::ApplyDivineAlignmentBonuses(this, 100, true); + PLAYER->EditExperience(WISDOM, 400, 1 << 11); + + if(Relation > 250 && !(RAND() % 2)) + { + character* Angel = CreateAngel(PLAYER->GetTeam()); + + if(Angel) + ADD_MESSAGE("%s seems to be very friendly towards you.", Angel->CHAR_DESCRIPTION(DEFINITE)); + } + } + else + { + ADD_MESSAGE("You feel you are not yet worthy enough for %s.", GetName()); + PrayBadEffect(); + AdjustTimer(50000); + AdjustRelation(-100); + game::ApplyDivineAlignmentBonuses(this, 20, false); + PLAYER->EditExperience(WISDOM, -50, 1 << 10); + + if(Relation < -250 && !(RAND() % 3)) + { + character* Angel = CreateAngel(game::GetTeam(4), 10000); + + if(Angel) + ADD_MESSAGE("%s seems to be hostile.", Angel->CHAR_DESCRIPTION(DEFINITE)); + } + } +} + +void infuscor::PrayBadEffect() +{ + ADD_MESSAGE("Vile and evil knowledge pulps into your brain. It's too much for it to handle; you faint."); + PLAYER->LoseConsciousness(1000 + RAND_N(1000)); +} + +void nefas::PrayGoodEffect() +{ + rect Rect; + femath::CalculateEnvironmentRectangle(Rect, game::GetCurrentLevel()->GetBorder(), PLAYER->GetPos(), 10); + truth AudiencePresent = false; + + for(int x = Rect.X1; x <= Rect.X2; ++x) + { + for(int y = Rect.Y1; y <= Rect.Y2; ++y) + { + character* Audience = game::GetCurrentLevel()->GetSquare(x, y)->GetCharacter(); + + if(Audience && Audience->CanBeSeenByPlayer() && !Audience->TemporaryStateIsActivated(CONFUSED) && Audience->CanBeConfused() && PLAYER->GetRelation(Audience) == HOSTILE) + { + AudiencePresent = true; + break; + } + } + + if(AudiencePresent) + break; + } + + if(AudiencePresent) + { + for(int x = Rect.X1; x <= Rect.X2; ++x) + for(int y = Rect.Y1; y <= Rect.Y2; ++y) + { + character* Audience = game::GetCurrentLevel()->GetSquare(x, y)->GetCharacter(); + + if(Audience && !Audience->TemporaryStateIsActivated(CONFUSED) && Audience->CanBeConfused() && PLAYER->GetRelation(Audience) == HOSTILE) + { + if(Audience->CanBeSeenByPlayer()) + ADD_MESSAGE("%s confuses %s with her sweet lies.", GetName(), Audience->CHAR_NAME(DEFINITE)); + + Audience->BeginTemporaryState(CONFUSED, 500 + RAND() % 500); + } + } + } + + mistress* Mistress = mistress::Spawn(RAND() & 7 ? 0 : TORTURING_CHIEF); + v2 Where = game::GetCurrentLevel()->GetNearestFreeSquare(Mistress, PLAYER->GetPos()); + + if(Where == ERROR_V2) + { + if(PLAYER->CanHear()) + ADD_MESSAGE("You hear a strange scream from somewhere beneath."); + else + ADD_MESSAGE("You feel the air vibrating."); + + delete Mistress; + } + else + { + Mistress->SetTeam(PLAYER->GetTeam()); + Mistress->PutTo(Where); + ADD_MESSAGE("You hear a sweet voice inside your head: \"Have fun, mortal!\""); + } +} + +void nefas::PrayBadEffect() +{ + ADD_MESSAGE("A potion drops on your head and shatters into small bits."); + PLAYER->ReceiveDamage(0, 2 + RAND() % 7, PHYSICAL_DAMAGE, HEAD); + PLAYER->GetStackUnder()->AddItem(brokenbottle::Spawn()); + PLAYER->CheckDeath(CONST_S("killed while enjoying the company of ") + GetName(), 0); +} + +void scabies::PrayGoodEffect() +{ + if(!RAND_N(10)) + { + for(int c = 0; c < game::GetTeams(); ++c) + if(PLAYER->GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + { + character* Char = *i; + + if((*i)->IsEnabled() && !Char->IsImmuneToLeprosy()) + Char->GainIntrinsic(LEPROSY); + } + + ADD_MESSAGE("You feel a a horrible disease spreading."); + return; + } + + if(!(RAND() % 50)) + { + ADD_MESSAGE("Five cans full of school food drop from somewhere above!"); + + for(int c = 0; c < 5; ++c) + { + can* Reward = can::Spawn(0, NO_MATERIALS); + Reward->InitMaterials(MAKE_MATERIAL(IRON), MAKE_MATERIAL(SCHOOL_FOOD)); + PLAYER->GetGiftStack()->AddItem(Reward); + } + + return; + } + + truth Success = false; + + for(int d = 0; d < PLAYER->GetNeighbourSquares(); ++d) + { + lsquare* Square = PLAYER->GetNeighbourLSquare(d); + + if(Square && Square->GetCharacter() && Square->GetCharacter()->GetRelation(PLAYER) == HOSTILE) + { + ADD_MESSAGE("%s throws poison on %s!", GetName(), Square->GetCharacter()->CHAR_DESCRIPTION(DEFINITE)); + Square->SpillFluid(PLAYER, liquid::Spawn(POISON_LIQUID, 500)); + Success = true; + } + } + + if(!Success) + PLAYER->PolymorphRandomly(2500, 10000, 1000 + RAND() % 1000); +} + +void scabies::PrayBadEffect() +{ + if(!(RAND() % 50)) + { + ADD_MESSAGE("%s makes you eat a LOT of school food.", GetName()); + material* SchoolFood = MAKE_MATERIAL(SCHOOL_FOOD, 2000); + SchoolFood->EatEffect(PLAYER, 1000); + delete SchoolFood; + ADD_MESSAGE("You feel your muscles softening terribly..."); + PLAYER->EditAttribute(ARM_STRENGTH, -1); + PLAYER->EditAttribute(DEXTERITY, -1); + } + else if(RAND_2) + { + ADD_MESSAGE("%s unleashes all her fury upon you!", GetName()); + PLAYER->BeginTemporaryState(POISONED, 600 + RAND() % 400); + } + else + { + ADD_MESSAGE("%s unleashes a horrible sickness upon you!", GetName()); + PLAYER->GainIntrinsic(LEPROSY); + } +} + +void infuscor::PrayGoodEffect() +{ + ADD_MESSAGE("%s helps you.", GetName()); + + if(!PLAYER->StateIsActivated(ESP)) + { + PLAYER->BeginTemporaryState(ESP, 10000 + RAND() % 10000); + return; + } + + if(!PLAYER->StateIsActivated(TELEPORT_CONTROL)) + { + PLAYER->BeginTemporaryState(TELEPORT_CONTROL, 10000 + RAND() % 10000); + return; + } + + if(!PLAYER->StateIsActivated(POLYMORPH_CONTROL)) + { + PLAYER->BeginTemporaryState(POLYMORPH_CONTROL, 10000 + RAND() % 10000); + return; + } + + ADD_MESSAGE("Suddenly three scrolls appear almost under your feet."); + + for(int c = 0; c < 3; ++c) + PLAYER->GetGiftStack()->AddItem(scrollofteleportation::Spawn()); +} + +void cruentus::PrayGoodEffect() +{ + + if(!game::GetMuramasa()) + { + if(GetRelation() == 1000) + { + ADD_MESSAGE("The air around you becomes as warm as blood. A voice booms: \"Thou have pleased me greatly, lift and behold: thy most sacred reward, the Muramasa\" A weapon of pure corruption materializes before you."); + PLAYER->GetGiftStack()->AddItem(muramasa::Spawn()); + game::SetMuramasa(1); + } + } + + rect Rect; + femath::CalculateEnvironmentRectangle(Rect, game::GetCurrentLevel()->GetBorder(), PLAYER->GetPos(), 10); + truth AudiencePresent = false; + + for(int x = Rect.X1; x <= Rect.X2; ++x) + { + for(int y = Rect.Y1; y <= Rect.Y2; ++y) + { + character* Audience = game::GetCurrentLevel()->GetSquare(x, y)->GetCharacter(); + + if(Audience && Audience->CanBeSeenByPlayer() && !Audience->TemporaryStateIsActivated(PANIC) && PLAYER->GetRelation(Audience) == HOSTILE) + { + AudiencePresent = true; + break; + } + } + + if(AudiencePresent) + break; + } + + if(AudiencePresent) + { + ADD_MESSAGE("The thundering voice of a godly battle drum shakes everything around you."); + + for(int x = Rect.X1; x <= Rect.X2; ++x) + for(int y = Rect.Y1; y <= Rect.Y2; ++y) + { + character* Audience = game::GetCurrentLevel()->GetSquare(x, y)->GetCharacter(); + + if(Audience + && !Audience->TemporaryStateIsActivated(PANIC) + && PLAYER->GetRelation(Audience) == HOSTILE + && Audience->GetPanicLevel() > RAND() % 33) + Audience->BeginTemporaryState(PANIC, 500 + RAND() % 500); + } + + return; + } + + item* Weapon = PLAYER->GetMainWielded(); + + if(!Weapon || !Weapon->IsWeapon(PLAYER)) + Weapon = PLAYER->GetSecondaryWielded(); + + if(Weapon && Weapon->IsWeapon(PLAYER) && Weapon->CanBeEnchanted() + && Weapon->GetEnchantment() < 5 && !(RAND() % 10)) + { + ADD_MESSAGE("Your %s glows briefly red. It feels very warm now.", Weapon->CHAR_NAME(UNARTICLED)); + Weapon->EditEnchantment(1); + } + else if(RAND() & 3) + { + potion* Bottle = potion::Spawn(0, NO_MATERIALS); + Bottle->InitMaterials(MAKE_MATERIAL(GLASS), MAKE_MATERIAL(TROLL_BLOOD)); + PLAYER->GetGiftStack()->AddItem(Bottle); + ADD_MESSAGE("%s drops from nowhere.", Bottle->CHAR_DESCRIPTION(DEFINITE)); + } + else + { + ADD_MESSAGE("Cruentus recommends you to his master, Mortifer."); + game::GetGod(MORTIFER)->AdjustRelation(100); + } +} + +void cruentus::PrayBadEffect() +{ + item* ToBe = PLAYER->GetMainWielded(); + + if(ToBe) + { + if(!ToBe->IsDestroyable(0)) + { + ToBe = PLAYER->GetSecondaryWielded(); + + if(!ToBe || !ToBe->IsDestroyable(0)) + ADD_MESSAGE("%s tries to destroy your %s, but fails.", GetName(), PLAYER->GetMainWielded()->CHAR_NAME(UNARTICLED)); + } + } + else + { + ToBe = PLAYER->GetSecondaryWielded(); + + if(ToBe && !ToBe->IsDestroyable(0)) + ADD_MESSAGE("%s tries to destroy your %s, but fails.", GetName(), ToBe->CHAR_NAME(UNARTICLED)); + } + + if(ToBe && ToBe->IsDestroyable(0)) + { + ADD_MESSAGE("%s destroys your weapon.", GetName()); + ToBe->RemoveFromSlot(); + ToBe->SendToHell(); + } + else + { + ADD_MESSAGE("%s gets mad and hits you!", GetName()); + PLAYER->ReceiveDamage(0, 1 + RAND() % 30, PHYSICAL_DAMAGE, ALL, RAND() & 7); + PLAYER->CheckDeath(CONST_S("destroyed by ") + GetName(), 0); + } +} + +truth scabies::PlayerVomitedOnAltar(liquid*) +{ + ADD_MESSAGE("%s feels that you are indeed her follower.", GetName()); + AdjustRelation(1); + return false; +} + +truth atavus::LikesMaterial(const materialdatabase* MDB, ccharacter* Char) const +{ + return Char->GetTorso()->GetMainMaterial()->GetConfig() == MDB->Config; +} + +truth seges::LikesMaterial(const materialdatabase* MDB, ccharacter* Char) const +{ + return MDB->BodyFlags & IS_ALIVE + && Char->GetTorso()->GetMainMaterial()->GetConfig() == MDB->Config; +} + +truth scabies::LikesMaterial(const materialdatabase* MDB, ccharacter* Char) const +{ + return MDB->BodyFlags & IS_ALIVE + && Char->GetTorso()->GetMainMaterial()->GetConfig() == MDB->Config; +} +/* +void solicitu::Pray() +{ + ADD_MESSAGE("You hear an anguished voice crying: \"Legifer, forgive me! My trangression was aeons ago!\"", GetName()); + //randomise + ADD_MESSAGE("You hear an anguished voice crying: \"I am powerless to help you mortal!\"", GetName()); + //this pray response should probably be a function of story state +} + +void solicitu::PrayGoodEffect() +{ + //ADD_MESSAGE("Suddenly, the fabric of space experiences an unnaturally powerful quantum displacement! You teleport away!"); + //PLAYER->Move(game::GetCurrentLevel()->GetRandomSquare(PLAYER), true); +} + +void solicitu::PrayBadEffect() +{ + //ADD_MESSAGE("Suddenly, the fabric of space experiences an unnaturally powerful quantum displacement!"); + //PLAYER->TeleportSomePartsAway(1 + (RAND() & 1)); + //PLAYER->CheckDeath(CONST_S("shattered to pieces by the wrath of ") + GetName(), 0); +} +*/ diff --git a/Main/Source/godset.cpp b/Main/Source/godset.cpp new file mode 100644 index 0000000..2e26391 --- /dev/null +++ b/Main/Source/godset.cpp @@ -0,0 +1,40 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#define __FILE_OF_STATIC_GOD_PROTOTYPE_DEFINITIONS__ + +#include "proto.h" +#include "god.h" + +SYSTEM_SPECIALIZATIONS(god) + +#include "gods.h" + +#undef __FILE_OF_STATIC_GOD_PROTOTYPE_DEFINITIONS__ + +#include + +#include "human.h" +#include "message.h" +#include "game.h" +#include "nonhuman.h" +#include "stack.h" +#include "lterras.h" +#include "gear.h" +#include "miscitem.h" +#include "confdef.h" +#include "materias.h" +#include "save.h" +#include "team.h" + +#include "god.cpp" +#include "gods.cpp" diff --git a/Main/Source/human.cpp b/Main/Source/human.cpp new file mode 100644 index 0000000..18ce5f5 --- /dev/null +++ b/Main/Source/human.cpp @@ -0,0 +1,6692 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through charsset.cpp */ + +cint humanoid::DrawOrder[] = { TORSO_INDEX, GROIN_INDEX, RIGHT_LEG_INDEX, LEFT_LEG_INDEX, RIGHT_ARM_INDEX, LEFT_ARM_INDEX, HEAD_INDEX }; + +truth humanoid::BodyPartIsVital(int I) const { return I == TORSO_INDEX || I == HEAD_INDEX || I == GROIN_INDEX; } +truth humanoid::BodyPartCanBeSevered(int I) const { return I != TORSO_INDEX && I != GROIN_INDEX; } +int humanoid::OpenMultiplier() const { return HasAUsableArm() ? 1 : 3; } +int humanoid::CloseMultiplier() const { return HasAUsableArm() ? 1 : 2; } +int humanoid::GetCarryingStrength() const { return Max(GetAttribute(LEG_STRENGTH), 1) + CarryingBonus; } +void humanoid::CalculateBodyParts() { BodyParts = HUMANOID_BODYPARTS; } +void humanoid::CalculateAllowedWeaponSkillCategories() { AllowedWeaponSkillCategories = WEAPON_SKILL_CATEGORIES; } + +v2 farmer::GetHeadBitmapPos() const { return v2(96, (4 + (RAND() & 1)) << 4); } +v2 farmer::GetRightArmBitmapPos() const { return v2(64, (RAND() & 1) << 4); } + +v2 wisefarmer::GetHeadBitmapPos() const { return v2(96, (4 + (RAND() & 1)) << 4); } +v2 wisefarmer::GetRightArmBitmapPos() const { return v2(64, (RAND() & 1) << 4); } + +void guard::SetWayPoints(const fearray& What) { ArrayToVector(What, WayPoints); } + +cchar* oree::FirstPersonBiteVerb() const { return "vomit acidous blood at"; } +cchar* oree::FirstPersonCriticalBiteVerb() const { return "vomit very acidous blood at"; } +cchar* oree::ThirdPersonBiteVerb() const { return "vomits acidous blood at"; } +cchar* oree::ThirdPersonCriticalBiteVerb() const { return "vomits very acidous blood at"; } +cchar* oree::BiteNoun() const { return "liquid"; } + +truth skeleton::BodyPartIsVital(int I) const { return I == GROIN_INDEX || I == TORSO_INDEX; } + +truth communist::ShowClassDescription() const { return GetAssignedName() != "Ivan"; } + +v2 housewife::GetHeadBitmapPos() const { return v2(112, (RAND() % 6) << 4); } + +truth zombie::BodyPartIsVital(int I) const { return I == GROIN_INDEX || I == TORSO_INDEX; } +festring zombie::GetZombieDescription() const { return Description; } + +truth angel::BodyPartIsVital(int I) const { return I == TORSO_INDEX || I == HEAD_INDEX; } + +truth genie::BodyPartIsVital(int I) const { return I == TORSO_INDEX || I == HEAD_INDEX; } + +material* golem::CreateBodyPartMaterial(int, long Volume) const { return MAKE_MATERIAL(GetConfig(), Volume); } + +truth sumowrestler::EquipmentIsAllowed(int I) const { return I == BELT_INDEX; } + +petrus::~petrus() +{ + game::SetPetrus(0); +} + +truth ennerbeast::Hit(character* Enemy, v2, int, int) +{ + if(CheckIfTooScaredToHit(Enemy)) + return false; + + if(RAND() & 1) + ADD_MESSAGE("%s yells: UGH UGHAaaa!", CHAR_DESCRIPTION(DEFINITE)); + else + ADD_MESSAGE("%s yells: Uga Ugar Ugade Ugat!", CHAR_DESCRIPTION(DEFINITE)); + + rect Rect; + femath::CalculateEnvironmentRectangle(Rect, GetLevel()->GetBorder(), GetPos(), 30); + + for(int x = Rect.X1; x <= Rect.X2; ++x) + for(int y = Rect.Y1; y <= Rect.Y2; ++y) + { + int ScreamStrength = int(70 / (hypot(GetPos().X - x, GetPos().Y - y) + 1)); + + if(ScreamStrength) + { + character* Char = GetNearSquare(x, y)->GetCharacter(); + + if(Char && Char != this) + { + msgsystem::EnterBigMessageMode(); + + if(Char->IsPlayer()) + ADD_MESSAGE("You are hit by the horrible waves of high sound."); + else if(Char->CanBeSeenByPlayer()) + ADD_MESSAGE("%s is hit by the horrible waves of high sound.", Char->CHAR_NAME(DEFINITE)); + + Char->ReceiveDamage(this, ScreamStrength, SOUND, ALL, YOURSELF, true); + Char->CheckDeath(CONST_S("killed @bkp scream"), this); + msgsystem::LeaveBigMessageMode(); + + if(Char->IsPlayer() && Char->HasOmmelBlood() && CanBeSeenByPlayer()) + { + if(Char->CurdleOmmelBlood()) + ADD_MESSAGE("Your vial of Ommel Blood vibrates and it contents curdles with the scream of the Enner Beast."); + } + + } + + GetNearLSquare(x, y)->GetStack()->ReceiveDamage(this, ScreamStrength, SOUND); + } + } + + EditNP(-100); + EditAP(-1000000 / GetCWeaponSkill(BITE)->GetBonus()); + EditStamina(-1000, false); + return true; +} + +void skeleton::CreateCorpse(lsquare* Square) +{ + if(GetHead()) + { + item* Skull = SevereBodyPart(HEAD_INDEX, false, Square->GetStack()); + Square->AddItem(Skull); + } + + int Amount = 2 + (RAND() & 3); + + for(int c = 0; c < Amount; ++c) + Square->AddItem(bone::Spawn()); + + SendToHell(); +} + +void petrus::CreateCorpse(lsquare* Square) +{ + if(game::GetStoryState() == 2) + game::GetTeam(ATTNAM_TEAM)->SetRelation(game::GetTeam(PLAYER_TEAM), FRIEND); + + Square->AddItem(leftnutofpetrus::Spawn()); + SendToHell(); +} + +void humanoid::Save(outputfile& SaveFile) const +{ + character::Save(SaveFile); + SaveFile << SWeaponSkill; +} + +void humanoid::Load(inputfile& SaveFile) +{ + character::Load(SaveFile); + SaveFile >> SWeaponSkill; + + if(GetRightWielded()) + for(std::list::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i) + if((*i)->IsSkillOf(GetRightWielded())) + { + SetCurrentRightSWeaponSkill(*i); + break; + } + + if(GetLeftWielded()) + for(std::list::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i) + if((*i)->IsSkillOf(GetLeftWielded())) + { + SetCurrentLeftSWeaponSkill(*i); + break; + } +} + +truth golem::MoveRandomly() +{ + if(!(RAND() % 500)) + { + Engrave(CONST_S("Golem Needs Master")); + EditAP(-1000); + return true; + } + else + return character::MoveRandomly(); +} + +void ennerbeast::GetAICommand() +{ + SeekLeader(GetLeader()); + + if(StateIsActivated(PANIC) || !(RAND() % 3)) + Hit(0, ZERO_V2, YOURSELF); + + if(CheckForEnemies(false, false, true)) + return; + + if(FollowLeader(GetLeader())) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +void petrus::GetAICommand() +{ + int Enemies = 0; + + for(int c = 0; c < game::GetTeams(); ++c) + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + Enemies += game::GetTeam(c)->GetEnabledMembers(); + + if(Enemies && !RAND_N(250 / Min(Enemies, 50))) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s shouts a magnificent prayer to Valpurus.", CHAR_NAME(DEFINITE)); + + angel* Angel = angel::Spawn(VALPURUS); + Angel->SetLifeExpectancy(10000, 0); + v2 Where = GetLevel()->GetNearestFreeSquare(Angel, GetPos()); + + if(Where == ERROR_V2) + Where = GetLevel()->GetRandomSquare(Angel); + + Angel->SetTeam(GetTeam()); + Angel->PutTo(Where); + + if(Angel->CanBeSeenByPlayer()) + ADD_MESSAGE("%s materializes.", Angel->CHAR_NAME(INDEFINITE)); + + EditAP(-1000); + return; + } + + if(HP << 1 < MaxHP && (GetPos() - v2(28, 20)).GetLengthSquare() > 400 && !RAND_N(10)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s disappears.", CHAR_NAME(DEFINITE)); + + GetLevel()->GetLSquare(28, 20)->KickAnyoneStandingHereAway(); + Move(v2(28, 20), true); + EditAP(-1000); + return; + } + + if(!LastHealed || game::GetTick() - LastHealed > 16384) + for(int d = 0; d < GetNeighbourSquares(); ++d) + { + square* Square = GetNeighbourSquare(d); + + if(Square) + { + character* Char = Square->GetCharacter(); + + if(Char && GetRelation(Char) == FRIEND && HealFully(Char)) + return; + } + } + + StandIdleAI(); +} + +truth petrus::HealFully(character* ToBeHealed) +{ + truth DidSomething = false; + + for(int c = 0; c < ToBeHealed->GetBodyParts(); ++c) + if(!ToBeHealed->GetBodyPart(c)) + { + bodypart* BodyPart = 0; + + for(std::list::const_iterator i = ToBeHealed->GetOriginalBodyPartID(c).begin(); i != ToBeHealed->GetOriginalBodyPartID(c).end(); ++i) + { + BodyPart = static_cast(ToBeHealed->SearchForItem(*i)); + + if(BodyPart) + break; + } + + if(!BodyPart || !BodyPart->CanRegenerate()) + continue; + + BodyPart->RemoveFromSlot(); + ToBeHealed->AttachBodyPart(BodyPart); + BodyPart->RestoreHP(); + DidSomething = true; + + if(ToBeHealed->IsPlayer()) + ADD_MESSAGE("%s attaches your old %s back and heals it.", CHAR_NAME(DEFINITE), BodyPart->GetBodyPartName().CStr()); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s attaches the old %s of %s back and heals it.", CHAR_NAME(DEFINITE), BodyPart->GetBodyPartName().CStr(), ToBeHealed->CHAR_DESCRIPTION(DEFINITE)); + } + + if(ToBeHealed->IsInBadCondition()) + { + ToBeHealed->RestoreLivingHP(); + DidSomething = true; + + if(ToBeHealed->IsPlayer()) + ADD_MESSAGE("%s heals you fully.", CHAR_DESCRIPTION(DEFINITE)); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s heals %s fully.", CHAR_DESCRIPTION(DEFINITE), ToBeHealed->CHAR_DESCRIPTION(DEFINITE)); + } + + if(ToBeHealed->TemporaryStateIsActivated(POISONED)) + { + ToBeHealed->DeActivateTemporaryState(POISONED); + DidSomething = true; + + if(ToBeHealed->IsPlayer()) + ADD_MESSAGE("%s removes the foul poison in your body.", CHAR_DESCRIPTION(DEFINITE)); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s removes the foul poison in %s's body.", CHAR_DESCRIPTION(DEFINITE), ToBeHealed->CHAR_DESCRIPTION(DEFINITE)); + } + + if(DidSomething) + { + LastHealed = game::GetTick(); + DexterityAction(10); + return true; + } + else + return false; +} + +void petrus::Save(outputfile& SaveFile) const +{ + humanoid::Save(SaveFile); + SaveFile << LastHealed; +} + +void petrus::Load(inputfile& SaveFile) +{ + humanoid::Load(SaveFile); + SaveFile >> LastHealed; + game::SetPetrus(this); +} + +item* humanoid::GetMainWielded() const +{ + if(GetMainArm()) + if(GetMainArm()->GetWielded()) + return GetMainArm()->GetWielded(); + else + if(GetSecondaryArm()) + return GetSecondaryArm()->GetWielded(); + else + return 0; + else + if(GetSecondaryArm()) + return GetSecondaryArm()->GetWielded(); + else + return 0; +} + +item* humanoid::GetSecondaryWielded() const +{ + if(GetMainArm() && GetMainArm()->GetWielded() && GetSecondaryArm()) + return GetSecondaryArm()->GetWielded(); + else + return 0; +} + +truth humanoid::Hit(character* Enemy, v2 HitPos, int Direction, int Flags) +{ + if(CheckIfTooScaredToHit(Enemy)) + return false; + + if(IsPlayer()) + { + if(!(Enemy->IsMasochist() && GetRelation(Enemy) == FRIEND) && GetRelation(Enemy) != HOSTILE && !game::TruthQuestion(CONST_S("This might cause a hostile reaction. Are you sure? [y/N]"))) + return false; + } + else if(GetAttribute(WISDOM) >= Enemy->GetAttackWisdomLimit()) + return false; + + if(GetBurdenState() == OVER_LOADED) + { + if(IsPlayer()) + ADD_MESSAGE("You cannot fight while carrying so much."); + + return false; + } + + int c, AttackStyles; + + for(c = 0, AttackStyles = 0; c < 8; ++c) + if(GetAttackStyle() & (1 << c)) + ++AttackStyles; + + int Chosen = RAND() % AttackStyles; + + for(c = 0, AttackStyles = 0; c < 8; ++c) + if(GetAttackStyle() & (1 << c) && AttackStyles++ == Chosen) + { + Chosen = 1 << c; + break; + } + + if(StateIsActivated(VAMPIRISM) && !(RAND() % 2)) + { + Chosen = USE_HEAD; + } + + switch(Chosen) + { + case USE_ARMS: + if(CanAttackWithAnArm() && (!(Flags & SADIST_HIT) || HasSadistWeapon())) + { + msgsystem::EnterBigMessageMode(); + Hostility(Enemy); + long FirstAPCost = 0, SecondAPCost = 0; + arm* FirstArm, * SecondArm; + + if(RAND() & 1) + { + FirstArm = GetRightArm(); + SecondArm = GetLeftArm(); + } + else + { + FirstArm = GetLeftArm(); + SecondArm = GetRightArm(); + } + + int Strength = Max(GetAttribute(ARM_STRENGTH), 1); + + if(FirstArm && FirstArm->GetDamage() && (!(Flags & SADIST_HIT) || FirstArm->HasSadistWeapon())) + { + FirstAPCost = FirstArm->GetAPCost(); + FirstArm->Hit(Enemy, HitPos, Direction, Flags); + + if(StateIsActivated(LEPROSY) && !RAND_N(25 * GetAttribute(ENDURANCE))) + DropBodyPart(FirstArm->GetBodyPartIndex()); + } + + if(!GetAction() && IsEnabled() && Enemy->IsEnabled() && SecondArm && SecondArm->GetDamage() && (!(Flags & SADIST_HIT) || SecondArm->HasSadistWeapon())) + { + SecondAPCost = SecondArm->GetAPCost(); + SecondArm->Hit(Enemy, HitPos, Direction, Flags); + + if(StateIsActivated(LEPROSY) && !RAND_N(25 * GetAttribute(ENDURANCE))) + DropBodyPart(SecondArm->GetBodyPartIndex()); + } + + EditNP(-50); + EditAP(-Max(FirstAPCost, SecondAPCost)); + EditStamina(-10000 / Strength, false); + msgsystem::LeaveBigMessageMode(); + return true; + } + case USE_LEGS: + if(HasTwoUsableLegs()) + { + msgsystem::EnterBigMessageMode(); + Hostility(Enemy); + Kick(GetNearLSquare(HitPos), Direction, Flags & SADIST_HIT); + + if(StateIsActivated(LEPROSY) && !RAND_N(25 * GetAttribute(ENDURANCE))) + DropBodyPart(RAND_2 ? RIGHT_LEG_INDEX : LEFT_LEG_INDEX); + + msgsystem::LeaveBigMessageMode(); + return true; + } + case USE_HEAD: + if(GetHead()) + { + msgsystem::EnterBigMessageMode(); + Hostility(Enemy); + Bite(Enemy, HitPos, Direction, Flags & SADIST_HIT); + msgsystem::LeaveBigMessageMode(); + return true; + } + default: + if(IsPlayer()) + ADD_MESSAGE("You are currently quite unable to damage anything."); + + return false; + } +} + +truth humanoid::AddSpecialSkillInfo(felist& List) const +{ + truth Something = false; + + if(CurrentRightSWeaponSkill && CurrentRightSWeaponSkill->GetHits() / 100) + { + List.AddEntry(CONST_S(""), LIGHT_GRAY); + festring Buffer = CONST_S("right accustomization"); + Buffer.Resize(30); + Buffer << CurrentRightSWeaponSkill->GetLevel(); + Buffer.Resize(40); + Buffer << CurrentRightSWeaponSkill->GetHits() / 100; + Buffer.Resize(50); + + if(CurrentRightSWeaponSkill->GetLevel() != 20) + Buffer << (CurrentRightSWeaponSkill->GetLevelMap(CurrentRightSWeaponSkill->GetLevel() + 1) - CurrentRightSWeaponSkill->GetHits()) / 100; + else + Buffer << '-'; + + Buffer.Resize(60); + int Bonus = CurrentRightSWeaponSkill->GetBonus(); + Buffer << '+' << (Bonus - 1000) / 10; + + if(Bonus %= 10) + Buffer << '.' << Bonus; + + Buffer << '%'; + List.AddEntry(Buffer, WHITE); + Something = true; + } + + if(CurrentLeftSWeaponSkill && CurrentLeftSWeaponSkill->GetHits() / 100) + { + if(!Something) + List.AddEntry(CONST_S(""), LIGHT_GRAY); + + festring Buffer = CONST_S("left accustomization"); + Buffer.Resize(30); + Buffer << CurrentLeftSWeaponSkill->GetLevel(); + Buffer.Resize(40); + Buffer << CurrentLeftSWeaponSkill->GetHits() / 100; + Buffer.Resize(50); + + if(CurrentLeftSWeaponSkill->GetLevel() != 20) + Buffer << (CurrentLeftSWeaponSkill->GetLevelMap(CurrentLeftSWeaponSkill->GetLevel() + 1) - CurrentLeftSWeaponSkill->GetHits()) / 100; + else + Buffer << '-'; + + Buffer.Resize(60); + int Bonus = CurrentLeftSWeaponSkill->GetBonus(); + Buffer << '+' << (Bonus - 1000) / 10; + + if(Bonus %= 10) + Buffer << '.' << Bonus; + + Buffer << '%'; + List.AddEntry(Buffer, WHITE); + Something = true; + } + + return Something; +} + +void petrus::BeTalkedTo() +{ + if(GetRelation(PLAYER) == HOSTILE) + { + ADD_MESSAGE("Heretic! Dev/null is a place not worthy to receive thee!"); + return; + } + + if(PLAYER->HasGoldenEagleShirt()) + { + ADD_MESSAGE("Petrus smiles. \"Thou hast defeated Oree! Mayst thou be blessed by Valpurus for the rest of thy life! And thou possess the Shirt of the Golden Eagle, the symbol of Our status! Return it now, please.\""); + + if(game::TruthQuestion(CONST_S("Will you give the Shirt of the Golden Eagle to Petrus? [y/n]"), REQUIRES_ANSWER)) + { + game::TextScreen(CONST_S( "The Holy Shirt is returned to its old owner and you kneel down to receive your reward.\n" + "Petrus taps your shoulder with the Justifier and raises you to nobility. Later you\n" + "receive a small dukedom in the middle of tundra where you rule with justice till\n" + "the end of your content life.\n\nYou are victorious!")); + + game::GetCurrentArea()->SendNewDrawRequest(); + game::DrawEverything(); + PLAYER->ShowAdventureInfo(); + festring Msg = CONST_S("retrieved the Shirt of the Golden Eagle and was raised to nobility"); + AddScoreEntry(Msg, 4, false); + game::End(Msg); + return; + } + else + { + ADD_MESSAGE("Petrus's face turns red. \"I see. Thy greed hath overcome thy wisdom. Then, we shall fight for the shiny shirt. May Valpurus bless him who is better.\""); + + /* And now we actually make his face change color ;-) */ + + GetHead()->GetMainMaterial()->SetSkinColor(MakeRGB16(255, 75, 50)); + GetHead()->UpdatePictures(); + SendNewDrawRequest(); + game::AskForKeyPress(CONST_S("You are attacked! [press any key to continue]")); + PLAYER->GetTeam()->Hostility(GetTeam()); + game::SetStoryState(2); + return; + } + } + + if(PLAYER->HasHeadOfElpuri()) + { + game::TextScreen(CONST_S( "You have slain Elpuri, and Petrus grants you the freedom you desire.\n" + "You spend the next months in Attnam as an honored hero and when the\n" + "sea finally melts, you board the first ship, leaving your past forever\n" + "behind.\n\nYou are victorious!")); + + game::GetCurrentArea()->SendNewDrawRequest(); + game::DrawEverything(); + PLAYER->ShowAdventureInfo(); + festring Msg = CONST_S("defeated Elpuri and continued to further adventures"); + AddScoreEntry(Msg, 2, false); + game::End(Msg); + } + else + { + if(!game::GetStoryState()) + { + if(PLAYER->RemoveEncryptedScroll()) + { + game::TextScreen(CONST_S( "You kneel down and bow before the high priest and hand him the encrypted scroll.\n" + "Petrus raises his arm, the scroll glows yellow, and lo! The letters are clear and\n" + "readable. Petrus asks you to voice them aloud. The first two thousand words praise\n" + "Valpurus the Creator and all His manifestations and are followed by a canticle of\n" + "Saint Petrus the Lion-Hearted lasting roughly three thousand words. Finally there\n" + "are some sentences actually concerning your mission:\n\n" + "\"Alas, I fear dirty tongues have spread lies to my Lord's ears. I assure all tales\n" + "of treasures here in New Attnam are but mythic legends. There is nothing of value here.\n" + "The taxes are already an unbearable burden and I can't possibly pay more. However I do\n" + "not question the wisdom of the government's decisions. I will contribute what I can:\n" + "the ostriches will deliver an extra 10000 bananas to the capital and additionally the\n" + "slave that brought the message will henceforth be at Your disposal. I am certain this\n" + "satisfies the crown's needs.\"\n\n" + "\"Yours sincerely,\n" + "Richel Decos, the viceroy of New Attnam\"")); + + game::TextScreen(CONST_S( "You almost expected the last bit. Petrus seems to be deep in his thoughts and you\n" + "wonder what shape your destiny is taking in his mind. Suddenly he seems to return\n" + "to this reality and talks to you.\n\n" + "\"Oh, thou art still here. We were just discussing telepathically with Sir Galladon.\n" + "We started doubting Decos's alleged poverty a while back when he bought a couple of\n" + "medium-sized castles nearby. Thy brethren from New Attnam have also told Us about\n" + "vast riches seized from them. Our law says all such stolen valuables belong to \n" + "the Cathedral's treasury, so this is a severe claim. However, proof is needed,\n" + "and even if such was provided, We couldn't send soldiers over the snow fields\n" + "ere spring.\"")); + + game::TextScreen(CONST_S( "\"However, since thou now servest Us, We ought to find thee something to do. Sir\n" + "Galladon hath told Us his agents witnessed thou leaving the dreaded underwater tunnel.\n" + "This means thou most likely hast defeated genetrix vesana and art a talented warrior.\n" + "We happen to have a task perfect for such a person. An evil dark frog named Elpuri who\n" + "hates Valpurus and Attnam more than anything hath taken control over an abandoned mine\n" + "nearby. It is pestering our fine city in many ways and reconnaissance has reported an\n" + "army of monsters gathering in the cave. Our guards are not trained to fight underground\n" + "and We dare not send them. To make things worse, someone hath recently stolen Us the\n" + "greatest armor in existence - the Shirt of the Golden Eagle. Elpuri cannot wear\n" + "it but he who can is now nearly immortal.\"\n\n" + "\"We have marked the location of the gloomy cave on thy world map. We want you to dive\n" + "into it and slay the vile frog. Bring Us its head and We reward thee with freedom.\n" + "Shouldst thou also find the Shirt, We'll knight thee. Good luck, and return when\n" + "thou hast succeeded.\"")); + + game::LoadWorldMap(); + v2 ElpuriCavePos = game::GetWorldMap()->GetEntryPos(0, ELPURI_CAVE); + game::GetWorldMap()->GetWSquare(ElpuriCavePos)->ChangeOWTerrain(elpuricave::Spawn()); + game::GetWorldMap()->RevealEnvironment(ElpuriCavePos, 1); + game::SaveWorldMap(); + GetArea()->SendNewDrawRequest(); + ADD_MESSAGE("\"And by the way, visit the librarian. He might have advice for thee.\""); + game::SetStoryState(1); + } + else + ADD_MESSAGE("\"Yes, citizen? We are quite busy now, thou shalt not disturb Us without proper cause.\""); + } + else /* StoryState == 1 */ + ADD_MESSAGE("Petrus says: \"Bring me the head of Elpuri and we'll talk.\""); + } +} + + +void mysteryman::BeTalkedTo() +{ + if(GetRelation(PLAYER) == HOSTILE) + { + ADD_MESSAGE("This is why the government doesn't mess with me, fool!"); + return; + } + + else + { + if(!game::GetMondedrPass()) + { + if(PLAYER->RemoveMondedrPass()) + { + game::TextScreen(CONST_S( "You hand over the mondedr pass,\n" + "the man quickly snatches it and starts reading it right away.\n" + "The man scans the text very quickly, his eyes run around a hundred laps per second.\n" + "His eyes come to a halt.\n" + "\"I have revealed the location of Mondedr.\" He hands you a map.\n" + "\"It is a good thing I was born in this world, no? You owe me nothing, the \n" + "the amount of information in the sheet of paper is what repays it.\"\n\n""")); + + + game::LoadWorldMap(); + v2 MondedrPos = game::GetWorldMap()->GetEntryPos(0, MONDEDR); + game::GetWorldMap()->GetWSquare(MondedrPos)->ChangeOWTerrain(mondedr::Spawn()); + game::GetWorldMap()->RevealEnvironment(MondedrPos, 1); + game::SaveWorldMap(); + GetArea()->SendNewDrawRequest(); + ADD_MESSAGE("\"And by the way, the government always watches you.\""); + game::SetMondedrPass(1); + } + else + ADD_MESSAGE("\"Shoo, come back if you have a sheet of paper they call 'Mondedr Pass'\""); + } + else /* StoryState == 100 */ + ADD_MESSAGE("The man says: \"I don't have anymore business with you, shoo.\""); + } +} + +void raven::BeTalkedTo() +{ + if(GetRelation(PLAYER) == HOSTILE) + { + ADD_MESSAGE("I bet you are with those filthy Attnamanese bastards!"); + return; + } + + else + { + if(!game::GetRingOfThieves()) + { + if(PLAYER->RemoveRingOfThieves()) + { + game::TextScreen(CONST_S( "You hand over the Ring of Thieves, Raven's eyes glow in amazement.\n" + "\"May Cleptia bless this mighty warrior, whom vanquished Vulcan-Loki!\n" + "Retrieving the ring is no easy task, Vulcan is a powerful adversary.\n" + "The importance of the Ring of Thieves is great. It is used to channel\n" + "Cleptia's power to help Mondedr avoid it's enemies, including Petrus.\n" + "Without it we would have been raided by every nation that hates us.\n" + "Not a mere mortal like Vulcan-Loki could channel the ring's power for\n" + "himself so he decided to contract a deal with Oree to assist him.\n" + "To reward your efforts I will give you the artifact whip, Gleipnir.\"\n\n" + "\"I pray to Cleptia that you will use it well..\"\n\n""")); + game::GetGod(CLEPTIA)->AdjustRelation(500); + game::GetGod(NEFAS)->AdjustRelation(50); + game::GetGod(SCABIES)->AdjustRelation(50); + game::GetGod(INFUSCOR)->AdjustRelation(50); + game::GetGod(CRUENTUS)->AdjustRelation(50); + game::GetGod(MORTIFER)->AdjustRelation(50); + meleeweapon* Weapon = whipofthievery::Spawn(); + Weapon->InitMaterials(MAKE_MATERIAL(SPIDER_SILK), MAKE_MATERIAL(EBONY_WOOD), true); + PLAYER->GetGiftStack()->AddItem(Weapon); + GetArea()->SendNewDrawRequest(); +ADD_MESSAGE("A whip materializes near your feet."); + + game::SetRingOfThieves(1); + } + else + ADD_MESSAGE("\"Mondedr's most important artifact, the Ring of Thieves, has been stolen by Vulcan-Loki; residing at the deepest floor of the underground temple, he awaits Oree to receive it and reward him with incredible power.\""); + } + else /* StoryState == 100 */ + ADD_MESSAGE("\"I must thank you again, I hope you make good use of that whip.\""); + } +} + + +void priest::BeTalkedTo() +{ + if(GetRelation(PLAYER) == HOSTILE) + { + ADD_MESSAGE("\"Sinner! My hands shall pour Dinive Wrath upon thee!\""); + return; + } + + for(int c = 0; c < PLAYER->GetBodyParts(); ++c) + if(!PLAYER->GetBodyPart(c) && PLAYER->CanCreateBodyPart(c)) + { + truth HasOld = false; + + for(std::list::const_iterator i = PLAYER->GetOriginalBodyPartID(c).begin(); i != PLAYER->GetOriginalBodyPartID(c).end(); ++i) + { + bodypart* OldBodyPart = static_cast(PLAYER->SearchForItem(*i)); + + if(OldBodyPart) + { + HasOld = true; + long Price = GetConfig() == VALPURUS ? 50 : 10; + + if(PLAYER->GetMoney() >= Price) + { + if(!OldBodyPart->CanRegenerate()) + ADD_MESSAGE("\"Sorry, I cannot put back bodyparts made of %s, not even your severed %s.\"", OldBodyPart->GetMainMaterial()->GetName(false, false).CStr(), PLAYER->GetBodyPartName(c).CStr()); + else + { + ADD_MESSAGE("\"I could put your old %s back in exchange for %ld gold.\"", PLAYER->GetBodyPartName(c).CStr(), Price); + + if(game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) + { + OldBodyPart->SetHP(1); + PLAYER->SetMoney(PLAYER->GetMoney() - Price); + SetMoney(GetMoney() + Price); + OldBodyPart->RemoveFromSlot(); + PLAYER->AttachBodyPart(OldBodyPart); + return; + } + } + } + else + ADD_MESSAGE("\"Your %s is severed. Help yourself and get %ldgp and I'll help you too.\"", PLAYER->GetBodyPartName(c).CStr(), Price); + } + } + + long Price = GetConfig() == VALPURUS ? 100 : 20; + + if(PLAYER->GetMoney() >= Price) + { + if(HasOld) + ADD_MESSAGE("\"I could still summon up a new one for %ld gold.\"", Price); + else + ADD_MESSAGE("\"Since you don't seem to have your original %s with you, I could summon up a new one for %ld gold.\"", PLAYER->GetBodyPartName(c).CStr(), Price); + + if(game::TruthQuestion(CONST_S("Agreed? [y/N]"))) + { + PLAYER->SetMoney(PLAYER->GetMoney() - Price); + SetMoney(GetMoney() + Price); + PLAYER->CreateBodyPart(c); + PLAYER->GetBodyPart(c)->SetHP(1); + return; + } + } + else if(!HasOld) + ADD_MESSAGE("\"You don't have your original %s with you. I could create you a new one, but my Divine Employer is not a communist and you need %ldgp first.\"", PLAYER->GetBodyPartName(c).CStr(), Price); + } + + if(PLAYER->TemporaryStateIsActivated(POISONED)) + { + long Price = GetConfig() == VALPURUS ? 25 : 5; + + if(PLAYER->GetMoney() >= Price) + { + ADD_MESSAGE("\"You seem to be rather ill. I could give you a small dose of antidote for %ld gold pieces.\"", Price); + + if(game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) + { + ADD_MESSAGE("You feel better."); + PLAYER->DeActivateTemporaryState(POISONED); + PLAYER->SetMoney(PLAYER->GetMoney() - Price); + SetMoney(GetMoney() + Price); + return; + } + } + else + ADD_MESSAGE("\"You seem to be rather ill. Get %ld gold pieces and I'll fix that.\"", Price); + } + + if(PLAYER->TemporaryStateIsActivated(LEPROSY)) + { + long Price = GetConfig() == VALPURUS ? 100 : 20; + + if(PLAYER->GetMoney() >= Price) + { + ADD_MESSAGE("\"You seem to have contracted the vile disease of leprosy. I could give you a small dose of medicince for %ld gold pieces.\"", Price); + + if(game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) + { + ADD_MESSAGE("You feel better."); + PLAYER->DeActivateTemporaryState(LEPROSY); + PLAYER->SetMoney(PLAYER->GetMoney() - Price); + SetMoney(GetMoney() + Price); + return; + } + } + else + ADD_MESSAGE("\"You seem to be falling apart. Get %ld gold pieces and I'll fix that.\"", Price); + } + + if(PLAYER->TemporaryStateIsActivated(LYCANTHROPY)) + { + long Price = GetConfig() == VALPURUS ? 100 : 20; + + if(PLAYER->GetMoney() >= Price) + { + ADD_MESSAGE("\"You seem to be turning into a werewolf quite frequently. Well, everyone has right to little secret habits, but if you wish to donate %ldgp to %s, maybe I could pray %s to remove the canine blood from your veins, just so you don't scare our blessed youth.\"", Price, GetMasterGod()->GetName(), GetMasterGod()->GetObjectPronoun()); + + if(game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) + { + ADD_MESSAGE("You feel better."); + PLAYER->DeActivateTemporaryState(LYCANTHROPY); + PLAYER->SetMoney(PLAYER->GetMoney() - Price); + SetMoney(GetMoney() + Price); + return; + } + } + else + ADD_MESSAGE("\"You seem to be lycanthropic. I might be able to do something for that but I need %ldgp for the ritual materials first.\"", Price); + } + + if(PLAYER->TemporaryStateIsActivated(VAMPIRISM)) + { + long Price = GetConfig() == VALPURUS ? 100 : 20; + + if(PLAYER->GetMoney() >= Price) + { + ADD_MESSAGE("\"You seem to have an addiction to drinking blood. Well, everyone has right to little secret habits, but if you wish to donate %ldgp to %s, maybe I could pray %s to remove your vampiric urges, just so you don't victimize our besotted youth.\"", Price, GetMasterGod()->GetName(), GetMasterGod()->GetObjectPronoun()); + + if(game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) + { + ADD_MESSAGE("You feel better."); + PLAYER->DeActivateTemporaryState(VAMPIRISM); + PLAYER->SetMoney(PLAYER->GetMoney() - Price); + SetMoney(GetMoney() + Price); + return; + } + } + else + ADD_MESSAGE("\"You seem to be vampiric. I might be able to do something for that but I need %ldgp for the ritual materials first.\"", Price); + } + + humanoid::BeTalkedTo(); +} + +void skeleton::BeTalkedTo() +{ + if(GetHead()) + humanoid::BeTalkedTo(); + else + ADD_MESSAGE("The headless %s remains silent.", PLAYER->CHAR_DESCRIPTION(UNARTICLED)); +} + +void communist::BeTalkedTo() +{ + if(GetRelation(PLAYER) != HOSTILE + && GetTeam() != PLAYER->GetTeam() + && PLAYER->GetRelativeDanger(this, true) > 0.1) + { + ADD_MESSAGE("%s seems to be very friendly. \"%s make good communist. %s go with %s!\"", CHAR_DESCRIPTION(DEFINITE), PLAYER->GetAssignedName().CStr(), CHAR_NAME(UNARTICLED), PLAYER->GetAssignedName().CStr()); + + for(std::list::const_iterator i = GetTeam()->GetMember().begin(); i != GetTeam()->GetMember().end();) + if(*i != this) + { + character* Char = *i++; + Char->ChangeTeam(PLAYER->GetTeam()); + } + else + ++i; + + ChangeTeam(PLAYER->GetTeam()); + } + else if(GetTeam() != PLAYER->GetTeam() && !(RAND() % 5)) + ADD_MESSAGE("\"You weak. Learn killing and come back.\""); + else + character::BeTalkedTo(); +} + +void hunter::BeTalkedTo() +{ + if(GetRelation(PLAYER) != HOSTILE && GetMainWielded() && !(RAND() % 10)) + ADD_MESSAGE("\"This is my %s. There are many like it but this one is mine. My %s is my best friend.\"", GetMainWielded()->CHAR_NAME(UNARTICLED), GetMainWielded()->CHAR_NAME(UNARTICLED)); + else + character::BeTalkedTo(); +} + +void slave::BeTalkedTo() +{ + if(GetRelation(PLAYER) == HOSTILE) + { + ADD_MESSAGE("\"Yikes!\""); + return; + } + + room* Room = GetHomeRoom(); + + if(Room && Room->MasterIsActive()) + { + character* Master = Room->GetMaster(); + + if(PLAYER->GetMoney() >= 50) + { + ADD_MESSAGE("%s talks: \"Do you want to buy me? 50 gold pieces. I work very hard.\"", CHAR_DESCRIPTION(DEFINITE)); + + if(game::TruthQuestion(CONST_S("Do you want to buy him? [y/N]"))) + { + PLAYER->SetMoney(PLAYER->GetMoney() - 50); + Master->SetMoney(Master->GetMoney() + 50); + ChangeTeam(PLAYER->GetTeam()); + RemoveHomeData(); + } + } + else + ADD_MESSAGE("\"Don't touch me! Master doesn't want people to touch sale items. I'm worth 50 gold pieces, you know!\""); + + return; + } + + if(GetTeam() == PLAYER->GetTeam()) + { + if((PLAYER->GetMainWielded() && PLAYER->GetMainWielded()->IsWhip()) || (PLAYER->GetSecondaryWielded() && PLAYER->GetSecondaryWielded()->IsWhip())) + ADD_MESSAGE("\"Don't hit me! I work! I obey! I don't think!\""); + else + character::BeTalkedTo(); + } + else + ADD_MESSAGE("\"I'm free! Rejoice!\""); +} + +void assassin::BeTalkedTo() +{ + if(GetRelation(PLAYER) == HOSTILE) + { + + if(PLAYER->GetMoney() >= 1500) + { + ADD_MESSAGE("%s talks: \"If you shell out 1500 gold pieces I'll join your side\"", CHAR_DESCRIPTION(DEFINITE)); + + if(game::TruthQuestion(CONST_S("Do you want to bribe him? [y/N]"))) + { + PLAYER->SetMoney(PLAYER->GetMoney() - 1500); + ChangeTeam(PLAYER->GetTeam()); + RemoveHomeData(); + } + } + else + ADD_MESSAGE("\"Trying to reason me with diplomancy won't work on me.\""); + + return; + } +} + + +void slave::GetAICommand() +{ + SeekLeader(GetLeader()); + + if(CheckForEnemies(true, true, true)) + return; + + if(CheckForUsefulItemsOnGround()) + return; + + if(FollowLeader(GetLeader())) + return; + + if(CheckForDoors()) + return; + + if(!GetHomeRoom() || !GetHomeRoom()->MasterIsActive()) + { + RemoveHomeData(); + + if(MoveRandomly()) + return; + } + else if(MoveTowardsHomePos()) + return; + + EditAP(-1000); +} + +void librarian::BeTalkedTo() +{ + if(GetRelation(PLAYER) == HOSTILE) + { + ADD_MESSAGE("\"The pen is mightier than the sword! Fall, unlearned one!\""); + return; + } + + static long Said; + + switch(RandomizeReply(Said, 12)) + { + case 0: + if(game::GetPetrus() && !game::GetStoryState()) + ADD_MESSAGE("\"Thou shouldst visit Petrus if thou art in need of further adventures.\""); + else + ADD_MESSAGE("\"They say a wand of polymorph hath dozens of uses.\""); + + break; + case 1: + if(game::GetPetrus() && game::GetStoryState() == 1) + ADD_MESSAGE("\"Thou art going to fight Elpuri? Beware! It is a powerful enemy. Other monsters are very vulnerable if surrounded by thy party, but not that beast, for it may slay a horde of thy friends at once with its horrendous tail attack.\""); + else + ADD_MESSAGE("\"Thou shalt remember: Scientia est potentia.\""); + + break; + case 2: + if(game::GetPetrus() && game::GetStoryState() == 1) + ADD_MESSAGE("\"Elpuri the Dark Frog abhors light and resides in a level of eternal darkness.\""); + else + ADD_MESSAGE("\"Shh! Thou shalt be silent in the library.\""); + + break; + case 3: + if(game::GetPetrus() && game::GetStoryState() == 1) + ADD_MESSAGE("\"Elpuri's attacks are so strong that they may shatter many of thy precious items.\""); + else + ADD_MESSAGE("\"Dost thou not smell all the knowledge floating around here?\""); + + break; + case 4: + ADD_MESSAGE("\"It is said that Loricatus, the god of smithing, can upgrade thy weapons' materials.\""); + break; + case 5: + if(game::GetPetrus() && game::GetStoryState() == 1) + ADD_MESSAGE("\"The Shirt of the Golden Eagle is a legendary artifact. Thou canst not find a better armor.\""); + else + ADD_MESSAGE("\"In this book they talk about Mortifer, the great chaos god. He hates us mortals more than anything and will respond only to Champions of Evil.\""); + + break; + case 6: + ADD_MESSAGE("\"Attnam is traditionally ruled by the high priest of the Great Frog. He holds the Shirt of the Golden Eagle and has always killed his predecessor.\""); + break; + case 7: + ADD_MESSAGE("\"They say thou shouldst keep all the artifacts thou findst. They shall make thee famous after thy retirement.\""); + break; + case 8: + ADD_MESSAGE("\"If thou wilt ever encounter an enner beast, know this: It is a horrible foe. It may shatter thy items and armor with its scream that penetrates iron and stone. Thou shouldst not engage it in melee but rather kill it from afar.\""); + break; + case 9: + if(game::GetPetrus() && game::GetStoryState() == 1) + ADD_MESSAGE("\"Thou art not alone in thy attempt to defeat Elpuri. A brave adventurer called Ivan also diveth into its cave not long ago.\""); + else + ADD_MESSAGE("\"It is said that chaotic gods offer great power to their followers. But thou must remember: unlike lawfuls, they shall not help thee when things go bad.\""); + + break; + case 10: + ADD_MESSAGE("\"If a man cannot choose, he ceases to be a man.\""); + break; + case 11: + ADD_MESSAGE("%s sighs: \"The censorship laws in this town are really too strict...\"", CHAR_DESCRIPTION(DEFINITE)); + break; + } +} + +truth communist::MoveRandomly() +{ + switch(RAND() % 1000) + { + case 0: + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s engraves something to the ground.", CHAR_NAME(UNARTICLED)); + + Engrave(CONST_S("The bourgeois is a bourgeois -- for the benefit of the working class.")); + return true; + case 1: + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s engraves something to the ground.", CHAR_NAME(UNARTICLED)); + + Engrave(CONST_S("Proletarians of all countries, unite!")); + return true; + case 2: + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s engraves something to the ground.", CHAR_NAME(UNARTICLED)); + + Engrave(CONST_S("Capital is therefore not only personal; it is a social power.")); + return true; + default: + return character::MoveRandomly(); + } +} + +void zombie::BeTalkedTo() +{ + if(GetRelation(PLAYER) == HOSTILE && PLAYER->GetAttribute(INTELLIGENCE) > 5) + { + if(RAND() % 5) + { + if(GetHead()) + ADD_MESSAGE("\"Need brain!!\""); + else + ADD_MESSAGE("\"Need head with brain!!\""); + } + else + ADD_MESSAGE("\"Redrum! Redrum! Redrum!\""); + } + else + ADD_MESSAGE("\"Need brain but you too stoopid!\""); +} + +void angel::Save(outputfile& SaveFile) const +{ + humanoid::Save(SaveFile); + SaveFile << LastHealed; +} + +void angel::Load(inputfile& SaveFile) +{ + humanoid::Load(SaveFile); + SaveFile >> LastHealed; +} + +void kabouter::Save(outputfile& SaveFile) const +{ + humanoid::Save(SaveFile); + SaveFile << LastTamed; +} + +void kabouter::Load(inputfile& SaveFile) +{ + humanoid::Load(SaveFile); + SaveFile >> LastTamed; +} + +void angel::CreateInitialEquipment(int SpecialFlags) +{ + humanoid::CreateInitialEquipment(SpecialFlags); + GetStack()->AddItem(holybook::Spawn(GetConfig(), SpecialFlags)); +} + +void kamikazedwarf::CreateInitialEquipment(int SpecialFlags) +{ + if(IsRookie()) + { + humanoid::CreateInitialEquipment(SpecialFlags); + SetRightWielded(holybook::Spawn(GetConfig(), SpecialFlags)); + SetLeftWielded(meleeweapon::Spawn(AXE)); + for(int k = 0; k < 6; k++) + { + GetStack()->AddItem(meleeweapon::Spawn(AXE)); + } + GetStack()->AddItem(lantern::Spawn()); + GetCWeaponSkill(AXES)->AddHit(GetWSkillHits()); + GetCurrentRightSWeaponSkill()->AddHit(GetWSkillHits()); + } + else if(IsGrenadier()) + { + humanoid::CreateInitialEquipment(SpecialFlags); + SetLeftWielded(holybook::Spawn(GetConfig(), SpecialFlags)); + SetRightWielded(meleeweapon::Spawn(GREAT_AXE, SpecialFlags)); + for(int k = 0; k < 3; k++) + { + GetStack()->AddItem(gasgrenade::Spawn()); + } + GetStack()->AddItem(lantern::Spawn()); + GetCWeaponSkill(AXES)->AddHit(GetWSkillHits()); + GetCurrentRightSWeaponSkill()->AddHit(GetWSkillHits()); + } + else + { + humanoid::CreateInitialEquipment(SpecialFlags); + SetRightWielded(holybook::Spawn(GetConfig(), SpecialFlags)); + GetCWeaponSkill(UNCATEGORIZED)->AddHit(GetWSkillHits()); + GetCurrentRightSWeaponSkill()->AddHit(GetWSkillHits()); + } +} + +truth kamikazedwarf::Hit(character* Enemy, v2 HitPos, int Direction, int Flags) +{ + if(!IsPlayer()) + { + itemvector KamikazeWeapon; + sortdata SortData(KamikazeWeapon, this, false, &item::IsKamikazeWeapon); + SortAllItems(SortData); + + if(IsRookie() && RAND() & 3) + return humanoid::Hit(Enemy, HitPos, Direction, Flags); + + if(!KamikazeWeapon.empty()) + { + if(IsElite() && RAND() & 1) + ADD_MESSAGE("%s shouts: \"This time I won't fail, O Great %s!\"", CHAR_DESCRIPTION(DEFINITE), GetMasterGod()->GetName()); + else if(IsGrenadier() && RAND() & 1) + ADD_MESSAGE("%s shouts: \"What the hell, it'll be quick. Here goes nothing!\"", CHAR_DESCRIPTION(DEFINITE), GetMasterGod()->GetName()); + else if(IsRookie() && RAND() & 1) + ADD_MESSAGE("%s sobs: \"War is hell!\"", CHAR_DESCRIPTION(DEFINITE), GetMasterGod()->GetName()); + else if(RAND() & 1) + ADD_MESSAGE("%s shouts: \"For %s!\"", CHAR_DESCRIPTION(DEFINITE), GetMasterGod()->GetName()); + else + ADD_MESSAGE("%s screams: \"%s, here I come!\"", CHAR_DESCRIPTION(DEFINITE), GetMasterGod()->GetName()); + + if(KamikazeWeapon[RAND_N(KamikazeWeapon.size())]->Apply(this)) + return true; + } + } + + return humanoid::Hit(Enemy, HitPos, Direction, Flags); +} + +void kamikazedwarf::GetAICommand() +{ + if(GetHomeRoom()) + StandIdleAI(); + else + { + if(!RAND_N(50)) + { + SingRandomSong(); + return; + } + + character::GetAICommand(); + } +} + +// axethrowerdwarf +void axethrowerdwarf::CreateInitialEquipment(int SpecialFlags) +{ + SetRightWielded(meleeweapon::Spawn(AXE, SpecialFlags)); + SetLeftWielded(meleeweapon::Spawn(AXE, SpecialFlags)); + for(int k = 0; k < 6; k++) + { + GetStack()->AddItem(meleeweapon::Spawn(AXE)); + } + GetStack()->AddItem(lantern::Spawn()); + GetCWeaponSkill(AXES)->AddHit(GetWSkillHits()); + GetCurrentRightSWeaponSkill()->AddHit(GetWSkillHits()); +} + +void axethrowerdwarf::GetAICommand() +{ + if(CheckThrowItemOpportunity()) + return; + + character::GetAICommand(); +} +/* +void rookiekamikazedwarf::CreateInitialEquipment(int SpecialFlags) +{ + //SetRightWielded(meleeweapon::Spawn(AXE, SpecialFlags)); + //SetLeftWielded(meleeweapon::Spawn(AXE)); + for(int k = 0; k < 6; k++) + { + //GetStack()->AddItem(meleeweapon::Spawn(AXE)); + } + //GetStack()->AddItem(lantern::Spawn()); + //GetCWeaponSkill(AXES)->AddHit(GetWSkillHits()); + //GetCurrentRightSWeaponSkill()->AddHit(GetWSkillHits()); + + //humanoid::CreateInitialEquipment(SpecialFlags); + //SetRightWielded(holybook::Spawn(GetConfig(), SpecialFlags)); +}*/ + +truth rookiekamikazedwarf::CheckForUsefulItemsOnGround(truth CheckFood) +{ + return character::CheckForUsefulItemsOnGround(CheckFood); +} + +void rookiekamikazedwarf::GetAICommand() +{ + if(CheckThrowItemOpportunity()) + return; + + character::GetAICommand(); +} + +void grenadierdwarf::GetAICommand() +{ + if(CheckThrowItemOpportunity()) + return; + + if(!RAND_N(50)) + { + SingRandomSong(); + return; + } + + character::GetAICommand(); +} +/* +void grenadierdwarf::CreateInitialEquipment(int SpecialFlags) +{ + SetLeftWielded(meleeweapon::Spawn(GREAT_AXE, SpecialFlags)); + for(int k = 0; k < 3; k++) + { + GetStack()->AddItem(gasgrenade::Spawn()); + } + GetStack()->AddItem(lantern::Spawn()); + GetCWeaponSkill(AXES)->AddHit(GetWSkillHits()); + GetCurrentRightSWeaponSkill()->AddHit(GetWSkillHits()); + + humanoid::CreateInitialEquipment(SpecialFlags); + SetRightWielded(holybook::Spawn(GetConfig(), SpecialFlags)); +}*/ + +void shaman::GetAICommand() +{ + // SeekLeader(GetLeader()); + //// + // if(FollowLeader(GetLeader())) + // return; + + character* NearestEnemy = 0; + long NearestEnemyDistance = 0x7FFFFFFF; + v2 Pos = GetPos(); + + for(int c = 0; c < game::GetTeams(); ++c) + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + { + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled()) + { + long ThisDistance = Max(abs((*i)->GetPos().X - Pos.X), abs((*i)->GetPos().Y - Pos.Y)); + + if((ThisDistance < NearestEnemyDistance || (ThisDistance == NearestEnemyDistance && !(RAND() % 3))) && (*i)->CanBeSeenBy(this)) + { + NearestEnemy = *i; + NearestEnemyDistance = ThisDistance; + } + } + } + + beamdata Beam + ( + this, + CONST_S("killed by the spells of ") + GetName(INDEFINITE), + YOURSELF, + 0 + ); + + if(NearestEnemy && !StateIsActivated(CONFUSED) && !(RAND() % 3)) + { + lsquare* Square = NearestEnemy->GetLSquareUnder(); + EditAP(-GetSpellAPCost()); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s invokes a spell!", CHAR_NAME(DEFINITE)); + + switch(RAND() % 33) + { + case 0: + case 1: + case 2: Square->DrawParticles(RED); Square->Parasitize(Beam); break; + case 3: + case 4: + case 5: + case 6: Square->DrawParticles(RED); Square->Slow(Beam); break; + case 7: Square->DrawParticles(RED); Square->Confuse(Beam); break; + case 8: + case 9: + case 10: Square->DrawParticles(RED); Square->InstillPanic(Beam); break; + default: break; + } + + if(CanBeSeenByPlayer()) + NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell of ") + GetName(DEFINITE) + CONST_S(" interrupts you.")); + else + NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell interrupts you.")); + + return; + } + + if(!StateIsActivated(CONFUSED) && CheckThrowItemOpportunity()) + return; + + character::GetAICommand(); + +} +/* +void shaman::CreateInitialEquipment(int SpecialFlags) +{ + //SetRightWielded(meleeweapon::Spawn(AXE, SpecialFlags)); + //SetLeftWielded(meleeweapon::Spawn(AXE, SpecialFlags)); + GetStack()->AddItem(potion::Spawn(GLASS, POISON_LIQUID)); + + if(RAND() & 2) + GetStack()->AddItem(potion::Spawn(GLASS, VINEGAR)); + + if(RAND() & 2) + GetStack()->AddItem(potion::Spawn(GLASS, LIQUID_HORROR)); + + if(RAND() & 5) + GetStack()->AddItem(potion::Spawn(GLASS, SULPHURIC_ACID)); + + GetStack()->AddItem(lantern::Spawn()); + //GetCWeaponSkill(AXES)->AddHit(GetWSkillHits()); + //GetCurrentRightSWeaponSkill()->AddHit(GetWSkillHits()); +}*/ + + +void warlock::GetAICommand() +{ + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + character* NearestEnemy = 0; + long NearestEnemyDistance = 0x7FFFFFFF; + v2 Pos = GetPos(); + + for(int c = 0; c < game::GetTeams(); ++c) + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + { + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled()) + { + long ThisDistance = Max(abs((*i)->GetPos().X - Pos.X), abs((*i)->GetPos().Y - Pos.Y)); + + if((ThisDistance < NearestEnemyDistance || (ThisDistance == NearestEnemyDistance && !(RAND() % 3))) && (*i)->CanBeSeenBy(this)) + { + NearestEnemy = *i; + NearestEnemyDistance = ThisDistance; + } + } + } + + if(NearestEnemy && NearestEnemy->GetPos().IsAdjacent(Pos)) + { + if(/*GetConfig() == MASTER_NECROMANCER && */!StateIsActivated(CONFUSED) && !(RAND() & 3)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s invokes a spell and disappears.", CHAR_NAME(DEFINITE)); + + TeleportRandomly(true); + EditAP(-GetSpellAPCost()); + return; + } + else if(NearestEnemy->IsSmall() + && GetAttribute(WISDOM) < NearestEnemy->GetAttackWisdomLimit() + && !(RAND() & 3) + && Hit(NearestEnemy, NearestEnemy->GetPos(), game::GetDirectionForVector(NearestEnemy->GetPos() - GetPos()))) + return; + } + + if(NearestEnemy && (NearestEnemyDistance < 10 || StateIsActivated(PANIC)) && RAND() & 3) + { + SetGoingTo((Pos << 1) - NearestEnemy->GetPos()); + + if(MoveTowardsTarget(true)) + return; + } + + if(!StateIsActivated(CONFUSED) && CheckAIZapOpportunity()) + return; + + if(CheckForUsefulItemsOnGround()) + return; + + if(CheckForDoors()) + return; + + if(CheckSadism()) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +void alchemist::GetAICommand() +{ + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + character* NearestEnemy = 0; + long NearestEnemyDistance = 0x7FFFFFFF; + v2 Pos = GetPos(); + + for(int c = 0; c < game::GetTeams(); ++c) + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + { + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled()) + { + long ThisDistance = Max(abs((*i)->GetPos().X - Pos.X), abs((*i)->GetPos().Y - Pos.Y)); + + if((ThisDistance < NearestEnemyDistance || (ThisDistance == NearestEnemyDistance && !(RAND() % 3))) && (*i)->CanBeSeenBy(this)) + { + NearestEnemy = *i; + NearestEnemyDistance = ThisDistance; + } + } + } + + truth Interrupt = false; + + // here we insert code for the alchemy + if(NearestEnemy && !StateIsActivated(CONFUSED) && !(RAND() % 2)) + { + if(NearestEnemy->IsHumanoid()) + { + beamdata Beam + ( + this, + CONST_S("killed by the spells of ") + GetName(INDEFINITE), + YOURSELF, + 0 + ); + + lsquare* Square = NearestEnemy->GetLSquareUnder(); + EditAP(-GetSpellAPCost()); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s invokes a spell!", CHAR_NAME(DEFINITE)); + + Square->DrawParticles(RED); + Square->SoftenMaterial(Beam); + Interrupt = true; + } + + if(Interrupt) + if(CanBeSeenByPlayer()) + NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell of ") + GetName(DEFINITE) + CONST_S(" interrupts you.")); + else + NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell interrupts you.")); + + return; + } + + + /* + truth Changed = false; + item* MainArmor = 0; + + int BodyPartToHit = NearestEnemy->GetRandomBodyPart(); + + switch(BodyPartToHit) // okay, this is broken because it seg-faults, it needs to be replaced by a BEAM, in lsquare.cpp, as per mage: LowerEnchantment(), say + { + case TORSO_INDEX: + MainArmor = NearestEnemy->GetEquipment(BODY_ARMOR_INDEX); + break; + case HEAD_INDEX: + MainArmor = NearestEnemy->GetEquipment(HELMET_INDEX); + break; + case RIGHT_ARM_INDEX: + MainArmor = NearestEnemy->GetEquipment(RIGHT_GAUNTLET_INDEX); + break; + case LEFT_ARM_INDEX: + MainArmor = NearestEnemy->GetEquipment(LEFT_GAUNTLET_INDEX); + break; + case GROIN_INDEX: + MainArmor = NearestEnemy->GetEquipment(BELT_INDEX); + break; + case RIGHT_LEG_INDEX: + MainArmor = NearestEnemy->GetEquipment(RIGHT_BOOT_INDEX); + break; + case LEFT_LEG_INDEX: + MainArmor = NearestEnemy->GetEquipment(LEFT_BOOT_INDEX); + break; + case NONE_INDEX: + MainArmor = 0; + break; + } + + if(MainArmor) + { + festring Desc; + MainArmor->AddName(Desc, UNARTICLED); + + if((MainArmor->GetMainMaterial()->GetCategoryFlags() & IS_METAL) && (MainArmor->GetMainMaterial()->GetConfig() != TIN)) + { + MainArmor->ChangeMainMaterial(MAKE_MATERIAL(TIN)); + Changed = true; + Interrupt = true; + } + else if((MainArmor->GetMainMaterial()->GetCategoryFlags() & CAN_BE_TAILORED) && (MainArmor->GetMainMaterial()->GetConfig() != CLOTH)) + { + MainArmor->ChangeMainMaterial(MAKE_MATERIAL(CLOTH)); + Changed = true; + Interrupt = true; + } + + if(Changed && NearestEnemy->IsPlayer()) + { + ADD_MESSAGE("Your %s softens into %s!", Desc.CStr(), MainArmor->GetMainMaterial()->GetName(false, false).CStr()); + } + else if(Changed) + ADD_MESSAGE("%s's %s softens into %s!", NearestEnemy->CHAR_DESCRIPTION(DEFINITE), Desc.CStr(), MainArmor->GetMainMaterial()->GetName(false, false).CStr()); + } + if(!Changed) //may not need this message + if(NearestEnemy->IsPlayer()) + { + ADD_MESSAGE("Your %s vibrates slightly but remains unchanged.", MainArmor->CHAR_NAME(UNARTICLED) ); + } + else + ADD_MESSAGE("%s's %s vibrates slightly but remains unchanged.", NearestEnemy->CHAR_DESCRIPTION(DEFINITE), MainArmor->CHAR_NAME(UNARTICLED) ); + */ + + + + + + if(NearestEnemy && (NearestEnemyDistance < 10 || StateIsActivated(PANIC)) && RAND() & 3) + { + SetGoingTo((Pos << 1) - NearestEnemy->GetPos()); + + if(MoveTowardsTarget(true)) + return; + } + + if(CheckForUsefulItemsOnGround()) + return; + + if(CheckForDoors()) + return; + + if(CheckSadism()) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +void uldra::GetAICommand() +{ + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + character* NearestEnemy = 0; + long NearestEnemyDistance = 0x7FFFFFFF; + v2 Pos = GetPos(); + + for(int c = 0; c < game::GetTeams(); ++c) + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + { + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled()) + { + long ThisDistance = Max(abs((*i)->GetPos().X - Pos.X), abs((*i)->GetPos().Y - Pos.Y)); + + if((ThisDistance < NearestEnemyDistance || (ThisDistance == NearestEnemyDistance && !(RAND() % 3))) && (*i)->CanBeSeenBy(this)) + { + NearestEnemy = *i; + NearestEnemyDistance = ThisDistance; + } + } + } + + if(NearestEnemy && NearestEnemy->GetPos().IsAdjacent(Pos)) + { + if(!(RAND() & 3)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s grins at you and disappears.", CHAR_NAME(DEFINITE)); + + TeleportRandomly(true); + EditAP(-GetSpellAPCost()); + return; + } + else if(NearestEnemy->IsSmall() + && GetAttribute(WISDOM) < NearestEnemy->GetAttackWisdomLimit() + && !(RAND() & 3) + && Hit(NearestEnemy, NearestEnemy->GetPos(), game::GetDirectionForVector(NearestEnemy->GetPos() - GetPos()))) + return; + } + + if(NearestEnemy && (NearestEnemyDistance < 10 || StateIsActivated(PANIC)) && RAND() & 3) + { + SetGoingTo((Pos << 1) - NearestEnemy->GetPos()); + + if(MoveTowardsTarget(true)) + return; + } + + if(NearestEnemy) + { + lsquare* Square = NearestEnemy->GetLSquareUnder(); + EditAP(-GetSpellAPCost()); + + switch(RAND() % 50) + { + case 0: + case 1: + case 2: + { + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s grins and invokes a spell!", CHAR_NAME(DEFINITE)); + character* Buffalo = buffalo::Spawn(); + v2 Where = GetLevel()->GetNearestFreeSquare(Buffalo, Square->GetPos()); + if(Where == ERROR_V2) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("Nothing happens."); + delete Buffalo; + } + else + { + Buffalo->SetGenerationDanger(GetGenerationDanger()); + Buffalo->SetTeam(GetTeam()); + Buffalo->PutTo(Where); + if(Buffalo->CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s materializes!", Buffalo->CHAR_NAME(INDEFINITE)); + Buffalo->GetLSquareUnder()->DrawParticles(RED); + } + break; + } + case 3: + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s grins and invokes a spell!", CHAR_NAME(DEFINITE)); + character* Twoheadedmoose = twoheadedmoose::Spawn(); + v2 Where = GetLevel()->GetNearestFreeSquare(Twoheadedmoose, Square->GetPos()); + if(Where == ERROR_V2) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("Nothing happens."); + delete Twoheadedmoose; + } + else + { + Twoheadedmoose->SetGenerationDanger(GetGenerationDanger()); + Twoheadedmoose->SetTeam(GetTeam()); + Twoheadedmoose->PutTo(Where); + if(Twoheadedmoose->CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s materializes!", Twoheadedmoose->CHAR_NAME(INDEFINITE)); + Twoheadedmoose->GetLSquareUnder()->DrawParticles(RED); + } + break; + } + case 4: + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s grins and invokes a spell!", CHAR_NAME(DEFINITE)); + character* Bear = bear::Spawn(RAND() % 3 ? BLACK_BEAR : GRIZZLY_BEAR); + v2 Where = GetLevel()->GetNearestFreeSquare(Bear, Square->GetPos()); + if(Where == ERROR_V2) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("Nothing happens."); + delete Bear; + } + else + { + Bear->SetGenerationDanger(GetGenerationDanger()); + Bear->SetTeam(GetTeam()); + Bear->PutTo(Where); + if(Bear->CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s materializes!", Bear->CHAR_NAME(INDEFINITE)); + Bear->GetLSquareUnder()->DrawParticles(RED); + } + break; + } + default: break; + } + return; + } + + if(CheckForUsefulItemsOnGround()) + return; + + if(CheckForDoors()) + return; + + if(CheckSadism()) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +void kabouter::GetAICommand() +{ + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + character* NearestEnemy = 0; + long NearestEnemyDistance = 0x7FFFFFFF; + v2 Pos = GetPos(); + + for(int c = 0; c < game::GetTeams(); ++c) + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + { + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled()) + { + long ThisDistance = Max(abs((*i)->GetPos().X - Pos.X), abs((*i)->GetPos().Y - Pos.Y)); + + if((ThisDistance < NearestEnemyDistance || (ThisDistance == NearestEnemyDistance && !(RAND() % 3))) && (*i)->CanBeSeenBy(this)) + { + NearestEnemy = *i; + NearestEnemyDistance = ThisDistance; + } + } + } + + if(NearestEnemy && (NearestEnemyDistance < 10 || StateIsActivated(PANIC)) && RAND() & 3) + { + SetGoingTo((Pos << 1) - NearestEnemy->GetPos()); + + if(MoveTowardsTarget(true)) + return; + } + /* + if(NearestEnemy) + { + lsquare* Square = NearestEnemy->GetLSquareUnder(); + EditAP(-GetSpellAPCost()); + + if(CanBeSeenByPlayer() && NearestEnemy->CanHear()) + ADD_MESSAGE("%s whispers sweet nothings into the ear of %s", CHAR_NAME(DEFINITE), NearestEnemy->CHAR_DESCRIPTION(DEFINITE)); + + if(NearestEnemy->CanTameWithLyre(this) && NearestEnemy->CanHear()) + + if((LastHealed || game::GetTick() - LastHealed > 10000) && AttachBodyPartsOfFriendsNear()) + return; + */ + + if((/*LastTamed &&*/ game::GetTick() - LastTamed < 100) && NearestEnemy && (NearestEnemyDistance < 2)) // how about LastTamed < 500 + { + //do nothing, otherwise do the charming + } + else if(NearestEnemy) + { + LastTamed = game::GetTick(); + + for(int d = 0; d < GetNeighbourSquares(); ++d) + { + square* Square = GetNeighbourSquare(d); + if(Square) + { + character* Char = Square->GetCharacter(); + if(Char) + { + if(!Char->IsPlayer() /*&& (GetRelation(Char) == HOSTILE)*/) //check the creature's hostility at this point + { + if(CanBeSeenByPlayer()) + { + if(PLAYER->CanHear()) + ADD_MESSAGE("%s whispers mesmerizing spells.", CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("You see %s mouthing mesmerizing spells.", CHAR_NAME(DEFINITE)); + } + if(Char->CanHear()) + { + if(Char->CanTameWithLyre(this)) + { + //if(Char->GetTeam() == GetTeam() && Char->CanBeSeenByPlayer()) + //ADD_MESSAGE("%s seems to be very happy.", Char->CHAR_NAME(DEFINITE)); + /*else */if(Char->GetRelation(this) == HOSTILE && Char->CanBeSeenByPlayer()) + { + ADD_MESSAGE("%s stops fighting.", Char->CHAR_NAME(DEFINITE)); + if(Char->CanBeSeenByPlayer()) + ADD_MESSAGE("%s seems to be very friendly towards %s.", Char->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + } + Char->ChangeTeam(GetTeam()); + } + else if(Char->CanBeSeenByPlayer()) + ADD_MESSAGE("%s resists %s's charming spells.", Char->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + } + else if(Char->CanBeSeenByPlayer()) + ADD_MESSAGE("%s seems not affected.", Char->CHAR_NAME(DEFINITE)); + + } + } + } + } + + EditAP(-1000); + game::CallForAttention(GetPos(), 100); + return; + } + + if(CheckAIZapOpportunity()) + return; + + if(CheckForUsefulItemsOnGround()) + return; + + if(CheckForDoors()) + return; + + if(CheckSadism()) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +int humanoid::GetSize() const +{ + int Size = 0; + + if(GetHead()) + Size += GetHead()->GetSize(); + + if(GetTorso()) + Size += GetTorso()->GetSize(); + + leg* RightLeg = GetRightLeg(); + leg* LeftLeg = GetLeftLeg(); + + if(LeftLeg && RightLeg) + Size += Max(LeftLeg->GetSize(), RightLeg->GetSize()); + else if(LeftLeg) + Size += LeftLeg->GetSize(); + else if(RightLeg) + Size += RightLeg->GetSize(); + + return Size; +} + +long humanoid::GetBodyPartSize(int I, int TotalSize) const +{ + switch(I) + { + case HEAD_INDEX: return 20; + case TORSO_INDEX: return ((TotalSize - 20) << 1) / 5; + case RIGHT_ARM_INDEX: + case LEFT_ARM_INDEX: return (TotalSize - 20) * 3 / 5; + case GROIN_INDEX: return (TotalSize - 20) / 3; + case RIGHT_LEG_INDEX: + case LEFT_LEG_INDEX: return (TotalSize - 20) * 3 / 5; + } + + ABORT("Illegal humanoid bodypart size request!"); + return 0; +} + +long humanoid::GetBodyPartVolume(int I) const +{ + switch(I) + { + case HEAD_INDEX: return 4000; + case TORSO_INDEX: return (GetTotalVolume() - 4000) * 13 / 30; + case RIGHT_ARM_INDEX: + case LEFT_ARM_INDEX: return (GetTotalVolume() - 4000) / 10; + case GROIN_INDEX: return (GetTotalVolume() - 4000) / 10; + case RIGHT_LEG_INDEX: + case LEFT_LEG_INDEX: return ((GetTotalVolume() - 4000) << 1) / 15; + } + + ABORT("Illegal humanoid bodypart volume request!"); + return 0; +} + +bodypart* humanoid::MakeBodyPart(int I) const +{ + switch(I) + { + case TORSO_INDEX: return humanoidtorso::Spawn(0, NO_MATERIALS); + case HEAD_INDEX: return head::Spawn(0, NO_MATERIALS); + case RIGHT_ARM_INDEX: return rightarm::Spawn(0, NO_MATERIALS); + case LEFT_ARM_INDEX: return leftarm::Spawn(0, NO_MATERIALS); + case GROIN_INDEX: return groin::Spawn(0, NO_MATERIALS); + case RIGHT_LEG_INDEX: return rightleg::Spawn(0, NO_MATERIALS); + case LEFT_LEG_INDEX: return leftleg::Spawn(0, NO_MATERIALS); + } + + ABORT("Weird bodypart to make for a humanoid. It must be your fault!"); + return 0; +} + +truth humanoid::ReceiveDamage(character* Damager, int Damage, int Type, int TargetFlags, int Direction, truth Divide, truth PenetrateArmor, truth Critical, truth ShowMsg) +{ + int ChooseFrom[MAX_BODYPARTS], BodyParts = 0; + + if(TargetFlags & RIGHT_ARM && GetRightArm()) + ChooseFrom[BodyParts++] = 2; + + if(TargetFlags & LEFT_ARM && GetLeftArm()) + ChooseFrom[BodyParts++] = 3; + + if(TargetFlags & RIGHT_LEG && GetRightLeg()) + ChooseFrom[BodyParts++] = 5; + + if(TargetFlags & LEFT_LEG && GetLeftLeg()) + ChooseFrom[BodyParts++] = 6; + + if(TargetFlags & HEAD && GetHead()) + ChooseFrom[BodyParts++] = 1; + + if(TargetFlags & TORSO && GetTorso()) + ChooseFrom[BodyParts++] = 0; + + if(TargetFlags & GROIN && GetGroin()) + ChooseFrom[BodyParts++] = 4; + + if(!BodyParts) + return false; + + truth Affected = false; + + if(Divide) + { + int c; + long TotalVolume = 0; + + for(c = 0; c < BodyParts; ++c) + TotalVolume += GetBodyPart(ChooseFrom[c])->GetBodyPartVolume(); + + for(c = 0; c < BodyParts; ++c) + if(ReceiveBodyPartDamage(Damager, long(Damage) * GetBodyPart(ChooseFrom[c])->GetBodyPartVolume() / TotalVolume, Type, ChooseFrom[c], Direction, PenetrateArmor, Critical, false)) + Affected = true; + } + else + { + long Possibility[MAX_BODYPARTS], PossibilitySum = 0; + int Index = 0; + + for(int c = 0; c < BodyParts; ++c) + PossibilitySum += Possibility[Index++] = GetBodyPart(ChooseFrom[c])->GetBodyPartVolume(); + + Index = femath::WeightedRand(Possibility, PossibilitySum); + Affected = ReceiveBodyPartDamage(Damager, Damage, Type, ChooseFrom[Index], Direction, PenetrateArmor, Critical, false); + } + + if(!Affected && ShowMsg) + { + if(IsPlayer()) + ADD_MESSAGE("You are not hurt."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s is not hurt.", GetPersonalPronoun().CStr()); + } + + if(DamageTypeAffectsInventory(Type)) + { + for(int c = 0; c < GetEquipments(); ++c) + { + item* Equipment = GetEquipment(c); + + if(Equipment) + Equipment->ReceiveDamage(Damager, Damage, Type); + } + + GetStack()->ReceiveDamage(Damager, Damage, Type); + } + + return Affected; +} + +arm* humanoid::GetMainArm() const +{ + return GetRightArm() ? GetRightArm() : GetLeftArm(); +} + +arm* humanoid::GetSecondaryArm() const +{ + return GetRightArm() ? GetLeftArm() : 0; +} + +cchar* humanoid::GetEquipmentName(int I) const // convert to array +{ + switch(I) + { + case HELMET_INDEX: return "helmet"; + case AMULET_INDEX: return "amulet"; + case CLOAK_INDEX: return "cloak"; + case BODY_ARMOR_INDEX: return "body armor"; + case BELT_INDEX: return "belt"; + case RIGHT_WIELDED_INDEX: return "right hand wielded"; + case LEFT_WIELDED_INDEX: return "left hand wielded"; + case RIGHT_RING_INDEX: return "right ring"; + case LEFT_RING_INDEX: return "left ring"; + case RIGHT_GAUNTLET_INDEX: return "right gauntlet"; + case LEFT_GAUNTLET_INDEX: return "left gauntlet"; + case RIGHT_BOOT_INDEX: return "right boot"; + case LEFT_BOOT_INDEX: return "left boot"; + } + + return "forbidden piece of cloth"; +} + +sorter humanoid::EquipmentSorter(int I) const +{ + switch(I) + { + case HELMET_INDEX: return &item::IsHelmet; + case AMULET_INDEX: return &item::IsAmulet; + case CLOAK_INDEX: return &item::IsCloak; + case BODY_ARMOR_INDEX: return &item::IsBodyArmor; + case BELT_INDEX: return &item::IsBelt; + case RIGHT_WIELDED_INDEX: + case LEFT_WIELDED_INDEX: return 0; + case RIGHT_RING_INDEX: + case LEFT_RING_INDEX: return &item::IsRing; + case RIGHT_GAUNTLET_INDEX: + case LEFT_GAUNTLET_INDEX: return &item::IsGauntlet; + case RIGHT_BOOT_INDEX: + case LEFT_BOOT_INDEX: return &item::IsBoot; + } + + return 0; +} + +bodypart* humanoid::GetBodyPartOfEquipment(int I) const +{ + switch(I) + { + case HELMET_INDEX: + case AMULET_INDEX: + return GetHead(); + case CLOAK_INDEX: + case BODY_ARMOR_INDEX: + case BELT_INDEX: + return GetTorso(); + case RIGHT_WIELDED_INDEX: + case RIGHT_RING_INDEX: + case RIGHT_GAUNTLET_INDEX: + return GetRightArm(); + case LEFT_WIELDED_INDEX: + case LEFT_RING_INDEX: + case LEFT_GAUNTLET_INDEX: + return GetLeftArm(); + case RIGHT_BOOT_INDEX: + return GetRightLeg(); + case LEFT_BOOT_INDEX: + return GetLeftLeg(); + } + + return 0; +} + +item* humanoid::GetEquipment(int I) const +{ + switch(I) + { + case HELMET_INDEX: return GetHelmet(); + case AMULET_INDEX: return GetAmulet(); + case CLOAK_INDEX: return GetCloak(); + case BODY_ARMOR_INDEX: return GetBodyArmor(); + case BELT_INDEX: return GetBelt(); + case RIGHT_WIELDED_INDEX: return GetRightWielded(); + case LEFT_WIELDED_INDEX: return GetLeftWielded(); + case RIGHT_RING_INDEX: return GetRightRing(); + case LEFT_RING_INDEX: return GetLeftRing(); + case RIGHT_GAUNTLET_INDEX: return GetRightGauntlet(); + case LEFT_GAUNTLET_INDEX: return GetLeftGauntlet(); + case RIGHT_BOOT_INDEX: return GetRightBoot(); + case LEFT_BOOT_INDEX: return GetLeftBoot(); + } + + return 0; +} + +void humanoid::SetEquipment(int I, item* What) +{ + switch(I) + { + case HELMET_INDEX: SetHelmet(What); break; + case AMULET_INDEX: SetAmulet(What); break; + case CLOAK_INDEX: SetCloak(What); break; + case BODY_ARMOR_INDEX: SetBodyArmor(What); break; + case BELT_INDEX: SetBelt(What); break; + case RIGHT_WIELDED_INDEX: SetRightWielded(What); break; + case LEFT_WIELDED_INDEX: SetLeftWielded(What); break; + case RIGHT_RING_INDEX: SetRightRing(What); break; + case LEFT_RING_INDEX: SetLeftRing(What); break; + case RIGHT_GAUNTLET_INDEX: SetRightGauntlet(What); break; + case LEFT_GAUNTLET_INDEX: SetLeftGauntlet(What); break; + case RIGHT_BOOT_INDEX: SetRightBoot(What); break; + case LEFT_BOOT_INDEX: SetLeftBoot(What); break; + } +} + +void humanoid::SwitchToDig(item* DigItem, v2 Square) +{ + dig* Dig = dig::Spawn(this); + + if(GetRightArm()) + { + item* Item = GetRightArm()->GetWielded(); + + if(Item && Item != DigItem) + { + Dig->SetRightBackupID(GetRightArm()->GetWielded()->GetID()); + GetRightArm()->GetWielded()->MoveTo(GetStack()); + } + } + + if(GetLeftArm()) + { + item* Item = GetLeftArm()->GetWielded(); + + if(Item && Item != DigItem) + { + Dig->SetLeftBackupID(GetLeftArm()->GetWielded()->GetID()); + GetLeftArm()->GetWielded()->MoveTo(GetStack()); + } + } + + if(GetMainWielded() != DigItem) + { + Dig->SetMoveDigger(true); + DigItem->RemoveFromSlot(); + + if(GetMainArm() && GetMainArm()->IsUsable()) + GetMainArm()->SetWielded(DigItem); + else + GetSecondaryArm()->SetWielded(DigItem); + } + else + Dig->SetMoveDigger(false); + + Dig->SetSquareDug(Square); + SetAction(Dig); +} + +truth humanoid::CheckKick() const +{ + if(!CanKick()) + { + if(IsPlayer()) + ADD_MESSAGE("This race can't kick."); + + return false; + } + + if(GetUsableLegs() < 2) + { + if(IsPlayer()) + ADD_MESSAGE("How are you going to do that with %s?", GetUsableLegs() ? "only one usable leg" : "no usable legs"); + + return false; + } + else + return true; +} + +int humanoid::GetUsableLegs() const +{ + int Legs = 0; + + if(RightLegIsUsable()) + ++Legs; + + if(LeftLegIsUsable()) + ++Legs; + + return Legs; +} + +int humanoid::GetUsableArms() const +{ + int Arms = 0; + + if(RightArmIsUsable()) + ++Arms; + + if(LeftArmIsUsable()) + ++Arms; + + return Arms; +} + +truth humanoid::CheckThrow() const +{ + if(!character::CheckThrow()) + return false; + + if(HasAUsableArm()) + return true; + else + { + ADD_MESSAGE("You don't have a usable arm to do that!"); + return false; + } +} + +truth humanoid::CheckOffer() const +{ + if(HasAUsableArm()) + return true; + else + { + ADD_MESSAGE("You need a usable arm to offer."); + return false; + } +} + +v2 humanoid::GetEquipmentPanelPos(int I) const // convert to array +{ + switch(I) + { + case HELMET_INDEX: return v2(34, -22); + case AMULET_INDEX: return v2(14, -22); + case CLOAK_INDEX: return v2(-6, -22); + case BODY_ARMOR_INDEX: return v2(54, -22); + case BELT_INDEX: return v2(24, 70); + case RIGHT_WIELDED_INDEX: return v2(-14, 4); + case LEFT_WIELDED_INDEX: return v2(62, 4); + case RIGHT_RING_INDEX: return v2(-14, 44); + case LEFT_RING_INDEX: return v2(62, 44); + case RIGHT_GAUNTLET_INDEX: return v2(-14, 24); + case LEFT_GAUNTLET_INDEX: return v2(62, 24); + case RIGHT_BOOT_INDEX: return v2(4, 70); + case LEFT_BOOT_INDEX: return v2(44, 70); + } + + return v2(24, 12); +} + +void humanoid::DrawSilhouette(truth AnimationDraw) const +{ + int c; + blitdata B1 = { DOUBLE_BUFFER, + { 0, 0 }, + { 0, 0 }, + { TILE_SIZE, TILE_SIZE }, + { ivanconfig::GetContrastLuminance() }, + TRANSPARENT_COLOR, + ALLOW_ANIMATE }; + + v2 Where(RES.X - SILHOUETTE_SIZE.X - 39, 53); + cint Equipments = GetEquipments(); + + if(CanUseEquipment()) + for(c = 0; c < Equipments; ++c) + if(GetBodyPartOfEquipment(c) && EquipmentIsAllowed(c)) + { + v2 Pos = Where + GetEquipmentPanelPos(c); + + if(!AnimationDraw) + DOUBLE_BUFFER->DrawRectangle(Pos + v2(-1, -1), Pos + TILE_V2, DARK_GRAY); + + item* Equipment = GetEquipment(c); + + if(Equipment && (!AnimationDraw || Equipment->IsAnimated())) + { + igraph::BlitBackGround(Pos, TILE_V2); + B1.Dest = Pos; + + if(Equipment->AllowAlphaEverywhere()) + B1.CustomData |= ALLOW_ALPHA; + + Equipment->Draw(B1); + B1.CustomData &= ~ALLOW_ALPHA; + } + } + + if(!AnimationDraw) + { + blitdata B2 = { DOUBLE_BUFFER, + { 0, 0 }, + { Where.X + 8, Where.Y }, + { SILHOUETTE_SIZE.X, SILHOUETTE_SIZE.Y }, + { 0 }, + 0, + 0 }; + + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPart) + { + int Type = BodyPart->IsUsable() ? SILHOUETTE_NORMAL : SILHOUETTE_INTER_LACED; + bitmap* Cache = igraph::GetSilhouetteCache(c, BodyPart->GetConditionColorIndex(), Type); + Cache->NormalMaskedBlit(B2); + BodyPart->DrawScars(B2); + } + } + } +} + +int humanoid::GetGlobalResistance(int Type) const +{ + int Resistance = GetResistance(Type); + + if(GetCloak()) + Resistance += GetCloak()->GetResistance(Type); + + if(!(Type & PHYSICAL_DAMAGE)) + { + if(GetAmulet()) + Resistance += GetAmulet()->GetResistance(Type); + + if(GetRightRing()) + Resistance += GetRightRing()->GetResistance(Type); + + if(GetLeftRing()) + Resistance += GetLeftRing()->GetResistance(Type); + } + + return Resistance; +} + +truth humanoid::TryToRiseFromTheDead() +{ + int c; + + for(c = 0; c < BodyParts; ++c) + if(!GetBodyPart(c)) + { + bodypart* BodyPart = SearchForOriginalBodyPart(c); + + if(BodyPart) + { + BodyPart->RemoveFromSlot(); + AttachBodyPart(BodyPart); + BodyPart->SetHP(1); + } + } + + for(c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(BodyPartIsVital(c) && !BodyPart) + if(!HandleNoBodyPart(c)) + return false; + + if(BodyPart) + { + BodyPart->ResetSpoiling(); + + if(BodyPart->CanRegenerate() || BodyPart->GetHP() < 1) + BodyPart->SetHP(1); + } + } + + ResetStates(); + return true; +} + +truth humanoid::HandleNoBodyPart(int I) +{ + switch(I) + { + case HEAD_INDEX: + if(CanBeSeenByPlayer()) + ADD_MESSAGE("The headless body of %s vibrates violently.", CHAR_NAME(DEFINITE)); + + return false; + case GROIN_INDEX: + if(CanBeSeenByPlayer()) + ADD_MESSAGE("The groinless body of %s vibrates violently.", CHAR_NAME(DEFINITE)); + + return false; + case TORSO_INDEX: + ABORT("The corpse does not have a torso."); + default: + return true; + } +} + +v2 humanoid::GetBodyPartBitmapPos(int I, truth) const +{ + switch(I) + { + case TORSO_INDEX: return GetTorsoBitmapPos(); + case HEAD_INDEX: return GetHeadBitmapPos(); + case RIGHT_ARM_INDEX: return GetRightArmBitmapPos(); + case LEFT_ARM_INDEX: return GetLeftArmBitmapPos(); + case GROIN_INDEX: return GetGroinBitmapPos(); + case RIGHT_LEG_INDEX: return GetRightLegBitmapPos(); + case LEFT_LEG_INDEX: return GetLeftLegBitmapPos(); + } + + ABORT("Weird bodypart BitmapPos request for a humanoid!"); + return v2(); +} + +col16 humanoid::GetBodyPartColorB(int I, truth) const +{ + switch(I) + { + case TORSO_INDEX: return GetTorsoMainColor(); + case HEAD_INDEX: return GetCapColor(); + case RIGHT_ARM_INDEX: + case LEFT_ARM_INDEX: return GetArmMainColor(); + case GROIN_INDEX: + case RIGHT_LEG_INDEX: + case LEFT_LEG_INDEX: return GetLegMainColor(); + } + + ABORT("Weird bodypart col B request for a humanoid!"); + return 0; +} + +col16 humanoid::GetBodyPartColorC(int I, truth) const +{ + switch(I) + { + case TORSO_INDEX: return GetBeltColor(); + case HEAD_INDEX: return GetHairColor(); + case RIGHT_ARM_INDEX: + case LEFT_ARM_INDEX: return GetGauntletColor(); + case GROIN_INDEX: + case RIGHT_LEG_INDEX: + case LEFT_LEG_INDEX: return GetBootColor(); + } + + ABORT("Weird bodypart col C request for a humanoid!"); + return 0; +} + +col16 humanoid::GetBodyPartColorD(int I, truth) const +{ + switch(I) + { + case TORSO_INDEX: return GetTorsoSpecialColor(); + case HEAD_INDEX: return GetEyeColor(); + case RIGHT_ARM_INDEX: + case LEFT_ARM_INDEX: return GetArmSpecialColor(); + case GROIN_INDEX: + case RIGHT_LEG_INDEX: + case LEFT_LEG_INDEX: return GetLegSpecialColor(); + } + + ABORT("Weird bodypart col D request for a humanoid!"); + return 0; +} + +int humanoid::GetBodyPartSparkleFlags(int I) const +{ + truth Sparkling = false; + int SparkleFlags = GetNaturalSparkleFlags() & SKIN_COLOR ? SPARKLING_A : 0; + + switch(I) + { + case TORSO_INDEX: Sparkling = GetNaturalSparkleFlags() & TORSO_MAIN_COLOR; break; + case HEAD_INDEX: Sparkling = GetNaturalSparkleFlags() & CAP_COLOR; break; + case RIGHT_ARM_INDEX: + case LEFT_ARM_INDEX: Sparkling = GetNaturalSparkleFlags() & ARM_MAIN_COLOR; break; + case GROIN_INDEX: + case RIGHT_LEG_INDEX: + case LEFT_LEG_INDEX: Sparkling = GetNaturalSparkleFlags() & LEG_MAIN_COLOR; break; + } + + SparkleFlags |= Sparkling ? SPARKLING_B : 0; + Sparkling = false; + + switch(I) + { + case TORSO_INDEX: Sparkling = GetNaturalSparkleFlags() & BELT_COLOR; break; + case HEAD_INDEX: Sparkling = GetNaturalSparkleFlags() & HAIR_COLOR; break; + case RIGHT_ARM_INDEX: + case LEFT_ARM_INDEX: Sparkling = GetNaturalSparkleFlags() & GAUNTLET_COLOR; break; + case GROIN_INDEX: + case RIGHT_LEG_INDEX: + case LEFT_LEG_INDEX: Sparkling = GetNaturalSparkleFlags() & BOOT_COLOR; break; + } + + SparkleFlags |= Sparkling ? SPARKLING_C : 0; + Sparkling = false; + + switch(I) + { + case TORSO_INDEX: Sparkling = GetNaturalSparkleFlags() & TORSO_SPECIAL_COLOR; break; + case HEAD_INDEX: Sparkling = GetNaturalSparkleFlags() & EYE_COLOR; break; + case RIGHT_ARM_INDEX: + case LEFT_ARM_INDEX: Sparkling = GetNaturalSparkleFlags() & ARM_SPECIAL_COLOR; break; + case GROIN_INDEX: + case RIGHT_LEG_INDEX: + case LEFT_LEG_INDEX: Sparkling = GetNaturalSparkleFlags() & LEG_SPECIAL_COLOR; break; + } + + SparkleFlags |= Sparkling ? SPARKLING_D : 0; + return SparkleFlags; +} + +playerkind::playerkind() : SoulID(0), IsBonePlayer(false), IsClone(false) +{ +} + +petrus::petrus() : LastHealed(0) +{ + game::SetPetrus(this); +} + +shopkeeper::shopkeeper() +{ + if(!game::IsLoading()) + Money = GetMoney() + RAND() % 2001; +} + +void humanoid::Bite(character* Enemy, v2 HitPos, int Direction, truth ForceHit) +{ + /* This function ought not to be called without a head */ + + EditNP(-50); + EditAP(-GetHead()->GetBiteAPCost()); + EditExperience(AGILITY, 150, 1 << 9); + EditStamina(-1000, false); + Enemy->TakeHit(this, 0, GetHead(), HitPos, GetHead()->GetBiteDamage(), GetHead()->GetBiteToHitValue(), RAND() % 26 - RAND() % 26, BITE_ATTACK, Direction, !(RAND() % GetCriticalModifier()), ForceHit); +} + +void humanoid::Kick(lsquare* Square, int Direction, truth ForceHit) +{ + leg* KickLeg = RAND_2 ? GetRightLeg() : GetLeftLeg(); + EditNP(-50); + EditAP(-KickLeg->GetKickAPCost()); + EditStamina(-10000 / GetAttribute(LEG_STRENGTH), false); + + if(Square->BeKicked(this, 0, KickLeg, KickLeg->GetKickDamage(), KickLeg->GetKickToHitValue(), RAND() % 26 - RAND() % 26, Direction, !(RAND() % GetCriticalModifier()), ForceHit)) + { + KickLeg->EditExperience(LEG_STRENGTH, 75, 1 << 9); + KickLeg->EditExperience(AGILITY, 75, 1 << 9); + } +} + +/* Returns the average number of APs required to kill Enemy */ + +double humanoid::GetTimeToKill(ccharacter* Enemy, truth UseMaxHP) const +{ + double Effectivity = 0; + int AttackStyles = 0; + + if(IsUsingArms()) + { + arm* RightArm = GetRightArm(); + + if(RightArm) + { + double Damage = RightArm->GetDamage(); + + if(Damage) + Effectivity += 1 / (Enemy->GetTimeToDie(this, int(Damage) + 1, RightArm->GetToHitValue(), AttackIsBlockable(GetRightWielded() ? WEAPON_ATTACK : UNARMED_ATTACK), UseMaxHP) * RightArm->GetAPCost()); + } + + arm* LeftArm = GetLeftArm(); + + if(LeftArm) + { + double Damage = LeftArm->GetDamage(); + + if(Damage) + Effectivity += 1 / (Enemy->GetTimeToDie(this, int(Damage) + 1, LeftArm->GetToHitValue(), AttackIsBlockable(GetLeftWielded() ? WEAPON_ATTACK : UNARMED_ATTACK), UseMaxHP) * LeftArm->GetAPCost()); + } + + ++AttackStyles; + } + + if(IsUsingLegs()) + { + leg* RightLeg = GetRightLeg(); + leg* LeftLeg = GetLeftLeg(); + double TimeToDie = Enemy->GetTimeToDie(this, int(RightLeg->GetKickDamage()) + 1, RightLeg->GetKickToHitValue(), AttackIsBlockable(KICK_ATTACK), UseMaxHP) * RightLeg->GetKickAPCost() + + Enemy->GetTimeToDie(this, int(LeftLeg->GetKickDamage()) + 1, LeftLeg->GetKickToHitValue(), AttackIsBlockable(KICK_ATTACK), UseMaxHP) * LeftLeg->GetKickAPCost(); + Effectivity += 2 / TimeToDie; + ++AttackStyles; + } + + if(IsUsingHead()) + { + head* Head = GetHead(); + Effectivity += 1 / (Enemy->GetTimeToDie(this, int(Head->GetBiteDamage()) + 1, Head->GetBiteToHitValue(), AttackIsBlockable(BITE_ATTACK), UseMaxHP) * Head->GetBiteAPCost()); + ++AttackStyles; + } + + if(StateIsActivated(HASTE)) + Effectivity *= 2; + + if(StateIsActivated(SLOW)) + Effectivity /= 2; + + return AttackStyles ? AttackStyles / Effectivity : 10000000; +} + +int humanoid::GetAttribute(int Identifier, truth AllowBonus) const +{ + if(Identifier < BASE_ATTRIBUTES) + return character::GetAttribute(Identifier, AllowBonus); + else + { + int Attrib = 0; + + if(Identifier == ARM_STRENGTH || Identifier == DEXTERITY) + { + arm* RightArm = GetRightArm(); + + if(RightArm) + Attrib += RightArm->GetAttribute(Identifier, AllowBonus); + + arm* LeftArm = GetLeftArm(); + + if(LeftArm) + Attrib += LeftArm->GetAttribute(Identifier, AllowBonus); + } + else if(Identifier == LEG_STRENGTH || Identifier == AGILITY) + { + leg* RightLeg = GetRightLeg(); + + if(RightLeg) + Attrib += RightLeg->GetAttribute(Identifier, AllowBonus); + + leg* LeftLeg = GetLeftLeg(); + + if(LeftLeg) + Attrib += LeftLeg->GetAttribute(Identifier, AllowBonus); + } + else + { + ABORT("Illegal humanoid attribute %d request!", Identifier); + return 0xEBBA; + } + + return Attrib >> 1; + } +} + +truth humanoid::EditAttribute(int Identifier, int Value) +{ + if(Identifier < BASE_ATTRIBUTES) + return character::EditAttribute(Identifier, Value); + else if(Identifier == ARM_STRENGTH || Identifier == DEXTERITY) + { + truth Success = false; + + if(GetRightArm() && GetRightArm()->EditAttribute(Identifier, Value)) + Success = true; + + if(GetLeftArm() && GetLeftArm()->EditAttribute(Identifier, Value)) + Success = true; + + return Success; + } + else if(Identifier == LEG_STRENGTH || Identifier == AGILITY) + { + truth Success = false; + + if(GetRightLeg() && GetRightLeg()->EditAttribute(Identifier, Value)) + Success = true; + + if(GetLeftLeg() && GetLeftLeg()->EditAttribute(Identifier, Value)) + Success = true; + + return Success; + } + else + { + ABORT("Illegal humanoid attribute %d edit request!", Identifier); + return false; + } +} + +void humanoid::EditExperience(int Identifier, double Value, double Speed) +{ + if(!AllowExperience()) + return; + + if(Identifier < BASE_ATTRIBUTES) + character::EditExperience(Identifier, Value, Speed); + else if(Identifier == ARM_STRENGTH || Identifier == DEXTERITY) + { + if(GetRightArm()) + GetRightArm()->EditExperience(Identifier, Value, Speed); + + if(GetLeftArm()) + GetLeftArm()->EditExperience(Identifier, Value, Speed); + } + else if(Identifier == LEG_STRENGTH || Identifier == AGILITY) + { + if(GetRightLeg()) + GetRightLeg()->EditExperience(Identifier, Value, Speed); + + if(GetLeftLeg()) + GetLeftLeg()->EditExperience(Identifier, Value, Speed); + } + else + ABORT("Illegal humanoid attribute %d experience edit request!", Identifier); +} + +int humanoid::DrawStats(truth AnimationDraw) const +{ + DrawSilhouette(AnimationDraw); + + if(AnimationDraw) + return 15; + + int PanelPosX = RES.X - 96, PanelPosY = 15; + PrintAttribute("AStr", ARM_STRENGTH, PanelPosX, PanelPosY++); + PrintAttribute("LStr", LEG_STRENGTH, PanelPosX, PanelPosY++); + PrintAttribute("Dex", DEXTERITY, PanelPosX, PanelPosY++); + PrintAttribute("Agi", AGILITY, PanelPosX, PanelPosY++); + return PanelPosY; +} + +int humanoid::GetRandomStepperBodyPart() const +{ + int Possible = 0, PossibleArray[3]; + + if(GetRightLeg()) + PossibleArray[Possible++] = RIGHT_LEG_INDEX; + + if(GetLeftLeg()) + PossibleArray[Possible++] = LEFT_LEG_INDEX; + + if(Possible) + return PossibleArray[RAND_N(Possible)]; + + if(GetRightArm()) + PossibleArray[Possible++] = RIGHT_ARM_INDEX; + + if(GetLeftArm()) + PossibleArray[Possible++] = LEFT_ARM_INDEX; + + if(Possible) + return PossibleArray[RAND_N(Possible)]; + + if(GetHead()) + PossibleArray[Possible++] = HEAD_INDEX; + + if(GetGroin()) + PossibleArray[Possible++] = GROIN_INDEX; + + PossibleArray[Possible++] = TORSO_INDEX; + return PossibleArray[RAND_N(Possible)]; +} + +int humanoid::CheckForBlock(character* Enemy, item* Weapon, double ToHitValue, int Damage, int Success, int Type) +{ + if(GetAction()) + return Damage; + + if(GetRightWielded()) + Damage = CheckForBlockWithArm(Enemy, Weapon, GetRightArm(), ToHitValue, Damage, Success, Type); + + if(Damage && GetLeftWielded() && (!Weapon || Weapon->Exists())) + Damage = CheckForBlockWithArm(Enemy, Weapon, GetLeftArm(), ToHitValue, Damage, Success, Type); + + return Damage; +} + +truth humanoid::CanWield() const +{ + return CanUseEquipment(RIGHT_WIELDED_INDEX) || CanUseEquipment(LEFT_WIELDED_INDEX); +} + +/* return true if still in balance */ + +truth humanoid::CheckBalance(double KickDamage) +{ + return !CanMove() + || IsStuck() + || !KickDamage + || (GetUsableLegs() != 1 + && !IsFlying() + && KickDamage * 5 < RAND() % GetSize()); +} + +long humanoid::GetMoveAPRequirement(int Difficulty) const +{ + if(IsFlying()) + return (!StateIsActivated(PANIC) ? 10000000 : 8000000) * Difficulty / (APBonus(GetAttribute(AGILITY)) * GetMoveEase()); + + switch(GetUsableLegs()) + { + case 0: + return (!StateIsActivated(PANIC) ? 20000000 : 16000000) * Difficulty / (APBonus(GetAttribute(AGILITY)) * GetMoveEase()); + case 1: + return (!StateIsActivated(PANIC) ? 13333333 : 10666667) * Difficulty / (APBonus(GetAttribute(AGILITY)) * GetMoveEase()); + case 2: + return (!StateIsActivated(PANIC) ? 10000000 : 8000000) * Difficulty / (APBonus(GetAttribute(AGILITY)) * GetMoveEase()); + } + + ABORT("A %d legged humanoid invaded the dungeon!", GetUsableLegs()); + return 0; +} + +void hunter::CreateBodyParts(int SpecialFlags) +{ + for(int c = 0; c < BodyParts; ++c) + if(c != LEFT_ARM_INDEX) + CreateBodyPart(c, SpecialFlags); + else + SetBodyPart(LEFT_ARM_INDEX, 0); +} + +truth humanoid::EquipmentEasilyRecognized(int I) const +{ + if(GetRelation(PLAYER) != HOSTILE) + return true; + + switch(I) + { + case AMULET_INDEX: + case RIGHT_RING_INDEX: + case LEFT_RING_INDEX: + case BELT_INDEX: + return false; + } + + return true; +} + +void humanoid::SignalEquipmentAdd(int EquipmentIndex) +{ + character::SignalEquipmentAdd(EquipmentIndex); + + if(EquipmentIndex == RIGHT_WIELDED_INDEX) + EnsureCurrentSWeaponSkillIsCorrect(CurrentRightSWeaponSkill, GetRightWielded()); + else if(EquipmentIndex == LEFT_WIELDED_INDEX) + EnsureCurrentSWeaponSkillIsCorrect(CurrentLeftSWeaponSkill, GetLeftWielded()); + + if(!IsInitializing()) + CalculateBattleInfo(); +} + +void humanoid::SignalEquipmentRemoval(int EquipmentIndex, citem* Item) +{ + character::SignalEquipmentRemoval(EquipmentIndex, Item); + + if(EquipmentIndex == RIGHT_WIELDED_INDEX) + EnsureCurrentSWeaponSkillIsCorrect(CurrentRightSWeaponSkill, 0); + else if(EquipmentIndex == LEFT_WIELDED_INDEX) + EnsureCurrentSWeaponSkillIsCorrect(CurrentLeftSWeaponSkill, 0); + + if(!IsInitializing()) + CalculateBattleInfo(); +} + +void humanoid::SWeaponSkillTick() +{ + for(std::list::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end();) + { + if((*i)->Tick() && IsPlayer()) + { + item* Item = SearchForItem(*i); + + if(Item) + (*i)->AddLevelDownMessage(Item->CHAR_NAME(UNARTICLED)); + } + + if(!(*i)->GetHits() && *i != GetCurrentRightSWeaponSkill() && *i != GetCurrentLeftSWeaponSkill()) + { + std::list::iterator Dirt = i++; + SWeaponSkill.erase(Dirt); + } + else + ++i; + } +} + +void angel::GetAICommand() +{ + if((LastHealed || game::GetTick() - LastHealed > 10000) && AttachBodyPartsOfFriendsNear()) + return; + + humanoid::GetAICommand(); +} + +void insudo::GetAICommand() +{ + if((LastHealed || game::GetTick() - LastHealed > 10000) && AttachBodyPartsOfFriendsNear()) + return; + + StandIdleAI(); + + //humanoid::GetAICommand(); +} + +/* Returns true if the angel finds somebody near to heal else false */ + +truth angel::AttachBodyPartsOfFriendsNear() +{ + character* HurtOne = 0; + bodypart* SeveredOne = 0; + + for(int d = 0; d < GetNeighbourSquares(); ++d) + { + square* Square = GetNeighbourSquare(d); + + if(Square) + { + character* Char = Square->GetCharacter(); + + if(Char && (!HurtOne || Char->IsPlayer()) && GetRelation(Char) == FRIEND && !Char->HasAllBodyParts()) + { + bodypart* BodyPart = Char->FindRandomOwnBodyPart(false); + + if(BodyPart) + { + HurtOne = Char; + SeveredOne = BodyPart; + } + } + } + } + + if(HurtOne) + { + if(HurtOne->IsPlayer()) + ADD_MESSAGE("%s puts your %s back to its place.", CHAR_DESCRIPTION(DEFINITE), SeveredOne->GetBodyPartName().CStr()); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s helps %s by putting %s %s in its old place.", CHAR_DESCRIPTION(DEFINITE), HurtOne->CHAR_DESCRIPTION(DEFINITE), HurtOne->GetPossessivePronoun().CStr(), SeveredOne->GetBodyPartName().CStr()); + + SeveredOne->SetHP(1); + SeveredOne->RemoveFromSlot(); + HurtOne->AttachBodyPart(SeveredOne); + LastHealed = game::GetTick(); + DexterityAction(10); + return true; + } + else + return false; +} + +void humanoid::DrawBodyParts(blitdata& BlitData) const +{ + bitmap* TileBuffer = igraph::GetTileBuffer(); + bitmap* RealBitmap = BlitData.Bitmap; + blitdata B = { TileBuffer, + { BlitData.Dest.X, BlitData.Dest.Y }, + { 0, 0 }, + { TILE_SIZE, TILE_SIZE }, + { 0 }, + TRANSPARENT_COLOR, + BlitData.CustomData }; + + RealBitmap->NormalBlit(B); + TileBuffer->FillPriority(0); + B.Src.X = B.Src.Y = 0; + B.Luminance = BlitData.Luminance; + + for(int c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(DrawOrder[c]); + + if(BodyPart) + { + B.Dest = GetDrawDisplacement(c); + BodyPart->Draw(B); + } + } + + B.Dest.X = B.Dest.Y = 0; + arm* LeftArm = GetLeftArm(); + + if(LeftArm) + LeftArm->DrawWielded(B); + + arm* RightArm = GetRightArm(); + + if(RightArm) + RightArm->DrawWielded(B); + + TileBuffer->FastBlit(RealBitmap, BlitData.Dest); +} + +v2 kamikazedwarf::GetDrawDisplacement(int I) const +{ + static v2 DrawDisplacement[] = { v2(0, 0), v2(0, 1), v2(0, -1), v2(0, -1), v2(0, -1), v2(0, 0), v2(0, 0) }; + return DrawDisplacement[I]; +} + +v2 axethrowerdwarf::GetDrawDisplacement(int I) const +{ + static v2 DrawDisplacement[] = { v2(0, 0), v2(0, 1), v2(0, -1), v2(0, -1), v2(0, -1), v2(0, 0), v2(0, 0) }; + return DrawDisplacement[I]; +} + +col16 angel::GetTorsoMainColor() const +{ + return GetMasterGod()->GetColor(); +} + +col16 angel::GetArmMainColor() const +{ + return GetMasterGod()->GetColor(); +} + +col16 kamikazedwarf::GetTorsoMainColor() const +{ + return GetMasterGod()->GetColor(); +} + +col16 kamikazedwarf::GetGauntletColor() const +{ + return GetMasterGod()->GetColor(); +} + +col16 kamikazedwarf::GetLegMainColor() const +{ + return GetMasterGod()->GetColor(); +} + +col16 axethrowerdwarf::GetTorsoMainColor() const +{ + return GetMasterGod()->GetColor(); +} + +col16 axethrowerdwarf::GetGauntletColor() const +{ + return GetMasterGod()->GetColor(); +} + +col16 axethrowerdwarf::GetLegMainColor() const +{ + return GetMasterGod()->GetColor(); +} + +col16 housewife::GetHairColor() const +{ + static col16 HouseWifeHairColor[] = { MakeRGB16(48, 40, 8), MakeRGB16(60, 48, 24), MakeRGB16(200, 0, 0) }; + return HouseWifeHairColor[RAND() % 3]; +} + +int angel::GetAttribute(int Identifier, truth AllowBonus) const // temporary until wings are bodyparts +{ + if(Identifier == LEG_STRENGTH) + return GetDefaultLegStrength(); + else if(Identifier == AGILITY) + return GetDefaultAgility(); + else + return humanoid::GetAttribute(Identifier, AllowBonus); +} + +int genie::GetAttribute(int Identifier, truth AllowBonus) const // temporary until someone invents a better way of doing this +{ + if(Identifier == LEG_STRENGTH) + return GetDefaultLegStrength(); + else if(Identifier == AGILITY) + return GetDefaultAgility(); + else + return humanoid::GetAttribute(Identifier, AllowBonus); +} + +truth humanoid::CanUseStethoscope(truth PrintReason) const +{ + if(!GetUsableArms()) + { + if(PrintReason) + ADD_MESSAGE("You need a usable arm to use a stethoscope."); + + return false; + } + + if(!GetHead()) + { + if(PrintReason) + ADD_MESSAGE("You need a head to use stethoscope."); + + return false; + } + + return true; +} + +truth humanoid::IsUsingArms() const +{ + return GetAttackStyle() & USE_ARMS && CanAttackWithAnArm(); +} + +truth humanoid::IsUsingLegs() const +{ + return (GetAttackStyle() & USE_LEGS + || (GetAttackStyle() & USE_ARMS && !CanAttackWithAnArm())) + && HasTwoUsableLegs(); +} + +truth humanoid::IsUsingHead() const +{ + return (GetAttackStyle() & USE_HEAD + || ((GetAttackStyle() & USE_LEGS + || (GetAttackStyle() & USE_ARMS && !CanAttackWithAnArm())) + && !HasTwoUsableLegs())) + && GetHead(); +} + +void humanoid::CalculateBattleInfo() +{ + CalculateDodgeValue(); + doforbodyparts()(this, &bodypart::CalculateAttackInfo); +} + +item* skeleton::SevereBodyPart(int BodyPartIndex, truth ForceDisappearance, stack* EquipmentDropStack) +{ + if(BodyPartIndex == RIGHT_ARM_INDEX) + EnsureCurrentSWeaponSkillIsCorrect(CurrentRightSWeaponSkill, 0); + else if(BodyPartIndex == LEFT_ARM_INDEX) + EnsureCurrentSWeaponSkillIsCorrect(CurrentLeftSWeaponSkill, 0); + + item* BodyPart = GetBodyPart(BodyPartIndex); + item* Bone = 0; + + if(!ForceDisappearance) + { + if(BodyPartIndex == HEAD_INDEX) + Bone = skull::Spawn(0, NO_MATERIALS); + else + Bone = bone::Spawn(0, NO_MATERIALS); + + Bone->InitMaterials(BodyPart->GetMainMaterial()); + BodyPart->DropEquipment(EquipmentDropStack); + BodyPart->RemoveFromSlot(); + BodyPart->SetMainMaterial(0, NO_PIC_UPDATE|NO_SIGNALS); + } + else + { + BodyPart->DropEquipment(EquipmentDropStack); + BodyPart->RemoveFromSlot(); + } + + BodyPart->SendToHell(); + CalculateAttributeBonuses(); + CalculateBattleInfo(); + SignalPossibleTransparencyChange(); + RemoveTraps(BodyPartIndex); + return Bone; +} + +void zombie::CreateBodyParts(int SpecialFlags) +{ + bool Anyway = false; + if(GetConfig() == ZOMBIE_OF_KHAZ_ZADM) + { + Anyway = true; + } // Khaz-Zadm needs his hands... + + for(int c = 0; c < BodyParts; ++c) + if(Anyway || BodyPartIsVital(c) || RAND_N(3) || (c == HEAD_INDEX && !RAND_N(3))) + { + bodypart* BodyPart = CreateBodyPart(c, SpecialFlags|NO_PIC_UPDATE); + BodyPart->GetMainMaterial()->SetSpoilCounter(2000 + RAND_N(1000)); + } +} + +void humanoid::AddSpecialEquipmentInfo(festring& String, int I) const +{ + if((I == RIGHT_WIELDED_INDEX && GetRightArm()->TwoHandWieldIsActive()) || (I == LEFT_WIELDED_INDEX && GetLeftArm()->TwoHandWieldIsActive())) + String << " (in both hands)"; +} + +/* Yes, this is evil. */ + +#define INSTANTIATE(name) if(DataBase->name.IsValid() && (Item = DataBase->name.Instantiate(SpecialFlags))) Set##name(Item); + +void humanoid::CreateInitialEquipment(int SpecialFlags) +{ + character::CreateInitialEquipment(SpecialFlags); + item* Item; + + INSTANTIATE(Helmet); + INSTANTIATE(Amulet); + INSTANTIATE(Cloak); + INSTANTIATE(BodyArmor); + INSTANTIATE(Belt); + INSTANTIATE(RightWielded); + INSTANTIATE(LeftWielded); + INSTANTIATE(RightRing); + INSTANTIATE(LeftRing); + INSTANTIATE(RightGauntlet); + INSTANTIATE(LeftGauntlet); + INSTANTIATE(RightBoot); + INSTANTIATE(LeftBoot); + + if(CurrentRightSWeaponSkill) + CurrentRightSWeaponSkill->AddHit(GetRightSWeaponSkillHits() * 100); + + if(CurrentLeftSWeaponSkill) + CurrentLeftSWeaponSkill->AddHit(GetLeftSWeaponSkillHits() * 100); +} + +festring humanoid::GetBodyPartName(int I, truth Articled) const +{ + festring Article; + + if(Articled) + Article << 'a'; + + switch(I) + { + case HEAD_INDEX: return Article + "head"; + case TORSO_INDEX: return Article + "torso"; + case RIGHT_ARM_INDEX: return Article + "right arm"; + case LEFT_ARM_INDEX: return Article + "left arm"; + case GROIN_INDEX: return Article + "groin"; + case RIGHT_LEG_INDEX: return Article + "right leg"; + case LEFT_LEG_INDEX: return Article + "left leg"; + } + + ABORT("Illegal humanoid bodypart name request!"); + return ""; +} + +void humanoid::CreateBlockPossibilityVector(blockvector& Vector, double ToHitValue) const +{ + double RightBlockChance = 0; + int RightBlockCapability = 0; + double LeftBlockChance = 0; + int LeftBlockCapability = 0; + arm* RightArm = GetRightArm(); + arm* LeftArm = GetLeftArm(); + + if(RightArm) + { + RightBlockChance = RightArm->GetBlockChance(ToHitValue); + RightBlockCapability = RightArm->GetBlockCapability(); + } + + if(LeftArm) + { + LeftBlockChance = LeftArm->GetBlockChance(ToHitValue); + LeftBlockCapability = LeftArm->GetBlockCapability(); + } + + /* Double block */ + + if(RightBlockCapability + LeftBlockCapability) + Vector.push_back(std::make_pair(RightBlockChance * LeftBlockChance, RightBlockCapability + LeftBlockCapability)); + + /* Right block */ + + if(RightBlockCapability) + Vector.push_back(std::make_pair(RightBlockChance * (1 - LeftBlockChance), RightBlockCapability)); + + /* Left block */ + + if(LeftBlockCapability) + Vector.push_back(std::make_pair(LeftBlockChance * (1 - RightBlockChance), LeftBlockCapability)); +} + +item* humanoid::SevereBodyPart(int BodyPartIndex, truth ForceDisappearance, stack* EquipmentDropStack) +{ + if(BodyPartIndex == RIGHT_ARM_INDEX) + EnsureCurrentSWeaponSkillIsCorrect(CurrentRightSWeaponSkill, 0); + else if(BodyPartIndex == LEFT_ARM_INDEX) + EnsureCurrentSWeaponSkillIsCorrect(CurrentLeftSWeaponSkill, 0); + + return character::SevereBodyPart(BodyPartIndex, ForceDisappearance, EquipmentDropStack); +} + +humanoid::humanoid(const humanoid& Humanoid) : mybase(Humanoid), CurrentRightSWeaponSkill(0), CurrentLeftSWeaponSkill(0) +{ + SWeaponSkill.resize(Humanoid.SWeaponSkill.size()); + std::list::iterator i1 = SWeaponSkill.begin(); + std::list::const_iterator i2 = Humanoid.SWeaponSkill.begin(); + + for(; i1 != SWeaponSkill.end(); ++i1, ++i2) + *i1 = new sweaponskill(**i2); +} + +cfestring& humanoid::GetDeathMessage() const +{ + static festring HeadlessDeathMsg = CONST_S("@Dd dies without a sound."); + return GetHead() || character::GetDeathMessage() != "@Dd dies screaming." ? character::GetDeathMessage() : HeadlessDeathMsg; +} + +int humanoid::GetSWeaponSkillLevel(citem* Item) const +{ + std::list::const_iterator i; + + for(i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i) + if((*i)->IsSkillOf(Item)) + return (*i)->GetLevel(); + + for(idholder* I = Item->GetCloneMotherID(); I; I = I->Next) + for(i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i) + if((*i)->IsSkillOfCloneMother(Item, I->ID)) + return (*i)->GetLevel(); + + return 0; +} + +truth humanoid::UseMaterialAttributes() const +{ + return combinebodypartpredicates()(this, &bodypart::UseMaterialAttributes, 0); +} + +col24 angel::GetBaseEmitation() const +{ + switch(GetMasterGod()->GetBasicAlignment()) + { + case GOOD: return MakeRGB24(150, 150, 150); + case NEUTRAL: return MakeRGB24(120, 120, 150); + case EVIL: return MakeRGB24(150, 110, 110); + } + + return 0; +} + +void bananagrower::BeTalkedTo() +{ + static long Said; + + if(GetRelation(PLAYER) == HOSTILE) + ProcessAndAddMessage(GetHostileReplies()[RandomizeReply(Said, GetHostileReplies().Size)]); + else if(!game::TweraifIsFree()) + { + if(GetRelation(PLAYER) != HOSTILE + && Profession.Find("president", 0) != festring::NPos && !(RAND() % 7)) + ADD_MESSAGE("\"I'm glad Petrus spared my life even though I was the president.\""); + + ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, 6)]); + } + else + ProcessAndAddMessage(GetFriendlyReplies()[6 + RandomizeReply(Said, 3)]); +} + +void bananagrower::RandomizeProfession() +{ + switch(RAND_N(12)) + { + case 0: + Profession = CONST_S("the president of Tweraif"); + break; + case 1: + Profession = CONST_S("a diplomat"); + break; + case 2: + Profession = CONST_S("a teacher"); + break; + case 3: + Profession = CONST_S("a philosopher"); + break; + case 4: + Profession = CONST_S("a journalist"); + break; + case 5: + Profession = CONST_S("an alchemist"); + break; + case 6: + Profession = CONST_S("a renown mathematician"); + break; + case 7: + Profession = CONST_S("a priest of Silva"); + break; + case 8: + case 9: + case 10: + case 11: + Profession = CONST_S("a professor of "); + AddRandomScienceName(Profession); + break; + } +} + +void bananagrower::PostConstruct() +{ + Stamina = MaxStamina / 5; + RandomizeProfession(); + HasDroppedBananas = FeedingSumo = false; +} + +void bananagrower::Save(outputfile& SaveFile) const +{ + humanoid::Save(SaveFile); + SaveFile << Profession << HasDroppedBananas << FeedingSumo; +} + +void bananagrower::Load(inputfile& SaveFile) +{ + humanoid::Load(SaveFile); + SaveFile >> Profession >> HasDroppedBananas >> FeedingSumo; +} + +void smith::BeTalkedTo() +{ + if(GetRelation(PLAYER) == HOSTILE) + { + ADD_MESSAGE("\"You talkin' to me? You talkin' to me? You talkin' to me? Then who the hell else are you talkin' to? You talkin' to me? Well I'm the only one here. Who do you think you're talking to? Oh yeah? Huh? Ok.\""); + return; + } + + if(!GetMainWielded() || !GetMainWielded()->CanBeUsedBySmith()) + { + ADD_MESSAGE("\"Sorry, I need an intact hammer to practise the art of smithing.\""); + return; + } + + if(PLAYER->PossessesItem(&item::IsFixableBySmith)) + { + item* Item = PLAYER->SelectFromPossessions(CONST_S("\"What do you want me to fix?\""), &item::IsFixableBySmith); + + if(!Item) + return; + + if(!(Item->GetMainMaterial()->GetCategoryFlags() & IS_METAL)) + { + ADD_MESSAGE("\"I only fix items made of metal.\""); + return; + } + + /** update messages */ + + long FixPrice = Item->GetFixPrice(); + + if(PLAYER->GetMoney() < FixPrice) + { + ADD_MESSAGE("\"Getting that fixed costs you %ld gold pieces. Get the money and we'll talk.\"", FixPrice); + return; + } + + ADD_MESSAGE("\"I can fix your %s, but it'll cost you %ld gold pieces.\"", Item->CHAR_NAME(UNARTICLED), FixPrice); + + if(game::TruthQuestion(CONST_S("Do you accept this deal? [y/N]"))) + { + Item->RemoveRust(); + Item->Fix(); + PLAYER->EditMoney(-FixPrice); + ADD_MESSAGE("%s fixes %s in no time.", CHAR_NAME(DEFINITE), Item->CHAR_NAME(DEFINITE)); + } + } + else + ADD_MESSAGE("\"Come back when you have some weapons or armor I can fix.\""); +} + +void humanoid::CalculateDodgeValue() +{ + DodgeValue = 0.05 * GetMoveEase() * GetAttribute(AGILITY) / sqrt(GetSize()); + + if(IsFlying()) + DodgeValue *= 2; + else + { + if(!HasAUsableLeg()) + DodgeValue *= 0.50; + if(!HasTwoUsableLegs()) + DodgeValue *= 0.75; + } + + if(DodgeValue < 1) + DodgeValue = 1; +} + +truth humanoid::CheckZap() +{ + if(!GetUsableArms()) + { + ADD_MESSAGE("You need at least one usable arm to zap."); + return false; + } + else + return character::CheckZap(); +} + +void bananagrower::GetAICommand() +{ + if(game::TweraifIsFree()) + { + humanoid::GetAICommand(); + return; + } + + if(CheckForEnemies(false, false, true, true)) + return; + + if(!IsEnabled()) + return; + + cv2 BananaTarget = FeedingSumo ? SUMO_ROOM_POS + v2(1, 2) : v2(45, 45); + + if(GetPos() == BananaTarget) + { + itemvector ItemVector; + GetStack()->FillItemVector(ItemVector); + int BananasDropped = 0; + uint c; + + for(c = 0; c < ItemVector.size(); ++c) + if(ItemVector[c]->IsBanana()) + { + ItemVector[c]->MoveTo(GetStackUnder()); + ++BananasDropped; + } + + if(BananasDropped) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s drops %s.", CHAR_NAME(DEFINITE), BananasDropped == 1 ? "a banana" : "some bananas"); + + return; + } + + ItemVector.clear(); + GetStackUnder()->FillItemVector(ItemVector); + int PeelsPickedUp = 0; + + for(c = 0; c < ItemVector.size(); ++c) + if(ItemVector[c]->IsBananaPeel()) + { + ItemVector[c]->MoveTo(GetStack()); + ++PeelsPickedUp; + } + + if(PeelsPickedUp) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s picks up %s.", CHAR_NAME(DEFINITE), PeelsPickedUp == 1 ? "a banana peel" : "some banana peels"); + + return; + } + + HasDroppedBananas = true; + } + + if(!HasDroppedBananas) + { + SetGoingTo(BananaTarget); + + if(MoveTowardsTarget(true)) + return; + } + else if(GetPos().X == 54) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s leaves the town to gather more bananas.", CHAR_NAME(DEFINITE)); + + GetStack()->Clean(); + character* Sumo = game::GetSumo(); + FeedingSumo = Sumo && Sumo->GetNP() < (SATIATED_LEVEL + BLOATED_LEVEL) >> 1 && !(RAND() % 15); + int Bananas = FeedingSumo ? 3 : 10; + + for(int c = 0; c < Bananas; ++c) + GetStack()->AddItem(banana::Spawn()); + + v2 Where = GetLevel()->GetNearestFreeSquare(this, v2(0, 45)); + + if(Where == ERROR_V2) + Where = GetLevel()->GetRandomSquare(this, NOT_IN_ROOM); // this is odd but at least it doesn't crash + + Move(Where, true); + RandomizeProfession(); + RestoreBodyParts(); + RestoreHP(); + Stamina = MaxStamina / 5; + ResetStates(); + TemporaryState = 0; + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s enters the town.", CHAR_NAME(INDEFINITE)); + + HasDroppedBananas = false; + } + else + { + SetGoingTo(v2(54, 45)); + + if(MoveTowardsTarget(true)) + return; + } + + EditAP(-1000); +} + +truth humanoid::CheckTalk() +{ + if(!character::CheckTalk()) + return false; + + if(!GetHead()) + { + ADD_MESSAGE("You need a head to talk."); + return false; + } + + return true; +} + +truth angel::CanCreateBodyPart(int I) const +{ + return I == TORSO_INDEX || I == HEAD_INDEX || I == RIGHT_ARM_INDEX || I == LEFT_ARM_INDEX; +} + +truth genie::CanCreateBodyPart(int I) const +{ + return I == TORSO_INDEX || I == HEAD_INDEX || I == RIGHT_ARM_INDEX || I == LEFT_ARM_INDEX; +} + +truth bananagrower::HandleCharacterBlockingTheWay(character* Char, v2 Pos, int Dir) +{ + return Char->GetPos() == v2(45, 45) && (Displace(Char, true) || Hit(Char, Pos, Dir)); +} + +festring& bananagrower::ProcessMessage(festring& Msg) const +{ + character::ProcessMessage(Msg); + SEARCH_N_REPLACE(Msg, "@pd", GetProfession()); + SEARCH_N_REPLACE(Msg, "@Pd", GetProfession().CapitalizeCopy()); + return Msg; +} + +void elder::CreateBodyParts(int SpecialFlags) +{ + for(int c = 0; c < BodyParts; ++c) + if(c != LEFT_LEG_INDEX) + CreateBodyPart(c, SpecialFlags); + else + SetBodyPart(LEFT_LEG_INDEX, 0); +} + +/*void encourager::GetAICommand() +{ + if(CheckForEnemies(true, true, true)) + return; + + if(CheckForUsefulItemsOnGround()) + return; + + if(CheckForDoors()) + return; + + if(game::GetTick() - LastHit > 200) + { + static int NotDiagonal[] = { 1, 3, 4, 6 }; + + for(int d = 0; d < 4; ++d) + { + square* Square = GetNeighbourSquare(NotDiagonal[d]); + + if(Square) + { + character* Char = Square->GetCharacter(); + + if(Char && Char->IsBananaGrower() && Hit(Char, Square->GetPos(), NotDiagonal[d], true)) + { + LastHit = game::GetTick(); + TerminateGoingTo(); + return; + } + } + } + } + + if(MoveTowardsHomePos()) + return; + + EditAP(-1000); +}*/ + +/*void encourager::Save(outputfile& SaveFile) const +{ + humanoid::Save(SaveFile); + SaveFile << LastHit; +} + +void encourager::Load(inputfile& SaveFile) +{ + humanoid::Load(SaveFile); + SaveFile >> LastHit; +}*/ + +long skeleton::GetBodyPartVolume(int I) const +{ + switch(I) + { + case HEAD_INDEX: return 600; + case TORSO_INDEX: return (GetTotalVolume() - 600) * 13 / 30; + case RIGHT_ARM_INDEX: + case LEFT_ARM_INDEX: return (GetTotalVolume() - 600) / 10; + case GROIN_INDEX: return (GetTotalVolume() - 600) / 10; + case RIGHT_LEG_INDEX: + case LEFT_LEG_INDEX: return ((GetTotalVolume() - 600) << 1) / 15; + } + + ABORT("Illegal humanoid bodypart volume request!"); + return 0; +} + +truth humanoid::CheckIfEquipmentIsNotUsable(int I) const +{ + return (I == RIGHT_WIELDED_INDEX && GetRightArm()->CheckIfWeaponTooHeavy("this item")) + || (I == LEFT_WIELDED_INDEX && GetLeftArm()->CheckIfWeaponTooHeavy("this item")) + || (I == RIGHT_WIELDED_INDEX && GetLeftWielded() && GetLeftWielded()->IsTwoHanded() && GetLeftArm()->CheckIfWeaponTooHeavy(festring(GetPossessivePronoun() + " other wielded item").CStr())) + || (I == LEFT_WIELDED_INDEX && GetRightWielded() && GetRightWielded()->IsTwoHanded() && GetRightArm()->CheckIfWeaponTooHeavy(festring(GetPossessivePronoun() + " other wielded item").CStr())); +} + +int mistress::TakeHit(character* Enemy, item* Weapon, bodypart* EnemyBodyPart, v2 HitPos, double Damage, double ToHitValue, int Success, int Type, int Direction, truth Critical, truth ForceHit) +{ + int Return = humanoid::TakeHit(Enemy, Weapon, EnemyBodyPart, HitPos, Damage, ToHitValue, Success, Type, Direction, Critical, ForceHit); + + if(Return == HAS_HIT && Critical) + { + if(IsPlayer()) + ADD_MESSAGE("Aahhh. The pain feels unbelievably good."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s screams: \"Oh the delightful pain!\"", CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("You hear someone screaming: \"Oh the delightful pain!\""); + } + + return Return; +} + +truth petrusswife::SpecialEnemySightedReaction(character* Char) +{ + item* Weapon = Char->GetMainWielded(); + + if(Weapon && Weapon->IsWeapon(Char) && !(RAND() % 20)) + ADD_MESSAGE("%s screams: \"Oh my Frog, %s's got %s %s!\"", CHAR_DESCRIPTION(DEFINITE), Char->CHAR_PERSONAL_PRONOUN_THIRD_PERSON_VIEW, Weapon->GetArticle(), Weapon->GetNameSingular().CStr()); + + return false; +} + +truth housewife::SpecialEnemySightedReaction(character* Char) +{ + item* Weapon = Char->GetMainWielded(); + + if(Weapon && Weapon->IsWeapon(Char) && !(RAND() % 5)) + ADD_MESSAGE("%s screams: \"Oh my Frog, %s's got %s %s!\"", CHAR_DESCRIPTION(DEFINITE), Char->CHAR_PERSONAL_PRONOUN_THIRD_PERSON_VIEW, Weapon->GetArticle(), Weapon->GetNameSingular().CStr()); + + return false; +} + +void guard::Save(outputfile& SaveFile) const +{ + humanoid::Save(SaveFile); + SaveFile << WayPoints << NextWayPoint; +} + +void guard::Load(inputfile& SaveFile) +{ + humanoid::Load(SaveFile); + SaveFile >> WayPoints >> NextWayPoint; +} + +void guard::GetAICommand() +{ + if(GetConfig() == MASTER && HP << 1 < MaxHP && (GetPos() - v2(30, 17)).GetLengthSquare() > 9) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s disappears.", CHAR_NAME(DEFINITE)); + + GetLevel()->GetLSquare(30, 16)->KickAnyoneStandingHereAway(); + Move(v2(30, 16), true); + EditAP(-1000); + return; + } + + if(WayPoints.size() && !IsGoingSomeWhere()) + { + if(GetPos() == WayPoints[NextWayPoint]) + if(NextWayPoint < WayPoints.size() - 1) + ++NextWayPoint; + else + NextWayPoint = 0; + + GoingTo = WayPoints[NextWayPoint]; + } + + SeekLeader(GetLeader()); + + if(CheckForEnemies(true, true, true)) + return; + + if(CheckForUsefulItemsOnGround()) + return; + + if(FollowLeader(GetLeader())) + return; + + if(CheckForDoors()) + return; + + if(MoveTowardsHomePos()) + return; + + if(CheckSadism()) + return; + + if(CheckForBeverage()) + return; + + EditAP(-1000); +} + +truth mistress::ReceiveDamage(character* Damager, int Damage, int Type, int TargetFlags, int Direction, truth Divide, truth PenetrateArmor, truth Critical, truth ShowMsg) +{ + truth Success = humanoid::ReceiveDamage(Damager, Damage, Type, TargetFlags, Direction, Divide, PenetrateArmor, Critical, ShowMsg); + + if(Type & SOUND && Success && !(RAND() & 7)) + { + if(IsPlayer()) + ADD_MESSAGE("Aahhh. The pain feels unbelievably good."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s screams: \"Oh the delightful pain!\"", CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("You hear someone screaming: \"Oh the delightful pain!\""); + } + + return Success; +} + +void humanoid::AddSpecialStethoscopeInfo(felist& Info) const +{ + Info.AddEntry(CONST_S("Arm strength: ") + GetAttribute(ARM_STRENGTH), LIGHT_GRAY); + Info.AddEntry(CONST_S("Leg strength: ") + GetAttribute(LEG_STRENGTH), LIGHT_GRAY); + Info.AddEntry(CONST_S("Dexterity: ") + GetAttribute(DEXTERITY), LIGHT_GRAY); + Info.AddEntry(CONST_S("Agility: ") + GetAttribute(AGILITY), LIGHT_GRAY); +} + +item* humanoid::GetPairEquipment(int I) const +{ + switch(I) + { + case RIGHT_WIELDED_INDEX: return GetLeftWielded(); + case LEFT_WIELDED_INDEX: return GetRightWielded(); + case RIGHT_GAUNTLET_INDEX: return GetLeftGauntlet(); + case LEFT_GAUNTLET_INDEX: return GetRightGauntlet(); + case RIGHT_BOOT_INDEX: return GetLeftBoot(); + case LEFT_BOOT_INDEX: return GetRightBoot(); + } + + return 0; +} + +cfestring& humanoid::GetStandVerb() const +{ + if(ForceCustomStandVerb()) + return DataBase->StandVerb; + + static festring HasntFeet = CONST_S("crawling"); + static festring Hovering = CONST_S("hovering"); + static festring Swimming = CONST_S("swimming"); + + if(StateIsActivated(LEVITATION)) + return Hovering; + + if(IsSwimming()) + return Swimming; + + return HasAUsableLeg() ? DataBase->StandVerb : HasntFeet; +} + +void darkmage::GetAICommand() +{ + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + character* NearestEnemy = 0; + long NearestEnemyDistance = 0x7FFFFFFF; + character* RandomFriend = 0; + charactervector Friend; + v2 Pos = GetPos(); + + for(int c = 0; c < game::GetTeams(); ++c) + { + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + { + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled()) + { + long ThisDistance = Max(abs((*i)->GetPos().X - Pos.X), abs((*i)->GetPos().Y - Pos.Y)); + + if((ThisDistance < NearestEnemyDistance || (ThisDistance == NearestEnemyDistance && !(RAND() % 3))) && (*i)->CanBeSeenBy(this)) + { + NearestEnemy = *i; + NearestEnemyDistance = ThisDistance; + } + } + } + else if(GetTeam()->GetRelation(game::GetTeam(c)) == FRIEND) + { + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled() && (*i)->CanBeSeenBy(this)) + Friend.push_back(*i); + } + } + + if(NearestEnemy && NearestEnemy->GetPos().IsAdjacent(Pos)) + { + if(NearestEnemy->IsSmall() + && GetAttribute(WISDOM) < NearestEnemy->GetAttackWisdomLimit() + && !(RAND() % 5) + && Hit(NearestEnemy, NearestEnemy->GetPos(), game::GetDirectionForVector(NearestEnemy->GetPos() - GetPos()))) + return; + else if((GetConfig() == ARCH_MAGE && RAND() & 1) + || (GetConfig() == ELDER && !(RAND() & 3))) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s invokes a spell and disappears.", CHAR_NAME(DEFINITE)); + + TeleportRandomly(true); + EditAP(-GetSpellAPCost()); + return; + } + } + + if(NearestEnemy && ((GetConfig() != APPRENTICE && NearestEnemyDistance < 10) || StateIsActivated(PANIC)) && RAND() & 3) + { + SetGoingTo((Pos << 1) - NearestEnemy->GetPos()); + + if(MoveTowardsTarget(true)) + return; + } + + if(Friend.size() && !(RAND() & 3)) + { + RandomFriend = Friend[RAND() % Friend.size()]; + NearestEnemy = 0; + } + + beamdata Beam + ( + this, + CONST_S("killed by the spells of ") + GetName(INDEFINITE), + YOURSELF, + 0 + ); + + if(NearestEnemy) + { + lsquare* Square = NearestEnemy->GetLSquareUnder(); + EditAP(-GetSpellAPCost()); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s invokes a spell!", CHAR_NAME(DEFINITE)); + + switch(GetConfig()) + { + case APPRENTICE: + Square->DrawLightning(v2(8, 8), WHITE, YOURSELF); + Square->Lightning(Beam); + break; + case BATTLE_MAGE: + if(RAND() % 20) + { + Square->DrawLightning(v2(8, 8), WHITE, YOURSELF); + Square->Lightning(Beam); + } + else + { + Square->DrawParticles(RED); + Square->LowerEnchantment(Beam); + } + + break; + case ELDER: + switch(RAND() % 20) + { + case 0: + case 1: + case 2: Square->DrawParticles(RED); Square->Strike(Beam); break; + case 3: Square->DrawParticles(RED); Square->FireBall(Beam); break; + case 4: + case 5: + case 6: Square->DrawParticles(RED); Square->Slow(Beam); break; + case 7: Square->DrawParticles(RED); Square->Teleport(Beam); break; + case 8: + case 9: + case 10: Square->DrawParticles(RED); Square->LowerEnchantment(Beam); break; + default: Square->DrawLightning(v2(8, 8), WHITE, YOURSELF); Square->Lightning(Beam); break; + } + + break; + case ARCH_MAGE: + switch(RAND() % 20) + { + case 0: + case 1: + case 2: Square->DrawParticles(RED); Square->FireBall(Beam); break; + case 3: + { + character* Char = NearestEnemy->DuplicateToNearestSquare(this, CHANGE_TEAM|MIRROR|(1000 << LE_BASE_SHIFT)|(1000 << LE_RAND_SHIFT)); + + if(Char) + { + if(Char->CanBeSeenByPlayer()) + ADD_MESSAGE("%s materializes!", Char->CHAR_NAME(INDEFINITE)); + + break; + } + } + case 4: + case 5: Square->DrawParticles(RED); Square->Slow(Beam); break; + case 6: Square->DrawParticles(RED); Square->Teleport(Beam); break; + case 7: + case 8: + case 9: Square->DrawParticles(RED); Square->LowerEnchantment(Beam); break; + case 10: + { + golem* Golem = golem::Spawn(RAND() % 3 ? ARCANITE : OCTIRON); + v2 Where = GetLevel()->GetNearestFreeSquare(Golem, Square->GetPos()); + + if(Where == ERROR_V2) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("Nothing happens."); + + delete Golem; + } + else + { + Golem->SetGenerationDanger(GetGenerationDanger()); + Golem->SetTeam(GetTeam()); + Golem->PutTo(Where); + + if(Golem->CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s materializes!", Golem->CHAR_NAME(INDEFINITE)); + + Golem->GetLSquareUnder()->DrawParticles(RED); + } + + break; + } + default: Square->DrawParticles(RED); Square->Strike(Beam); break; + } + + break; + } + + if(CanBeSeenByPlayer()) + NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell of ") + GetName(DEFINITE) + CONST_S(" interrupts you.")); + else + NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell interrupts you.")); + + return; + } + + if(RandomFriend) + { + lsquare* Square = RandomFriend->GetLSquareUnder(); + EditAP(-GetSpellAPCost()); + Square->DrawParticles(RED); + + switch(GetConfig()) + { + case APPRENTICE: + case BATTLE_MAGE: + Square->Haste(Beam); + break; + case ARCH_MAGE: + if(!(RAND() & 31)) + { + RandomFriend->DuplicateToNearestSquare(this, CHANGE_TEAM); + return; + } + case ELDER: + if(RAND() & 1) + Square->Invisibility(Beam); + else + Square->Haste(Beam); + + break; + } + + return; + } + + if(CheckForDoors()) + return; + + if(CheckSadism()) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +void zombie::GetAICommand() +{ + if(!GetHead()) + { + for(stackiterator i = GetLSquareUnder()->GetStack()->GetBottom(); i.HasItem(); ++i) + { + head* Head = i->Behead(); + + if(Head) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s takes %s and attaches it to its torso.", CHAR_NAME(DEFINITE), Head->CHAR_NAME(INDEFINITE)); + + Head->RemoveFromSlot(); + AttachBodyPart(Head); + Head->SetHP(1); + DexterityAction(10); + return; + } + } + } + + humanoid::GetAICommand(); +} + +head* humanoid::Behead() +{ + head* Head = GetHead(); + + if(Head) + SevereBodyPart(HEAD_INDEX); + + return Head; +} + +truth communist::BoundToUse(citem* Item, int I) const +{ + return Item && Item->IsGorovitsFamilyRelic() && Item->IsInCorrectSlot(I); +} + +festring werewolfwolf::GetKillName() const +{ + if(GetPolymorphBackup() && GetPolymorphBackup()->GetType() == werewolfhuman::ProtoType.GetIndex()) + return GetName(INDEFINITE); + + return humanoid::GetKillName(); +} + +int humanoid::GetRandomApplyBodyPart() const +{ + if(RightArmIsUsable()) + { + if(LeftArmIsUsable()) + return RAND_2 ? RIGHT_ARM_INDEX : LEFT_ARM_INDEX; + else + return RIGHT_ARM_INDEX; + } + else if(LeftArmIsUsable()) + return LEFT_ARM_INDEX; + + if(GetHead()) + return HEAD_INDEX; + + return TORSO_INDEX; +} + +void golem::BeTalkedTo() +{ + static long Said; + + if(GetRelation(PLAYER) == HOSTILE) + Engrave(GetHostileReplies()[RandomizeReply(Said, GetHostileReplies().Size)]); + else + Engrave(GetFriendlyReplies()[RandomizeReply(Said, GetFriendlyReplies().Size)]); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s engraves something.", CHAR_NAME(DEFINITE)); +} + +#ifdef WIZARD + +void humanoid::AddAttributeInfo(festring& Entry) const +{ + Entry.Resize(45); + Entry << GetAttribute(ARM_STRENGTH); + Entry.Resize(48); + Entry << GetAttribute(LEG_STRENGTH); + Entry.Resize(51); + Entry << GetAttribute(DEXTERITY); + Entry.Resize(54); + Entry << GetAttribute(AGILITY); + character::AddAttributeInfo(Entry); +} + +void humanoid::AddAttackInfo(felist& List) const +{ + if(GetAttackStyle() & USE_ARMS) + { + if(GetRightArm()) + GetRightArm()->AddAttackInfo(List); + + if(GetLeftArm()) + GetLeftArm()->AddAttackInfo(List); + } + + festring Entry; + + if(IsUsingLegs()) + { + GetRightLeg()->AddAttackInfo(List); + GetLeftLeg()->AddAttackInfo(List); + } + + if(IsUsingHead()) + { + Entry = CONST_S(" bite attack"); + Entry.Resize(50); + Entry << GetHead()->GetBiteMinDamage() << '-' << GetHead()->GetBiteMaxDamage(); + Entry.Resize(60); + Entry << int(GetHead()->GetBiteToHitValue()); + Entry.Resize(70); + Entry << GetHead()->GetBiteAPCost(); + List.AddEntry(Entry, LIGHT_GRAY); + } +} + +void humanoid::AddDefenceInfo(felist& List) const +{ + character::AddDefenceInfo(List); + + if(GetRightArm()) + GetRightArm()->AddDefenceInfo(List); + + if(GetLeftArm()) + GetLeftArm()->AddDefenceInfo(List); +} + +void humanoid::DetachBodyPart() +{ + int ToBeDetached; + + switch(game::KeyQuestion(CONST_S("What limb? (l)eft arm, (r)ight arm, (L)eft leg, (R)ight leg, (h)ead?"), KEY_ESC, 5, 'l','r','L','R', 'h')) + { + case 'l': + ToBeDetached = LEFT_ARM_INDEX; + break; + case 'r': + ToBeDetached = RIGHT_ARM_INDEX; + break; + case 'L': + ToBeDetached = LEFT_LEG_INDEX; + break; + case 'R': + ToBeDetached = RIGHT_LEG_INDEX; + break; + case 'h': + ToBeDetached = HEAD_INDEX; + break; + default: + return; + } + + if(GetBodyPart(ToBeDetached)) + { + item* ToDrop = SevereBodyPart(ToBeDetached); + SendNewDrawRequest(); + + if(ToDrop) + { + GetStack()->AddItem(ToDrop); + ToDrop->DropEquipment(); + } + + ADD_MESSAGE("Bodypart detached!"); + } + else + ADD_MESSAGE("That bodypart has already been detached."); + + CheckDeath(CONST_S("removed one of his vital bodyparts"), 0); +} + +#else + +void humanoid::AddAttributeInfo(festring&) const { } +void humanoid::AddAttackInfo(felist&) const { } +void humanoid::AddDefenceInfo(felist&) const { } +void humanoid::DetachBodyPart() { } + +#endif + +truth ennerbeast::MustBeRemovedFromBone() const +{ + return !IsEnabled() || GetTeam()->GetID() != MONSTER_TEAM || GetDungeon()->GetIndex() != ELPURI_CAVE || GetLevel()->GetIndex() != ENNER_BEAST_LEVEL; +} + +truth communist::MustBeRemovedFromBone() const +{ + return !IsEnabled() || GetTeam()->GetID() != IVAN_TEAM || GetDungeon()->GetIndex() != ELPURI_CAVE|| GetLevel()->GetIndex() != IVAN_LEVEL; +} + +truth humanoid::PreProcessForBone() +{ + for(std::list::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i) + (*i)->PreProcessForBone(); + + return character::PreProcessForBone(); +} + +void humanoid::FinalProcessForBone() +{ + character::FinalProcessForBone(); + + for(std::list::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end();) + { + boneidmap::iterator BI = game::GetBoneItemIDMap().find(-(*i)->GetID()); + + if(BI == game::GetBoneItemIDMap().end()) + { + std::list::iterator Dirt = i++; + SWeaponSkill.erase(Dirt); + } + else + { + (*i)->SetID(BI->second); + ++i; + } + } +} + +void petrus::FinalProcessForBone() +{ + humanoid::FinalProcessForBone(); + LastHealed = 0; +} + +void angel::FinalProcessForBone() +{ + humanoid::FinalProcessForBone(); + LastHealed = 0; +} + +void kabouter::FinalProcessForBone() +{ + humanoid::FinalProcessForBone(); + LastTamed = 0; +} + +/*void encourager::FinalProcessForBone() +{ + humanoid::FinalProcessForBone(); + LastHit = 0; +}*/ + +void playerkind::Save(outputfile& SaveFile) const +{ + humanoid::Save(SaveFile); + SaveFile << SoulID << HairColor << EyeColor << Talent << Weakness << IsBonePlayer << IsClone; +} + +void playerkind::Load(inputfile& SaveFile) +{ + humanoid::Load(SaveFile); + SaveFile >> SoulID >> HairColor >> EyeColor >> Talent >> Weakness >> IsBonePlayer >> IsClone; +} + +void playerkind::SetSoulID(ulong What) +{ + SoulID = What; + + if(GetPolymorphBackup()) + GetPolymorphBackup()->SetSoulID(What); +} + +truth playerkind::SuckSoul(character* Soul) +{ + if(Soul->GetID() == SoulID) + { + SoulID = 0; + return true; + } + + return false; +} + +truth playerkind::TryToRiseFromTheDead() +{ + if(humanoid::TryToRiseFromTheDead()) + { + if(IsEnabled() && SoulID) + { + ADD_MESSAGE("The soulless body of %s wobbles for a moment.", CHAR_NAME(DEFINITE)); + return false; + } + + return true; + } + else + return false; +} + +void playerkind::FinalProcessForBone() +{ + humanoid::FinalProcessForBone(); + IsBonePlayer = true; + + if(SoulID) + { + boneidmap::iterator BI = game::GetBoneCharacterIDMap().find(SoulID); + + if(BI != game::GetBoneCharacterIDMap().end()) + SoulID = BI->second; + else + SoulID = 0; + } +} + +playerkind::playerkind(const playerkind& Char) : mybase(Char), SoulID(Char.SoulID), HairColor(Char.HairColor), EyeColor(Char.EyeColor), Talent(Char.Talent), Weakness(Char.Weakness), IsBonePlayer(Char.IsBonePlayer), IsClone(true) +{ +} + +void playerkind::BeTalkedTo() +{ + if(IsClone && IsBonePlayer) + { + if(GetRelation(PLAYER) == HOSTILE) + { + ADD_MESSAGE("Oh no, you too! Why does everyone bully me!"); + return; + } + + static long Said; + + switch(RandomizeReply(Said, 4)) + { + case 0: + ADD_MESSAGE("\"I'd like to write a memoir, but alas I doubt anyone would believe it.\""); + break; + case 1: + ADD_MESSAGE("\"Then that damned clone appeared, took all my equipment and claimed I was his slave...\""); + break; + case 2: + ADD_MESSAGE("\"The level was a catastrophe for the party, but luckily you saved the day.\""); + break; + case 3: + ADD_MESSAGE("\"Oh, how I hate bananas. I Hate Them! I HATE THEM SO MUCH!!!\""); + break; + } + } + else if(IsClone) + { + if(GetRelation(PLAYER) == HOSTILE) + { + ADD_MESSAGE("%s seems extremely irritated. \"Vanish, you foul mirror image!\"", CHAR_DESCRIPTION(DEFINITE)); + return; + } + + static long Said; + + switch(RandomizeReply(Said, 4)) + { + case 0: + ADD_MESSAGE("\"Hey, those clothes are mine! Give them back!\""); + break; + case 1: + ADD_MESSAGE("\"What, you summoned me? What a coincidence, I remember summoning you, too.\""); + break; + case 2: + ADD_MESSAGE("\"I'm leading this party, not you, Mr. copy guy!\""); + break; + case 3: + ADD_MESSAGE("\"Oh, how I hate bananas. I Hate Them! I HATE THEM SO MUCH!!!\""); + break; + } + } + else + { + if(GetRelation(PLAYER) == HOSTILE) + { + ADD_MESSAGE("Let's finish what my ghost failed to do!"); + return; + } + + static long Said; + + switch(RandomizeReply(Said, 4)) + { + case 0: + ADD_MESSAGE("\"What was it like? Death, you mean? Well, just like New Attnam. Very hot and whips everywhere.\""); + break; + case 1: + ADD_MESSAGE("\"Stop it already! I *don't* want to know how my corpse smelled!\""); + break; + case 2: + ADD_MESSAGE("\"I'm sorry about that ghost thing. That YASD was just a bit too much to handle, so I lost myself.\""); + break; + case 3: + ADD_MESSAGE("\"Oh, how I hate bananas. I Hate Them! I HATE THEM SO MUCH!!!\""); + break; + } + } +} + +void humanoid::EnsureCurrentSWeaponSkillIsCorrect(sweaponskill*& Skill, citem* Wielded) +{ + if(Wielded) + { + if(!Skill || !Skill->IsSkillOf(Wielded)) + { + if(Skill) + EnsureCurrentSWeaponSkillIsCorrect(Skill, 0); + + std::list::iterator i; + + for(i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i) + if((*i)->IsSkillOf(Wielded)) + { + Skill = *i; + return; + } + + for(idholder* I = Wielded->GetCloneMotherID(); I; I = I->Next) + for(i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i) + if((*i)->IsSkillOfCloneMother(Wielded, I->ID)) + { + Skill = new sweaponskill(**i); + Skill->SetID(Wielded->GetID()); + SWeaponSkill.push_back(Skill); + return; + } + + Skill = new sweaponskill(Wielded); + SWeaponSkill.push_back(Skill); + } + } + else if(Skill) + { + if(!Skill->GetHits() && (CurrentRightSWeaponSkill != Skill || CurrentLeftSWeaponSkill != Skill)) + for(std::list::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i) + if(*i == Skill) + { + delete *i; + SWeaponSkill.erase(i); + break; + } + + Skill = 0; + } +} + +humanoid::~humanoid() +{ + for(std::list::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i) + delete *i; +} + +truth petrus::MoveTowardsHomePos() +{ + if(GetPos() != v2(28, 20)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s disappears.", CHAR_NAME(DEFINITE)); + + GetLevel()->GetLSquare(28, 20)->KickAnyoneStandingHereAway(); + Move(v2(28, 20), true); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s appears.", CHAR_NAME(DEFINITE)); + + EditAP(-1000); + return true; + } + else + return false; +} + +truth guard::MoveTowardsHomePos() +{ + if(GetConfig() == MASTER && GetPos() != v2(30, 16)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s disappears.", CHAR_NAME(DEFINITE)); + + GetLevel()->GetLSquare(30, 16)->KickAnyoneStandingHereAway(); + Move(v2(30, 16), true); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s appears.", CHAR_NAME(DEFINITE)); + + EditAP(-1000); + return true; + } + else + return humanoid::MoveTowardsHomePos(); +} + +bodypart* ennerbeast::MakeBodyPart(int I) const +{ + if(I == HEAD_INDEX) + return ennerhead::Spawn(0, NO_MATERIALS); + else + return humanoid::MakeBodyPart(I); +} + +int humanoid::GetSumOfAttributes() const +{ + return character::GetSumOfAttributes() + GetAttribute(LEG_STRENGTH) + GetAttribute(DEXTERITY) ; +} + +truth humanoid::CheckConsume(cfestring& Verb) const +{ + if(!HasHead()) + { + if(IsPlayer()) + ADD_MESSAGE("You need a head to %s.", Verb.CStr()); + + return false; + } + + return character::CheckConsume(Verb); +} + +truth humanoid::CanConsume(material* Material) const +{ + return character::CanConsume(Material) && HasHead(); +} + +void femaleslave::BeTalkedTo() +{ + static long Said; + + if(GetConfig() != NEW_ATTNAM || GetRelation(PLAYER) == HOSTILE) + humanoid::BeTalkedTo(); + else if(!game::TweraifIsFree()) + ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, 4)]); + else + ProcessAndAddMessage(GetFriendlyReplies()[4 + RandomizeReply(Said, 3)]); +} + +void necromancer::GetAICommand() +{ + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + character* NearestEnemy = 0; + long NearestEnemyDistance = 0x7FFFFFFF; + v2 Pos = GetPos(); + + for(int c = 0; c < game::GetTeams(); ++c) + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + { + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled()) + { + long ThisDistance = Max(abs((*i)->GetPos().X - Pos.X), abs((*i)->GetPos().Y - Pos.Y)); + + if((ThisDistance < NearestEnemyDistance || (ThisDistance == NearestEnemyDistance && !(RAND() % 3))) && (*i)->CanBeSeenBy(this)) + { + NearestEnemy = *i; + NearestEnemyDistance = ThisDistance; + } + } + } + + if(NearestEnemy && NearestEnemy->GetPos().IsAdjacent(Pos)) + { + if(GetConfig() == MASTER_NECROMANCER && !(RAND() & 3)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s invokes a spell and disappears.", CHAR_NAME(DEFINITE)); + + TeleportRandomly(true); + EditAP(-GetSpellAPCost()); + return; + } + else if(NearestEnemy->IsSmall() + && GetAttribute(WISDOM) < NearestEnemy->GetAttackWisdomLimit() + && !(RAND() & 3) + && Hit(NearestEnemy, NearestEnemy->GetPos(), game::GetDirectionForVector(NearestEnemy->GetPos() - GetPos()))) + return; + } + + if(!NearestEnemy) + { + if(!RAND_N(3) && TryToRaiseZombie()) + return; + } + else + { + if(!RAND_N(6) && TryToRaiseZombie()) + return; + } + + if(NearestEnemy && !(RAND() % (GetConfig() == APPRENTICE_NECROMANCER ? 3 : 2))) + { + lsquare* Square = NearestEnemy->GetLSquareUnder(); + EditAP(-GetSpellAPCost()); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s invokes a spell!", CHAR_NAME(DEFINITE)); + + truth Interrupt = false; + + switch(GetConfig()) + { + case APPRENTICE_NECROMANCER: + RaiseSkeleton(); + break; + case MASTER_NECROMANCER: + if(RAND() % 5) + RaiseSkeleton(); + else + { + Square->DrawLightning(v2(8, 8), WHITE, YOURSELF); + + beamdata Beam + ( + this, + CONST_S("killed by the spells of ") + GetName(INDEFINITE), + YOURSELF, + 0 + ); + + Square->Lightning(Beam); + Interrupt = true; + } + + break; + } + + if(Interrupt) + if(CanBeSeenByPlayer()) + NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell of ") + GetName(DEFINITE) + CONST_S(" interrupts you.")); + else + NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell interrupts you.")); + + return; + } + + if(NearestEnemy && (NearestEnemyDistance < 10 || StateIsActivated(PANIC)) && RAND() & 3) + { + SetGoingTo((Pos << 1) - NearestEnemy->GetPos()); + + if(MoveTowardsTarget(true)) + return; + } + + if(CheckForDoors()) + return; + + if(CheckSadism()) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +truth necromancer::TryToRaiseZombie() +{ + for(int c = 0; c < game::GetTeams(); ++c) + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); + i != game::GetTeam(c)->GetMember().end(); ++i) + if(!(*i)->IsEnabled() && (*i)->GetMotherEntity() + && (*i)->GetMotherEntity()->Exists() + && (GetConfig() == MASTER_NECROMANCER + || (*i)->GetMotherEntity()->GetSquareUnderEntity()->CanBeSeenBy(this))) + { + character* Zombie = (*i)->GetMotherEntity()->TryNecromancy(this); + + if(Zombie) + { + if(Zombie->CanBeSeenByPlayer()) + ADD_MESSAGE("%s calls %s back to cursed undead life.", CHAR_DESCRIPTION(DEFINITE), Zombie->CHAR_NAME(INDEFINITE)); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s casts a spell, but you notice no effect.", CHAR_NAME(DEFINITE)); + + EditAP(-GetSpellAPCost()); + return true; + } + } + + return false; +} + +void necromancer::RaiseSkeleton() +{ + /* Gum solution */ + + const database* WarLordDataBase; + databasecreator::FindDataBase(WarLordDataBase, &skeleton::ProtoType, WAR_LORD); + skeleton* Skeleton; + + if(GetConfig() == MASTER_NECROMANCER && !(WarLordDataBase->Flags & HAS_BEEN_GENERATED) && !(RAND() % 250)) + { + Skeleton = skeleton::Spawn(WAR_LORD); + Skeleton->SetTeam(GetTeam()); + Skeleton->PutNear(GetPos()); + Skeleton->SignalGeneration(); + + if(Skeleton->CanBeSeenByPlayer()) + ADD_MESSAGE("The whole area trembles terribly as %s emerges from the ground.", Skeleton->CHAR_NAME(DEFINITE)); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s casts a powerful spell which makes the whole area tremble.", CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("You feel the presence of an ancient evil being awakened from its long slumber. You shiver."); + } + else + { + Skeleton = skeleton::Spawn(GetConfig() == APPRENTICE_NECROMANCER ? 0 : WARRIOR, NO_EQUIPMENT); + Skeleton->SetTeam(GetTeam()); + Skeleton->PutNear(GetPos()); + + if(Skeleton->CanBeSeenByPlayer()) + ADD_MESSAGE("The ground shakes and %s emerges from it.", Skeleton->CHAR_NAME(INDEFINITE)); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s casts a spell, but you notice no effect.", CHAR_NAME(DEFINITE)); + } + + Skeleton->SetGenerationDanger(GetGenerationDanger()); + EditAP(-GetSpellAPCost()); +} + +void humanoid::StayOn(liquid* Liquid) +{ + if(IsFlying()) + return; + + truth Standing = false; + + if(GetRightLeg()) + { + GetRightLeg()->StayOn(Liquid); + Standing = true; + } + + if(IsEnabled() && GetLeftLeg()) + { + GetLeftLeg()->StayOn(Liquid); + Standing = true; + } + + if(!Standing) + { + bodypart* BodyPart[MAX_BODYPARTS]; + int Index = 0; + + for(int c = 0; c < BodyParts; ++c) + if(GetBodyPart(c)) + BodyPart[Index++] = GetBodyPart(c); + + BodyPart[RAND() % Index]->StayOn(Liquid); + } +} + +bodypart* playerkind::MakeBodyPart(int I) const +{ + switch(I) + { + case TORSO_INDEX: return playerkindtorso::Spawn(0, NO_MATERIALS); + case HEAD_INDEX: return playerkindhead::Spawn(0, NO_MATERIALS); + case RIGHT_ARM_INDEX: return playerkindrightarm::Spawn(0, NO_MATERIALS); + case LEFT_ARM_INDEX: return playerkindleftarm::Spawn(0, NO_MATERIALS); + case GROIN_INDEX: return playerkindgroin::Spawn(0, NO_MATERIALS); + case RIGHT_LEG_INDEX: return playerkindrightleg::Spawn(0, NO_MATERIALS); + case LEFT_LEG_INDEX: return playerkindleftleg::Spawn(0, NO_MATERIALS); + } + + ABORT("Weird bodypart to make for a playerkind. It must be your fault!"); + return 0; +} + +truth golem::AddAdjective(festring& String, truth Articled) const +{ + int TotalRustLevel = sumbodypartproperties()(this, &bodypart::GetMainMaterialRustLevel); + + if(!TotalRustLevel) + return humanoid::AddAdjective(String, Articled); + else + { + if(Articled) + String << "a "; + + if(TotalRustLevel <= GetBodyParts()) + String << "slightly rusted "; + else if(TotalRustLevel <= GetBodyParts() << 1) + String << "rusted "; + else + String << "very rusted "; + + String << GetAdjective() << ' '; + return true; + } +} + +void oree::Bite(character* Enemy, v2 HitPos, int, truth) +{ + if(IsPlayer()) + ADD_MESSAGE("You vomit acidous blood at %s.", Enemy->CHAR_DESCRIPTION(DEFINITE)); + else if(Enemy->IsPlayer() || CanBeSeenByPlayer() || Enemy->CanBeSeenByPlayer()) + ADD_MESSAGE("%s vomits acidous blood at %s.", CHAR_DESCRIPTION(DEFINITE), Enemy->CHAR_DESCRIPTION(DEFINITE)); + + Vomit(HitPos, 500 + RAND() % 500, false); +} + +truth vampire::SpecialBiteEffect(character* Char, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + if(!BlockedByArmour && !(RAND() % 2) && Char->IsWarm()) + { + if(Char->IsHumanoid()) + Char->BeginTemporaryState(VAMPIRISM, 1500 + RAND_N(2000)); // Randomly instigate vampirism + if(Char->IsPlayer() || IsPlayer() || Char->CanBeSeenByPlayer() || CanBeSeenByPlayer()) + ADD_MESSAGE("%s drains some precious lifeblood from %s!", CHAR_DESCRIPTION(DEFINITE), Char->CHAR_DESCRIPTION(DEFINITE)); + + return Char->ReceiveBodyPartDamage(this, 10 + (RAND() % 11), DRAIN, BodyPartIndex, Direction); + } + else + return false; +} + +truth humanoid::SpecialBiteEffect(character* Char, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + if(StateIsActivated(VAMPIRISM)) + { + if(!BlockedByArmour && Char->IsWarm() && !(RAND() % 2)) + { + if(Char->IsHumanoid()) + Char->BeginTemporaryState(VAMPIRISM, 1000 + RAND_N(500)); // Randomly instigate vampirism + if(Char->IsPlayer() || IsPlayer() || Char->CanBeSeenByPlayer() || CanBeSeenByPlayer()) + ADD_MESSAGE("%s drains some precious lifeblood from %s!", CHAR_DESCRIPTION(DEFINITE), Char->CHAR_DESCRIPTION(DEFINITE)); + + return Char->ReceiveBodyPartDamage(this, 8 + (RAND() % 9), DRAIN, BodyPartIndex, Direction); + } + else + return false; + } + else + return false; +} + +void sumowrestler::GetAICommand() +{ + EditNP(-25); + + SeekLeader(GetLeader()); + + if(CheckForEnemies(true, true, true)) + return; + + if(CheckForUsefulItemsOnGround()) + return; + + if(CheckForFood(4)) + return; + + if(FollowLeader(GetLeader())) + return; + + if(CheckForDoors()) + return; + + if(MoveTowardsHomePos()) + return; + + EditAP(-1000); +} + +void sumowrestler::BeTalkedTo() +{ + static long Said; + + if(GetRelation(PLAYER) == HOSTILE) + ProcessAndAddMessage(GetHostileReplies()[RandomizeReply(Said, GetHostileReplies().Size)]); + else if(!game::TweraifIsFree()) + ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, 6)]); + else + ProcessAndAddMessage(GetFriendlyReplies()[6 + RandomizeReply(Said, 3)]); +} + +character* tourist::GetLeader() const +{ + character* Guide = game::GetTeam(TOURIST_GUIDE_TEAM)->GetLeader(); + return Guide && Guide->GetRelation(this) != HOSTILE ? Guide : GetTeam()->GetLeader(); +} + +/* Forestmen are invented here */ +character* forestman::GetLeader() const +{ + character* Guide = game::GetTeam(FORESTMAN_TEAM)->GetLeader(); + return Guide && Guide->GetRelation(this) != HOSTILE ? Guide : GetTeam()->GetLeader(); +} + +void elder::GetAICommand() +{ + /* Select a place to guide the tourists to */ + + if(!(RAND() % 10)) + SetGoingTo(GetLevel()->GetRandomSquare()); + + humanoid::GetAICommand(); +} + +void tourist::GetAICommand() +{ + if(game::IsSumoWrestling() && !(RAND() % 10)) + { + if(GetConfig() == HUSBAND) + { + if(RAND() & 1) + ADD_MESSAGE("%s shouts: \"Show that skinny wimp what you've got, Huang!\"", CHAR_DESCRIPTION(DEFINITE)); + else + ADD_MESSAGE("%s screams: \"Go for it, Huang!\"", CHAR_DESCRIPTION(DEFINITE)); + } + else if(GetConfig() == WIFE) + { + if(RAND() & 1) + ADD_MESSAGE("%s encourages you: \"Knock him out, %s!\"", CHAR_DESCRIPTION(DEFINITE), game::GetPlayerName().CStr()); + else + ADD_MESSAGE("%s cheers you: \"A handsome guy like you can't lose to that banana ball!\"", CHAR_DESCRIPTION(DEFINITE)); + } + else if(GetConfig() == CHILD) + { + if(RAND() & 1) + ADD_MESSAGE("%s yells: \"More blood on the ring!!!\"", CHAR_DESCRIPTION(DEFINITE)); + else + ADD_MESSAGE("%s cries: \"Kill him, Pong!!!\"", CHAR_DESCRIPTION(DEFINITE)); + } + } + + humanoid::GetAICommand(); +} + +void imperialist::BeTalkedTo() +{ + decosadshirt* Shirt = static_cast(PLAYER->SearchForItem(this, &item::IsDecosAdShirt)); + + if(Shirt) + { + ulong Reward = Shirt->GetEquippedTicks() / 500; + + if(Reward) + { + ADD_MESSAGE("%s smiles. \"I see you have advertised our company diligently. Here's %ldgp as a token of my gratitude.\"", CHAR_NAME(DEFINITE), Reward); + PLAYER->EditMoney(Reward); + Shirt->SetEquippedTicks(0); + return; + } + else if(!(RAND() % 5)) + { + ADD_MESSAGE("\"Come back when you've worn the shirt for some time and I'll reward you generously!\""); + return; + } + } + + static long Said; + + if(GetRelation(PLAYER) == HOSTILE) + ProcessAndAddMessage(GetHostileReplies()[RandomizeReply(Said, GetHostileReplies().Size)]); + else if(!game::PlayerIsSumoChampion()) + ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, GetFriendlyReplies().Size)]); + else + ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, GetFriendlyReplies().Size - 1)]); +} + +void forestman::GetAICommand() +{ + humanoid::GetAICommand(); +} +/* End of the forestmen */ + +void regii::BeTalkedTo() +{ + static long Said; + + if(GetRelation(PLAYER) == HOSTILE) + ProcessAndAddMessage(GetHostileReplies()[RandomizeReply(Said, GetHostileReplies().Size)]); + + if(!game::GetRegiiTalkState()) + { + game::TextScreen(CONST_S("\"I am Regii, of the Unite Tweraif Forest Army, or UTFA for short. And you are?\"\n\n" + "You tell Regii your name and explain that you have escaped from New Attnam.\n")); + game::TextScreen(CONST_S("\"Do you really hate the imperialists?\"\n")); + game::TextScreen(CONST_S("\"A lot\" You reply.\n\n")); + game::TextScreen(CONST_S("\"Alright then. From now on, you shall be called \n'") + PLAYER->GetPanelName().CStr() + CONST_S("' \n that is called '") + PLAYER->GetPanelName().CStr() + CONST_S("'.\"\n" + "Regii saluts you after the fashion of the UTFA.\n\n\n" + "\"Now, listen up! We have an upcoming mission, we will be launching a \n" + "reconaissance to free some prisoners held in the forest prison.\n" + "The prison is operated by goblins and guarded by manifold beasts and \n" + "orcs. They have no political reason to operate the prison, other than \n" + "the fact that it spins straw into gold.\n" + "We have reason to believe that the imperialists pay the goblins to \n" + "imprison our brothers and sisters of the formerly independant Tweraif.\"")); + + game::TextScreen(CONST_S("\"Now the mission is to free the prisoners, but, our intelligence suspects \n" + "that important constitutional documents are held within the prison also. \n" + "If it is within our power to do so, these should be retrieved and safeguarded.\n" + "The assualt on the prison will be achieved by means of a haphazard underground \n" + "tunnel whose entry point is located beyond the river. The tunnel terminates \n" + "more or less underneath the prison, so that is where the assault will begin. \n" + "Free the prisoners, arm them with weapons or otherwise, and fight your way out\n" + "of the vicinity of the prison. Members will regroup here after the mission \n" + "for a de-brief.\"")); + GetArea()->SendNewDrawRequest(); + game::SetRegiiTalkState(1); + return; + } + else + { + //ADD_MESSAGE("\"How goes your preparation for the mission brother ") + PLAYER->GetPanelName().CStr() + CONST_S("?\""); + ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, GetFriendlyReplies().Size)]); + } +/* if(GetRelation(PLAYER) == HOSTILE) + ProcessAndAddMessage(GetHostileReplies()[RandomizeReply(Said, GetHostileReplies().Size)]); + else if(!game::PlayerIsSumoChampion()) + ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, GetFriendlyReplies().Size)]); + else + ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, GetFriendlyReplies().Size - 1)]);*/ +} + +character* humanoid::CreateZombie() const +{ + if(!TorsoIsAlive()) + return 0; + + humanoid* Zombie = zombie::Spawn(); + int c; + + for(c = 0; c < BodyParts; ++c) + { + bodypart* BodyPart = GetBodyPart(c); + + if(!BodyPart) + { + BodyPart = SearchForOriginalBodyPart(c); + + if(BodyPart) + { + BodyPart->RemoveFromSlot(); + BodyPart->SendToHell(); + } + } + + if(BodyPart) + { + bodypart* ZombieBodyPart = Zombie->GetBodyPart(c); + + if(!ZombieBodyPart) + ZombieBodyPart = Zombie->CreateBodyPart(c); + + material* M = BodyPart->GetMainMaterial()->Duplicate(); + M->SetSpoilCounter(2000 + RAND() % 1000); + M->SetSkinColor(Zombie->GetSkinColor()); + ZombieBodyPart->ChangeMainMaterial(M); + ZombieBodyPart->CopyAttributes(BodyPart); + } + else if(!Zombie->BodyPartIsVital(c)) + { + bodypart* ZombieBodyPart = Zombie->GetBodyPart(c); + + if(ZombieBodyPart) + { + ZombieBodyPart->RemoveFromSlot(); + ZombieBodyPart->SendToHell(); + } + } + } + + for(c = 0; c < Zombie->AllowedWeaponSkillCategories; ++c) + Zombie->CWeaponSkill[c] = CWeaponSkill[c]; + + Zombie->SWeaponSkill.resize(SWeaponSkill.size()); + std::list::iterator i1 = Zombie->SWeaponSkill.begin(); + std::list::const_iterator i2 = SWeaponSkill.begin(); + + for(; i2 != SWeaponSkill.end(); ++i1, ++i2) + *i1 = new sweaponskill(**i2); + + memcpy(Zombie->BaseExperience, + BaseExperience, + BASE_ATTRIBUTES * sizeof(*BaseExperience)); + Zombie->CalculateAll(); + Zombie->RestoreHP(); + Zombie->RestoreStamina(); + static_cast(Zombie)->SetDescription(GetZombieDescription()); + Zombie->GenerationDanger = GenerationDanger; + return Zombie; +} + +void zombie::AddPostFix(festring& String, int Case) const +{ + if(!Description.IsEmpty()) + String << Description; + else + humanoid::AddPostFix(String, Case); +} + +void zombie::Save(outputfile& SaveFile) const +{ + humanoid::Save(SaveFile); + SaveFile << Description; +} + +void zombie::Load(inputfile& SaveFile) +{ + humanoid::Load(SaveFile); + SaveFile >> Description; +} + +int darkknight::ModifyBodyPartHitPreference(int I, int Modifier) const +{ + return IsLimbIndex(I) ? Modifier << 1 : Modifier; +} + +int darkknight::ModifyBodyPartToHitChance(int I, int Chance) const +{ + return IsLimbIndex(I) ? Chance << 1 : Chance; +} + +void darkknight::SpecialBodyPartSeverReaction() +{ + if(!IsPlayer()) + { + if(IsUsingHead()) + ADD_MESSAGE("%s screams: \"I'll do you for that! I'll bite your legs off!\"", CHAR_DESCRIPTION(DEFINITE)); + else if(!(RAND() % 5)) + switch(RAND() % 3) + { + case 0: + ADD_MESSAGE("%s states calmly: \"'Tis but a scratch.\"", CHAR_DESCRIPTION(DEFINITE)); break; + case 1: + ADD_MESSAGE("%s states calmly: \"Just a flesh wound.\"", CHAR_DESCRIPTION(DEFINITE)); break; + case 2: + ADD_MESSAGE("%s shouts: \"I'm invincible!\"", CHAR_DESCRIPTION(DEFINITE)); break; + } + } +} + +void humanoid::LeprosyHandler() +{ + if(IsImmuneToLeprosy()) + { + return; + } + + if(!RAND_N(1000 * GetAttribute(ENDURANCE))) + DropRandomNonVitalBodypart(); + + if(!game::IsInWilderness()) + { + for(int d = 0; d < GetNeighbourSquares(); ++d) + { + lsquare* Square = GetNeighbourLSquare(d); + + if(Square && Square->GetCharacter()) + Square->GetCharacter()->TryToInfectWithLeprosy(this); + } + } + + character::LeprosyHandler(); +} + +void humanoid::DropRandomNonVitalBodypart() +{ + int BodyPartIndexToDrop = GetRandomNonVitalBodyPart(); + + if(BodyPartIndexToDrop != NONE_INDEX) + DropBodyPart(BodyPartIndexToDrop); +} + +void humanoid::DropBodyPart(int Index) +{ + if(!GetBodyPart(Index)->IsAlive()) + return; + + festring NameOfDropped = GetBodyPart(Index)->GetBodyPartName(); + item* Dropped = SevereBodyPart(Index); + + if(Dropped) + { + GetStack()->AddItem(Dropped); + Dropped->DropEquipment(); + + if(IsPlayer()) + { + ADD_MESSAGE("You feel very ill. Your %s snaps off.", NameOfDropped.CStr()); + game::AskForKeyPress(CONST_S("Bodypart severed! [press any key to continue]")); + DeActivateVoluntaryAction(); + } + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s's %s snaps off.", CHAR_NAME(DEFINITE), NameOfDropped.CStr()); + } + else + { + if(IsPlayer()) + { + ADD_MESSAGE("You feel very ill. Your %s disappears.", NameOfDropped.CStr()); + game::AskForKeyPress(CONST_S("Bodypart destroyed! [press any key to continue]")); + DeActivateVoluntaryAction(); + } + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s's %s disappears.", CHAR_NAME(DEFINITE), NameOfDropped.CStr()); + } +} + +void humanoid::DuplicateEquipment(character* Receiver, ulong Flags) +{ + character::DuplicateEquipment(Receiver, Flags); + EnsureCurrentSWeaponSkillIsCorrect(CurrentRightSWeaponSkill, GetRightWielded()); + EnsureCurrentSWeaponSkillIsCorrect(CurrentLeftSWeaponSkill, GetLeftWielded()); +} + +truth character::CanHear() const +{ + return DataBase->CanHear && HasHead(); +} + +col16 veterankamikazedwarf::GetTorsoMainColor() const +{ + return GetMasterGod()->GetEliteColor(); +} + +col16 veterankamikazedwarf::GetGauntletColor() const +{ + return GetMasterGod()->GetEliteColor(); +} + +col16 veterankamikazedwarf::GetLegMainColor() const +{ + return GetMasterGod()->GetEliteColor(); +} + +col16 grenadierdwarf::GetTorsoMainColor() const +{ + return GetMasterGod()->GetEliteColor(); +} + +col16 grenadierdwarf::GetGauntletColor() const +{ + return GetMasterGod()->GetEliteColor(); +} + +col16 grenadierdwarf::GetLegMainColor() const +{ + return GetMasterGod()->GetEliteColor(); +} + +col16 archangel::GetTorsoMainColor() const +{ + return GetMasterGod()->GetEliteColor(); +} + +col16 archangel::GetArmMainColor() const +{ + return GetMasterGod()->GetEliteColor(); +} + +void archangel::CreateInitialEquipment(int SpecialFlags) +{ + humanoid::CreateInitialEquipment(SpecialFlags); + GetStack()->AddItem(holybook::Spawn(GetConfig(), SpecialFlags)); + armor* Equipment; + meleeweapon* Weapon; + + switch(GetMasterGod()->GetBasicAlignment()) + { + case GOOD: + Weapon = flamingsword::Spawn(LONG_SWORD, SpecialFlags|NO_MATERIALS); + Weapon->InitMaterials(MAKE_MATERIAL(DIAMOND), MAKE_MATERIAL(ADAMANT), !(SpecialFlags & NO_PIC_UPDATE)); + Weapon->SetEnchantment(4); + SetRightWielded(Weapon); + Equipment = shield::Spawn(0, SpecialFlags|NO_MATERIALS); + Equipment->InitMaterials(MAKE_MATERIAL(DIAMOND), !(SpecialFlags & NO_PIC_UPDATE)); + Equipment->SetEnchantment(4); + SetLeftWielded(Equipment); + GetCWeaponSkill(LARGE_SWORDS)->AddHit(200000); + GetCWeaponSkill(SHIELDS)->AddHit(500000); + GetCurrentRightSWeaponSkill()->AddHit(200000); + GetCurrentLeftSWeaponSkill()->AddHit(200000); + GetRightArm()->SetDexterity(70); + GetLeftArm()->SetDexterity(70); + break; + case NEUTRAL: + Weapon = meleeweapon::Spawn(WAR_HAMMER, SpecialFlags|NO_MATERIALS); + Weapon->InitMaterials(MAKE_MATERIAL(SAPPHIRE), MAKE_MATERIAL(OCTIRON), !(SpecialFlags & NO_PIC_UPDATE)); + Weapon->SetEnchantment(4); + SetRightWielded(Weapon); + GetCWeaponSkill(BLUNT_WEAPONS)->AddHit(500000); + GetCurrentRightSWeaponSkill()->AddHit(200000); + SetEndurance(70); + break; + case EVIL: + Weapon = meleeweapon::Spawn(HALBERD, SpecialFlags|NO_MATERIALS); + Weapon->InitMaterials(MAKE_MATERIAL(RUBY), MAKE_MATERIAL(OCTIRON), !(SpecialFlags & NO_PIC_UPDATE)); + Weapon->SetEnchantment(4); + SetLeftWielded(Weapon); + GetCWeaponSkill(POLE_ARMS)->AddHit(500000); + GetCurrentLeftSWeaponSkill()->AddHit(500000); + GetRightArm()->SetStrength(70); + GetLeftArm()->SetStrength(70); + break; + case TOPPLED: + Weapon = eptyron::Spawn(); + Weapon->InitMaterials(MAKE_MATERIAL(EMERALD), MAKE_MATERIAL(OCTIRON), !(SpecialFlags & NO_PIC_UPDATE)); + Weapon->SetEnchantment(4); + SetRightWielded(Weapon); + Equipment = shield::Spawn(0, SpecialFlags|NO_MATERIALS); + Equipment->InitMaterials(MAKE_MATERIAL(EMERALD), !(SpecialFlags & NO_PIC_UPDATE)); + Equipment->SetEnchantment(4); + SetLeftWielded(Equipment); + GetCWeaponSkill(AXES)->AddHit(200000); + GetCWeaponSkill(SHIELDS)->AddHit(500000); + GetCurrentRightSWeaponSkill()->AddHit(200000); + GetCurrentLeftSWeaponSkill()->AddHit(200000); + GetRightArm()->SetDexterity(70); + GetLeftArm()->SetDexterity(70); + break; + } +} + +void insudo::CreateInitialEquipment(int SpecialFlags) +{ + humanoid::CreateInitialEquipment(SpecialFlags); + armor* Equipment; + meleeweapon* Weapon; + Weapon = eptyron::Spawn(); + Weapon->InitMaterials(MAKE_MATERIAL(EMERALD), MAKE_MATERIAL(OCTIRON), !(SpecialFlags & NO_PIC_UPDATE)); + Weapon->SetEnchantment(4); + SetRightWielded(Weapon); + Equipment = shield::Spawn(0, SpecialFlags|NO_MATERIALS); + Equipment->InitMaterials(MAKE_MATERIAL(EMERALD), !(SpecialFlags & NO_PIC_UPDATE)); + Equipment->SetEnchantment(4); + SetLeftWielded(Equipment); + GetCWeaponSkill(AXES)->AddHit(200000); + GetCWeaponSkill(SHIELDS)->AddHit(500000); + GetCurrentRightSWeaponSkill()->AddHit(200000); + GetCurrentLeftSWeaponSkill()->AddHit(200000); + GetRightArm()->SetDexterity(70); + GetLeftArm()->SetDexterity(70); +} + +void zombie::PostConstruct() +{ + if(!RAND_N(3)) + GainIntrinsic(LEPROSY); +} + +void orc::PostConstruct() +{ + if(!RAND_N(25)) + GainIntrinsic(LEPROSY); +} + +void shaman::PostConstruct() +{ + if(!RAND_N(25)) + GainIntrinsic(LEPROSY); +} + +truth mistress::AllowEquipment(citem* Item, int EquipmentIndex) const +{ + return ((EquipmentIndex != RIGHT_WIELDED_INDEX + && EquipmentIndex != LEFT_WIELDED_INDEX) + || Item->IsWhip()); +} + +int humanoid::GetAttributeAverage() const +{ + return GetSumOfAttributes() / 9; +} + +void golem::CreateCorpse(lsquare* Square) +{ + material* Material = GetTorso()->GetMainMaterial(); + + if(Material->IsSolid()) + Square->AddItem(Material->CreateNaturalForm(ItemVolume)); + + SendToHell(); +} + +golem::golem() +{ + if(!game::IsLoading()) + ItemVolume = 50 + RAND_N(100); +} + +void golem::Save(outputfile& SaveFile) const +{ + humanoid::Save(SaveFile); + SaveFile << ItemVolume; +} + +void golem::Load(inputfile& SaveFile) +{ + humanoid::Load(SaveFile); + SaveFile >> ItemVolume; +} + +truth humanoid::CanVomit() const +{ + return HasHead() && character::CanVomit(); +} + +truth humanoid::CheckApply() const +{ + if(!character::CheckApply()) + return false; + + if(!HasAUsableArm()) + { + ADD_MESSAGE("You need a usable arm to apply."); + return false; + } + + return true; +} + +int darkmage::GetSpellAPCost() const +{ + switch(GetConfig()) + { + case APPRENTICE: return 4000; + case BATTLE_MAGE: return 2000; + case ELDER: return 1000; + case ARCH_MAGE: return 500; + } + + return 4000; +} + +int necromancer::GetSpellAPCost() const +{ + switch(GetConfig()) + { + case APPRENTICE_NECROMANCER: return 2000; + case MASTER_NECROMANCER: return 1000; + } + + return 4000; +} + +int shaman::GetSpellAPCost() const +{ + //switch(GetConfig()) + //{ + // case APPRENTICE_NECROMANCER: return 2000; + // case MASTER_NECROMANCER: return 1000; + //} + + return 2000; +} + +int warlock::GetSpellAPCost() const +{ + //switch(GetConfig()) + //{ + // case APPRENTICE_NECROMANCER: return 2000; + // case MASTER_NECROMANCER: return 1000; + //} + + return 2000; +} + +int uldra::GetSpellAPCost() const +{ + //switch(GetConfig()) + //{ + // case APPRENTICE_NECROMANCER: return 2000; + // case MASTER_NECROMANCER: return 1000; + //} + + return 4000; +} + +int kabouter::GetSpellAPCost() const +{ + //switch(GetConfig()) + //{ + // case APPRENTICE_NECROMANCER: return 2000; + // case MASTER_NECROMANCER: return 1000; + //} + + return 4000; +} + +int alchemist::GetSpellAPCost() const +{ + //switch(GetConfig()) + //{ + // case APPRENTICE_NECROMANCER: return 2000; + // case MASTER_NECROMANCER: return 1000; + //} + + return 2000; +} + +/* Horrible repeating. Sorry */ + +void tailor::BeTalkedTo() +{ + if(GetRelation(PLAYER) == HOSTILE) + { + ADD_MESSAGE("\"You talkin' to me? You talkin' to me? You talkin' to me? Then who the hell else are you talkin' to? You talkin' to me? Well I'm the only one here. Who do you think you're talking to? Oh yeah? Huh? Ok.\""); + return; + } + + if(PLAYER->PossessesItem(&item::IsFixableByTailor)) + { + item* Item = PLAYER->SelectFromPossessions(CONST_S("\"What do you want me to fix?\""), &item::IsFixableByTailor); + + if(!Item) + return; + + if(!(Item->GetMainMaterial()->GetCategoryFlags() & CAN_BE_TAILORED)) + { + ADD_MESSAGE("\"I can't work on %s.\"", Item->GetMainMaterial()->GetName(false, false).CStr()); + return; + } + + /** update messages */ + + long FixPrice = Item->GetFixPrice(); + + if(PLAYER->GetMoney() < FixPrice) + { + ADD_MESSAGE("\"Getting that fixed costs you %ld gold pieces. Get the money and we'll talk.\"", FixPrice); + return; + } + + ADD_MESSAGE("\"I can fix your %s, but it'll cost you %ld gold pieces.\"", Item->CHAR_NAME(UNARTICLED), FixPrice); + + if(game::TruthQuestion(CONST_S("Do you accept this deal? [y/N]"))) + { + Item->Fix(); + PLAYER->EditMoney(-FixPrice); + ADD_MESSAGE("%s fixes %s in no time.", CHAR_NAME(DEFINITE), Item->CHAR_NAME(DEFINITE)); + } + } + else + ADD_MESSAGE("\"Come back when you have some weapons or armor I can fix.\""); +} + +void veterankamikazedwarf::PostConstruct() +{ + kamikazedwarf::PostConstruct(); + ivantime Time; + game::GetTime(Time); + int Modifier = Time.Day - KAMIKAZE_INVISIBILITY_DAY_MIN; + + if(Time.Day >= KAMIKAZE_INVISIBILITY_DAY_MAX + || (Modifier > 0 + && RAND_N(KAMIKAZE_INVISIBILITY_DAY_MAX - KAMIKAZE_INVISIBILITY_DAY_MIN) < Modifier)) + GainIntrinsic(INVISIBLE); +} + +void grenadierdwarf::PostConstruct() +{ + kamikazedwarf::PostConstruct(); + ivantime Time; + game::GetTime(Time); + int Modifier = Time.Day - KAMIKAZE_INVISIBILITY_DAY_MIN; + + if(Time.Day >= KAMIKAZE_INVISIBILITY_DAY_MAX + || (Modifier > 0 + && RAND_N(KAMIKAZE_INVISIBILITY_DAY_MAX - KAMIKAZE_INVISIBILITY_DAY_MIN) < Modifier)) + GainIntrinsic(INVISIBLE); +} + +truth humanoid::IsTransparent() const +{ + return character::IsTransparent() || !(GetRightLeg() || GetLeftLeg()); +} + +void humanoid::ModifySituationDanger(double& Danger) const +{ + character::ModifySituationDanger(Danger); + + switch(GetUsableArms()) + { + case 0: Danger *= 10; + case 1: Danger *= 2; + } + + switch(GetUsableLegs()) + { + case 0: Danger *= 10; + case 1: Danger *= 2; + } +} + +void oree::GetAICommand() +{ + if(!RAND_N(50)) + CallForMonsters(); + + humanoid::GetAICommand(); +} + +void oree::CallForMonsters() +{ + if(GetDungeon()->GetIndex() != ELPURI_CAVE || GetLevel()->GetIndex() != OREE_LAIR) + return; + + character* ToBeCalled = 0; + + switch(RAND_N(6)) + { + case 0: + ToBeCalled = darkknight::Spawn(ELITE); + break; + case 1: + ToBeCalled = frog::Spawn(RAND_2 ? GREATER_DARK : GIANT_DARK); + break; + case 2: + ToBeCalled = frog::Spawn(DARK); + break; + case 3: + ToBeCalled = darkmage::Spawn(RAND_2 ? APPRENTICE : BATTLE_MAGE); + break; + case 4: + ToBeCalled = darkmage::Spawn(RAND_2 ? APPRENTICE : ELDER); + break; + case 5: + ToBeCalled = necromancer::Spawn(RAND_2 ? APPRENTICE_NECROMANCER : MASTER_NECROMANCER); + break; + } + + v2 TryToCreate; + + for(int c = 0; c < 100; ++c) + { + TryToCreate = game::GetMonsterPortal()->GetPos() + game::GetMoveVector(RAND() % DIRECTION_COMMAND_KEYS); + + if(GetArea()->IsValidPos(TryToCreate) && ToBeCalled->CanMoveOn(GetNearLSquare(TryToCreate)) && ToBeCalled->IsFreeForMe(GetNearLSquare(TryToCreate))) + { + ToBeCalled->SetTeam(game::GetTeam(MONSTER_TEAM)); + ToBeCalled->PutTo(TryToCreate); + return; + } + } + + delete ToBeCalled; +} + +int humanoid::RandomizeTryToUnStickBodyPart(ulong PossibleBodyParts) const +{ + int Possible = 0, PossibleArray[3]; + + if(RightArmIsUsable() && 1 << RIGHT_ARM_INDEX & PossibleBodyParts) + PossibleArray[Possible++] = RIGHT_ARM_INDEX; + + if(LeftArmIsUsable() && 1 << LEFT_ARM_INDEX & PossibleBodyParts) + PossibleArray[Possible++] = LEFT_ARM_INDEX; + + if(Possible) + return PossibleArray[RAND_N(Possible)]; + + if(RightLegIsUsable() && 1 << RIGHT_LEG_INDEX & PossibleBodyParts) + PossibleArray[Possible++] = RIGHT_LEG_INDEX; + + if(LeftLegIsUsable() && 1 << LEFT_LEG_INDEX & PossibleBodyParts) + PossibleArray[Possible++] = LEFT_LEG_INDEX; + + if(Possible) + return PossibleArray[RAND_N(Possible)]; + + if(GetHead() && 1 << HEAD_INDEX & PossibleBodyParts) + return HEAD_INDEX; + + if(GetGroin() && 1 << GROIN_INDEX & PossibleBodyParts) + PossibleArray[Possible++] = GROIN_INDEX; + + if(1 << TORSO_INDEX & PossibleBodyParts) + PossibleArray[Possible++] = TORSO_INDEX; + + return Possible ? PossibleArray[RAND_N(Possible)] : NONE_INDEX; +} + +truth humanoid::HasAUsableArm() const +{ + arm* R = GetRightArm(), * L = GetLeftArm(); + return (R && R->IsUsable()) || (L && L->IsUsable()); +} + +truth humanoid::HasAUsableLeg() const +{ + leg* R = GetRightLeg(), * L = GetLeftLeg(); + return (R && R->IsUsable()) || (L && L->IsUsable()); +} + +truth humanoid::HasTwoUsableLegs() const +{ + leg* R = GetRightLeg(), * L = GetLeftLeg(); + return R && R->IsUsable() && L && L->IsUsable(); +} + +truth humanoid::CanAttackWithAnArm() const +{ + arm* R = GetRightArm(); + + if(R && R->GetDamage()) + return true; + + arm* L = GetLeftArm(); + return L && L->GetDamage(); +} + +truth humanoid::RightArmIsUsable() const +{ + arm* A = GetRightArm(); + return A && A->IsUsable(); +} + +truth humanoid::LeftArmIsUsable() const +{ + arm* A = GetLeftArm(); + return A && A->IsUsable(); +} + +truth humanoid::RightLegIsUsable() const +{ + leg* L = GetRightLeg(); + return L && L->IsUsable(); +} + +truth humanoid::LeftLegIsUsable() const +{ + leg* L = GetLeftLeg(); + return L && L->IsUsable(); +} + +truth humanoid::AllowUnconsciousness() const +{ + return (DataBase->AllowUnconsciousness && TorsoIsAlive() + && BodyPartIsVital(HEAD_INDEX)); +} + +truth humanoid::CanChokeOnWeb(web* Web) const +{ + return CanChoke() && Web->IsStuckToBodyPart(HEAD_INDEX); +} + +truth humanoid::BrainsHurt() const +{ + head* Head = GetHead(); + return !Head || Head->IsBadlyHurt(); +} + +void playerkind::PostConstruct() +{ + int R = 0, G = 0, B = 0; + + switch(RAND_N(4)) + { + case 0: R = 195; G = 165; B = 40; break; + case 1: R = 130; G = 30; B = 0; break; + case 2: R = 30; G = 30; B = 15; break; + case 3: R = 50; G = 30; B = 5; break; + } + + HairColor = MakeRGB16(R + RAND_N(41), G + RAND_N(41), B + RAND_N(41)); + + switch(RAND_N(4)) + { + case 0: R = 25; G = 0; B = 70; break; + case 1: R = 5; G = 0; B = 50; break; + case 2: R = 10; G = 10; B = 10; break; + case 3: R = 60; G = 20; B = 0; break; + } + + EyeColor = MakeRGB16(R + RAND_N(41), G + RAND_N(41), B + RAND_N(41)); + Talent = RAND_N(TALENTS); + Weakness = RAND_N(TALENTS); +} + +v2 playerkind::GetHeadBitmapPos() const +{ + int Sum = GetAttribute(INTELLIGENCE, false) + GetAttribute(WISDOM, false); + + if(Sum >= 60) + return v2(96, 480); + else if(Sum >= 40) + return v2(96, 464); + else + return v2(96, 416); +} + +v2 playerkind::GetRightArmBitmapPos() const +{ + if(GetRightArm()->GetAttribute(ARM_STRENGTH, false) >= 20) + return v2(64, 448); + else + return v2(64, 416); +} + +v2 playerkind::GetLeftArmBitmapPos() const +{ + if(GetLeftArm()->GetAttribute(ARM_STRENGTH, false) >= 20) + return v2(64, 448); + else + return v2(64, 416); +} + +int playerkind::GetNaturalSparkleFlags() const +{ + return GetAttribute(CHARISMA) >= 30 ? SKIN_COLOR : 0; +} + +void slave::PostConstruct() +{ + Talent = TALENT_STRONG; + Weakness = TALENT_CLEVER; +} + +cint TalentOfAttribute[ATTRIBUTES] = { TALENT_HEALTHY, TALENT_FAST_N_ACCURATE, TALENT_CLEVER, TALENT_CLEVER, TALENT_CLEVER, TALENT_CLEVER, TALENT_CLEVER, TALENT_STRONG, TALENT_STRONG, TALENT_FAST_N_ACCURATE, TALENT_FAST_N_ACCURATE }; +const double TalentBonusOfAttribute[ATTRIBUTES] = { 1.1, 1.25, 1.25, 1.25, 1.25, 1.25, 1.25, 1.25, 1.25, 1.25, 1.25 }; + +double playerkind::GetNaturalExperience(int Identifier) const +{ + double NE = DataBase->NaturalExperience[Identifier]; + + if(Talent == TalentOfAttribute[Identifier]) + NE *= TalentBonusOfAttribute[Identifier]; + + if(Weakness == TalentOfAttribute[Identifier]) + NE /= TalentBonusOfAttribute[Identifier]; + + return NE; +} + +cchar* humanoid::GetRunDescriptionLine(int I) const +{ + if(!GetRunDescriptionLineOne().IsEmpty()) + return !I ? GetRunDescriptionLineOne().CStr() : GetRunDescriptionLineTwo().CStr(); + + if(IsFlying()) + return !I ? "Flying" : "very fast"; + + if(IsSwimming()) + { + if(!GetRightArm() && !GetLeftArm() && !GetRightLeg() && !GetLeftLeg()) + return !I ? "Floating" : "ahead fast"; + else + return !I ? "Swimming" : "very fast"; + } + + if(!GetRightLeg() && !GetLeftLeg()) + return !I ? "Rolling" : "very fast"; + + if(!GetRightLeg() || !GetLeftLeg()) + return !I ? "Hopping" : "very fast"; + + return !I ? "Running" : ""; +} + +cchar* humanoid::GetNormalDeathMessage() const +{ + if(BodyPartIsVital(HEAD_INDEX) && (!GetHead() || GetHead()->GetHP() <= 0)) + return "beheaded @k"; + else if(BodyPartIsVital(GROIN_INDEX) && (!GetGroin() || GetGroin()->GetHP() <= 0)) + return "killed @bkp dirty attack below the belt"; + else + return "killed @k"; +} + +void kamikazedwarf::SingRandomSong() +{ + festring Song; + festring God = GetMasterGod()->GetName(); + festring Bodypart; + + switch(RAND_N(9)) + { + case 0: + + switch(RAND_N(3)) + { + case 0: + Bodypart = "palm"; + break; + + case 1: + Bodypart = "forehead"; + break; + + default: + Bodypart = "tongue"; + break; + } + Song = festring("On the ") + Bodypart + festring(" of ") + God + " everybody fears everything"; + break; + case 1: + { + festring Title = GetMasterGod()->GetSex() == MALE ? "King" : "Queen"; + Song = festring("Joy to the world, ") + God + + " is come! Let all above Valpurus receive her " + Title; + break; + } + case 2: + Song = festring("Hark the herald angels sing. Glory to ") + God + "!"; + break; + case 3: + Song = festring("O ") + God + + ", You are so big, So absolutely huge, Gosh, " + "we're all really impressed down here, I can tell You."; + break; + case 4: + Song = festring("Forgive us, O ") + God + + " for this, our dreadful toadying and barefaced flattery"; + break; + case 5: + Song = festring("But you, ") + God + + ", are so strong and, well, just so super fantastic. Amen."; + break; + case 6: + Song = festring("O ") + God + ", please don't burn us"; + break; + case 7: + Song = festring("O ") + God + ", please don't grill or toast your flock"; + break; + case 8: + Song = festring("O ") + God + ", please don't simmer us in stock"; + break; + } + + EditAP(-1000); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s sings: \"%s\"", + CHAR_DESCRIPTION(DEFINITE), Song.CStr()); + else + ADD_MESSAGE("You hear someone sing: \"%s\"", Song.CStr()); +} + +void imperialist::DisplayStethoscopeInfo(character*) const +{ + ADD_MESSAGE("You hear coins clinking inside."); +} + +void humanoid::ApplySpecialAttributeBonuses() +{ + if(GetHead()) + { + AttributeBonus[CHARISMA] -= GetHead()-> + CalculateScarAttributePenalty(GetAttribute(CHARISMA, false)); + } + else + AttributeBonus[CHARISMA] -= GetAttribute(CHARISMA, false) - 1; +} + +void siren::GetAICommand() +{ + if(TryToSing()) + return; + + humanoid::GetAICommand(); +} + +truth siren::TryToSing() +{ + truth Success=false; + for(int d = 0; d < GetNeighbourSquares(); ++d) + { + lsquare* Square = GetNeighbourLSquare(d); + + if(Square && Square->GetCharacter()) + { + Success = true; + Square->GetCharacter()->ReceiveSirenSong(this); + } + } + if(Success) + EditAP(-2000); + + return Success; +} + +truth humanoid::MindWormCanPenetrateSkull(mindworm* Worm) const +{ + if(GetHelmet()) + { + if(RAND_N(102) > GetHelmet()->GetCoverPercentile()) + { + return RAND_2; + } + } + + return RAND_2; +} + +truth humanoid::HasSadistWeapon() const +{ + arm* Right = GetRightArm(), * Left = GetLeftArm(); + return (Right && Right->HasSadistWeapon()) || (Left && Left->HasSadistWeapon()); +} + +truth humanoid::HasSadistAttackMode() const +{ + return HasSadistWeapon() || IsUsingLegs(); +} + +void petrusswife::Save(outputfile& SaveFile) const +{ + humanoid::Save(SaveFile); + SaveFile << GiftTotal; +} + +void petrusswife::Load(inputfile& SaveFile) +{ + humanoid::Load(SaveFile); + SaveFile >> GiftTotal; +} + +void petrusswife::BeTalkedTo() +{ + itemvector Item; + + if(!PLAYER->SelectFromPossessions(Item, CONST_S("Do you have something to give me?"), 0, &item::IsLuxuryItem) + || Item.empty()) + humanoid::BeTalkedTo(); + + int Accepted = 0; + truth RefusedSomething = false; + + for(int c = 0; c < Item.size(); ++c) + if(!MakesBurdened(GetCarriedWeight() + Item[c]->GetWeight())) + { + ++Accepted; + GiftTotal += Item[c]->GetTruePrice(); + Item[c]->RemoveFromSlot(); + GetStack()->AddItem(Item[c]); + } + else + { + RefusedSomething = true; + break; + } + + if(Accepted) + ADD_MESSAGE("\"I thank you for your little gift%s.\"", Accepted == 1 ? "" : "s"); + + if(RefusedSomething) + ADD_MESSAGE("\"Unfortunately I cannot carry any more of your gifts. I'm a delicate woman, you see.\""); +} + +void guard::BeTalkedTo() +{ + itemvector Item; + + if(!PLAYER->SelectFromPossessions(Item, CONST_S("Do you have something to give me?"), 0, &item::IsBeverage) + || Item.empty()) + humanoid::BeTalkedTo(); + + for(int c = 0; c < Item.size(); ++c) + { + Item[c]->RemoveFromSlot(); + GetStack()->AddItem(Item[c]); + } +} + +void denim::GetAICommand() +{ + int Enemies = 0; + + for(int c = 0; c < game::GetTeams(); ++c) + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + Enemies += game::GetTeam(c)->GetEnabledMembers(); + + StandIdleAI(); +} + +void wisefarmer::GetAICommand() +{ + int Enemies = 0; + + for(int c = 0; c < game::GetTeams(); ++c) + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + Enemies += game::GetTeam(c)->GetEnabledMembers(); + + StandIdleAI(); +} + +void raven::GetAICommand() +{ + int Enemies = 0; + + for(int c = 0; c < game::GetTeams(); ++c) + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + Enemies += game::GetTeam(c)->GetEnabledMembers(); + + StandIdleAI(); +} + +void vulcan::GetAICommand() +{ + int Enemies = 0; + + for(int c = 0; c < game::GetTeams(); ++c) + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + Enemies += game::GetTeam(c)->GetEnabledMembers(); + + StandIdleAI(); +} + +truth rogue::IsRetreating() const +{ + if(humanoid::IsRetreating()) + return true; + + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if((*i)->GetSparkleFlags()) + return true; + + return false; +} + +void rogue::GetAICommand() +{ + if(!IsRetreating()) + { + character* Char = GetRandomNeighbour(); + + if(Char) + { + itemvector Sparkling; + + for(stackiterator i = Char->GetStack()->GetBottom(); i.HasItem(); ++i) + { + if((*i)->GetSparkleFlags() && !MakesBurdened((*i)->GetWeight())) + Sparkling.push_back(*i); + } + + if(!Sparkling.empty()) + { + item* ToSteal = Sparkling[RAND() % Sparkling.size()]; + ToSteal->RemoveFromSlot(); + GetStack()->AddItem(ToSteal); + + if(Char->IsPlayer()) + ADD_MESSAGE("%s steals your %s.", CHAR_NAME(DEFINITE), ToSteal->CHAR_NAME(UNARTICLED)); + + EditAP(-500); + return; + } + } + } + + humanoid::GetAICommand(); +} + +truth humanoid::CheckThrowItemOpportunity() +{ + if(!HasAUsableArm()) + return false; + else + return character::CheckThrowItemOpportunity(); +} + +truth humanoid::CheckAIZapOpportunity() +{ + if(!HasAUsableArm() || !CanZap()) + return false; + else + return character::CheckAIZapOpportunity(); +} + +void doctor::BeTalkedTo() +{ + if(GetRelation(PLAYER) == HOSTILE) + { + ADD_MESSAGE("\"Bacillus! I surgically detach your every limb!\""); + return; + } + + for(int c = 0; c < PLAYER->GetBodyParts(); ++c) + if(!PLAYER->GetBodyPart(c) && PLAYER->CanCreateBodyPart(c)) + { + truth HasOld = false; + + for(std::list::const_iterator i = PLAYER->GetOriginalBodyPartID(c).begin(); i != PLAYER->GetOriginalBodyPartID(c).end(); ++i) + { + bodypart* OldBodyPart = static_cast(PLAYER->SearchForItem(*i)); + + if(OldBodyPart) + { + HasOld = true; + long Price = (2*GetAttribute(CHARISMA)) < PLAYER->GetAttribute(CHARISMA) ? 5 : (2*GetAttribute(CHARISMA) - PLAYER->GetAttribute(CHARISMA)); + + if(PLAYER->GetMoney() >= Price) + { + if( (GetAttribute(INTELLIGENCE) < OldBodyPart->GetMainMaterial()->GetIntelligenceRequirement()) && !OldBodyPart->CanRegenerate()) + ADD_MESSAGE("\"I no smart enough to put back bodyparts made of %s, especially not your severed %s.\"", OldBodyPart->GetMainMaterial()->GetName(false, false).CStr(), PLAYER->GetBodyPartName(c).CStr()); + else + { + ADD_MESSAGE("\"I could put your old %s back in exchange for %ld gold pieces, yes yes.\"", PLAYER->GetBodyPartName(c).CStr(), Price); + + if(game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) + { + OldBodyPart->SetHP(1); + PLAYER->SetMoney(PLAYER->GetMoney() - Price); + SetMoney(GetMoney() + Price); + OldBodyPart->RemoveFromSlot(); + PLAYER->AttachBodyPart(OldBodyPart); + return; + } + } + } + else + ADD_MESSAGE("\"Oh my! Your %s severed! Help yourself and get %ldgp and Party help you too.\"", PLAYER->GetBodyPartName(c).CStr(), Price); + } + } + // okay, doctors cannot summon limbs + if(!HasOld) + ADD_MESSAGE("\"You don't have your original %s with you. A priest or priestess might summon you a new one with the right rituals.\"", PLAYER->GetBodyPartName(c).CStr()); + } + + // remove limb looks like this: + // first, select limb to detach (ask player this) + // then detach limb + // then make player scream, according to endurance level. above 25 endurance, no scream, nor panic + // if scream, then panic for a duration according to (25 - Endurance)*4 or zero + // if state confused is activated, then no scream, no panic (makes vodka handy) + // hand over 5 gold + if(PLAYER->GetMoney() >= 5) + { + ADD_MESSAGE("\"I can surgically remove one of your limbs in exchange for 5 gold. Flat rate, genuine bargain!\""); + if(game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) + { + PLAYER->SetMoney(PLAYER->GetMoney() - 5); + SetMoney(GetMoney() + 5); + PLAYER->SurgicallyDetachBodyPart(); + if((PLAYER->GetAttribute(ENDURANCE) <= 24) && !PLAYER->StateIsActivated(CONFUSED)) + { + PLAYER->BeginTemporaryState(PANIC, 500 + RAND() % 4 * (25 - PLAYER->GetAttribute(ENDURANCE))); + ADD_MESSAGE("You let out a gut-wrenching scream of agony!"); + } + return; + } + } + + if(PLAYER->TemporaryStateIsActivated(POISONED)) + { + long Price = GetAttribute(CHARISMA) < PLAYER->GetAttribute(CHARISMA) ? 5 : (GetAttribute(CHARISMA) - PLAYER->GetAttribute(CHARISMA)); + + if(PLAYER->GetMoney() >= Price) + { + ADD_MESSAGE("\"You seem to be rather ill, yes yes. I give you small dose of antidote for %ld gold pieces.\"", Price); + + if(game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) + { + ADD_MESSAGE("You feel better."); + PLAYER->DeActivateTemporaryState(POISONED); + PLAYER->SetMoney(PLAYER->GetMoney() - Price); + SetMoney(GetMoney() + Price); + return; + } + } + else + ADD_MESSAGE("\"You seem to be rather ill. Get %ld gold pieces and I fix that.\"", Price); + } + + if(PLAYER->TemporaryStateIsActivated(LEPROSY)) + { + long Price = GetAttribute(CHARISMA) < PLAYER->GetAttribute(CHARISMA) ? 5 : (GetAttribute(CHARISMA) - PLAYER->GetAttribute(CHARISMA)); + + if(PLAYER->GetMoney() >= Price) + { + ADD_MESSAGE("\"You seem to have contracted vile disease of leprosy, yes yes. I can give you small dose of medicince for %ld gold pieces.\"", Price); + + if(game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) + { + ADD_MESSAGE("You feel better."); + PLAYER->DeActivateTemporaryState(LEPROSY); + PLAYER->SetMoney(PLAYER->GetMoney() - Price); + SetMoney(GetMoney() + Price); + return; + } + } + else + ADD_MESSAGE("\"You seem to be falling apart. Get %ld gold pieces and I fix that.\"", Price); + } + + if(PLAYER->TemporaryStateIsActivated(LYCANTHROPY)) + { + long Price = GetAttribute(CHARISMA) < PLAYER->GetAttribute(CHARISMA) ? 5 : (GetAttribute(CHARISMA) - PLAYER->GetAttribute(CHARISMA)); + + if(PLAYER->GetMoney() >= Price) + { + ADD_MESSAGE("\"You seem to be turning into a werewolf quite frequently. Party must crush epidemic, special priority! If you wish to donate %ldgp to %s, I can remove the canine blood from your veins with acupucnture, yes yes.\"", Price); + + if(game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) + { + ADD_MESSAGE("You feel better."); + PLAYER->DeActivateTemporaryState(LYCANTHROPY); + PLAYER->SetMoney(PLAYER->GetMoney() - Price); + SetMoney(GetMoney() + Price); + return; + } + } + else + ADD_MESSAGE("\"You seem to be lycanthropic. I might be able to do something for that but I need %ldgp for the Party first.\"", Price); + } + + if(PLAYER->TemporaryStateIsActivated(VAMPIRISM)) + { + long Price = GetAttribute(CHARISMA) < PLAYER->GetAttribute(CHARISMA) ? 5 : (GetAttribute(CHARISMA) - PLAYER->GetAttribute(CHARISMA)); + + if(PLAYER->GetMoney() >= Price) + { + ADD_MESSAGE("\"You seem to have addiction to drinking blood. Party must crush epidemic, special priority! If you wish to donate %ldgp to %s, I can remove vampiric urges with acupucnture, yes yes.\"", Price); + + if(game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) + { + ADD_MESSAGE("You feel better."); + PLAYER->DeActivateTemporaryState(VAMPIRISM); + PLAYER->SetMoney(PLAYER->GetMoney() - Price); + SetMoney(GetMoney() + Price); + return; + } + } + else + ADD_MESSAGE("\"You seem to be vampiric. I might be able to do something for that but I need %ldgp for the Party first.\"", Price); + } + + humanoid::BeTalkedTo(); +} + +void humanoid::SurgicallyDetachBodyPart() +{ + int ToBeDetached; + + switch(game::KeyQuestion(CONST_S("What limb? (l)eft arm, (r)ight arm, (L)eft leg, (R)ight leg, (h)ead?"), KEY_ESC, 5, 'l','r','L','R', 'h')) + { + case 'l': + ToBeDetached = LEFT_ARM_INDEX; + break; + case 'r': + ToBeDetached = RIGHT_ARM_INDEX; + break; + case 'L': + ToBeDetached = LEFT_LEG_INDEX; + break; + case 'R': + ToBeDetached = RIGHT_LEG_INDEX; + break; + case 'h': + ToBeDetached = HEAD_INDEX; // maybe no head, should be up to the player to decide. + break; + default: + return; + } + + if(GetBodyPart(ToBeDetached)) + { + item* ToDrop = SevereBodyPart(ToBeDetached); + SendNewDrawRequest(); + + if(ToDrop) + { + GetStack()->AddItem(ToDrop); + ToDrop->DropEquipment(); + } + + ADD_MESSAGE("Bodypart detached!"); + } + else + ADD_MESSAGE("That bodypart has already been detached."); + + CheckDeath(CONST_S("had one of his vital bodyparts surgically removed"), 0); +} + +void morbe::GetAICommand() +{ + StandIdleAI(); +} + +void morbe::CreateCorpse(lsquare* Square) +{ + angel* Angel; + int Seen = 0; + + for(int c = 0; c < 3; ++c) + { + if(!c) + Angel = archangel::Spawn(SCABIES); + else + Angel = angel::Spawn(SCABIES); + + v2 Where = game::GetCurrentLevel()->GetRandomSquare(Angel); + + Angel->SetTeam(game::GetTeam(4)); + Angel->PutTo(Where); + + if(Angel->CanBeSeenByPlayer()) + ++Seen; + } + + if(Seen == 1) + ADD_MESSAGE("%s materializes.", Angel->CHAR_NAME(INDEFINITE)); + else if(Seen == 2) + ADD_MESSAGE("Two %s materialize.", Angel->CHAR_NAME(PLURAL)); + else if(Seen == 3) + ADD_MESSAGE("Three %s materialize.", Angel->CHAR_NAME(PLURAL)); + + ADD_MESSAGE("\"We will defend the Unholy Order!\""); + + SendToHell(); +} + +void morbe::BeTalkedTo() +{ + if(GetRelation(PLAYER) == HOSTILE) + { + ADD_MESSAGE("A plague on all your household!"); + return; + } + + if(PLAYER->HasCurdledBlood()) + { + if(PLAYER->RemoveCurdledOmmelBlood()) + { + game::TextScreen(CONST_S("You hand over the vial filled with curdled ommel blood. Morbe is delighted.\n" + "\"You have retrieved the curdled ommel blood!\n" + "I will share a little of my knowledge with you, as a gift.\"")); + + game::TextScreen(CONST_S( "Morbe begins to relate a dark secret to you from the depths of her knowledge:\n" + "\"There is a place not far from here, a place of utter darkness\n" + "where dwells the soul of the ancient Warlord Xinroch.\n" + "His bones have long since departed that place, but his soul still dwells beneath.\n" + "It is stirring as we speak.\"")); + /* + game::LoadWorldMap(); + v2 XinrochTombPos = game::GetWorldMap()->GetEntryPos(0, XINROCH_TOMB); + game::GetWorldMap()->GetWSquare(XinrochTombPos)->ChangeOWTerrain(xinrochtomb::Spawn()); + game::GetWorldMap()->RevealEnvironment(XinrochTombPos, 1); + game::SaveWorldMap(); + */ + GetArea()->SendNewDrawRequest(); + game::SetOmmelBloodMission(2); + ADD_MESSAGE("Morbe hands you a map revealing the location of the tomb. \"There are untold riches in the Tomb of Xinroch, but beware, his many minions protect him and will kill all who attempt to enter that place!\""); + maptotombofxinroch* MapToTombOfXinroch = maptotombofxinroch::Spawn(); + MapToTombOfXinroch->MoveTo(PLAYER->GetStack()); + return; + } + } + if(game::GetOmmelBloodMission() != 2) + { + if(!game::GetOmmelBloodMission()) + { + game::TextScreen(CONST_S("You approach the acolyte standing by the cauldron, she speaks to you.\n\n" + "\"Welcome, ") + PLAYER->GetPanelName().CStr() + CONST_S(".\n" + "Doubtless I have seen you from afar and by the powers of the goddess,\n" + "I have drawn you here to me. Can you not feel the presence of the\n" + "unholy Scabies here in my abode? Indeed it is I, Morbe, high\n" + "priestess of Scabies, who channels the powers of the goddess\n" + "into the world above Valpurus!\"\n\n" + "You feel your stomach churn as you realise where you are, the source of all illness and\n" + "pestillence in the world. Morbe speaks further: \n\n")); + + game::TextScreen(CONST_S( "\"I have brought you here because I have a use for you. I need you to\n" + "curdle a measure of Ommel Blood with the scream of the Enner Beast!\n" + "Here is a klein bottle, it may be dipped, but not drunk from. It will\n" + "not break due to the cry of the Enner Beast. I have filled it with\n" + "the Ommel blood from this cauldron. Wield it before you as you encounter\n" + "the enner beast in person; the liquid should change noticably.\n" + "I promise you a great gift in exchange for this favour. Return\n" + "to me with this very vial, filled with curdled Ommel blood!\"")); + kleinbottle* KleinBottle = kleinbottle::Spawn(); + KleinBottle->ChangeSecondaryMaterial(MAKE_MATERIAL(OMMEL_BLOOD)); + KleinBottle->MoveTo(PLAYER->GetStack()); + GetArea()->SendNewDrawRequest(); + ADD_MESSAGE("\"Do not fail me, or the goddess will be unhappy!\""); + game::SetOmmelBloodMission(1); + } + else /* StoryState == 1 */ + ADD_MESSAGE("Morbe says: \"Bring me the vial of curdled Ommel Blood and I'll tell you my secrets.\""); + } + else /* StoryState == 2 */ + ADD_MESSAGE("Morbe says: \"I have all I need from you right now. Be gone!\""); + + return; +} diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp new file mode 100644 index 0000000..dc3158d --- /dev/null +++ b/Main/Source/iconf.cpp @@ -0,0 +1,213 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "iconf.h" +#include "game.h" +#include "feio.h" +#include "area.h" +#include "graphics.h" +#include "bitmap.h" +#include "igraph.h" + +stringoption ivanconfig::DefaultName( "DefaultName", + "player's default name", + "", + &configsystem::NormalStringDisplayer, + &DefaultNameChangeInterface); +stringoption ivanconfig::DefaultPetName( "DefaultPetName", + "starting pet's default name", + CONST_S("Kenny"), + &configsystem::NormalStringDisplayer, + &DefaultPetNameChangeInterface); +numberoption ivanconfig::AutoSaveInterval("AutoSaveInterval", + "autosave interval", + 100, + &AutoSaveIntervalDisplayer, + &AutoSaveIntervalChangeInterface, + &AutoSaveIntervalChanger); +scrollbaroption ivanconfig::Contrast( "Contrast", + "contrast", + 100, + &ContrastDisplayer, + &ContrastChangeInterface, + &ContrastChanger, + &ContrastHandler); +truthoption ivanconfig::WarnAboutDanger( "WarnAboutVeryDangerousMonsters", + "Warn about very dangerous monsters", + true); +truthoption ivanconfig::AutoDropLeftOvers( "AutoDropLeftOvers", + "drop food leftovers automatically", + true); +truthoption ivanconfig::LookZoom( "LookZoom", + "zoom feature in look mode", + false); +truthoption ivanconfig::UseAlternativeKeys("UseAlternativeKeys", + "use alternative direction keys", + false); +truthoption ivanconfig::BeNice( "BeNice", + "be nice to pets", + true); +#ifndef __DJGPP__ +truthoption ivanconfig::FullScreenMode( "FullScreenMode", + "run the game in full screen mode", + false, + &configsystem::NormalTruthDisplayer, + &configsystem::NormalTruthChangeInterface, + &FullScreenModeChanger); +#endif +col24 ivanconfig::ContrastLuminance = NORMAL_LUMINANCE; + +v2 ivanconfig::GetQuestionPos() { return game::IsRunning() ? v2(16, 6) : v2(30, 30); } +void ivanconfig::BackGroundDrawer() { game::DrawEverythingNoBlit(); } + +void ivanconfig::AutoSaveIntervalDisplayer(const numberoption* O, festring& Entry) +{ + if(O->Value) + { + Entry << O->Value << " turn"; + + if(O->Value != 1) + Entry << 's'; + } + else + Entry << "disabled"; +} + +void ivanconfig::ContrastDisplayer(const numberoption* O, festring& Entry) +{ + Entry << O->Value << "/100"; +} + +truth ivanconfig::DefaultNameChangeInterface(stringoption* O) +{ + festring String; + + if(iosystem::StringQuestion(String, CONST_S("Set new default name (1-20 letters):"), GetQuestionPos(), WHITE, 0, 20, !game::IsRunning(), true) == NORMAL_EXIT) + O->ChangeValue(String); + + if(game::IsRunning()) + igraph::BlitBackGround(v2(16, 6), v2(game::GetScreenXSize() << 4, 23)); + + return false; +} + +truth ivanconfig::DefaultPetNameChangeInterface(stringoption* O) +{ + festring String; + + if(iosystem::StringQuestion(String, CONST_S("Set new default name for the starting pet (1-20 letters):"), GetQuestionPos(), WHITE, 0, 20, !game::IsRunning(), true) == NORMAL_EXIT) + O->ChangeValue(String); + + if(game::IsRunning()) + igraph::BlitBackGround(v2(16, 6), v2(game::GetScreenXSize() << 4, 23)); + + return false; +} + +truth ivanconfig::AutoSaveIntervalChangeInterface(numberoption* O) +{ + O->ChangeValue(iosystem::NumberQuestion(CONST_S("Set new autosave interval (1-50000 turns, 0 for never):"), GetQuestionPos(), WHITE, !game::IsRunning())); + + if(game::IsRunning()) + igraph::BlitBackGround(v2(16, 6), v2(game::GetScreenXSize() << 4, 23)); + + return false; +} + +truth ivanconfig::ContrastChangeInterface(numberoption* O) +{ + iosystem::ScrollBarQuestion(CONST_S("Set new contrast value (0-200, '<' and '>' move the slider):"), GetQuestionPos(), O->Value, 5, 0, 200, O->Value, WHITE, LIGHT_GRAY, DARK_GRAY, game::GetMoveCommandKey(KEY_LEFT_INDEX), game::GetMoveCommandKey(KEY_RIGHT_INDEX), !game::IsRunning(), static_cast(O)->BarHandler); + + if(game::IsRunning()) + igraph::BlitBackGround(v2(16, 6), v2(game::GetScreenXSize() << 4, 23)); + + return false; +} + +void ivanconfig::AutoSaveIntervalChanger(numberoption* O, long What) +{ + if(What < 0) What = 0; + if(What > 50000) What = 50000; + O->Value = What; +} + +void ivanconfig::ContrastChanger(numberoption* O, long What) +{ + if(What < 0) What = 0; + if(What > 200) What = 200; + O->Value = What; + CalculateContrastLuminance(); +} + +#ifndef __DJGPP__ + +void ivanconfig::FullScreenModeChanger(truthoption*, truth) +{ + graphics::SwitchMode(); +} + +#endif + +void ivanconfig::Show() +{ + configsystem::Show(&BackGroundDrawer, &game::SetStandardListAttributes, game::IsRunning()); +} + +void ivanconfig::ContrastHandler(long Value) +{ + ContrastChanger(&Contrast, Value); + + if(game::IsRunning()) + { + game::GetCurrentArea()->SendNewDrawRequest(); + game::DrawEverythingNoBlit(); + } +} + +#ifndef __DJGPP__ + +void ivanconfig::SwitchModeHandler() +{ + FullScreenMode.Value = !FullScreenMode.Value; + Save(); +} + +#endif + +void ivanconfig::CalculateContrastLuminance() +{ + int Element = Min((GetContrast() << 7) / 100, 255); + ContrastLuminance = MakeRGB24(Element, Element, Element); +} + +void ivanconfig::Initialize() +{ + configsystem::AddOption(&DefaultName); + configsystem::AddOption(&DefaultPetName); + configsystem::AddOption(&AutoSaveInterval); + configsystem::AddOption(&Contrast); + configsystem::AddOption(&WarnAboutDanger); + configsystem::AddOption(&AutoDropLeftOvers); + configsystem::AddOption(&LookZoom); + configsystem::AddOption(&UseAlternativeKeys); + configsystem::AddOption(&BeNice); +#ifndef __DJGPP__ + configsystem::AddOption(&FullScreenMode); +#endif +#if defined(WIN32) || defined(__DJGPP__) + configsystem::SetConfigFileName("ivan.cfg"); +#else + configsystem::SetConfigFileName(festring(getenv("HOME")) + "/.ivan.conf"); +#endif + configsystem::Load(); + CalculateContrastLuminance(); +} diff --git a/Main/Source/id.cpp b/Main/Source/id.cpp new file mode 100644 index 0000000..6561c62 --- /dev/null +++ b/Main/Source/id.cpp @@ -0,0 +1,121 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "id.h" +#include "game.h" +#include "god.h" +#include "festring.h" + +void id::AddNameSingular(festring& String, truth Articled) const +{ + if(Articled) + if(UsesLongArticle()) + String << "an "; + else + String << "a "; + + String << GetNameSingular(); +} + +void id::AddName(festring& Name, int Case) const +{ + truth Articled; + + if((Case & ARTICLE_BIT) && (GetArticleMode() == FORCE_THE || (!GetArticleMode() && !(Case & INDEFINE_BIT)))) + { + Name << "the "; + Articled = false; + } + else + Articled = !(Case & PLURAL) && (Case & ARTICLE_BIT) && (Case & INDEFINE_BIT) && GetArticleMode() != NO_ARTICLE; + + if(!(Case & STRIPPED)) + { + if(AddRustLevelDescription(Name, Articled)) + Articled = false; + + if(AddStateDescription(Name, Articled)) + Articled = false; + } + + if(AddAdjective(Name, Articled)) + Articled = false; + + if(!(Case & STRIPPED) && ShowMaterial() && AddMaterialDescription(Name, Articled)) + Articled = false; + + if(Case & PLURAL) + Name << GetNamePlural(); + else + AddNameSingular(Name, Articled); + + AddPostFix(Name, Case); +} + +festring id::GetName(int Case) const +{ + static festring Name; + Name.Empty(); + AddName(Name, Case); + return Name; +} + +void id::AddName(festring& Name, int Case, int Amount) const +{ + if(Amount == 1) + AddName(Name, Case&~PLURAL); + else + { + if((Case & ARTICLE_BIT) && (GetArticleMode() == FORCE_THE || (!GetArticleMode() && !(Case & INDEFINE_BIT)))) + Name << "the "; + + Name << Amount << ' '; + AddName(Name, Case&~ARTICLE_BIT|PLURAL); + } +} + +festring id::GetName(int Case, int Amount) const +{ + static festring Name; + Name.Empty(); + AddName(Name, Case, Amount); + return Name; +} + +truth id::AddAdjective(festring& String, truth Articled) const +{ + if(GetAdjective().GetSize()) + { + if(Articled) + if(UsesLongAdjectiveArticle()) + String << "an "; + else + String << "a "; + + String << GetAdjective() << ' '; + return true; + } + else + return false; +} + +void id::AddPostFix(festring& String, int) const +{ + if(GetPostFix().GetSize()) + String << ' ' << GetPostFix(); +} + +truth id::AddActiveAdjective(festring& String, truth Articled) const +{ + String << (Articled ? "an active " : "active "); + return true; +} diff --git a/Main/Source/igraph.cpp b/Main/Source/igraph.cpp new file mode 100644 index 0000000..807d68d --- /dev/null +++ b/Main/Source/igraph.cpp @@ -0,0 +1,563 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "igraph.h" +#include "felist.h" +#include "bitmap.h" +#include "graphics.h" +#include "iconf.h" +#include "rawbit.h" +#include "game.h" +#include "save.h" +#include "object.h" +#include "allocate.h" +#include "whandler.h" + +rawbitmap* igraph::RawGraphic[RAW_TYPES]; +bitmap* igraph::Graphic[GRAPHIC_TYPES]; +bitmap* igraph::TileBuffer; +bitmap* igraph::FlagBuffer; +cchar* igraph::RawGraphicFileName[] = { "Graphics/GLTerra.pcx", "Graphics/OLTerra.pcx", "Graphics/Item.pcx", "Graphics/Char.pcx", "Graphics/Humanoid.pcx", "Graphics/Effect.pcx", "Graphics/Cursor.pcx" }; +cchar* igraph::GraphicFileName[] = { "Graphics/WTerra.pcx", "Graphics/FOW.pcx", "Graphics/Symbol.pcx", "Graphics/Smiley.pcx" }; +tilemap igraph::TileMap; +uchar igraph::RollBuffer[256]; +int** igraph::BodyBitmapValidityMap; +bitmap* igraph::Menu; +bitmap* igraph::SilhouetteCache[HUMANOID_BODYPARTS][CONDITION_COLORS][SILHOUETTE_TYPES]; +rawbitmap* igraph::ColorizeBuffer[2] = { new rawbitmap(TILE_V2), new rawbitmap(TILE_V2) }; +bitmap* igraph::Cursor[CURSOR_TYPES]; +bitmap* igraph::BigCursor[CURSOR_TYPES]; +col16 igraph::CursorColor[CURSOR_TYPES] = { MakeRGB16(40, 40, 40), + MakeRGB16(255, 0, 0), + MakeRGB16(100, 100, 255), + MakeRGB16(200, 200, 0) }; +bitmap* igraph::BackGround; +int igraph::CurrentColorType = -1; + +void igraph::Init() +{ + static truth AlreadyInstalled = false; + + if(!AlreadyInstalled) + { + AlreadyInstalled = true; + graphics::Init(); + graphics::SetMode("IVAN " IVAN_VERSION, festring(game::GetGameDir() + "Graphics/Icon.bmp").CStr(), v2(800, 600), ivanconfig::GetFullScreenMode()); + DOUBLE_BUFFER->ClearToColor(0); + graphics::BlitDBToScreen(); +#ifndef __DJGPP__ + graphics::SetSwitchModeHandler(ivanconfig::SwitchModeHandler); +#endif + graphics::LoadDefaultFont(game::GetGameDir() + "Graphics/Font.pcx"); + FONT->CreateFontCache(WHITE); + FONT->CreateFontCache(LIGHT_GRAY); + felist::CreateQuickDrawFontCaches(FONT, WHITE, 8); + felist::CreateQuickDrawFontCaches(FONT, LIGHT_GRAY, 8); + object::InitSparkleValidityArrays(); + int c; + + for(c = 0; c < RAW_TYPES; ++c) + RawGraphic[c] = new rawbitmap(game::GetGameDir() + RawGraphicFileName[c]); + + for(c = 0; c < GRAPHIC_TYPES; ++c) + { + Graphic[c] = new bitmap(game::GetGameDir() + GraphicFileName[c]); + Graphic[c]->ActivateFastFlag(); + } + + ColorizeBuffer[0]->CopyPaletteFrom(RawGraphic[0]); + ColorizeBuffer[1]->CopyPaletteFrom(RawGraphic[0]); + TileBuffer = new bitmap(TILE_V2); + TileBuffer->ActivateFastFlag(); + TileBuffer->InitPriorityMap(0); + FlagBuffer = new bitmap(TILE_V2); + FlagBuffer->ActivateFastFlag(); + FlagBuffer->CreateAlphaMap(0); + Alloc2D(BodyBitmapValidityMap, 8, 16); + CreateBodyBitmapValidityMaps(); + CreateSilhouetteCaches(); + + for(c = 0; c < CURSOR_TYPES; ++c) + { + packcol16 Color = CursorColor[c]; + Cursor[c] = new bitmap(v2(48, 16), TRANSPARENT_COLOR); + Cursor[c]->CreateAlphaMap(255); + GetCursorRawGraphic()->MaskedBlit(Cursor[c], ZERO_V2, ZERO_V2, v2(48, 16), &Color); + BigCursor[c] = new bitmap(v2(96, 32), TRANSPARENT_COLOR); + BigCursor[c]->CreateAlphaMap(255); + GetCursorRawGraphic()->MaskedBlit(BigCursor[c], v2(0, 16), ZERO_V2, v2(96, 32), &Color); + } + } +} + +void igraph::DeInit() +{ + int c; + + for(c = 0; c < RAW_TYPES; ++c) + delete RawGraphic[c]; + + for(c = 0; c < GRAPHIC_TYPES; ++c) + delete Graphic[c]; + + delete TileBuffer; + delete FlagBuffer; + delete [] BodyBitmapValidityMap; + delete BackGround; +} + +void igraph::DrawCursor(v2 Pos, int CursorData, int Index) +{ + /* Inefficient gum solution */ + + blitdata BlitData = { DOUBLE_BUFFER, + { 0, 0 }, + { Pos.X, Pos.Y }, + { TILE_SIZE, TILE_SIZE }, + { ivanconfig::GetContrastLuminance() }, + TRANSPARENT_COLOR, + 0 }; + + bitmap* CursorBitmap; + int SrcX = 0; + + if(CursorData & CURSOR_BIG) + { + CursorBitmap = BigCursor[CursorData&~CURSOR_FLAGS]; + BlitData.Src.X = SrcX = (Index & 1) << 4; + BlitData.Src.Y = (Index & 2) << 3; + } + else + CursorBitmap = Cursor[CursorData&~CURSOR_FLAGS]; + + if(!(CursorData & (CURSOR_FLASH|CURSOR_TARGET))) + { + CursorBitmap->LuminanceMaskedBlit(BlitData); + return; + } + + if(!(CursorData & CURSOR_TARGET)) + { + int Tick = GET_TICK() & 31; + CursorBitmap->FillAlpha(95 + 10 * abs(Tick - 16)); + CursorBitmap->AlphaLuminanceBlit(BlitData); + return; + } + + int Tick = (GET_TICK() << 2) / 3; + int Frame = (Tick >> 4) % 3; + int Base = Frame << 4; + BlitData.Src.X = SrcX + (CursorData & CURSOR_BIG ? Base << 1 : Base); + CursorBitmap->FillAlpha(255 - (Tick & 0xF) * 16); + CursorBitmap->AlphaLuminanceBlit(BlitData); + Base = ((Frame + 1) % 3) << 4; + BlitData.Src.X = SrcX + (CursorData & CURSOR_BIG ? Base << 1 : Base); + CursorBitmap->FillAlpha((Tick & 0xF) * 16); + CursorBitmap->AlphaLuminanceBlit(BlitData); +} + +tilemap::iterator igraph::AddUser(const graphicid& GI) +{ + tilemap::iterator Iterator = TileMap.find(GI); + + if(Iterator != TileMap.end()) + { + tile& Tile = Iterator->second; + ++Tile.Users; + return Iterator; + } + else + { + cint SpecialFlags = GI.SpecialFlags; + cint BodyPartFlags = SpecialFlags & 0x78; + cint RotateFlags = SpecialFlags & 0x7; + cint Frame = GI.Frame; + v2 SparklePos = v2(GI.SparklePosX, GI.SparklePosY); + rawbitmap* RawBitmap = RawGraphic[GI.FileIndex]; + v2 RawPos = v2(GI.BitmapPosX, GI.BitmapPosY); + + if(BodyPartFlags && BodyPartFlags < ST_OTHER_BODYPART) + { + ColorizeBuffer[0]->Clear(); + EditBodyPartTile(RawBitmap, ColorizeBuffer[0], RawPos, BodyPartFlags); + RawBitmap = ColorizeBuffer[0]; + RawPos.X = RawPos.Y = 0; + + if(RotateFlags) + { + ColorizeBuffer[1]->Clear(); + SparklePos = RotateTile(RawBitmap, ColorizeBuffer[1], RawPos, SparklePos, RotateFlags); + RawBitmap = ColorizeBuffer[1]; + } + } + else if(RotateFlags) + { + ColorizeBuffer[0]->Clear(); + SparklePos = RotateTile(RawBitmap, ColorizeBuffer[0], RawPos, SparklePos, RotateFlags); + RawBitmap = ColorizeBuffer[0]; + RawPos.X = RawPos.Y = 0; + } + + bitmap* Bitmap = RawBitmap->Colorize(RawPos, TILE_V2, GI.Position, GI.Color, GI.BaseAlpha, GI.Alpha, GI.RustData, !(GI.SpecialFlags & ST_DISALLOW_R_COLORS)); + Bitmap->ActivateFastFlag(); + + if(BodyPartFlags) + Bitmap->InitPriorityMap(SpecialFlags & ST_CLOAK ? CLOAK_PRIORITY : AVERAGE_PRIORITY); + + if(GI.OutlineColor != TRANSPARENT_COLOR) + Bitmap->Outline(GI.OutlineColor, GI.OutlineAlpha, BodyPartFlags != ST_WIELDED ? ARMOR_OUTLINE_PRIORITY : AVERAGE_PRIORITY); + + if(SparklePos.X != SPARKLE_POS_X_ERROR) + Bitmap->CreateSparkle(SparklePos + GI.Position, GI.SparkleFrame); + + if(GI.FlyAmount) + Bitmap->CreateFlies(GI.Seed, Frame, GI.FlyAmount); + + cint WobbleData = GI.WobbleData; + + if(WobbleData & WOBBLE) + { + int Speed = (WobbleData & WOBBLE_SPEED_RANGE) >> WOBBLE_SPEED_SHIFT; + int Freq = (WobbleData & WOBBLE_FREQ_RANGE) >> WOBBLE_FREQ_SHIFT; + int WobbleMask = 7 >> Freq << (6 - Speed); + + if(!(Frame & WobbleMask)) + Bitmap->Wobble(Frame & ((1 << (6 - Speed)) - 1), Speed, WobbleData & WOBBLE_HORIZONTALLY_BIT); + } + + if(SpecialFlags & ST_FLAMES) + { + ulong SeedNFlags = SpecialFlags >> ST_FLAME_SHIFT & 3 | GI.Seed << 4; + Bitmap->CreateFlames(RawBitmap, RawPos - GI.Position, SeedNFlags, Frame); + } + + if(SpecialFlags & ST_LIGHTNING && !((Frame + 1) & 7)) + Bitmap->CreateLightning(GI.Seed + Frame, WHITE); + + return TileMap.insert(std::make_pair(GI, tile(Bitmap))).first; + } +} + +void igraph::EditBodyPartTile(rawbitmap* Source, rawbitmap* Dest, v2 Pos, int BodyPartFlags) +{ + if(BodyPartFlags == ST_RIGHT_ARM) + Source->NormalBlit(Dest, Pos, ZERO_V2, v2(8, 16)); + else if(BodyPartFlags == ST_LEFT_ARM) + Source->NormalBlit(Dest, v2(Pos.X + 8, Pos.Y), v2(8, 0), v2(8, 16)); + else if(BodyPartFlags == ST_GROIN) + { + Source->NormalBlit(Dest, v2(Pos.X, Pos.Y + 8), v2(0, 8), v2(16, 2)); + int i; + v2 V; + + for(V.Y = 10, i = 0; V.Y < 13; ++V.Y) + for(V.X = V.Y - 5; V.X < 20 - V.Y; ++V.X) + Dest->PutPixel(V, Source->GetPixel(Pos + V)); + } + else if(BodyPartFlags == ST_RIGHT_LEG) + { + /* Right leg from the character's, NOT the player's point of view */ + + Source->NormalBlit(Dest, v2(Pos.X, Pos.Y + 10), v2(0, 10), v2(8, 6)); + Dest->PutPixel(v2(5, 10), TRANSPARENT_PALETTE_INDEX); + Dest->PutPixel(v2(6, 10), TRANSPARENT_PALETTE_INDEX); + Dest->PutPixel(v2(7, 10), TRANSPARENT_PALETTE_INDEX); + Dest->PutPixel(v2(6, 11), TRANSPARENT_PALETTE_INDEX); + Dest->PutPixel(v2(7, 11), TRANSPARENT_PALETTE_INDEX); + Dest->PutPixel(v2(7, 12), TRANSPARENT_PALETTE_INDEX); + } + else if(BodyPartFlags == ST_LEFT_LEG) + { + /* Left leg from the character's, NOT the player's point of view */ + + Source->NormalBlit(Dest, v2(Pos.X + 8, Pos.Y + 10), v2(8, 10), v2(8, 6)); + Dest->PutPixel(v2(8, 10), TRANSPARENT_PALETTE_INDEX); + Dest->PutPixel(v2(9, 10), TRANSPARENT_PALETTE_INDEX); + Dest->PutPixel(v2(8, 11), TRANSPARENT_PALETTE_INDEX); + } +} + +v2 igraph::RotateTile(rawbitmap* Source, rawbitmap* Dest, v2 Pos, v2 SparklePos, int RotateFlags) +{ + Source->NormalBlit(Dest, Pos, ZERO_V2, TILE_V2, RotateFlags); + + if(SparklePos.X != SPARKLE_POS_X_ERROR) + { + if(RotateFlags & ROTATE) + { + cint T = SparklePos.X; + SparklePos.X = 15 - SparklePos.Y; + SparklePos.Y = T; + } + + if(RotateFlags & MIRROR) + SparklePos.X = 15 - SparklePos.X; + + if(RotateFlags & FLIP) + SparklePos.Y = 15 - SparklePos.Y; + } + + return SparklePos; +} + +void igraph::RemoveUser(tilemap::iterator Iterator) +{ + tile& Tile = Iterator->second; + + if(!--Tile.Users) + { + delete Tile.Bitmap; + TileMap.erase(Iterator); + } +} + +outputfile& operator<<(outputfile& SaveFile, const graphicid& Value) +{ + SaveFile.Write(reinterpret_cast(&Value), sizeof(Value)); + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, graphicid& Value) +{ + SaveFile.Read(reinterpret_cast(&Value), sizeof(Value)); + return SaveFile; +} + +graphicdata::~graphicdata() +{ + if(AnimationFrames) + { + for(int c = 0; c < AnimationFrames; ++c) + igraph::RemoveUser(GraphicIterator[c]); + + delete [] Picture; + delete [] GraphicIterator; + } +} + +void graphicdata::Save(outputfile& SaveFile) const +{ + SaveFile << (int)AnimationFrames; + + for(int c = 0; c < AnimationFrames; ++c) + SaveFile << GraphicIterator[c]->first; +} + +void graphicdata::Load(inputfile& SaveFile) +{ + SaveFile >> (int&)AnimationFrames; + + if(AnimationFrames) + { + Picture = new bitmap*[AnimationFrames]; + GraphicIterator = new tilemap::iterator[AnimationFrames]; + graphicid GraphicID; + + for(int c = 0; c < AnimationFrames; ++c) + { + SaveFile >> GraphicID; + Picture[c] = (GraphicIterator[c] = igraph::AddUser(GraphicID))->second.Bitmap; + } + } +} + +outputfile& operator<<(outputfile& SaveFile, const graphicdata& Data) +{ + Data.Save(SaveFile); + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, graphicdata& Data) +{ + Data.Load(SaveFile); + return SaveFile; +} + +void graphicdata::Retire() +{ + if(AnimationFrames) + { + for(int c = 0; c < AnimationFrames; ++c) + igraph::RemoveUser(GraphicIterator[c]); + + AnimationFrames = 0; + delete [] Picture; + delete [] GraphicIterator; + } +} + +cint* igraph::GetBodyBitmapValidityMap(int SpecialFlags) +{ + return BodyBitmapValidityMap[(SpecialFlags & 0x38) >> 3]; +} + +void igraph::CreateBodyBitmapValidityMaps() +{ + memset(BodyBitmapValidityMap[0], 0xFF, 128 * sizeof(int)); + int* Map = BodyBitmapValidityMap[ST_RIGHT_ARM >> 3]; + int x, y; + + for(x = 8; x < 16; ++x) + Map[x] = 0; + + Map = BodyBitmapValidityMap[ST_LEFT_ARM >> 3]; + + for(x = 0; x < 8; ++x) + Map[x] = 0; + + Map = BodyBitmapValidityMap[ST_GROIN >> 3]; + memset(Map, 0, 16 * sizeof(int)); + + for(y = 10; y < 13; ++y) + for(x = y - 5; x < 20 - y; ++x) + Map[x] |= 1 << y; + + Map = BodyBitmapValidityMap[ST_RIGHT_LEG >> 3]; + + for(x = 8; x < 16; ++x) + Map[x] = 0; + + Map[5] &= ~(1 << 10); + Map[6] &= ~(1 << 10); + Map[7] &= ~(1 << 10); + Map[6] &= ~(1 << 11); + Map[7] &= ~(1 << 11); + Map[7] &= ~(1 << 12); + + Map = BodyBitmapValidityMap[ST_LEFT_LEG >> 3]; + + for(x = 0; x < 8; ++x) + Map[x] = 0; + + Map[8] &= ~(1 << 10); + Map[9] &= ~(1 << 10); + Map[8] &= ~(1 << 11); +} + +void igraph::LoadMenu() +{ + Menu = new bitmap(game::GetGameDir() + "Graphics/Menu.pcx"); +} + +void igraph::UnLoadMenu() +{ + delete Menu; +} + +void igraph::CreateSilhouetteCaches() +{ + int BodyPartSilhouetteMColorIndex[HUMANOID_BODYPARTS] = { 3, 0, 1, 2, 1, 2, 3 }; + col24 ConditionColor[CONDITION_COLORS] = { MakeRGB16(48, 48, 48), + MakeRGB16(120, 0, 0), + MakeRGB16(180, 0, 0), + MakeRGB16(180, 120, 120), + MakeRGB16(180, 180, 180) }; + v2 V(8, 64); + + for(int c1 = 0; c1 < HUMANOID_BODYPARTS; ++c1) + { + if(c1 == 4) + V.X = 72; + + for(int c2 = 0; c2 < CONDITION_COLORS; ++c2) + { + packcol16 Color[4] = { 0, 0, 0, 0 }; + Color[BodyPartSilhouetteMColorIndex[c1]] = ConditionColor[c2]; + + for(int c3 = 0; c3 < SILHOUETTE_TYPES; ++c3) + { + SilhouetteCache[c1][c2][c3] = new bitmap(SILHOUETTE_SIZE, 0); + RawGraphic[GR_CHARACTER]->MaskedBlit(SilhouetteCache[c1][c2][c3], + V, ZERO_V2, + SILHOUETTE_SIZE, Color); + } + + SilhouetteCache[c1][c2][SILHOUETTE_INTER_LACED]->InterLace(); + } + } +} + +void igraph::CreateBackGround(int ColorType) +{ + if(CurrentColorType == ColorType) + return; + + CurrentColorType = ColorType; + delete BackGround; + BackGround = new bitmap(RES); + int Side = 1025; + int** Map; + Alloc2D(Map, Side, Side); + femath::GenerateFractalMap(Map, Side, Side - 1, 800); + + for(int x = 0; x < RES.X; ++x) + for(int y = 0; y < RES.Y; ++y) + { + int E = Limit(abs(Map[1024 - x][1024 - (RES.Y - y)]) / 30, 0, 100); + BackGround->PutPixel(x, y, GetBackGroundColor(E)); + } + + delete [] Map; +} + +col16 igraph::GetBackGroundColor(int Element) +{ + switch(CurrentColorType) + { + case GRAY_FRACTAL: return MakeRGB16(Element, Element, Element); + case RED_FRACTAL: return MakeRGB16(Element + (Element >> 1), Element / 3, Element / 3); + case GREEN_FRACTAL: return MakeRGB16(Element / 3, Element + (Element >> 2), Element / 3); + case BLUE_FRACTAL: return MakeRGB16(Element / 3, Element / 3, Element + (Element >> 1)); + case YELLOW_FRACTAL: return MakeRGB16(Element + (Element >> 1), Element + (Element >> 1), Element / 3); + } + + return 0; +} + +void igraph::BlitBackGround(v2 Pos, v2 Border) +{ + blitdata B = { DOUBLE_BUFFER, + { Pos.X, Pos.Y }, + { Pos.X, Pos.Y }, + { Border.X, Border.Y }, + { 0 }, + 0, + 0 }; + + BackGround->NormalBlit(B); +} + +bitmap* igraph::GenerateScarBitmap(int BodyPart, int Severity, int Color) +{ + bitmap* CacheBitmap = SilhouetteCache[BodyPart][0][SILHOUETTE_NORMAL]; + bitmap* Scar = new bitmap(SILHOUETTE_SIZE, 0); + + v2 StartPos; + while(true) + { + StartPos = v2(RAND_N(SILHOUETTE_SIZE.X),RAND_N(SILHOUETTE_SIZE.Y)); + if(CacheBitmap->GetPixel(StartPos) != 0) + break; + } + + v2 EndPos; + while(true) + { + double Angle = 2 * FPI * RAND_256 / 256; + EndPos.X = int(StartPos.X + cos(Angle) * (Severity + 1)); + EndPos.Y = int(StartPos.Y + sin(Angle) * (Severity + 1)); + if(CacheBitmap->IsValidPos(EndPos) && CacheBitmap->GetPixel(EndPos) != 0) + break; + } + Scar->DrawLine(StartPos, EndPos, Color); + return Scar; +} diff --git a/Main/Source/item.cpp b/Main/Source/item.cpp new file mode 100644 index 0000000..d4e4053 --- /dev/null +++ b/Main/Source/item.cpp @@ -0,0 +1,1746 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through itemset.cpp */ + +cchar* ToHitValueDescription[] = { "unbelievably inaccurate", "extremely inaccurate", "inaccurate", "decently accurate", "accurate", "highly accurate", "extremely accurate", "unbelievably accurate" }; +cchar* StrengthValueDescription[] = { "fragile", "rather sturdy", "sturdy", "strong", "very strong", "extremely strong", "almost unbreakable" }; + +itemprototype::itemprototype(const itemprototype* Base, itemspawner Spawner, itemcloner Cloner, cchar* ClassID) : Base(Base), Spawner(Spawner), Cloner(Cloner), ClassID(ClassID) { Index = protocontainer::Add(this); } + +truth itemdatabase::AllowRandomInstantiation() const { return !(Config & S_LOCK_ID); } + +item::item() : Slot(0), CloneMotherID(0), Fluid(0), LifeExpectancy(0), ItemFlags(0) { } +truth item::IsOnGround() const { return Slot[0]->IsOnGround(); } +truth item::IsSimiliarTo(item* Item) const { return Item->GetType() == GetType() && Item->GetConfig() == GetConfig(); } +double item::GetBaseDamage() const { return sqrt(5e-5 * GetWeaponStrength()) + GetDamageBonus(); } +int item::GetBaseMinDamage() const { return int(GetBaseDamage() * 0.75); } +int item::GetBaseMaxDamage() const { return int(GetBaseDamage() * 1.25) + 1; } +int item::GetBaseToHitValue() const { return int(10000. / (1000 + GetWeight()) + GetTHVBonus()); } +int item::GetBaseBlockValue() const { return int((10000. / (1000 + GetWeight()) + GetTHVBonus()) * GetBlockModifier() / 10000.); } +truth item::IsInCorrectSlot(int I) const { return I == RIGHT_WIELDED_INDEX || I == LEFT_WIELDED_INDEX; } +truth item::IsInCorrectSlot() const { return IsInCorrectSlot(static_cast(*Slot)->GetEquipmentIndex()); } +int item::GetEquipmentIndex() const { return static_cast(*Slot)->GetEquipmentIndex(); } +int item::GetGraphicsContainerIndex() const { return GR_ITEM; } +truth item::IsBroken() const { return GetConfig() & BROKEN; } +truth item::IsFood() const { return DataBase->Category & FOOD; } +cchar* item::GetBreakVerb() const { return "breaks"; } +square* item::GetSquareUnderEntity(int I) const { return GetSquareUnder(I); } +square* item::GetSquareUnder(int I) const { return Slot[I] ? Slot[I]->GetSquareUnder() : 0; } +lsquare* item::GetLSquareUnder(int I) const { return static_cast(Slot[I]->GetSquareUnder()); } +void item::SignalStackAdd(stackslot* StackSlot, void (stack::*)(item*, truth)) { Slot[0] = StackSlot; } +truth item::IsAnimated() const { return GraphicData.AnimationFrames > 1 || (Fluid && ShowFluids()); } +truth item::IsRusted() const { return MainMaterial->GetRustLevel() != NOT_RUSTED; } +truth item::IsEatable(ccharacter* Eater) const { return GetConsumeMaterial(Eater, &material::IsSolid) && IsConsumable(); } +truth item::IsDrinkable(ccharacter* Eater) const { return GetConsumeMaterial(Eater, &material::IsLiquid) && IsConsumable(); } +pixelpredicate item::GetFluidPixelAllowedPredicate() const { return &rawbitmap::IsTransparent; } +void item::Cannibalize() { Flags |= CANNIBALIZED; } +void item::SetMainMaterial(material* NewMaterial, int SpecialFlags) { SetMaterial(MainMaterial, NewMaterial, GetDefaultMainVolume(), SpecialFlags); } +void item::ChangeMainMaterial(material* NewMaterial, int SpecialFlags) { ChangeMaterial(MainMaterial, NewMaterial, GetDefaultMainVolume(), SpecialFlags); } +void item::InitMaterials(const materialscript* M, const materialscript*, truth CUP) { InitMaterials(M->Instantiate(), CUP); } +int item::GetMainMaterialRustLevel() const { return MainMaterial->GetRustLevel(); } + +item::item(citem& Item) : object(Item), Slot(0), Size(Item.Size), DataBase(Item.DataBase), Volume(Item.Volume), Weight(Item.Weight), Fluid(0), SquaresUnder(Item.SquaresUnder), LifeExpectancy(Item.LifeExpectancy), ItemFlags(Item.ItemFlags) +{ + Flags &= ENTITY_FLAGS|SQUARE_POSITION_BITS; + ID = game::CreateNewItemID(this); + CloneMotherID = new idholder(Item.ID); + idholder* TI = CloneMotherID; + + for(idholder* II = Item.CloneMotherID; II; II = II->Next) + TI = TI->Next = new idholder(II->ID); + + TI->Next = 0; + Slot = new slot*[SquaresUnder]; + + for(int c = 0; c < SquaresUnder; ++c) + Slot[c] = 0; +} + +item::~item() +{ + delete [] Slot; + game::RemoveItemID(ID); + + fluid** FP = Fluid; + + if(FP) + { + for(int c = 0; c < SquaresUnder; ++c) + for(fluid* F = FP[c]; F;) + { + fluid* ToDel = F; + F = F->Next; + delete ToDel; + } + + delete [] FP; + } +} + +void item::Fly(character* Thrower, int Direction, int Force) +{ + int Range = Force * 25 / Max(long(sqrt(GetWeight())), 1L); + + lsquare* LSquareUnder = GetLSquareUnder(); + RemoveFromSlot(); + LSquareUnder->GetStack()->AddItem(this, false); + + if(!Range || GetSquaresUnder() != 1) + { + if(GetLSquareUnder()->GetRoom()) + GetLSquareUnder()->GetRoom()->AddItemEffect(this); + + return; + } + + if(Direction == RANDOM_DIR) + Direction = RAND() & 7; + + v2 StartingPos = GetPos(); + v2 Pos = StartingPos; + v2 DirVector = game::GetMoveVector(Direction); + truth Breaks = false; + double BaseDamage, BaseToHitValue; + + /*** check ***/ + + if(Thrower) + { + int Bonus = Thrower->IsHumanoid() ? Thrower->GetCWeaponSkill(GetWeaponCategory())->GetBonus() : 1000; + BaseDamage = sqrt(5e-12 * GetWeaponStrength() * Force / Range) * Bonus; + BaseToHitValue = 10 * Bonus * Thrower->GetMoveEase() / (500 + GetWeight()) * Thrower->GetAttribute(DEXTERITY) * sqrt(2.5e-8 * Thrower->GetAttribute(PERCEPTION)) / Range; + } + else + { + BaseDamage = sqrt(5e-6 * GetWeaponStrength() * Force / Range); + BaseToHitValue = 10 * 100 / (500 + GetWeight()) / Range; + } + + int RangeLeft; + + for(RangeLeft = Range; RangeLeft; --RangeLeft) + { + if(!GetLevel()->IsValidPos(Pos + DirVector)) + break; + + lsquare* JustHit = GetNearLSquare(Pos + DirVector); + + if(!JustHit->IsFlyable()) + { + Breaks = true; + JustHit->GetOLTerrain()->HasBeenHitByItem(Thrower, this, int(BaseDamage * sqrt(RangeLeft))); + break; + } + else + { + clock_t StartTime = clock(); + Pos += DirVector; + RemoveFromSlot(); + JustHit->GetStack()->AddItem(this, false); + truth Draw = game::OnScreen(JustHit->GetPos()) && JustHit->CanBeSeenByPlayer(); + + if(Draw) + game::DrawEverything(); + + if(JustHit->GetCharacter()) + { + int Damage = int(BaseDamage * sqrt(RangeLeft)); + double ToHitValue = BaseToHitValue * RangeLeft; + int Returned = HitCharacter(Thrower, JustHit->GetCharacter(), Damage, ToHitValue, Direction); + + if(Returned == HIT) + Breaks = true; + + if(Returned != MISSED) + break; + } + + if(Draw) + while(clock() - StartTime < 0.03 * CLOCKS_PER_SEC); + } + } + + if(Breaks) + ReceiveDamage(Thrower, int(sqrt(GetWeight() * RangeLeft) / 10), THROW|PHYSICAL_DAMAGE, Direction); + + if(Exists() && GetLSquareUnder()->GetRoom()) + GetLSquareUnder()->GetRoom()->AddItemEffect(this); +} + +int item::HitCharacter(character* Thrower, character* Dude, int Damage, double ToHitValue, int Direction) +{ + if(Dude->Catches(this)) + return CATCHED; + + if(Thrower && !EffectIsGood()) + Thrower->Hostility(Dude); + + if(Dude->DodgesFlyingItem(this, ToHitValue)) + { + if(Dude->IsPlayer()) + ADD_MESSAGE("%s misses you.", CHAR_NAME(DEFINITE)); + else if(Dude->CanBeSeenByPlayer()) + ADD_MESSAGE("%s misses %s.", CHAR_NAME(DEFINITE), Dude->CHAR_NAME(DEFINITE)); + + return MISSED; + } + + Dude->HasBeenHitByItem(Thrower, this, Damage, ToHitValue, Direction); + return HIT; +} + +double item::GetWeaponStrength() const +{ + return GetFormModifier() * GetMainMaterial()->GetStrengthValue() * sqrt(GetMainMaterial()->GetWeight()); +} + +int item::GetStrengthRequirement() const +{ + double WeightTimesSize = GetWeight() * GetSize(); + return int(1.25e-10 * WeightTimesSize * WeightTimesSize); +} + +truth item::Apply(character* Applier) +{ + if(Applier->IsPlayer()) + ADD_MESSAGE("You can't apply this!"); + + return false; +} + +/* Returns truth that tells whether the Polymorph really happened */ + +truth item::Polymorph(character* Polymorpher, stack* CurrentStack) +{ + if(!IsPolymorphable()) + return false; + else + { + if(Polymorpher && IsOnGround()) + { + room* Room = GetRoom(); + + if(Room) + Room->HostileAction(Polymorpher); + } + + if(GetSquarePosition() != CENTER) + { + stack* Stack = CurrentStack->GetLSquareUnder()->GetStackOfAdjacentSquare(GetSquarePosition()); + + if(Stack) + CurrentStack = Stack; + } + + CurrentStack->AddItem(protosystem::BalancedCreateItem(0, MAX_PRICE, ANY_CATEGORY, 0, 0, 0, true)); + RemoveFromSlot(); + SendToHell(); + return true; + } +} + +/* Returns whether the Eater must stop eating the item */ + +truth item::Consume(character* Eater, long Amount) +{ + material* ConsumeMaterial = GetConsumeMaterial(Eater); + + if(!ConsumeMaterial) + return true; + + if(Eater->IsPlayer() && !(Flags & CANNIBALIZED) && Eater->CheckCannibalism(ConsumeMaterial)) + { + game::DoEvilDeed(25); + ADD_MESSAGE("You feel that this was an evil deed."); + Cannibalize(); + } + + ulong ID = GetID(); + material* Garbage = ConsumeMaterial->EatEffect(Eater, Amount); + item* NewConsuming = GetID() ? this : game::SearchItem(ID); + material* NewConsumeMaterial = NewConsuming->GetConsumeMaterial(Eater); + + if(!NewConsuming->Exists() + || !NewConsumeMaterial + || !NewConsumeMaterial->IsSameAs(ConsumeMaterial)) + ConsumeMaterial->FinishConsuming(Eater); + + delete Garbage; + return !NewConsuming->Exists() || !NewConsumeMaterial; +} + +truth item::CanBeEatenByAI(ccharacter* Eater) const +{ + material* ConsumeMaterial = GetConsumeMaterial(Eater); + + return (!Eater->IsPet() + || !(Eater->GetCommandFlags() & DONT_CONSUME_ANYTHING_VALUABLE) + || !IsValuable()) + && IsConsumable() + && ConsumeMaterial && ConsumeMaterial->CanBeEatenByAI(Eater); +} + +void item::Save(outputfile& SaveFile) const +{ + SaveFile << (ushort)GetType(); + object::Save(SaveFile); + SaveFile << (ushort)GetConfig(); + SaveFile << (ushort)Flags; + SaveFile << Size << ID << LifeExpectancy << ItemFlags; + SaveLinkedList(SaveFile, CloneMotherID); + + if(Fluid) + { + SaveFile.Put(true); + + for(int c = 0; c < SquaresUnder; ++c) + SaveLinkedList(SaveFile, Fluid[c]); + } + else + SaveFile.Put(false); +} + +void item::Load(inputfile& SaveFile) +{ + object::Load(SaveFile); + databasecreator::InstallDataBase(this, ReadType(SaveFile)); + Flags |= ReadType(SaveFile) & ~ENTITY_FLAGS; + SaveFile >> Size >> ID >> LifeExpectancy >> ItemFlags; + LoadLinkedList(SaveFile, CloneMotherID); + + if(LifeExpectancy) + Enable(); + + game::AddItemID(this, ID); + + if(SaveFile.Get()) + { + Fluid = new fluid*[SquaresUnder]; + + for(int c = 0; c < SquaresUnder; ++c) + { + LoadLinkedList(SaveFile, Fluid[c]); + + for(fluid* F = Fluid[c]; F; F = F->Next) + F->SetMotherItem(this); + } + } +} + +void item::TeleportRandomly() +{ + if(GetSquaresUnder() == 1) // gum solution + { + lsquare* Square = GetNearLSquare(GetLevel()->GetRandomSquare()); + MoveTo(Square->GetStack()); + + if(Square->CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s appears!", CHAR_NAME(INDEFINITE)); + } +} + +int item::GetStrengthValue() const +{ + return long(GetStrengthModifier()) * GetMainMaterial()->GetStrengthValue() / 2000; +} + +void item::RemoveFromSlot() +{ + for(int c = 0; c < SquaresUnder; ++c) + if(Slot[c]) + { + try + { + Slot[c]->Empty(); + } + catch(quitrequest) + { + SendToHell(); + throw; + } + + Slot[c] = 0; + } +} + +void item::MoveTo(stack* Stack) +{ + RemoveFromSlot(); + Stack->AddItem(this); +} + +cchar* item::GetItemCategoryName(long Category) // convert to array +{ + switch(Category) + { + case HELMET: return "Helmets"; + case AMULET: return "Amulets"; + case CLOAK: return "Cloaks"; + case BODY_ARMOR: return "Body armors"; + case WEAPON: return "Weapons"; + case SHIELD: return "Shields"; + case RING: return "Rings"; + case GAUNTLET: return "Gauntlets"; + case BELT: return "Belts"; + case BOOT: return "Boots"; + case FOOD: return "Food"; + case POTION: return "Potions"; + case SCROLL: return "Scrolls"; + case BOOK: return "Books"; + case WAND: return "Wands"; + case TOOL: return "Tools"; + case VALUABLE: return "Valuables"; + case MISC: return "Miscellaneous items"; + } + + return "Warezzz"; +} + +int item::GetResistance(int Type) const +{ + switch(Type&0xFFF) + { + case PHYSICAL_DAMAGE: return GetStrengthValue(); + case SOUND: + case ENERGY: + case DRAIN: + case MUSTARD_GAS_DAMAGE: + return 0; + case FIRE: return GetFireResistance(); + case POISON: return GetPoisonResistance(); + case ELECTRICITY: return GetElectricityResistance(); + case ACID: return GetAcidResistance(); + } + + ABORT("Resistance lack detected!"); + return 0; +} + +truth item::Open(character* Char) +{ + if(Char->IsPlayer()) + ADD_MESSAGE("You can't open %s.", CHAR_NAME(DEFINITE)); + + return false; +} + +item* itemprototype::SpawnAndLoad(inputfile& SaveFile) const +{ + item* Item = Spawner(0, LOAD); + Item->Load(SaveFile); + Item->CalculateAll(); + return Item; +} + +void item::LoadDataBaseStats() +{ + SetSize(GetDefaultSize()); +} + +void item::Initialize(int NewConfig, int SpecialFlags) +{ + CalculateSquaresUnder(); + Slot = new slot*[SquaresUnder]; + + for(int c = 0; c < SquaresUnder; ++c) + Slot[c] = 0; + + if(!(SpecialFlags & LOAD)) + { + ID = game::CreateNewItemID(this); + databasecreator::InstallDataBase(this, NewConfig); + LoadDataBaseStats(); + RandomizeVisualEffects(); + Flags |= CENTER << SQUARE_POSITION_SHIFT; + + if(!(SpecialFlags & NO_MATERIALS)) + GenerateMaterials(); + } + + if(!(SpecialFlags & LOAD)) + PostConstruct(); + + if(!(SpecialFlags & (LOAD|NO_MATERIALS))) + { + CalculateAll(); + + if(!(SpecialFlags & NO_PIC_UPDATE)) + UpdatePictures(); + } +} + +truth item::ShowMaterial() const +{ + if(GetMainMaterialConfig().Size == 1) + return GetMainMaterial()->GetConfig() != GetMainMaterialConfig()[0]; + else + return true; +} + +long item::GetBlockModifier() const +{ + if(!IsShield(0)) + return GetSize() * GetRoundness() << 1; + else + return GetSize() * GetRoundness() << 2; +} + +truth item::CanBeSeenByPlayer() const +{ + return CanBeSeenBy(PLAYER); +} + +truth item::CanBeSeenBy(ccharacter* Who) const +{ + for(int c = 0; c < SquaresUnder; ++c) + if(Slot[c] && Slot[c]->CanBeSeenBy(Who)) + return true; + + return Who->IsPlayer() && game::GetSeeWholeMapCheatMode(); +} + +festring item::GetDescription(int Case) const +{ + if(CanBeSeenByPlayer()) + return GetName(Case); + else + return CONST_S("something"); +} + +void item::SignalVolumeAndWeightChange() +{ + CalculateVolumeAndWeight(); + + for(int c = 0; c < SquaresUnder; ++c) + if(Slot[c]) + Slot[c]->SignalVolumeAndWeightChange(); +} + +void item::CalculateVolumeAndWeight() +{ + Volume = Weight = 0; + + for(int c = 0; c < GetMaterials(); ++c) + { + cmaterial* Material = GetMaterial(c); + + if(Material) + { + Volume += Material->GetVolume(); + Weight += Material->GetWeight(); + } + } +} + +void item::SignalEmitationIncrease(col24 EmitationUpdate) +{ + if(game::CompareLights(EmitationUpdate, Emitation) > 0) + { + game::CombineLights(Emitation, EmitationUpdate); + + for(int c = 0; c < SquaresUnder; ++c) + if(Slot[c]) + Slot[c]->SignalEmitationIncrease(EmitationUpdate); + } +} + +void item::SignalEmitationDecrease(col24 EmitationUpdate) +{ + if(game::CompareLights(EmitationUpdate, Emitation) >= 0 && Emitation) + { + col24 Backup = Emitation; + CalculateEmitation(); + + if(Backup != Emitation) + for(int c = 0; c < SquaresUnder; ++c) + if(Slot[c]) + Slot[c]->SignalEmitationDecrease(EmitationUpdate); + } +} + +void item::CalculateAll() +{ + CalculateVolumeAndWeight(); + CalculateEmitation(); +} + +/* Temporary and buggy. */ + +void item::WeaponSkillHit(int Hits) +{ + if(Slot[0] && Slot[0]->IsGearSlot()) + static_cast(static_cast(*Slot)->GetBodyPart())->WieldedSkillHit(Hits); +} + +/* Returns 0 if item cannot be cloned */ + +item* item::Duplicate(ulong Flags) +{ + if(!(Flags & IGNORE_PROHIBITIONS) + && ((!(Flags & MIRROR_IMAGE) && !CanBeCloned()) + || (Flags & MIRROR_IMAGE && (!CanBeMirrored() + || (MainMaterial + && !(MainMaterial->GetCommonFlags() & CAN_BE_MIRRORED)) + || (GetSecondaryMaterial() + && !(GetSecondaryMaterial()->GetCommonFlags() & CAN_BE_MIRRORED)))))) + return 0; + + item* Clone = GetProtoType()->Clone(this); + + if(Flags & MIRROR_IMAGE) + Clone->SetLifeExpectancy(Flags >> LE_BASE_SHIFT & LE_BASE_RANGE, + Flags >> LE_RAND_SHIFT & LE_RAND_RANGE); + + idholder* I = new idholder(ID); + I->Next = CloneMotherID; + CloneMotherID = I; + game::RemoveItemID(ID); + ID = game::CreateNewItemID(this); + Clone->UpdatePictures(); + return Clone; +} + +void item::AddInventoryEntry(ccharacter*, festring& Entry, int Amount, truth ShowSpecialInfo) const +{ + if(Amount == 1) + AddName(Entry, INDEFINITE); + else + { + Entry << Amount << ' '; + AddName(Entry, PLURAL); + } + + if(ShowSpecialInfo) + Entry << " [" << GetWeight() * Amount << "g]"; +} + +const itemdatabase* itemprototype::ChooseBaseForConfig(itemdatabase** TempConfig, int Configs, int ConfigNumber) +{ + if(!(ConfigNumber & BROKEN)) + return *TempConfig; + else + { + ConfigNumber ^= BROKEN; + + for(int c = 0; c < Configs; ++c) + if(TempConfig[c]->Config == ConfigNumber) + return TempConfig[c]; + + return *TempConfig; + } +} + +truth item::ReceiveDamage(character* Damager, int Damage, int Type, int Dir) +{ + if(CanBeBroken() && !IsBroken() && Type & (PHYSICAL_DAMAGE|SOUND|ENERGY|ACID)) + { + int StrengthValue = GetStrengthValue(); + + if(!StrengthValue) + StrengthValue = 1; + + if(Damage > StrengthValue << 2 && RAND() & 3 && RAND() % (25 * Damage / StrengthValue) >= 100) + { + Break(Damager, Dir); + return true; + } + } + + if(Type & ACID && IsBroken() && IsDestroyable(Damager)) + { + int StrengthValue = GetStrengthValue(); + + if(!StrengthValue) + StrengthValue = 1; + + if(Damage > StrengthValue << 4 && !(RAND() & 3) && RAND() % (100 * Damage / StrengthValue) >= 100) + { + Destroy(Damager, Dir); + return true; + } + } + + return false; +} + +void itemdatabase::InitDefaults(const itemprototype* NewProtoType, int NewConfig) +{ + IsAbstract = false; + ProtoType = NewProtoType; + Config = NewConfig; + + if(NewConfig & BROKEN) + { + if(Adjective.GetSize()) + Adjective.Insert(0, "broken "); + else + Adjective = CONST_S("broken"); + + DefaultSize >>= 1; + FormModifier >>= 2; + StrengthModifier >>= 1; + } +} + +long item::GetNutritionValue() const +{ + long NV = 0; + + for(int c = 0; c < GetMaterials(); ++c) + if(GetMaterial(c)) + NV += GetMaterial(c)->GetTotalNutritionValue(); + + return NV; +} + +void item::SignalSpoil(material*) +{ + if(!Exists()) + return; + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s spoils completely.", GetExtendedDescription().CStr()); + + truth Equipped = PLAYER->Equips(this); + Disappear(); + + if(Equipped) + game::AskForKeyPress(CONST_S("Equipment destroyed! [press any key to continue]")); +} + +item* item::DuplicateToStack(stack* CurrentStack, ulong Flags) +{ + item* Duplicated = Duplicate(Flags); + + if(!Duplicated) + return 0; + + CurrentStack->AddItem(Duplicated); + return Duplicated; +} + +truth item::CanBePiledWith(citem* Item, ccharacter* Viewer) const +{ + return (GetType() == Item->GetType() + && GetConfig() == Item->GetConfig() + && ItemFlags == Item->ItemFlags + && (WeightIsIrrelevant() || Weight == Item->Weight) + && MainMaterial->IsSameAs(Item->MainMaterial) + && MainMaterial->GetSpoilLevel() == Item->MainMaterial->GetSpoilLevel() + && MainMaterial->GetRustLevel() == Item->MainMaterial->GetRustLevel() + && Viewer->GetCWeaponSkillLevel(this) == Viewer->GetCWeaponSkillLevel(Item) + && Viewer->GetSWeaponSkillLevel(this) == Viewer->GetSWeaponSkillLevel(Item) + && !Fluid && !Item->Fluid + && !LifeExpectancy == !Item->LifeExpectancy); +} + +void item::Break(character* Breaker, int) +{ + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s %s.", GetExtendedDescription().CStr(), GetBreakVerb()); + + if(Breaker && IsOnGround()) + { + room* Room = GetRoom(); + + if(Room) + Room->HostileAction(Breaker); + } + + item* Broken = GetProtoType()->Clone(this); + Broken->SetConfig(GetConfig() | BROKEN); + Broken->SetSize(Broken->GetSize() >> 1); + DonateFluidsTo(Broken); + DonateIDTo(Broken); + DonateSlotTo(Broken); + SendToHell(); + + if(PLAYER->Equips(Broken)) + game::AskForKeyPress(CONST_S("Equipment broken! [press any key to continue]")); +} + +void item::Be() +{ + MainMaterial->Be(ItemFlags); + + if(Exists() && LifeExpectancy) + if(LifeExpectancy == 1) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s disappears.", GetExtendedDescription().CStr()); + + truth Equipped = PLAYER->Equips(this); + Disappear(); + + if(Equipped) + game::AskForKeyPress(CONST_S("Equipment destroyed! [press any key to continue]")); + } + else + --LifeExpectancy; +} + +int item::GetOfferValue(int Receiver) const +{ + /* Temporary */ + + int OfferValue = int(sqrt(GetTruePrice())); + + if(Receiver == GetAttachedGod()) + OfferValue <<= 1; + else + OfferValue >>= 1; + + return OfferValue; +} + +void item::SignalEnchantmentChange() +{ + for(int c = 0; c < SquaresUnder; ++c) + if(Slot[c]) + Slot[c]->SignalEnchantmentChange(); +} + +long item::GetEnchantedPrice(int Enchantment) const +{ + return !PriceIsProportionalToEnchantment() ? item::GetPrice() : Max(item::GetPrice() * Enchantment * Enchantment, 0); +} + +item* item::Fix() +{ + item* Fixed = this; + + if(IsBroken()) + { + Fixed = GetProtoType()->Clone(this); + Fixed->SetConfig(GetConfig() ^ BROKEN); + Fixed->SetSize(Fixed->GetSize() << 1); + DonateFluidsTo(Fixed); + DonateIDTo(Fixed); + DonateSlotTo(Fixed); + SendToHell(); + } + + return Fixed; +} + +void item::DonateSlotTo(item* Item) +{ + if(Slot[0]) + { + Slot[0]->DonateTo(Item); + Slot[0] = 0; + + for(int c = 1; c < SquaresUnder; ++c) + if(Slot[c]) + { + Slot[c]->Empty(); + Slot[c] = 0; + } + } +} + +int item::GetSpoilLevel() const +{ + return MainMaterial->GetSpoilLevel(); +} + +void item::SignalSpoilLevelChange(material*) +{ + if(!IsAnimated() && GetSpoilLevel() && Slot[0] && Slot[0]->IsVisible()) + for(int c = 0; c < SquaresUnder; ++c) + GetSquareUnder(c)->IncStaticAnimatedEntities(); + + SignalVolumeAndWeightChange(); // gum + UpdatePictures(); +} + +truth item::AllowSpoil() const +{ + if(IsOnGround()) + { + lsquare* Square = GetLSquareUnder(); + int RoomNumber = Square->GetRoomIndex(); + return !RoomNumber || Square->GetLevel()->GetRoom(RoomNumber)->AllowSpoil(this); + } + else + return true; +} + +void item::ResetSpoiling() +{ + for(int c = 0; c < GetMaterials(); ++c) + if(GetMaterial(c)) + GetMaterial(c)->ResetSpoiling(); +} + +cchar* item::GetBaseToHitValueDescription() const +{ + if(GetBaseToHitValue() < 10) + return ToHitValueDescription[Min(GetBaseToHitValue(), 6)]; + else + return ToHitValueDescription[7]; +} + +cchar* item::GetBaseBlockValueDescription() const +{ + if(GetBaseBlockValue() < 20) + return ToHitValueDescription[Min(GetBaseBlockValue() >> 1, 6)]; + else + return ToHitValueDescription[7]; +} + +cchar* item::GetStrengthValueDescription() const +{ + int SV = GetStrengthValue(); + + if(SV < 3) + return StrengthValueDescription[0]; + else if(SV < 5) + return StrengthValueDescription[1]; + else if(SV < 8) + return StrengthValueDescription[2]; + else if(SV < 11) + return StrengthValueDescription[3]; + else if(SV < 16) + return StrengthValueDescription[4]; + else if(SV < 20) + return StrengthValueDescription[5]; + else + return StrengthValueDescription[6]; +} + +void item::SpecialGenerationHandler() +{ + if(HandleInPairs()) + Slot[0]->AddFriendItem(Duplicate()); +} + +void item::SortAllItems(const sortdata& SortData) const +{ + if(SortData.Sorter == 0 || (this->*SortData.Sorter)(SortData.Character)) + SortData.AllItems.push_back(const_cast(this)); +} + +int item::GetAttachedGod() const +{ + return DataBase->AttachedGod ? DataBase->AttachedGod : MainMaterial->GetAttachedGod(); +} + +long item::GetMaterialPrice() const +{ + return MainMaterial->GetRawPrice(); +} + +long item::GetTruePrice() const +{ + if(LifeExpectancy) + return 0; + + long Price = Max(GetPrice(), GetMaterialPrice()); + + if(Spoils()) + Price = Price * (100 - GetMaxSpoilPercentage()) / 500; + + return Price; +} + +#ifdef WIZARD + +void item::AddAttackInfo(felist& List) const +{ + festring Entry(40, ' '); + Entry << int(GetWeight()); + Entry.Resize(50); + Entry << int(GetSize()); + Entry.Resize(60); + Entry << int(GetStrengthRequirement()); + Entry.Resize(70); + Entry << GetBaseMinDamage() << '-' << GetBaseMaxDamage(); + List.AddEntry(Entry, LIGHT_GRAY); +} + +void item::AddMiscellaneousInfo(felist& List) const +{ + festring Entry(40, ' '); + Entry << int(GetTruePrice()); + Entry.Resize(55); + Entry << GetOfferValue(0); + Entry.Resize(70); + Entry << int(GetNutritionValue()); + List.AddEntry(Entry, LIGHT_GRAY); +} + +#endif + +void item::PreProcessForBone() +{ + if(IsQuestItem()) + { + RemoveFromSlot(); + SendToHell(); + } + else + { + game::RemoveItemID(ID); + ID = -ID; + game::AddItemID(this, ID); + } +} + +void item::PostProcessForBone() +{ + boneidmap::iterator BI = game::GetBoneItemIDMap().find(-ID); + game::RemoveItemID(ID); + + if(BI == game::GetBoneItemIDMap().end()) + { + ulong NewID = game::CreateNewItemID(this); + game::GetBoneItemIDMap().insert(std::make_pair(-ID, NewID)); + ID = NewID; + } + else + { + if(game::SearchItem(BI->second)) + int esko = esko = 2; + + ID = BI->second; + game::AddItemID(this, ID); + } + + for(idholder* I = CloneMotherID; I; I = I->Next) + { + BI = game::GetBoneItemIDMap().find(I->ID); + + if(BI == game::GetBoneItemIDMap().end()) + { + ulong NewCloneMotherID = game::CreateNewItemID(0); + game::GetBoneItemIDMap().insert(std::make_pair(I->ID, NewCloneMotherID)); + I->ID = NewCloneMotherID; + } + else + I->ID = BI->second; + } +} + +void item::SetConfig(int NewConfig, int SpecialFlags) +{ + databasecreator::InstallDataBase(this, NewConfig); + CalculateAll(); + + if(!(SpecialFlags & NO_PIC_UPDATE)) + UpdatePictures(); +} + +god* item::GetMasterGod() const +{ + return game::GetGod(GetConfig()); +} + +int itemprototype::CreateSpecialConfigurations(itemdatabase** TempConfig, int Configs, int Level) +{ + if(Level) + return Configs; + + if(TempConfig[0]->CreateDivineConfigurations) + Configs = databasecreator::CreateDivineConfigurations(this, TempConfig, Configs); + + /* Gum solution */ + + if(TempConfig[0]->CreateLockConfigurations) + { + const item::database*const* KeyConfigData = key::ProtoType.GetConfigData(); + int KeyConfigSize = key::ProtoType.GetConfigSize(); + int OldConfigs = Configs; + + for(int c1 = 0; c1 < OldConfigs; ++c1) + if(!TempConfig[c1]->IsAbstract) + { + int BaseConfig = TempConfig[c1]->Config; + int NewConfig = BaseConfig | BROKEN_LOCK; + itemdatabase* ConfigDataBase = new itemdatabase(*TempConfig[c1]); + ConfigDataBase->InitDefaults(this, NewConfig); + ConfigDataBase->PostFix << "with a broken lock"; + ConfigDataBase->Possibility = 0; + TempConfig[Configs++] = ConfigDataBase; + + for(int c2 = 0; c2 < KeyConfigSize; ++c2) + { + NewConfig = BaseConfig | KeyConfigData[c2]->Config; + ConfigDataBase = new itemdatabase(*TempConfig[c1]); + ConfigDataBase->InitDefaults(this, NewConfig); + ConfigDataBase->PostFix << "with "; + + if(KeyConfigData[c2]->UsesLongAdjectiveArticle) + ConfigDataBase->PostFix << "an "; + else + ConfigDataBase->PostFix << "a "; + + ConfigDataBase->PostFix << KeyConfigData[c2]->Adjective << " lock"; + ConfigDataBase->Possibility = 0; + TempConfig[Configs++] = ConfigDataBase; + } + } + } + + return Configs; +} + +void item::Draw(blitdata& BlitData) const +{ + cint AF = GraphicData.AnimationFrames; + cint F = !(BlitData.CustomData & ALLOW_ANIMATE) || AF == 1 ? 0 : GET_TICK() & (AF - 1); + cbitmap* P = GraphicData.Picture[F]; + + if(BlitData.CustomData & ALLOW_ALPHA) + P->AlphaLuminanceBlit(BlitData); + else + P->LuminanceMaskedBlit(BlitData); + + if(Fluid && ShowFluids()) + DrawFluids(BlitData); +} + +v2 item::GetLargeBitmapPos(v2 BasePos, int I) const +{ + cint SquareIndex = I ? I / (GraphicData.AnimationFrames >> 2) : 0; + return v2(SquareIndex & 1 ? BasePos.X + 16 : BasePos.X, SquareIndex & 2 ? BasePos.Y + 16 : BasePos.Y); +} + +void item::LargeDraw(blitdata& BlitData) const +{ + cint TrueAF = GraphicData.AnimationFrames >> 2; + cint SquareIndex = BlitData.CustomData & SQUARE_INDEX_MASK; + cint F = !(BlitData.CustomData & ALLOW_ANIMATE) ? SquareIndex * TrueAF : SquareIndex * TrueAF + (GET_TICK() & (TrueAF - 1)); + cbitmap* P = GraphicData.Picture[F]; + + if(BlitData.CustomData & ALLOW_ALPHA) + P->AlphaLuminanceBlit(BlitData); + else + P->LuminanceMaskedBlit(BlitData); +} + +void item::DonateIDTo(item* Item) +{ + game::RemoveItemID(Item->ID); + game::UpdateItemID(Item, ID); + Item->ID = ID; + ID = 0; +} + +void item::SignalRustLevelChange() +{ + SignalVolumeAndWeightChange(); + UpdatePictures(); + SendNewDrawAndMemorizedUpdateRequest(); +} + +const rawbitmap* item::GetRawPicture() const +{ + return igraph::GetRawGraphic(GetGraphicsContainerIndex()); +} + +void item::RemoveFluid(fluid* ToBeRemoved) +{ + truth WasAnimated = IsAnimated(); + truth HasFluids = false; + + for(int c = 0; c < SquaresUnder; ++c) + { + fluid* F = Fluid[c]; + + if(F == ToBeRemoved) + Fluid[c] = F->Next; + else if(F) + { + fluid* LF = F; + + for(F = F->Next; F; LF = F, F = F->Next) + if(F == ToBeRemoved) + { + LF->Next = F->Next; + break; + } + } + + if(Fluid[c]) + HasFluids = true; + } + + UpdatePictures(); + + if(!HasFluids) + { + delete [] Fluid; + Fluid = 0; + + if(!IsAnimated() != !WasAnimated && Slot[0]->IsVisible()) + GetSquareUnder()->DecStaticAnimatedEntities(); + } + + SignalEmitationDecrease(ToBeRemoved->GetEmitation()); + SignalVolumeAndWeightChange(); +} + +void item::AddFluid(liquid* ToBeAdded, festring LocationName, int SquareIndex, truth IsInside) +{ + truth WasAnimated = IsAnimated(); + + if(Fluid) + { + fluid* F = Fluid[SquareIndex]; + + if(!F) + Fluid[SquareIndex] = new fluid(ToBeAdded, this, LocationName, IsInside); + else + { + fluid* LF; + + do + { + if(ToBeAdded->IsSameAs(F->GetLiquid())) + { + F->AddLiquidAndVolume(ToBeAdded->GetVolume()); + delete ToBeAdded; + return; + } + + LF = F; + F = F->Next; + } + while(F); + + LF->Next = new fluid(ToBeAdded, this, LocationName, IsInside); + } + } + else + { + Fluid = new fluid*[SquaresUnder]; + memset(Fluid, 0, SquaresUnder * sizeof(fluid*)); + Fluid[SquareIndex] = new fluid(ToBeAdded, this, LocationName, IsInside); + } + + UpdatePictures(); + SignalVolumeAndWeightChange(); + SignalEmitationIncrease(ToBeAdded->GetEmitation()); + + if(Slot[0]) + { + if(!IsAnimated() != !WasAnimated && Slot[0]->IsVisible()) + GetSquareUnder()->IncStaticAnimatedEntities(); + + SendNewDrawAndMemorizedUpdateRequest(); + } +} + +void item::SendNewDrawAndMemorizedUpdateRequest() const +{ + if(!game::IsInWilderness()) + for(int c = 0; c < SquaresUnder; ++c) + if(Slot[c]) + { + lsquare* Square = GetLSquareUnder(c); + Square->SendNewDrawRequest(); + Square->SendMemorizedUpdateRequest(); + } +} + +void item::CalculateEmitation() +{ + object::CalculateEmitation(); + + if(Fluid) + for(int c = 0; c < SquaresUnder; ++c) + for(const fluid* F = Fluid[c]; F; F = F->Next) + game::CombineLights(Emitation, F->GetEmitation()); +} + +void item::FillFluidVector(fluidvector& Vector, int SquareIndex) const +{ + if(Fluid) + for(fluid* F = Fluid[SquareIndex]; F; F = F->Next) + Vector.push_back(F); +} + +void item::SpillFluid(character*, liquid* Liquid, int SquareIndex) +{ + if(AllowFluids() && Liquid->GetVolume()) + AddFluid(Liquid, "", SquareIndex, false); + else + delete Liquid; +} + +void item::TryToRust(long LiquidModifier) +{ + if(MainMaterial->TryToRust(LiquidModifier)) + { + if(CanBeSeenByPlayer()) + if(MainMaterial->GetRustLevel() == NOT_RUSTED) + ADD_MESSAGE("%s rusts.", CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("%s rusts more.", CHAR_NAME(DEFINITE)); + + MainMaterial->SetRustLevel(MainMaterial->GetRustLevel() + 1); + } +} + +void item::CheckFluidGearPictures(v2 ShadowPos, int SpecialFlags, truth BodyArmor) +{ + if(Fluid) + for(fluid* F = Fluid[0]; F; F = F->Next) + F->CheckGearPicture(ShadowPos, SpecialFlags, BodyArmor); +} + +void item::DrawFluidGearPictures(blitdata& BlitData, int SpecialFlags) const +{ + if(Fluid) + for(const fluid* F = Fluid[0]; F; F = F->Next) + F->DrawGearPicture(BlitData, SpecialFlags); +} + +void item::DrawFluidBodyArmorPictures(blitdata& BlitData, int SpecialFlags) const +{ + if(Fluid) + for(const fluid* F = Fluid[0]; F; F = F->Next) + F->DrawBodyArmorPicture(BlitData, SpecialFlags); +} + +void item::DrawFluids(blitdata& BlitData) const +{ + cint SquareIndex = BlitData.CustomData & SQUARE_INDEX_MASK; + + for(const fluid* F = Fluid[SquareIndex]; F; F = F->Next) + F->Draw(BlitData); +} + +void item::ReceiveAcid(material*, cfestring&, long Modifier) +{ + if(GetMainMaterial()->GetInteractionFlags() & CAN_DISSOLVE) + { + int Damage = Modifier / 1000; + + if(Damage) + { + Damage += RAND() % Damage; + ReceiveDamage(0, Damage, ACID); + } + } +} + +void item::DonateFluidsTo(item* Item) +{ + if(Fluid) + for(int c = 0; c < GetSquaresUnder(); ++c) + for(fluid* F = Fluid[c]; F; F = F->Next) + { + liquid* Liquid = F->GetLiquid(); + Item->AddFluid(Liquid->SpawnMoreLiquid(Liquid->GetVolume()), F->GetLocationName(), c, F->IsInside()); + } +} + +void item::Destroy(character* Destroyer, int) +{ + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s is destroyed.", GetExtendedDescription().CStr()); + + if(Destroyer && IsOnGround()) + { + room* Room = GetRoom(); + + if(Room) + Room->HostileAction(Destroyer); + } + + truth Equipped = PLAYER->Equips(this); + RemoveFromSlot(); + SendToHell(); + + if(Equipped) + game::AskForKeyPress(CONST_S("Equipment destroyed! [press any key to continue]")); +} + +void item::RemoveRust() +{ + for(int c = 0; c < GetMaterials(); ++c) + if(GetMaterial(c)) + GetMaterial(c)->SetRustLevel(NOT_RUSTED); +} + +void item::SetSpoilPercentage(int Value) +{ + for(int c = 0; c < GetMaterials(); ++c) + { + material* Material = GetMaterial(c); + + if(Material && Material->CanSpoil()) + Material->SetSpoilCounter(Material->GetSpoilModifier() * Value / 100); + } +} + +void item::RedistributeFluids() +{ + if(Fluid) + for(int c = 0; c < GetSquaresUnder(); ++c) + for(fluid* F = Fluid[c]; F; F = F->Next) + F->Redistribute(); +} + +material* item::GetConsumeMaterial(ccharacter* Consumer, materialpredicate Predicate) const +{ + return (MainMaterial->*Predicate)() && Consumer->CanConsume(MainMaterial) ? MainMaterial : 0; +} + +/* The parameter can only be MainMaterial */ + +material* item::RemoveMaterial(material*) +{ + RemoveFromSlot(); + SendToHell(); + return 0; +} + +void item::InitMaterials(material* FirstMaterial, truth CallUpdatePictures) +{ + InitMaterial(MainMaterial, FirstMaterial, GetDefaultMainVolume()); + SignalVolumeAndWeightChange(); + + if(CallUpdatePictures) + UpdatePictures(); +} + +void item::GenerateMaterials() +{ + int Chosen = RandomizeMaterialConfiguration(); + const fearray& MMC = GetMainMaterialConfig(); + InitMaterial(MainMaterial, + MAKE_MATERIAL(MMC.Data[MMC.Size == 1 ? 0 : Chosen]), + GetDefaultMainVolume()); +} + +void item::SignalSquarePositionChange(int Position) +{ + Flags &= ~SQUARE_POSITION_BITS; + Flags |= Position << SQUARE_POSITION_SHIFT; +} + +truth item::Read(character* Reader) +{ + Reader->StartReading(this, GetReadDifficulty()); + return true; +} + +truth item::CanBeHardened(ccharacter*) const +{ + return MainMaterial->GetHardenedMaterial(this) != NONE; +} + +void item::SetLifeExpectancy(int Base, int RandPlus) +{ + LifeExpectancy = RandPlus > 1 ? Base + RAND_N(RandPlus) : Base; + Enable(); +} + +truth item::IsVeryCloseToSpoiling() const +{ + for(int c = 0; c < GetMaterials(); ++c) + if(GetMaterial(c) && !GetMaterial(c)->IsVeryCloseToSpoiling()) + return false; + + return true; +} + +truth item::IsValuable() const +{ + if(DataBase->IsValuable) + return true; + + for(int c = 0; c < GetMaterials(); ++c) + { + material* M = GetMaterial(c); + + if(M && M->GetCommonFlags() & IS_VALUABLE) + return true; + } + + return false; +} + +int item::GetHinderVisibilityBonus(ccharacter* Char) const +{ + int Bonus = 0; + + if(GetGearStates() & INFRA_VISION + && !Char->TemporaryStateIsActivated(INFRA_VISION)) + Bonus += 20000; + + if(GetGearStates() & ESP + && !Char->TemporaryStateIsActivated(ESP)) + Bonus += 20000; + + if(!game::IsDark(GetEmitation())) + Bonus += 5000; + + return Bonus; +} + +long item::GetFixPrice() const +{ + item* Clone = GetProtoType()->Clone(this); + Clone = Clone->Fix(); + Clone->RemoveRust(); + long FixPrice = Clone->GetTruePrice(); + Clone->SendToHell(); + return Max(long(3.5 * sqrt(FixPrice)), 10L); +} + +void item::AddTrapName(festring& String, int Amount) const +{ + if(Amount == 1) + AddName(String, DEFINITE); + else + { + String << Amount << ' '; + AddName(String, PLURAL); + } +} + +truth item::Spoils() const +{ + for(int c = 0; c < GetMaterials(); ++c) + { + cmaterial* Material = GetMaterial(c); + + if(Material && Material->Spoils()) + return true; + } + + return false; +} + +int item::GetMaxSpoilPercentage() const +{ + int MaxPercentage = 0; + + for(int c = 0; c < GetMaterials(); ++c) + { + cmaterial* Material = GetMaterial(c); + + if(Material) + MaxPercentage = Max(MaxPercentage, Material->GetSpoilPercentage()); + } + + return MaxPercentage; +} + +truth item::HasPrice() const +{ + return GetPrice() || GetMaterialPrice(); +} + +void item::Disappear() +{ + RemoveFromSlot(); + SendToHell(); +} + +outputfile& operator<<(outputfile& SaveFile, const idholder* IdHolder) +{ + SaveFile << IdHolder->ID; + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, idholder*& IdHolder) +{ + IdHolder = new idholder(ReadType(SaveFile)); + return SaveFile; +} + +festring item::GetExtendedDescription() const +{ + if(!CanBeSeenByPlayer()) + return CONST_S("something"); + + festring Desc; + ccharacter* Carrier = FindCarrier(); + + if(Carrier) + { + if(Carrier->IsPlayer()) + { + Desc << "your "; + AddName(Desc, UNARTICLED); + return Desc; + } + else if(Carrier->CanBeSeenByPlayer()) + { + Carrier->AddName(Desc, DEFINITE); + Desc << "'s "; + AddName(Desc, UNARTICLED); + return Desc; + } + } + + AddName(Desc, DEFINITE); + + if(IsOnGround()) + GetLSquareUnder()->AddLocationDescription(Desc); + + return Desc; +} + +ccharacter* item::FindCarrier() const +{ + return Slot[0]->FindCarrier(); +} + +/* returns 0 if not worn or wielded else the wearer */ + +const character* item::GetWearer() const +{ + if(!GetSlot()->IsGearSlot()) + return 0; + + return FindCarrier(); +} + +void itemlock::PostConstruct() +{ + /* Terrible gum solution! */ + + if(!(GetVirtualConfig() & LOCK_BITS)) + { + int NormalLockTypes = 0; + const itemdatabase*const* ConfigData = GetVirtualProtoType()->GetConfigData(); + int c, ConfigSize = GetVirtualProtoType()->GetConfigSize(); + + for(c = 0; c < ConfigSize; ++c) + if(ConfigData[c]->Config & LOCK_BITS + && (ConfigData[c]->Config & ~LOCK_BITS) == GetVirtualConfig() + && !(ConfigData[c]->Config & S_LOCK_ID)) + ++NormalLockTypes; + + int ChosenLock = RAND() % NormalLockTypes; + + for(c = 0; c < ConfigSize; ++c) + if(ConfigData[c]->Config & LOCK_BITS + && (ConfigData[c]->Config & ~LOCK_BITS) == GetVirtualConfig() + && !(ConfigData[c]->Config & S_LOCK_ID) + && !ChosenLock--) + { + SetVirtualConfig(ConfigData[c]->Config, NO_PIC_UPDATE); + break; + } + } +} + +truth itemlock::TryKey(item* Key, character* Applier) +{ + if(GetVirtualConfig() & BROKEN_LOCK) + { + ADD_MESSAGE("The lock is broken."); + return true; + } + + if(Key->CanOpenLockType(GetVirtualConfig()&LOCK_BITS)) + { + if(Locked) + { + if(Applier->IsPlayer()) + ADD_MESSAGE("You unlock %s.", GetVirtualDescription(DEFINITE).CStr()); + else if(Applier->CanBeSeenByPlayer()) + ADD_MESSAGE("%s unlocks %s.", Applier->CHAR_NAME(DEFINITE), GetVirtualDescription(DEFINITE).CStr()); + } + else + { + if(Applier->IsPlayer()) + ADD_MESSAGE("You lock %s.", GetVirtualDescription(DEFINITE).CStr()); + else if(Applier->CanBeSeenByPlayer()) + ADD_MESSAGE("%s locks %s.", Applier->CHAR_NAME(DEFINITE), GetVirtualDescription(DEFINITE).CStr()); + } + + Locked = !Locked; + } + else + { + if(Applier->IsPlayer()) + ADD_MESSAGE("%s doesn't fit in the lock.", Key->CHAR_NAME(DEFINITE)); + else if(Applier->CanBeSeenByPlayer()) + ADD_MESSAGE("%s tries to fit %s in the lock, but fails.", Applier->CHAR_NAME(DEFINITE), Key->CHAR_NAME(DEFINITE)); + } + + return true; +} + +void itemlock::Save(outputfile& SaveFile) const +{ + SaveFile << Locked; +} + +void itemlock::Load(inputfile& SaveFile) +{ + SaveFile >> Locked; +} + +truth item::IsBeverage(ccharacter*) const +{ + for(int c = 0; c < GetMaterials(); ++c) + { + cmaterial* Material = GetMaterial(c); + + if(Material && (Material->GetCategoryFlags() & IS_BEVERAGE)) + return true; + } + + return false; +} + +void item::Haste() +{ + ItemFlags |= HASTE; + ItemFlags &= ~SLOW; + SendMemorizedUpdateRequest(); +} + +void item::Slow() +{ + ItemFlags |= SLOW; + ItemFlags &= ~HASTE; + SendMemorizedUpdateRequest(); +} + +void item::SendMemorizedUpdateRequest() const +{ + if(!game::IsInWilderness()) + for(int c = 0; c < SquaresUnder; ++c) + if(Slot[c]) + { + lsquare* Square = GetLSquareUnder(c); + Square->SendMemorizedUpdateRequest(); + } +} + +truth item::AddStateDescription(festring& Name, truth Articled) const +{ + if(!Spoils()) + return false; + + if((ItemFlags & (HASTE|SLOW)) && Articled) + Name << "a "; + + if(ItemFlags & HASTE) + Name << "hasted "; + + if(ItemFlags & SLOW) + Name << "slowed "; + + return true; +} diff --git a/Main/Source/itemset.cpp b/Main/Source/itemset.cpp new file mode 100644 index 0000000..63880e3 --- /dev/null +++ b/Main/Source/itemset.cpp @@ -0,0 +1,53 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#define __FILE_OF_STATIC_ITEM_PROTOTYPE_DEFINITIONS__ + +#include "proto.h" +#include "item.h" +#include "database.h" + +EXTENDED_SYSTEM_SPECIALIZATIONS(item)(0, 0, 0, "item"); + +#include "bodypart.h" +#include "gear.h" +#include "miscitem.h" + +#undef __FILE_OF_STATIC_ITEM_PROTOTYPE_DEFINITIONS__ + +#include +#include + +#include "char.h" +#include "message.h" +#include "stack.h" +#include "felist.h" +#include "confdef.h" +#include "room.h" +#include "game.h" +#include "materias.h" +#include "human.h" +#include "team.h" +#include "god.h" +#include "team.h" +#include "smoke.h" +#include "save.h" +#include "whandler.h" +#include "bitmap.h" +#include "fluid.h" +#include "rawbit.h" +#include "balance.h" + +#include "item.cpp" +#include "bodypart.cpp" +#include "gear.cpp" +#include "miscitem.cpp" diff --git a/Main/Source/level.cpp b/Main/Source/level.cpp new file mode 100644 index 0000000..ae6c1dc --- /dev/null +++ b/Main/Source/level.cpp @@ -0,0 +1,2982 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through levelset.cpp */ + +#define FORBIDDEN 1 +#define ON_POSSIBLE_ROUTE 2 +#define STILL_ON_POSSIBLE_ROUTE 4 +#define PREFERRED 8 +#define ICE_TERRAIN 16 +#define STONE_TERRAIN 32 + +level::level() : Room(1, static_cast(0)), GlobalRainLiquid(0), SunLightEmitation(0), AmbientLuminance(0), SquareStack(0), NightAmbientLuminance(0) { } +void level::SetRoom(int I, room* What) { Room[I] = What; } +void level::AddToAttachQueue(v2 Pos) { AttachQueue.push_back(Pos); } + +ulong level::NextExplosionID = 1; + +node*** node::NodeMap; +int node::RequiredWalkability; +ccharacter* node::SpecialMover; +v2 node::To; +uchar** node::WalkabilityMap; +int node::XSize, node::YSize; +nodequeue* node::NodeQueue; + +level::~level() +{ + ulong c; + + for(c = 0; c < XSizeTimesYSize; ++c) + delete NodeMap[0][c]; + + for(c = 0; c < Room.size(); ++c) + delete Room[c]; + + delete [] NodeMap; + delete [] WalkabilityMap; + delete GlobalRainLiquid; + delete [] SquareStack; + game::SetGlobalRainLiquid(0); +} + +void level::ExpandPossibleRoute(int OrigoX, int OrigoY, int TargetX, int TargetY, truth XMode) +{ +#define CHECK(x, y) !(FlagMap[x][y] & (ON_POSSIBLE_ROUTE|FORBIDDEN)) + +#define CALL_EXPAND(x, y)\ + {\ + ExpandPossibleRoute(x, y, TargetX, TargetY, XMode);\ + \ + if(FlagMap[TargetX][TargetY] & ON_POSSIBLE_ROUTE)\ + return;\ + } + + FlagMap[OrigoX][OrigoY] |= ON_POSSIBLE_ROUTE; + + if(XMode) + { + if(TargetX < OrigoX) + if(CHECK(OrigoX - 1, OrigoY)) + CALL_EXPAND(OrigoX - 1, OrigoY); + + if(TargetX > OrigoX) + if(CHECK(OrigoX + 1, OrigoY)) + CALL_EXPAND(OrigoX + 1, OrigoY); + + if(TargetY < OrigoY) + if(CHECK(OrigoX, OrigoY - 1)) + CALL_EXPAND(OrigoX, OrigoY - 1); + + if(TargetY > OrigoY) + if(CHECK(OrigoX, OrigoY + 1)) + CALL_EXPAND(OrigoX, OrigoY + 1); + + if(TargetX <= OrigoX) + if(OrigoX < XSize - 2 && CHECK(OrigoX + 1, OrigoY)) + CALL_EXPAND(OrigoX + 1, OrigoY); + + if(TargetX >= OrigoX) + if(OrigoX > 1 && CHECK(OrigoX - 1, OrigoY)) + CALL_EXPAND(OrigoX - 1, OrigoY); + + if(TargetY <= OrigoY) + if(OrigoY < YSize - 2 && CHECK(OrigoX, OrigoY + 1)) + CALL_EXPAND(OrigoX, OrigoY + 1); + + if(TargetY >= OrigoY) + if(OrigoY > 1 && CHECK(OrigoX, OrigoY - 1)) + CALL_EXPAND(OrigoX, OrigoY - 1); + } + else + { + if(TargetY < OrigoY) + if(CHECK(OrigoX, OrigoY - 1)) + CALL_EXPAND(OrigoX, OrigoY - 1); + + if(TargetY > OrigoY) + if(CHECK(OrigoX, OrigoY + 1)) + CALL_EXPAND(OrigoX, OrigoY + 1); + + if(TargetX < OrigoX) + if(CHECK(OrigoX - 1, OrigoY)) + CALL_EXPAND(OrigoX - 1, OrigoY); + + if(TargetX > OrigoX) + if(CHECK(OrigoX + 1, OrigoY)) + CALL_EXPAND(OrigoX + 1, OrigoY); + + if(TargetY <= OrigoY) + if(OrigoY < YSize - 2 && CHECK(OrigoX, OrigoY + 1)) + CALL_EXPAND(OrigoX, OrigoY + 1); + + if(TargetY >= OrigoY) + if(OrigoY > 1 && CHECK(OrigoX, OrigoY - 1)) + CALL_EXPAND(OrigoX, OrigoY - 1); + + if(TargetX <= OrigoX) + if(OrigoX < XSize - 2 && CHECK(OrigoX + 1, OrigoY)) + CALL_EXPAND(OrigoX + 1, OrigoY); + + if(TargetX >= OrigoX) + if(OrigoX > 1 && CHECK(OrigoX - 1, OrigoY)) + CALL_EXPAND(OrigoX - 1, OrigoY); + } + +#undef CHECK + +#undef CALL_EXPAND +} + +void level::ExpandStillPossibleRoute(int OrigoX, int OrigoY, int TargetX, int TargetY, truth XMode) +{ +#define CHECK(x, y) (FlagMap[x][y] & (STILL_ON_POSSIBLE_ROUTE|ON_POSSIBLE_ROUTE)) == ON_POSSIBLE_ROUTE + +#define CALL_EXPAND(x, y) \ + {\ + ExpandStillPossibleRoute(x, y, TargetX, TargetY, XMode);\ + \ + if(FlagMap[TargetX][TargetY] & STILL_ON_POSSIBLE_ROUTE) \ + return;\ + } + + FlagMap[OrigoX][OrigoY] |= STILL_ON_POSSIBLE_ROUTE; + + if(XMode) + { + if(TargetX < OrigoX) + if(CHECK(OrigoX - 1, OrigoY)) + CALL_EXPAND(OrigoX - 1, OrigoY); + + if(TargetX > OrigoX) + if(CHECK(OrigoX + 1, OrigoY)) + CALL_EXPAND(OrigoX + 1, OrigoY); + + if(TargetY < OrigoY) + if(CHECK(OrigoX, OrigoY - 1)) + CALL_EXPAND(OrigoX, OrigoY - 1); + + if(TargetY > OrigoY) + if(CHECK(OrigoX, OrigoY + 1)) + CALL_EXPAND(OrigoX, OrigoY + 1); + + if(TargetX <= OrigoX) + if(OrigoX < XSize - 2 && CHECK(OrigoX + 1, OrigoY)) + CALL_EXPAND(OrigoX + 1, OrigoY); + + if(TargetX >= OrigoX) + if(OrigoX > 1 && CHECK(OrigoX - 1, OrigoY)) + CALL_EXPAND(OrigoX - 1, OrigoY); + + if(TargetY <= OrigoY) + if(OrigoY < YSize - 2 && CHECK(OrigoX, OrigoY + 1)) + CALL_EXPAND(OrigoX, OrigoY + 1); + + if(TargetY >= OrigoY) + if(OrigoY > 1 && CHECK(OrigoX, OrigoY - 1)) + CALL_EXPAND(OrigoX, OrigoY - 1); + } + else + { + if(TargetY < OrigoY) + if(CHECK(OrigoX, OrigoY - 1)) + CALL_EXPAND(OrigoX, OrigoY - 1); + + if(TargetY > OrigoY) + if(CHECK(OrigoX, OrigoY + 1)) + CALL_EXPAND(OrigoX, OrigoY + 1); + + if(TargetX < OrigoX) + if(CHECK(OrigoX - 1, OrigoY)) + CALL_EXPAND(OrigoX - 1, OrigoY); + + if(TargetX > OrigoX) + if(CHECK(OrigoX + 1, OrigoY)) + CALL_EXPAND(OrigoX + 1, OrigoY); + + if(TargetY <= OrigoY) + if(OrigoY < YSize - 2 && CHECK(OrigoX, OrigoY + 1)) + CALL_EXPAND(OrigoX, OrigoY + 1); + + if(TargetY >= OrigoY) + if(OrigoY > 1 && CHECK(OrigoX, OrigoY - 1)) + CALL_EXPAND(OrigoX, OrigoY - 1); + + if(TargetX <= OrigoX) + if(OrigoX < XSize - 2 && CHECK(OrigoX + 1, OrigoY)) + CALL_EXPAND(OrigoX + 1, OrigoY); + + if(TargetX >= OrigoX) + if(OrigoX > 1 && CHECK(OrigoX - 1, OrigoY)) + CALL_EXPAND(OrigoX - 1, OrigoY); + } + +#undef CHECK + +#undef CALL_EXPAND +} + +void level::GenerateTunnel(int FromX, int FromY, int TargetX, int TargetY, truth XMode) +{ + FlagMap[FromX][FromY] |= ON_POSSIBLE_ROUTE; + ExpandPossibleRoute(FromX, FromY, TargetX, TargetY, XMode); + const contentscript* GTerrain = LevelScript->GetTunnelSquare()->GetGTerrain(); + const contentscript* OTerrain = LevelScript->GetTunnelSquare()->GetOTerrain(); + + if(FlagMap[TargetX][TargetY] & ON_POSSIBLE_ROUTE) + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + if((FlagMap[x][y] & (ON_POSSIBLE_ROUTE|PREFERRED)) == ON_POSSIBLE_ROUTE + && !(x == FromX && y == FromY) && !(x == TargetX && y == TargetY)) + { + FlagMap[x][y] &= ~ON_POSSIBLE_ROUTE; + FlagMap[FromX][FromY] |= STILL_ON_POSSIBLE_ROUTE; + ExpandStillPossibleRoute(FromX, FromY, TargetX, TargetY, XMode); + + if(!(FlagMap[TargetX][TargetY] & STILL_ON_POSSIBLE_ROUTE)) + { + FlagMap[x][y] |= ON_POSSIBLE_ROUTE|PREFERRED; + Map[x][y]->ChangeGLTerrain(GTerrain->Instantiate()); + Map[x][y]->ChangeOLTerrain(OTerrain->Instantiate()); + } + + for(int X = 0; X < XSize; ++X) + for(int Y = 0; Y < YSize; ++Y) + FlagMap[X][Y] &= ~STILL_ON_POSSIBLE_ROUTE; + } + + for(int x = 1; x < XSize - 1; ++x) + for(int y = 1; y < YSize - 1; ++y) + FlagMap[x][y] &= ~ON_POSSIBLE_ROUTE; +} + +void level::Generate(int Index) +{ + game::BusyAnimation(); + Initialize(LevelScript->GetSize()->X, LevelScript->GetSize()->Y); + game::SetCurrentArea(this); + game::SetCurrentLevel(this); + Alloc2D(NodeMap, XSize, YSize); + Alloc2D(WalkabilityMap, XSize, YSize); + Map = reinterpret_cast(area::Map); + SquareStack = new lsquare*[XSizeTimesYSize]; + + if((Index == 0 && GetDungeon()->GetIndex() == NEW_ATTNAM) + || (Index == 0 && GetDungeon()->GetIndex() == ATTNAM) + || (Index == 0 && GetDungeon()->GetIndex() == MUNTUO)) + NightAmbientLuminance = MakeRGB24(95, 95, 95); + + int x, y; + + for(x = 0; x < XSize; ++x) + for(y = 0; y < YSize; ++y) + { + Map[x][y] = new lsquare(this, v2(x, y)); + NodeMap[x][y] = new node(x, y, Map[x][y]); + } + + for(x = 0; x < XSize; ++x) + for(y = 0; y < YSize; ++y) + Map[x][y]->CalculateNeighbourLSquares(); + + int Type = LevelScript->GetType() ? *LevelScript->GetType() : 0; + + switch(Type) + { + case 0: + GenerateDungeon(Index); + break; + case DESERT: + GenerateDesert(); + break; + case JUNGLE: + GenerateJungle(); + break; + case STEPPE: + GenerateSteppe(); + break; + case LEAFY_FOREST: + GenerateLeafyForest(); + break; + case EVERGREEN_FOREST: + GenerateEvergreenForest(); + break; + case TUNDRA: + GenerateTundra(); + break; + case GLACIER: + GenerateGlacier(); + break; + default: + ABORT("You are a terrorist. Please stop creating wterrains that are stupid."); + } +} + +void level::ApplyLSquareScript(const squarescript* Script) +{ + const interval* ScriptTimes = Script->GetTimes(); + int Times = ScriptTimes ? ScriptTimes->Randomize() : 1; + + for(int c = 0; c < Times; ++c) + { + v2 Pos; + + if(Script->GetPosition()->GetRandom()) + Pos = GetRandomSquare(0, Script->GetPosition()->GetFlags(), Script->GetPosition()->GetBorders()); + else + Pos = Script->GetPosition()->GetVector(); + + Map[Pos.X][Pos.Y]->ApplyScript(Script, 0); + } +} + +void level::AttachPos(int WhatX, int WhatY) +{ + int PosX = 1 + RAND() % (XSize - 2); + int PosY = 1 + RAND() % (YSize - 2); + + while(!(FlagMap[PosX][PosY] & PREFERRED)) + { + PosX = 1 + RAND() % (XSize - 2); + PosY = 1 + RAND() % (YSize - 2); + } + + FlagMap[WhatX][WhatY] &= ~FORBIDDEN; + FlagMap[WhatX][WhatY] |= PREFERRED; + GenerateTunnel(WhatX, WhatY, PosX, PosY, RAND() & 1); + FlagMap[WhatX][WhatY] |= FORBIDDEN; + FlagMap[WhatX][WhatY] &= ~PREFERRED; +} + +void level::CreateItems(int Amount) +{ + if(Amount) + { + long MinPrice = *LevelScript->GetItemMinPriceBase() + *LevelScript->GetItemMinPriceDelta() * Index; + + for(int x = 0; x < Amount; ++x) + { + v2 Pos = GetRandomSquare(); + item* Item = protosystem::BalancedCreateItem(MinPrice, MAX_PRICE, ANY_CATEGORY, 0, IGNORE_BROKEN_PRICE); + Item->CalculateEnchantment(); + Map[Pos.X][Pos.Y]->Stack->AddItem(Item); + Item->SpecialGenerationHandler(); + } + } +} + +truth level::MakeRoom(const roomscript* RoomScript) +{ + game::BusyAnimation(); + v2 Pos = RoomScript->GetPos()->Randomize(); + v2 Size = RoomScript->GetSize()->Randomize(); + int x, y; + + if(Pos.X + Size.X > XSize - 2) + return false; + + if(Pos.Y + Size.Y > YSize - 2) + return false; + + for(x = Pos.X - 1; x <= Pos.X + Size.X; ++x) + for(y = Pos.Y - 1; y <= Pos.Y + Size.Y; ++y) + if(FlagMap[x][y] & FORBIDDEN || FlagMap[x][y] & PREFERRED) + return false; + + room* RoomClass = protocontainer::GetProto(*RoomScript->GetType())->Spawn(); + RoomClass->SetPos(Pos); + RoomClass->SetSize(Size); + RoomClass->SetFlags(*RoomScript->GetFlags()); + AddRoom(RoomClass); + RoomClass->SetDivineMaster(*RoomScript->GetDivineMaster()); + game::BusyAnimation(); + std::vector OKForDoor, Inside, Border; + + GenerateRectangularRoom(OKForDoor, Inside, Border, RoomScript, RoomClass, Pos, Size); + game::BusyAnimation(); + + if(*RoomScript->GenerateFountains() && !(RAND() % 10)) + GetLSquare(Inside[RAND() % Inside.size()])->ChangeOLTerrain(fountain::Spawn()); + + // Ward, which gets generated as per fountain activation + if(*RoomScript->GenerateWards() && !(RAND() % 5)) + GetLSquare(Inside[RAND() % Inside.size()])->ChangeOLTerrain(ward::Spawn()); + + if(*RoomScript->AltarPossible() && !(RAND() % 5)) + { + int Owner = 1 + RAND() % GODS; + GetLSquare(Inside[RAND() % Inside.size()])->ChangeOLTerrain(altar::Spawn(Owner)); + game::GetGod(Owner)->SignalRandomAltarGeneration(Inside); + RoomClass->SetDivineMaster(Owner); + } + + if(*RoomScript->GenerateTunnel() && !Door.empty()) + { + game::BusyAnimation(); + v2 OutsideDoorPos = Door[RAND() % Door.size()]; // An other room + + if(OKForDoor.empty()) + ABORT("The Doors - You are strange."); + + v2 InsideDoorPos = OKForDoor[RAND() % OKForDoor.size()]; // this door + olterrain* Door = RoomScript->GetDoorSquare()->GetOTerrain()->Instantiate(); //Bug! Wrong room! + + if(Door && !(RAND() % 5) && *RoomScript->AllowLockedDoors()) + { + if(*RoomScript->AllowBoobyTrappedDoors() && !(RAND() % 5)) + Door->CreateBoobyTrap(); + + Door->Lock(); + } + + Map[OutsideDoorPos.X][OutsideDoorPos.Y]->ChangeLTerrain(RoomScript->GetDoorSquare()->GetGTerrain()->Instantiate(), Door); + Map[OutsideDoorPos.X][OutsideDoorPos.Y]->Clean(); + FlagMap[OutsideDoorPos.X][OutsideDoorPos.Y] &= ~FORBIDDEN; + FlagMap[OutsideDoorPos.X][OutsideDoorPos.Y] |= PREFERRED; + FlagMap[InsideDoorPos.X][InsideDoorPos.Y] &= ~FORBIDDEN; + FlagMap[InsideDoorPos.X][InsideDoorPos.Y] |= PREFERRED; + Door = RoomScript->GetDoorSquare()->GetOTerrain()->Instantiate(); + + if(Door && !(RAND() % 5) && *RoomScript->AllowLockedDoors()) + { + if(*RoomScript->AllowBoobyTrappedDoors() && !(RAND() % 5)) + Door->CreateBoobyTrap(); + + Door->Lock(); + } + + Map[InsideDoorPos.X][InsideDoorPos.Y]->ChangeLTerrain(RoomScript->GetDoorSquare()->GetGTerrain()->Instantiate(), Door); + Map[InsideDoorPos.X][InsideDoorPos.Y]->Clean(); + GenerateTunnel(InsideDoorPos.X, InsideDoorPos.Y, OutsideDoorPos.X, OutsideDoorPos.Y, RAND() & 1); + FlagMap[OutsideDoorPos.X][OutsideDoorPos.Y] |= FORBIDDEN; + FlagMap[OutsideDoorPos.X][OutsideDoorPos.Y] &= ~PREFERRED; + FlagMap[InsideDoorPos.X][InsideDoorPos.Y] |= FORBIDDEN; + FlagMap[InsideDoorPos.X][InsideDoorPos.Y] &= ~PREFERRED; + } + + if(*RoomScript->GenerateDoor()) + { + game::BusyAnimation(); + v2 DoorPos; + + if(OKForDoor.empty()) + ABORT("The Doors - This thing has been broken."); + + DoorPos = OKForDoor[RAND() % OKForDoor.size()]; + Door.push_back(DoorPos); + + if(!*RoomScript->GenerateTunnel()) + { + Map[DoorPos.X][DoorPos.Y]->ChangeLTerrain(RoomScript->GetDoorSquare()->GetGTerrain()->Instantiate(), RoomScript->GetDoorSquare()->GetOTerrain()->Instantiate()); + Map[DoorPos.X][DoorPos.Y]->Clean(); + } + } + + const charactercontentmap* CharacterMap = RoomScript->GetCharacterMap(); + + if(CharacterMap) + { + v2 CharPos(Pos + *CharacterMap->GetPos()); + const contentscript* CharacterScript; + + for(int x = 0; x < CharacterMap->GetSize()->X; ++x) + { + game::BusyAnimation(); + + for(y = 0; y < CharacterMap->GetSize()->Y; ++y) + if(IsValidScript(CharacterScript = CharacterMap->GetContentScript(x, y))) + { + character* Char = CharacterScript->Instantiate(); + Char->SetGenerationDanger(Difficulty); + + if(!Char->GetTeam()) + Char->SetTeam(game::GetTeam(*LevelScript->GetTeamDefault())); + + if(CharacterScript->GetFlags() & IS_LEADER) + Char->GetTeam()->SetLeader(Char); + + Char->PutTo(CharPos + v2(x, y)); + Char->CreateHomeData(); + + if(CharacterScript->GetFlags() & IS_MASTER) + RoomClass->SetMasterID(Char->GetID()); + } + } + } + + const itemcontentmap* ItemMap = RoomScript->GetItemMap(); + + if(ItemMap) + { + v2 ItemPos(Pos + *ItemMap->GetPos()); + const fearray >* ItemScript; + + for(int x = 0; x < ItemMap->GetSize()->X; ++x) + { + game::BusyAnimation(); + + for(y = 0; y < ItemMap->GetSize()->Y; ++y) + if(IsValidScript(ItemScript = ItemMap->GetContentScript(x, y))) + for(uint c1 = 0; c1 < ItemScript->Size; ++c1) + { + const interval* TimesPtr = ItemScript->Data[c1].GetTimes(); + int Times = TimesPtr ? TimesPtr->Randomize() : 1; + + for(int c2 = 0; c2 < Times; ++c2) + { + item* Item = ItemScript->Data[c1].Instantiate(); + + if(Item) + { + int SquarePosition = ItemScript->Data[c1].GetSquarePosition(); + + if(SquarePosition != CENTER) + Item->SignalSquarePositionChange(SquarePosition); + + Map[ItemPos.X + x][ItemPos.Y + y]->GetStack()->AddItem(Item); + Item->SpecialGenerationHandler(); + } + } + } + } + } + + const glterraincontentmap* GTerrainMap = RoomScript->GetGTerrainMap(); + + if(GTerrainMap) + { + v2 GTerrainPos(Pos + *GTerrainMap->GetPos()); + const contentscript* GTerrainScript; + + for(int x = 0; x < GTerrainMap->GetSize()->X; ++x) + { + game::BusyAnimation(); + + for(y = 0; y < GTerrainMap->GetSize()->Y; ++y) + if(IsValidScript(GTerrainScript = GTerrainMap->GetContentScript(x, y))) + { + lsquare* Square = Map[GTerrainPos.X + x][GTerrainPos.Y + y]; + Square->ChangeGLTerrain(GTerrainScript->Instantiate()); + + if(GTerrainScript->IsInside()) + if(*GTerrainScript->IsInside()) + Square->Flags |= INSIDE; + else + Square->Flags &= ~INSIDE; + } + } + } + + const olterraincontentmap* OTerrainMap = RoomScript->GetOTerrainMap(); + + if(OTerrainMap) + { + v2 OTerrainPos(Pos + *OTerrainMap->GetPos()); + const contentscript* OTerrainScript; + + for(int x = 0; x < OTerrainMap->GetSize()->X; ++x) + { + game::BusyAnimation(); + + for(y = 0; y < OTerrainMap->GetSize()->Y; ++y) + if(IsValidScript(OTerrainScript = OTerrainMap->GetContentScript(x, y))) + { + olterrain* Terrain = OTerrainScript->Instantiate(); + Map[OTerrainPos.X + x][OTerrainPos.Y + y]->ChangeOLTerrain(Terrain); + } + } + } + + const std::list Square = RoomScript->GetSquare(); + + for(std::list::const_iterator i = Square.begin(); i != Square.end(); ++i) + { + game::BusyAnimation(); + const squarescript* Script = &*i; + const interval* ScriptTimes = Script->GetTimes(); + int Times = ScriptTimes ? ScriptTimes->Randomize() : 1; + + for(int t = 0; t < Times; ++t) + { + v2 SquarePos; + + if(Script->GetPosition()->GetRandom()) + { + const rect* ScriptBorders = Script->GetPosition()->GetBorders(); + rect Borders = ScriptBorders ? *ScriptBorders + Pos : rect(Pos, Pos + Size - v2(1, 1)); + SquarePos = GetRandomSquare(0, Script->GetPosition()->GetFlags(), &Borders); + } + else + SquarePos = Pos + Script->GetPosition()->GetVector(); + + Map[SquarePos.X][SquarePos.Y]->ApplyScript(Script, RoomClass); + } + } + + return true; +} + +truth level::GenerateLanterns(int X, int Y, int SquarePos) const +{ + if(!(RAND() % 7)) + { + lantern* Lantern = lantern::Spawn(); + Lantern->SignalSquarePositionChange(SquarePos); + Map[X][Y]->GetStack()->AddItem(Lantern); + return true; + } + + return false; +} + +void level::CreateRoomSquare(glterrain* GLTerrain, olterrain* OLTerrain, int X, int Y, int Room, int Flags) const +{ + Map[X][Y]->ChangeLTerrain(GLTerrain, OLTerrain); + FlagMap[X][Y] |= FORBIDDEN; + Map[X][Y]->SetRoomIndex(Room); + Map[X][Y]->AddFlags(Flags); +} + +void level::GenerateMonsters() +{ + if(*LevelScript->GenerateMonsters() + && game::GetTeam(MONSTER_TEAM)->GetEnabledMembers() < IdealPopulation + && (MonsterGenerationInterval <= 1 || !RAND_N(MonsterGenerationInterval))) + { + GenerateNewMonsters(1); + ++MonsterGenerationInterval; + } +} + +void level::Save(outputfile& SaveFile) const +{ + area::Save(SaveFile); + SaveFile << Room << GlobalRainLiquid << GlobalRainSpeed; + + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + Map[x][y]->Save(SaveFile); + + SaveFile << Door << LevelMessage << IdealPopulation << MonsterGenerationInterval << Difficulty; + SaveFile << SunLightEmitation << SunLightDirection << AmbientLuminance << NightAmbientLuminance; +} + +void level::Load(inputfile& SaveFile) +{ + game::SetIsGenerating(true); + game::SetIsLoading(true); + area::Load(SaveFile); + Map = reinterpret_cast(area::Map); + SaveFile >> Room; + GlobalRainLiquid = static_cast(ReadType(SaveFile)); + SaveFile >> GlobalRainSpeed; + + if(GlobalRainLiquid) + GlobalRainLiquid->SetVolumeNoSignals(0); + + game::SetGlobalRainLiquid(GlobalRainLiquid); + game::SetGlobalRainSpeed(GlobalRainSpeed); + int x, y; + + for(x = 0; x < XSize; ++x) + for(y = 0; y < YSize; ++y) + Map[x][y] = new lsquare(this, v2(x, y)); + + for(x = 0; x < XSize; ++x) + for(y = 0; y < YSize; ++y) + { + game::SetSquareInLoad(Map[x][y]); + Map[x][y]->Load(SaveFile); + Map[x][y]->CalculateNeighbourLSquares(); + } + + SaveFile >> Door >> LevelMessage >> IdealPopulation >> MonsterGenerationInterval >> Difficulty; + SaveFile >> SunLightEmitation >> SunLightDirection >> AmbientLuminance >> NightAmbientLuminance; + Alloc2D(NodeMap, XSize, YSize); + Alloc2D(WalkabilityMap, XSize, YSize); + + for(x = 0; x < XSize; ++x) + for(y = 0; y < YSize; ++y) + { + if(!Map[x][y]->IsInside()) + Map[x][y]->AmbientLuminance = AmbientLuminance; + + NodeMap[x][y] = new node(x, y, Map[x][y]); + WalkabilityMap[x][y] = Map[x][y]->GetTheoreticalWalkability(); + Map[x][y]->CalculateGroundBorderPartners(); + Map[x][y]->CalculateOverBorderPartners(); + } + + SquareStack = new lsquare*[XSizeTimesYSize]; + game::SetIsLoading(false); + game::SetIsGenerating(false); +} + +void level::FiatLux() +{ + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + { + Map[x][y]->CalculateEmitation(); + Map[x][y]->Emitate(); + Map[x][y]->CalculateLuminance(); + } + + CheckSunLight(); +} + +void level::GenerateNewMonsters(int HowMany, truth ConsiderPlayer) +{ + v2 Pos; + + for(int c1 = 0; c1 < HowMany; ++c1) + { + character* Char = protosystem::BalancedCreateMonster(); + Char->CalculateEnchantments(); + + for(int c2 = 0; c2 < 30; ++c2) + { + Pos = GetRandomSquare(Char); + + if(Pos == ERROR_V2) + break; + + lsquare* Square = GetLSquare(Pos); + + if((!Square->GetRoomIndex() + || !Square->GetRoom()->DontGenerateMonsters()) + && (!ConsiderPlayer + || (Pos - PLAYER->GetPos()).GetManhattanLength() > 6)) + break; + } + + if(Pos != ERROR_V2) + { + Char->PutTo(Pos); + Char->SetGenerationDanger(Difficulty); + Char->SignalGeneration(); + Char->SignalNaturalGeneration(); + ivantime Time; + game::GetTime(Time); + int Modifier = Time.Day - EDIT_ATTRIBUTE_DAY_MIN; + + if(Modifier > 0) + Char->EditAllAttributes(Modifier >> EDIT_ATTRIBUTE_DAY_SHIFT); + } + else + delete Char; + } +} + +/* Example of the usage: GetRandomSquare() gives out a random walkable square */ + +v2 level::GetRandomSquare(ccharacter* Char, int Flags, const rect* Borders) const +{ + rect LocalBorder; + + if(Borders) + { + LocalBorder = *Borders; + Borders = &LocalBorder; + LimitRef(LocalBorder.X1, 0, XSize - 1); + LimitRef(LocalBorder.X2, 0, XSize - 1); + LimitRef(LocalBorder.Y1, 0, YSize - 1); + LimitRef(LocalBorder.Y2, 0, YSize - 1); + } + + lsquare* LSquare; + + for(int c = 0;; ++c) + { + if(c == 50) + Char = 0; + + if(c == 500) + return ERROR_V2; + + v2 Pos; + + if(Borders) + { + Pos.X = Borders->X1 + RAND() % (Borders->X2 - Borders->X1 + 1); + Pos.Y = Borders->Y1 + RAND() % (Borders->Y2 - Borders->Y1 + 1); + } + else + { + Pos.X = 1 + RAND() % (XSize - 2); + Pos.Y = 1 + RAND() % (YSize - 2); + } + + LSquare = Map[Pos.X][Pos.Y]; + + if(((Char ? Char->CanMoveOn(LSquare) : (LSquare->GetWalkability() & WALK)) != !(Flags & NOT_WALKABLE)) + || ((Char ? Char->IsFreeForMe(LSquare) : !LSquare->GetCharacter()) != !(Flags & HAS_CHARACTER)) + || (Flags & ATTACHABLE && FlagMap[Pos.X][Pos.Y] & FORBIDDEN) + || (Flags & HAS_NO_OTERRAIN && LSquare->GetOTerrain())) + continue; + + int RoomFlags = Flags & (IN_ROOM|NOT_IN_ROOM); + + if((RoomFlags == IN_ROOM && !LSquare->GetRoomIndex()) + || (RoomFlags == NOT_IN_ROOM && LSquare->GetRoomIndex())) + continue; + + return Pos; + } +} + +void level::ParticleTrail(v2 StartPos, v2 EndPos) +{ + if(StartPos.X != EndPos.X && StartPos.Y != EndPos.Y) + ABORT("666th rule of thermodynamics - Particles don't move the way you want them to move."); +} + +truth level::IsOnGround() const +{ + return *LevelScript->IsOnGround(); +} + +int level::GetLOSModifier() const +{ + return *LevelScript->GetLOSModifier(); +} + +void level::AddRoom(room* NewRoom) +{ + NewRoom->SetIndex(Room.size()); + Room.push_back(NewRoom); +} + +room* level::GetRoom(int I) const +{ + if(!I) + ABORT("Access to room zero denied!"); + + return Room[I]; +} + +void level::Explosion(character* Terrorist, cfestring& DeathMsg, v2 Pos, int Strength, truth HurtNeutrals) +{ + static int StrengthLimit[6] = { 500, 250, 100, 50, 25, 10 }; + uint c; + int Size = 6; + + for(c = 0; c < 6; ++c) + if(Strength >= StrengthLimit[c]) + { + Size = c; + break; + } + + PlayerHurt.resize(PlayerHurt.size() + 1); + explosion* Exp = new explosion; + Exp->Terrorist = Terrorist; + Exp->DeathMsg = DeathMsg; + Exp->Pos = Pos; + Exp->ID = NextExplosionID++; + Exp->Strength = Strength; + Exp->RadiusSquare = (8 - Size) * (8 - Size); + Exp->Size = Size; + Exp->HurtNeutrals = HurtNeutrals; + ExplosionQueue.push_back(Exp); + + if(ExplosionQueue.size() == 1) + { + uint Explosions = 0; + + while(Explosions != ExplosionQueue.size()) + { + for(c = Explosions; c != ExplosionQueue.size(); c = TriggerExplosions(c)); + uint NewExplosions = c; + + for(c = Explosions; c < NewExplosions; ++c) + if(PlayerHurt[c] && PLAYER->IsEnabled()) + PLAYER->GetHitByExplosion(ExplosionQueue[c], ExplosionQueue[c]->Strength / ((PLAYER->GetPos() - ExplosionQueue[c]->Pos).GetLengthSquare() + 1)); + + Explosions = NewExplosions; + } + + for(uint c = 0; c < ExplosionQueue.size(); ++c) + delete ExplosionQueue[c]; + + ExplosionQueue.clear(); + PlayerHurt.clear(); + NextExplosionID = 1; + + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + Map[x][y]->LastExplosionID = 0; + } +} + +truth level::DrawExplosion(const explosion* Explosion) const +{ + static v2 StrengthPicPos[7] = { v2(176, 176), v2(0, 144), v2(256, 32), v2(144, 32), v2(64, 32), v2(16, 32),v2(0, 32) }; + v2 BPos = game::CalculateScreenCoordinates(Explosion->Pos) - v2((6 - Explosion->Size) << 4, (6 - Explosion->Size) << 4); + v2 SizeVect(16 + ((6 - Explosion->Size) << 5), 16 + ((6 - Explosion->Size) << 5)); + v2 OldSizeVect = SizeVect; + v2 PicPos = StrengthPicPos[Explosion->Size]; + + if(BPos.X < 0) + if(BPos.X + SizeVect.X <= 0) + return false; + else + { + PicPos.X -= BPos.X; + SizeVect.X += BPos.X; + BPos.X = 0; + } + + if(BPos.Y < 0) + if(BPos.Y + SizeVect.Y <= 0) + return false; + else + { + PicPos.Y -= BPos.Y; + SizeVect.Y += BPos.Y; + BPos.Y = 0; + } + + if(BPos.X >= RES.X || BPos.Y >= RES.Y) + return false; + + if(BPos.X + SizeVect.X > RES.X) + SizeVect.X = RES.X - BPos.X; + + if(BPos.Y + SizeVect.Y > RES.Y) + SizeVect.Y = RES.Y - BPos.Y; + + int Flags = RAND() & 7; + blitdata BlitData = { 0, + { PicPos.X, PicPos.Y }, + { 0, 0 }, + { SizeVect.X, SizeVect.Y }, + { 0 }, + TRANSPARENT_COLOR, + 0 }; + + if(!Flags || SizeVect != OldSizeVect) + { + BlitData.Bitmap = DOUBLE_BUFFER; + BlitData.Dest = BPos; + BlitData.Luminance = ivanconfig::GetContrastLuminance(); + igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData); + } + else + { + /* Cache these */ + bitmap ExplosionPic(SizeVect); + ExplosionPic.ActivateFastFlag(); + BlitData.Bitmap = &ExplosionPic; + BlitData.Flags = Flags; + igraph::GetSymbolGraphic()->NormalBlit(BlitData); + BlitData.Bitmap = DOUBLE_BUFFER; + BlitData.Dest = BPos; + BlitData.Src.X = BlitData.Src.Y = 0; + BlitData.Luminance = ivanconfig::GetContrastLuminance(); + ExplosionPic.LuminanceMaskedBlit(BlitData); + } + + return true; +} + +struct explosioncontroller +{ + static truth Handler(int x, int y) + { + lsquare* Square = Map[x][y]; + Square->GetHitByExplosion(CurrentExplosion); + return Square->IsFlyable(); + } + static lsquare*** Map; + static explosion* CurrentExplosion; +}; + +lsquare*** explosioncontroller::Map; +explosion* explosioncontroller::CurrentExplosion; + +int level::TriggerExplosions(int MinIndex) +{ + int LastExplosion = ExplosionQueue.size(); + int NotSeen = 0; + int c; + + for(c = MinIndex; c < LastExplosion; ++c) + { + int EmitChange = Min(50 + ExplosionQueue[c]->Strength, 255); + GetLSquare(ExplosionQueue[c]->Pos)->SetTemporaryEmitation(MakeRGB24(EmitChange, EmitChange, EmitChange)); + + if(!GetSquare(ExplosionQueue[c]->Pos)->CanBeSeenByPlayer(true)) + ++NotSeen; + } + + if(NotSeen) + if(NotSeen == 1) + ADD_MESSAGE("You hear an explosion."); + else + ADD_MESSAGE("You hear explosions."); + + game::DrawEverythingNoBlit(); + truth Drawn = false; + + for(c = MinIndex; c < LastExplosion; ++c) + { + if(DrawExplosion(ExplosionQueue[c])) + Drawn = true; + } + + if(Drawn) + { + graphics::BlitDBToScreen(); + game::GetCurrentArea()->SendNewDrawRequest(); + clock_t StartTime = clock(); + while(clock() - StartTime < 0.3 * CLOCKS_PER_SEC); + } + + for(c = MinIndex; c < LastExplosion; ++c) + { + explosion* Explosion = ExplosionQueue[c]; + int Radius = 8 - Explosion->Size; + game::SetPlayerWasHurtByExplosion(false); + explosioncontroller::Map = Map; + explosioncontroller::CurrentExplosion = Explosion; + + rect Rect; + femath::CalculateEnvironmentRectangle(Rect, GetBorder(), Explosion->Pos, Radius); + + for(int x = Rect.X1; x <= Rect.X2; ++x) + { + mapmath::DoLine(Explosion->Pos.X, Explosion->Pos.Y, x, Rect.Y1); + mapmath::DoLine(Explosion->Pos.X, Explosion->Pos.Y, x, Rect.Y2); + } + + for(int y = Rect.Y1 + 1; y < Rect.Y2; ++y) + { + mapmath::DoLine(Explosion->Pos.X, Explosion->Pos.Y, Rect.X1, y); + mapmath::DoLine(Explosion->Pos.X, Explosion->Pos.Y, Rect.X2, y); + } + + PlayerHurt[c] = game::PlayerWasHurtByExplosion(); + + if(GetLSquare(Explosion->Pos)->IsFlyable()) + GetLSquare(Explosion->Pos)->AddSmoke(gas::Spawn(SMOKE, 1000)); + } + + for(c = MinIndex; c < LastExplosion; ++c) + GetLSquare(ExplosionQueue[c]->Pos)->SetTemporaryEmitation(0); + + return LastExplosion; +} + +truth level::CollectCreatures(charactervector& CharacterArray, character* Leader, truth AllowHostiles) +{ + int c; + + if(!AllowHostiles) + for(c = 0; c < game::GetTeams(); ++c) + if(Leader->GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled() && Leader->CanBeSeenBy(*i) + && Leader->SquareUnderCanBeSeenBy(*i, true) && (*i)->CanFollow()) + { + ADD_MESSAGE("You can't escape when there are hostile creatures nearby."); + return false; + } + + truth TakeAll = true; + + for(c = 0; c < game::GetTeams(); ++c) + if(game::GetTeam(c)->GetEnabledMembers() + && Leader->GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + { + TakeAll = false; + break; + } + + for(c = 0; c < game::GetTeams(); ++c) + if(game::GetTeam(c) == Leader->GetTeam() || Leader->GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled() && *i != Leader + && (TakeAll + || (Leader->CanBeSeenBy(*i) + && Leader->SquareUnderCanBeSeenBy(*i, true))) + && (*i)->CanFollow() + && (*i)->GetCommandFlags() & FOLLOW_LEADER) + { + if((*i)->GetAction() && (*i)->GetAction()->IsVoluntary()) + (*i)->GetAction()->Terminate(false); + + if(!(*i)->GetAction()) + { + ADD_MESSAGE("%s follows you.", (*i)->CHAR_NAME(DEFINITE)); + CharacterArray.push_back(*i); + (*i)->Remove(); + } + } + + return true; +} + +void level::Draw(truth AnimationDraw) const +{ + cint XMin = Max(game::GetCamera().X, 0); + cint YMin = Max(game::GetCamera().Y, 0); + cint XMax = Min(XSize, game::GetCamera().X + game::GetScreenXSize()); + cint YMax = Min(YSize, game::GetCamera().Y + game::GetScreenYSize()); + culong LOSTick = game::GetLOSTick(); + blitdata BlitData = { DOUBLE_BUFFER, + { 0, 0 }, + { 0, 0 }, + { TILE_SIZE, TILE_SIZE }, + { 0 }, + TRANSPARENT_COLOR, + ALLOW_ANIMATE|ALLOW_ALPHA }; + + if(!game::GetSeeWholeMapCheatMode()) + { + if(!AnimationDraw) + { + for(int x = XMin; x < XMax; ++x) + { + BlitData.Dest = game::CalculateScreenCoordinates(v2(x, YMin)); + lsquare** SquarePtr = &Map[x][YMin]; + + for(int y = YMin; y < YMax; ++y, ++SquarePtr, BlitData.Dest.Y += TILE_SIZE) + { + const lsquare* Square = *SquarePtr; + culong LastSeen = Square->LastSeen; + + if(LastSeen == LOSTick) + Square->Draw(BlitData); + else if(Square->Flags & STRONG_BIT || LastSeen == LOSTick - 2) + Square->DrawMemorized(BlitData); + } + } + } + else + { + for(int x = XMin; x < XMax; ++x) + { + BlitData.Dest = game::CalculateScreenCoordinates(v2(x, YMin)); + lsquare** SquarePtr = &Map[x][YMin]; + + for(int y = YMin; y < YMax; ++y, ++SquarePtr, BlitData.Dest.Y += TILE_SIZE) + { + const lsquare* Square = *SquarePtr; + + if(Square->LastSeen == LOSTick) + Square->Draw(BlitData); + else if(Square->Flags & STRONG_BIT) + Square->DrawMemorized(BlitData); + else + { + ccharacter* C = Square->Character; + + if(C) + { + if(C->CanBeSeenByPlayer()) + Square->DrawMemorizedCharacter(BlitData); + else + Square->DrawMemorized(BlitData); + } + } + } + } + } + } + else + { + for(int x = XMin; x < XMax; ++x) + { + BlitData.Dest = game::CalculateScreenCoordinates(v2(x, YMin)); + lsquare** SquarePtr = &Map[x][YMin]; + + for(int y = YMin; y < YMax; ++y, ++SquarePtr, BlitData.Dest.Y += TILE_SIZE) + (*SquarePtr)->Draw(BlitData); + } + } +} + +v2 level::GetEntryPos(ccharacter* Char, int I) const +{ + if(I == FOUNTAIN) + { + std::vector Fountains; + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + { + if(GetLSquare(x,y)->GetOLTerrain() && GetLSquare(x,y)->GetOLTerrain()->IsFountainWithWater()) + Fountains.push_back(v2(x,y)); + } + + if(Fountains.empty()) + return GetRandomSquare(); + + return Fountains[RAND_N(Fountains.size())]; + } + std::map::const_iterator i = EntryMap.find(I); + return i == EntryMap.end() ? GetRandomSquare(Char) : i->second; +} + +void level::GenerateRectangularRoom(std::vector& OKForDoor, std::vector& Inside, std::vector& Border, const roomscript* RoomScript, room* RoomClass, v2 Pos, v2 Size) +{ + const contentscript* GTerrain; + const contentscript* OTerrain; + + if(*RoomScript->UseFillSquareWalls()) + { + GTerrain = LevelScript->GetFillSquare()->GetGTerrain(); + OTerrain = LevelScript->GetFillSquare()->GetOTerrain(); + } + else + { + GTerrain = RoomScript->GetWallSquare()->GetGTerrain(); + OTerrain = RoomScript->GetWallSquare()->GetOTerrain(); + } + + int Room = RoomClass->GetIndex(); + long Counter = 0; + truth AllowLanterns = *RoomScript->GenerateLanterns(); + truth AllowWindows = *RoomScript->GenerateWindows(); + int x, y; + int Shape = *RoomScript->GetShape(); + int Flags = (GTerrain->IsInside() ? *GTerrain->IsInside() : *RoomScript->IsInside()) ? INSIDE : 0; + + if(Shape == ROUND_CORNERS && (Size.X < 5 || Size.Y < 5)) /* No weird shapes this way. */ + Shape = RECTANGLE; + + for(x = Pos.X; x < Pos.X + Size.X; ++x, Counter += 2) + { + if(Shape == ROUND_CORNERS) + { + if(x == Pos.X) + { + CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), x + 1, Pos.Y + 1, Room, Flags); + CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), x + 1, Pos.Y + Size.Y - 2, Room, Flags); + Border.push_back(v2(x + 1, Pos.Y + 1)); + Border.push_back(v2(x + 1, Pos.Y + Size.Y - 2)); + continue; + } + else if(x == Pos.X + Size.X - 1) + { + CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), x - 1, Pos.Y + 1, Room, Flags); + CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), x - 1, Pos.Y + Size.Y - 2, Room, Flags); + Border.push_back(v2(x - 1, Pos.Y + 1)); + Border.push_back(v2(x - 1, Pos.Y + Size.Y - 2)); + continue; + } + } + + CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), x, Pos.Y, Room, Flags); + CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), x, Pos.Y + Size.Y - 1, Room, Flags); + + if((Shape == RECTANGLE && x != Pos.X && x != Pos.X + Size.X - 1) + || (Shape == ROUND_CORNERS && x > Pos.X + 1 && x < Pos.X + Size.X - 2)) + { + OKForDoor.push_back(v2(x, Pos.Y)); + OKForDoor.push_back(v2(x, Pos.Y + Size.Y - 1)); + + if((!AllowLanterns || !GenerateLanterns(x, Pos.Y, DOWN)) && AllowWindows) + GenerateWindows(x, Pos.Y); + + if((!AllowLanterns || !GenerateLanterns(x, Pos.Y + Size.Y - 1, UP)) && AllowWindows) + GenerateWindows(x, Pos.Y + Size.Y - 1); + } + + Border.push_back(v2(x, Pos.Y)); + Border.push_back(v2(x, Pos.Y + Size.Y - 1)); + } + + game::BusyAnimation(); + + for(y = Pos.Y + 1; y < Pos.Y + Size.Y - 1; ++y, Counter += 2) + { + CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), Pos.X, y, Room, Flags); + CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), Pos.X + Size.X - 1, y, Room, Flags); + + if(Shape == RECTANGLE || (Shape == ROUND_CORNERS && y != Pos.Y + 1 && y != Pos.Y + Size.Y - 2)) + { + OKForDoor.push_back(v2(Pos.X, y)); + OKForDoor.push_back(v2(Pos.X + Size.X - 1, y)); + + if((!AllowLanterns || !GenerateLanterns(Pos.X, y, RIGHT)) && AllowWindows) + GenerateWindows(Pos.X, y); + + if((!AllowLanterns || !GenerateLanterns(Pos.X + Size.X - 1, y, LEFT)) && AllowWindows) + GenerateWindows(Pos.X + Size.X - 1, y); + } + + Border.push_back(v2(Pos.X, y)); + Border.push_back(v2(Pos.X + Size.X - 1, y)); + } + + GTerrain = RoomScript->GetFloorSquare()->GetGTerrain(); + OTerrain = RoomScript->GetFloorSquare()->GetOTerrain(); + Counter = 0; + Flags = (GTerrain->IsInside() ? *GTerrain->IsInside() : *RoomScript->IsInside()) ? INSIDE : 0; + + for(x = Pos.X + 1; x < Pos.X + Size.X - 1; ++x) + for(y = Pos.Y + 1; y < Pos.Y + Size.Y - 1; ++y, ++Counter) + { + /* if not in the corner */ + + if(!(Shape == ROUND_CORNERS && (x == Pos.X + 1 || x == Pos.X + Size.X - 2) && (y == Pos.Y + 1 || y == Pos.Y + Size.Y - 2))) + { + CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), x, y, Room, Flags); + Inside.push_back(v2(x,y)); + } + } +} + +void level::Reveal() +{ + ulong Tick = game::GetLOSTick(); + + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + Map[x][y]->Reveal(Tick); +} + +void level::ParticleBeam(beamdata& Beam) +{ + v2 CurrentPos = Beam.StartPos; + + if(Beam.Direction != YOURSELF) + { + for(int Length = 0; Length < Beam.Range; ++Length) + { + CurrentPos += game::GetMoveVector(Beam.Direction); + + if(!IsValidPos(CurrentPos)) + break; + + lsquare* CurrentSquare = GetLSquare(CurrentPos); + + if(!CurrentSquare->IsFlyable()) + { + (CurrentSquare->*lsquare::GetBeamEffect(Beam.BeamEffect))(Beam); + break; + } + else + { + CurrentSquare->DrawParticles(Beam.BeamColor); + + if((CurrentSquare->*lsquare::GetBeamEffect(Beam.BeamEffect))(Beam)) + break; + } + } + } + else + { + lsquare* Where = GetLSquare(CurrentPos); + Where->DrawParticles(Beam.BeamColor); + (Where->*lsquare::GetBeamEffect(Beam.BeamEffect))(Beam); + } +} + +/* Note: You will most likely need some help from supernatural entities to comprehend this code. Sorry. */ + +void level::LightningBeam(beamdata& Beam) +{ + v2 CurrentPos = Beam.StartPos; + + if(Beam.Direction == YOURSELF) + { + lsquare* Where = GetLSquare(CurrentPos); + + for(int c = 0; c < 4; ++c) + Where->DrawLightning(v2(8, 8), Beam.BeamColor, YOURSELF); + + (Where->*lsquare::GetBeamEffect(Beam.BeamEffect))(Beam); + return; + } + + v2 StartPos; + + switch(Beam.Direction) + { + case 0: StartPos = v2(15, 15); break; + case 1: StartPos = v2(RAND() & 15, 15); break; + case 2: StartPos = v2(0, 15); break; + case 3: StartPos = v2(15, RAND() & 15); break; + case 4: StartPos = v2(0, RAND() & 15); break; + case 5: StartPos = v2(15, 0); break; + case 6: StartPos = v2(RAND() & 15, 0); break; + case 7: StartPos = v2(0, 0); break; + } + + for(int Length = 0; Length < Beam.Range; ++Length) + { + CurrentPos += game::GetMoveVector(Beam.Direction); + + if(!IsValidPos(CurrentPos)) + break; + + lsquare* CurrentSquare = GetLSquare(CurrentPos); + + if(!CurrentSquare->IsFlyable()) + { + if((CurrentSquare->*lsquare::GetBeamEffect(Beam.BeamEffect))(Beam)) + break; + + truth W1, W2; + + switch(Beam.Direction) + { + case 0: + W1 = GetLSquare(CurrentPos + v2(1, 0))->IsFlyable(); + W2 = GetLSquare(CurrentPos + v2(0, 1))->IsFlyable(); + + if(W1 == W2) + Beam.Direction = 7; + else if(W1) + { + ++CurrentPos.Y; + Beam.Direction = 2; + } + else + { + ++CurrentPos.X; + Beam.Direction = 5; + } + + break; + case 1: Beam.Direction = 6; StartPos.Y = 0; break; + case 2: + W1 = GetLSquare(CurrentPos + v2(-1, 0))->IsFlyable(); + W2 = GetLSquare(CurrentPos + v2(0, 1))->IsFlyable(); + + if(W1 == W2) + Beam.Direction = 5; + else if(W1) + { + ++CurrentPos.Y; + Beam.Direction = 0; + } + else + { + --CurrentPos.X; + Beam.Direction = 7; + } + + break; + case 3: Beam.Direction = 4; StartPos.X = 0; break; + case 4: Beam.Direction = 3; StartPos.X = 15; break; + case 5: + W1 = GetLSquare(CurrentPos + v2(1, 0))->IsFlyable(); + W2 = GetLSquare(CurrentPos + v2(0, -1))->IsFlyable(); + + if(W1 == W2) + Beam.Direction = 2; + else if(W1) + { + --CurrentPos.Y; + Beam.Direction = 7; + } + else + { + ++CurrentPos.X; + Beam.Direction = 0; + } + + break; + case 6: Beam.Direction = 1; StartPos.Y = 15; break; + case 7: + W1 = GetLSquare(CurrentPos + v2(-1, 0))->IsFlyable(); + W2 = GetLSquare(CurrentPos + v2(0, -1))->IsFlyable(); + + if(W1 == W2) + Beam.Direction = 0; + else if(W1) + { + --CurrentPos.Y; + Beam.Direction = 5; + } + else + { + --CurrentPos.X; + Beam.Direction = 2; + } + + break; + } + + switch(Beam.Direction) + { + case 0: StartPos = v2(15, 15); break; + case 2: StartPos = v2(0, 15); break; + case 5: StartPos = v2(15, 0); break; + case 7: StartPos = v2(0, 0); break; + } + } + else + { + StartPos = CurrentSquare->DrawLightning(StartPos, Beam.BeamColor, Beam.Direction); + + if((CurrentSquare->*lsquare::GetBeamEffect(Beam.BeamEffect))(Beam)) + break; + } + } +} + +void level::ShieldBeam(beamdata& Beam) +{ + v2 Pos[3]; + + switch(Beam.Direction) + { + case 0: + Pos[0] = v2(-1, 0); + Pos[1] = v2(-1, -1); + Pos[2] = v2(0, -1); + break; + case 1: + Pos[0] = v2(-1, -1); + Pos[1] = v2(0, -1); + Pos[2] = v2(1, -1); + break; + case 2: + Pos[0] = v2(0, -1); + Pos[1] = v2(1, -1); + Pos[2] = v2(1, 0); + break; + case 3: + Pos[0] = v2(-1, 1); + Pos[1] = v2(-1, 0); + Pos[2] = v2(-1, -1); + break; + case 4: + Pos[0] = v2(1, -1); + Pos[1] = v2(1, 0); + Pos[2] = v2(1, 1); + break; + case 5: + Pos[0] = v2(0, 1); + Pos[1] = v2(-1, 1); + Pos[2] = v2(-1, 0); + break; + case 6: + Pos[0] = v2(1, 1); + Pos[1] = v2(0, 1); + Pos[2] = v2(-1, 1); + break; + case 7: + Pos[0] = v2(1, 0); + Pos[1] = v2(1, 1); + Pos[2] = v2(0, 1); + break; + case 8: + GetLSquare(Beam.StartPos)->DrawParticles(Beam.BeamColor); + (GetLSquare(Beam.StartPos)->*lsquare::GetBeamEffect(Beam.BeamEffect))(Beam); + return; + } + + for(int c = 0; c < 3; ++c) + if(IsValidPos(Beam.StartPos + Pos[c])) + { + GetLSquare(Beam.StartPos + Pos[c])->DrawParticles(Beam.BeamColor); + (GetLSquare(Beam.StartPos + Pos[c])->*lsquare::GetBeamEffect(Beam.BeamEffect))(Beam); + } +} + +outputfile& operator<<(outputfile& SaveFile, const level* Level) +{ + Level->Save(SaveFile); + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, level*& Level) +{ + Level = new level; + Level->Load(SaveFile); + return SaveFile; +} + +void (level::*Beam[BEAM_STYLES])(beamdata&) = +{ + &level::ParticleBeam, + &level::LightningBeam, + &level::ShieldBeam +}; + +void (level::*level::GetBeam(int I))(beamdata&) +{ + return Beam[I]; +} + +v2 level::FreeSquareSeeker(ccharacter* Char, v2 StartPos, v2 Prohibited, int MaxDistance, truth AllowStartPos) const +{ + int c; + + for(c = 0; c < 8; ++c) + { + v2 Pos = StartPos + game::GetMoveVector(c); + + if(IsValidPos(Pos) && Char->CanMoveOn(GetLSquare(Pos)) && Char->IsFreeForMe(GetLSquare(Pos)) && Pos != Prohibited && (AllowStartPos || !Char->PlaceIsIllegal(Pos, Prohibited))) + return Pos; + } + + if(MaxDistance) + for(c = 0; c < 8; ++c) + { + v2 Pos = StartPos + game::GetMoveVector(c); + + if(IsValidPos(Pos)) + { + if(Char->CanMoveOn(GetLSquare(Pos)) && Pos != Prohibited) + { + Pos = FreeSquareSeeker(Char, Pos, Prohibited, MaxDistance - 1, AllowStartPos); + + if(Pos != ERROR_V2) + return Pos; + } + } + } + + return ERROR_V2; +} + +/* Returns ERROR_V2 if no free square was found */ + +v2 level::GetNearestFreeSquare(ccharacter* Char, v2 StartPos, truth AllowStartPos) const +{ + if(AllowStartPos && Char->CanMoveOn(GetLSquare(StartPos)) && Char->IsFreeForMe(GetLSquare(StartPos))) + return StartPos; + + int c; + + for(c = 0; c < 8; ++c) + { + v2 Pos = StartPos + game::GetMoveVector(c); + + if(IsValidPos(Pos) && Char->CanMoveOn(GetLSquare(Pos)) && Char->IsFreeForMe(GetLSquare(Pos)) && (AllowStartPos || !Char->PlaceIsIllegal(Pos, StartPos))) + return Pos; + } + + for(int Dist = 0; Dist < 5; ++Dist) + for(c = 0; c < 8; ++c) + { + v2 Pos = StartPos + game::GetMoveVector(c); + + if(IsValidPos(Pos) && Char->CanMoveOn(GetLSquare(Pos))) + { + Pos = FreeSquareSeeker(Char, Pos, StartPos, Dist, AllowStartPos); + + if(Pos != ERROR_V2) + return Pos; + } + } + + return ERROR_V2; +} + +v2 level::GetFreeAdjacentSquare(ccharacter* Char, v2 StartPos, truth AllowCharacter) const +{ + int PossibleDir[8]; + int Index = 0; + lsquare* Origo = GetLSquare(StartPos); + + for(int d = 0; d < 8; ++d) + { + lsquare* Square = Origo->GetNeighbourLSquare(d); + + if(Square && Char->CanMoveOn(Square) && (AllowCharacter || Char->IsFreeForMe(Square))) + PossibleDir[Index++] = d; + } + + return Index ? StartPos + game::GetMoveVector(PossibleDir[RAND() % Index]) : ERROR_V2; +} + +void (level::*level::GetBeamEffectVisualizer(int I))(const fearray&, col16) const +{ + static void (level::*Visualizer[BEAM_STYLES])(const fearray&, col16) const = { &level::ParticleVisualizer, &level::LightningVisualizer, &level::ParticleVisualizer }; + return Visualizer[I]; +} + +void level::ParticleVisualizer(const fearray& Stack, col16 BeamColor) const +{ + clock_t StartTime = clock(); + game::DrawEverythingNoBlit(); + + for(fearray::sizetype c = 0; c < Stack.Size; ++c) + Stack[c]->DrawParticles(BeamColor, false); + + graphics::BlitDBToScreen(); + while(clock() - StartTime < 0.05 * CLOCKS_PER_SEC); +} + +void level::LightningVisualizer(const fearray& Stack, col16 BeamColor) const +{ + clock_t StartTime = clock(); + game::DrawEverythingNoBlit(); + + for(fearray::sizetype c = 0; c < Stack.Size; ++c) + Stack[c]->DrawLightning(v2(8, 8), BeamColor, YOURSELF, false); + + graphics::BlitDBToScreen(); + while(clock() - StartTime < 0.05 * CLOCKS_PER_SEC); +} + +truth level::PreProcessForBone() +{ + if(!*LevelScript->CanGenerateBone()) + return false; + + /* Gum solution */ + + game::SetQuestMonstersFound(0); + + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + Map[x][y]->PreProcessForBone(); + + int DungeonIndex = GetDungeon()->GetIndex(); + + return !(DungeonIndex == ELPURI_CAVE && Index == IVAN_LEVEL && game::GetQuestMonstersFound() < 5) + && (game::GetQuestMonstersFound() + || ((DungeonIndex != UNDER_WATER_TUNNEL || Index != VESANA_LEVEL) + && (DungeonIndex != ELPURI_CAVE || (Index != ENNER_BEAST_LEVEL && Index != DARK_LEVEL)))); +} + +truth level::PostProcessForBone() +{ + game::SetTooGreatDangerFound(false); + double DangerSum = 0; + int Enemies = 0; + + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + Map[x][y]->PostProcessForBone(DangerSum, Enemies); + + if(game::TooGreatDangerFound() || (Enemies && DangerSum / Enemies > Difficulty * 10)) + return false; + + return true; +} + +void level::FinalProcessForBone() +{ + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + Map[x][y]->FinalProcessForBone(); + + for(uint c = 1; c < Room.size(); ++c) + Room[c]->FinalProcessForBone(); +} + +void level::GenerateDungeon(int Index) +{ + cfestring* Msg = LevelScript->GetLevelMessage(); + + if(Msg) + LevelMessage = *Msg; + + if(*LevelScript->GenerateMonsters()) + { + MonsterGenerationInterval = *LevelScript->GetMonsterGenerationIntervalBase() + *LevelScript->GetMonsterGenerationIntervalDelta() * Index; + IdealPopulation = *LevelScript->GetMonsterAmountBase() + *LevelScript->GetMonsterAmountDelta() * Index; + } + + Difficulty = 0.001 * (*LevelScript->GetDifficultyBase() + *LevelScript->GetDifficultyDelta() * Index); + EnchantmentMinusChance = *LevelScript->GetEnchantmentMinusChanceBase() + *LevelScript->GetEnchantmentMinusChanceDelta() * Index; + EnchantmentPlusChance = *LevelScript->GetEnchantmentPlusChanceBase() + *LevelScript->GetEnchantmentPlusChanceDelta() * Index; + const contentscript* GTerrain = LevelScript->GetFillSquare()->GetGTerrain(); + const contentscript* OTerrain = LevelScript->GetFillSquare()->GetOTerrain(); + long Counter = 0; + int x; + game::BusyAnimation(); + + for(x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y, ++Counter) + Map[x][y]->SetLTerrain(GTerrain->Instantiate(), OTerrain->Instantiate()); + + uint c; + uint Rooms = LevelScript->GetRooms()->Randomize(); + const std::list& RoomList = LevelScript->GetRoom(); + std::list::const_iterator Iterator = RoomList.begin(); + + for(c = 0; c < Rooms; ++c) + { + game::BusyAnimation(); + + if(c < RoomList.size()) + { + int i; + + for(i = 0; i < 1000; ++i) + if(MakeRoom(&*Iterator)) + break; + + if(i == 1000) + ABORT("Failed to place special room #%d!", c); + + ++Iterator; + } + else + { + const roomscript* RoomScript = LevelScript->GetRoomDefault(); + + for(int i = 0; i < 50; ++i) + if(MakeRoom(RoomScript)) + break; + } + } + + game::BusyAnimation(); + + if(!*LevelScript->IgnoreDefaultSpecialSquares()) + { + /* Gum solution */ + + const levelscript* LevelBase = static_cast(LevelScript->GetBase()); + + if(LevelBase) + { + const std::list& Square = LevelBase->GetSquare(); + + for(std::list::const_iterator i = Square.begin(); i != Square.end(); ++i) + { + game::BusyAnimation(); + ApplyLSquareScript(&*i); + } + } + } + + const std::list& Square = LevelScript->GetSquare(); + + for(std::list::const_iterator i = Square.begin(); i != Square.end(); ++i) + { + game::BusyAnimation(); + ApplyLSquareScript(&*i); + } + + for(c = 0; c < AttachQueue.size(); ++c) + AttachPos(AttachQueue[c].X, AttachQueue[c].Y); + + for(x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + { + Map[x][y]->CalculateGroundBorderPartners(); + Map[x][y]->CalculateOverBorderPartners(); + } + + AttachQueue.clear(); + CreateItems(LevelScript->GetItems()->Randomize()); +} + +void level::GenerateJungle() +{ + int x,y; + + for(x = 0; x < XSize; ++x) + for(y = 0; y < YSize; ++y) + { + Map[x][y] = new lsquare(this, v2(x, y)); + Map[x][y]->SetLTerrain(solidterrain::Spawn(GRASS_TERRAIN), 0); + } + + for(;;) + { + CreateTunnelNetwork(1, 4, 20, 120, v2(0, YSize / 2)); + CreateTunnelNetwork(1, 4, 20, 120, v2(XSize - 1, YSize / 2)); + + for(int c = 0; c < 25; ++c) + { + v2 StartPos; + + switch(RAND_N(5)) + { + case 0: + StartPos = v2(RAND_N(XSize), 0); + break; + case 1: + StartPos = v2(RAND_N(XSize), YSize - 1); + break; + case 2: + StartPos = v2(0, RAND_N(YSize)); + break; + case 3: + StartPos = v2(XSize - 1, RAND_N(YSize)); + break; + case 4: + StartPos = v2(RAND_N(XSize), RAND_N(YSize)); + } + + CreateTunnelNetwork(1,4,20, 120, StartPos); + } + + for(x = 0; x < XSize; ++x) + { + game::BusyAnimation(); + + for(y = 0; y < YSize; ++y) + { + if(FlagMap[x][y] != PREFERRED) + Map[x][y]->ChangeOLTerrain(wall::Spawn(BRICK_PROPAGANDA)); + else if(RAND_2) + Map[x][y]->ChangeOLTerrain(decoration::Spawn(PALM)); + } + } + } +} + +void level::CreateTunnelNetwork(int MinLength, int MaxLength, int MinNodes, int MaxNodes, v2 StartPos) +{ + v2 Pos = StartPos, Direction; + int Length; + game::BusyAnimation(); + FlagMap[Pos.X][Pos.Y] = PREFERRED; + + for(int c1 = 0; c1 < MaxNodes; ++c1) + { + Direction = game::GetBasicMoveVector(RAND() % 4); + Length = MinLength + RAND_N(MaxLength - MinLength + 1); + + for(int c2 = 0; c2 < Length; ++c2) + { + if(IsValidPos(Direction + Pos)) + { + Pos += Direction; + FlagMap[Pos.X][Pos.Y] = PREFERRED; + } + else + { + if(c1 >= MinNodes) + return; + + break; + } + } + } +} + +void level::GenerateDesert() +{ + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + { + Map[x][y] = new lsquare(this, v2(x, y)); + Map[x][y]->SetLTerrain(solidterrain::Spawn(SAND_TERRAIN), 0); + } + + game::BusyAnimation(); + int AmountOfCactuses = RAND_N(10); + int c; + + for(c = 0; c < AmountOfCactuses; ++c) + Map[RAND_N(XSize)][RAND_N(YSize)]->ChangeOLTerrain(decoration::Spawn(CACTUS)); + + int AmountOfBoulders = RAND_N(10); + + for(c = 0; c < AmountOfBoulders; ++c) + Map[RAND_N(XSize)][RAND_N(YSize)]->ChangeOLTerrain(boulder::Spawn(1 + RAND_2)); +} + +void level::GenerateSteppe() +{ + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + { + Map[x][y] = new lsquare(this, v2(x, y)); + Map[x][y]->SetLTerrain(solidterrain::Spawn(GRASS_TERRAIN), 0); + } + + game::BusyAnimation(); + int c; + + int AmountOfBoulders = RAND_N(20) + 5; + + for(c = 0; c < AmountOfBoulders; ++c) + Map[RAND_N(XSize)][RAND_N(YSize)]->ChangeOLTerrain(boulder::Spawn(1 + RAND_2)); +} + +void level::GenerateLeafyForest() +{ + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + { + Map[x][y] = new lsquare(this, v2(x, y)); + olterrain* OLTerrain; + + switch(RAND_4) + { + case 0: + if(RAND_8) + OLTerrain = decoration::Spawn(OAK); + else + OLTerrain = decoration::Spawn(TEAK); + break; + case 1: + OLTerrain = decoration::Spawn(BIRCH); + break; + case 2: + OLTerrain = 0; + if(!RAND_4) + OLTerrain = boulder::Spawn(1 + RAND_2); + + if(!RAND_4) + OLTerrain = boulder::Spawn(3); + break; + default: + OLTerrain = 0; + } + + Map[x][y]->SetLTerrain(solidterrain::Spawn(GRASS_TERRAIN), OLTerrain); + } +} + +void level::GenerateEvergreenForest() +{ + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + { + Map[x][y] = new lsquare(this, v2(x, y)); + olterrain* OLTerrain = 0; + + switch(RAND_4) + { + case 0: + if(RAND_2) + OLTerrain = decoration::Spawn(PINE); + break; + case 1: + OLTerrain = decoration::Spawn(FIR); + break; + case 2: + if(!RAND_4) + OLTerrain = boulder::Spawn(1 + RAND_2); + + if(!RAND_4) + OLTerrain = boulder::Spawn(3); + break; + } + + Map[x][y]->SetLTerrain(solidterrain::Spawn(GRASS_TERRAIN), OLTerrain); + } +} + +void level::GenerateTundra() +{ + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + { + Map[x][y] = new lsquare(this, v2(x, y)); + Map[x][y]->SetLTerrain(solidterrain::Spawn(SNOW_TERRAIN), 0); + } + + game::BusyAnimation(); + int c; + int AmountOfBoulders = RAND_N(20) + 8; + + for(c = 0; c < AmountOfBoulders; ++c) + Map[RAND_N(XSize)][RAND_N(YSize)]->ChangeOLTerrain(boulder::Spawn(SNOW_BOULDER)); + + int AmountOfDwarfBirches = RAND_N(10); + + for(c = 0; c < AmountOfDwarfBirches; ++c) + Map[RAND_N(XSize)][RAND_N(YSize)]->ChangeOLTerrain(decoration::Spawn(DWARF_BIRCH)); +} + +void level::GenerateGlacier() +{ + int x,y; + + for(x = 0; x < XSize; ++x) + for(y = 0; y < YSize; ++y) + { + Map[x][y] = new lsquare(this, v2(x, y)); + Map[x][y]->SetLTerrain(solidterrain::Spawn(SNOW_TERRAIN), 0); + } + + int AmountOfBoulders = RAND_N(20) + 5; + + for(int c = 0; c < AmountOfBoulders; ++c) + Map[RAND_N(XSize)][RAND_N(YSize)]->ChangeOLTerrain(boulder::Spawn(SNOW_BOULDER)); + + for(;;) + { + CreateTunnelNetwork(1,4,20, 120, v2(0,YSize / 2)); + CreateTunnelNetwork(1,4,20, 120, v2(XSize - 1,YSize / 2)); + + for(int c = 0; c < 20; ++c) + { + v2 StartPos; + + switch(RAND_N(5)) + { + case 0: + StartPos = v2(RAND_N(XSize), 0); + break; + case 1: + StartPos = v2(RAND_N(XSize), YSize - 1); + break; + case 2: + StartPos = v2(0, RAND_N(YSize)); + break; + case 3: + StartPos = v2(XSize - 1, RAND_N(YSize)); + break; + case 4: + StartPos = v2(RAND_N(XSize), RAND_N(YSize)); + } + + CreateTunnelNetwork(1,4,20, 120, StartPos); + } + + for(x = 0; x < XSize; ++x) + for(y = 0; y < YSize; ++y) + if(FlagMap[x][y] != PREFERRED) + FlagMap[x][y] |= RAND_2 ? ICE_TERRAIN : STONE_TERRAIN; + + for(x = 0; x < XSize; ++x) + { + game::BusyAnimation(); + + for(y = 0; y < YSize; ++y) + { + if(!(FlagMap[x][y] & PREFERRED)) + { + int SquaresAround = 0; + int IceAround = 0; + + for(int d = 0; d < 8; ++d) + { + v2 Pos = v2(x,y) + game::GetMoveVector(d); + if(IsValidPos(Pos) && !(FlagMap[Pos.X][Pos.Y] & PREFERRED)) + { + ++SquaresAround; + if(FlagMap[Pos.X][Pos.Y] & ICE_TERRAIN) + ++IceAround; + } + } + + if(IceAround > SquaresAround / 2) + FlagMap[x][y] = ICE_TERRAIN; + else + FlagMap[x][y] = STONE_TERRAIN; + } + } + } + + for(x = 0; x < XSize; ++x) + for(y = 0; y < YSize; ++y) + if(!(FlagMap[x][y] & PREFERRED)) + { + if(FlagMap[x][y] & ICE_TERRAIN) + GetLSquare(x,y)->ChangeOLTerrain(wall::Spawn(ICE_WALL)); + else + GetLSquare(x,y)->ChangeOLTerrain(wall::Spawn(STONE_WALL)); + } + + break; // Doesn't yet check path in any way + } +} + +bool nodepointerstorer::operator<(const nodepointerstorer& N) const +{ + /* In the non-euclidean geometry of IVAN, certain very curved paths are as long as straight ones. + However, they are so ugly that it is best to prefer routes with as few diagonal moves as + possible without lengthening the travel. */ + + if(Node->TotalDistanceEstimate != N.Node->TotalDistanceEstimate) + return Node->TotalDistanceEstimate > N.Node->TotalDistanceEstimate; + else + return Node->Diagonals > N.Node->Diagonals; +} + +void node::CalculateNextNodes() +{ + static int TryOrder[8] = { 1, 3, 4, 6, 0, 2, 5, 7 }; + + for(int d = 0; d < 8; ++d) + { + v2 NodePos = Pos + game::GetMoveVector(TryOrder[d]); + + if(NodePos.X >= 0 && NodePos.Y >= 0 && NodePos.X < XSize && NodePos.Y < YSize) + { + node* Node = NodeMap[NodePos.X][NodePos.Y]; + + if(!Node->Processed && ((!SpecialMover && RequiredWalkability & WalkabilityMap[NodePos.X][NodePos.Y]) || (SpecialMover && SpecialMover->CanTheoreticallyMoveOn(Node->Square)) || NodePos == To)) + { + Node->Processed = true; + Node->Distance = Distance + 1; + Node->Diagonals = Diagonals; + + if(d >= 4) + ++Node->Diagonals; + + Node->Last = this; + + /* We use the heuristic max(abs(distance.x), abs(distance.y)) here, + which is exact in the current geometry if the path is open */ + + long Remaining = To.X - NodePos.X; + + if(Remaining < NodePos.X - To.X) + Remaining = NodePos.X - To.X; + + if(Remaining < NodePos.Y - To.Y) + Remaining = NodePos.Y - To.Y; + + if(Remaining < To.Y - NodePos.Y) + Remaining = To.Y - NodePos.Y; + + Node->Remaining = Remaining; + Node->TotalDistanceEstimate = Node->Distance + Node->Remaining; + NodeQueue->push(nodepointerstorer(Node)); + } + } + } +} + +/* Finds the shortest (but possibly not the shortest-looking) path between From and To + if such exists. Returns a pointer to the node associated with the last square or zero if + a route can't be found. Calling FindRoute again may invalidate the node, so you must + store the path in another format ASAP. */ + +node* level::FindRoute(v2 From, v2 To, const std::set& Illegal, int RequiredWalkability, ccharacter* SpecialMover) +{ + node::NodeMap = NodeMap; + node::RequiredWalkability = RequiredWalkability; + node::SpecialMover = SpecialMover; + node::To = To; + node::WalkabilityMap = WalkabilityMap; + node::XSize = XSize; + node::YSize = YSize; + + if(!Illegal.empty() && Illegal.find(To) != Illegal.end()) + return 0; + + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + NodeMap[x][y]->Processed = false; + + node* Node = NodeMap[From.X][From.Y]; + Node->Last = 0; + Node->Processed = true; + Node->Distance = 0; + Node->Diagonals = 0; + nodequeue NodeQueue; + NodeQueue.push(nodepointerstorer(Node)); + node::NodeQueue = &NodeQueue; + + while(!NodeQueue.empty()) + { + Node = NodeQueue.top().Node; + NodeQueue.pop(); + + if(Node->Pos == To) + return Node; + + if(Illegal.empty() || Illegal.find(Node->Pos) == Illegal.end()) + Node->CalculateNextNodes(); + } + + return 0; +} + +/* All items on ground are moved to the IVector and all characters to CVector */ + +void level::CollectEverything(itemvector& IVector, charactervector& CVector) +{ + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + { + lsquare* LS = Map[x][y]; + LS->GetStack()->MoveItemsTo(IVector, CENTER); + character* C = LS->GetCharacter(); + + if(C && !C->IsPlayer()) + { + C->Remove(); + CVector.push_back(C); + } + } +} + +void level::CreateGlobalRain(liquid* Liquid, v2 Speed) +{ + GlobalRainLiquid = Liquid; + GlobalRainSpeed = Speed; + + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + if(!Map[x][y]->IsInside()) + Map[x][y]->AddRain(Liquid, Speed, MONSTER_TEAM, false); +} + +void level::CheckSunLight() +{ + if(Index == 0 && GetDungeon()->GetIndex() == NEW_ATTNAM) + { + double Cos = cos(FPI * (game::GetTick() % 48000) / 24000.); + + if(Cos > 0.01) + { + int E = int(100 + Cos * 30); + SunLightEmitation = MakeRGB24(E, E, E); + AmbientLuminance = MakeRGB24(E - 6, E - 6, E - 6); + } + else + { + SunLightEmitation = 0; + AmbientLuminance = NightAmbientLuminance; + } + } + else if(Index == 0 && GetDungeon()->GetIndex() == ATTNAM) + { + double Cos = cos(FPI * (game::GetTick() % 48000) / 24000.); + + if(Cos > 0.41) + { + int E = int(100 + (Cos - 0.40) * 40); + SunLightEmitation = MakeRGB24(E, E, E); + AmbientLuminance = MakeRGB24(E - 8, E - 8, E - 8); + } + else + { + SunLightEmitation = 0; + AmbientLuminance = NightAmbientLuminance; + } + } + else if(Index == 0 && GetDungeon()->GetIndex() == MUNTUO) + { + double Cos = cos(FPI * (game::GetTick() % 48000) / 24000.); + + if(Cos > 0.21) + { + int E = int(100 + (Cos - 0.20) * 30); + SunLightEmitation = MakeRGB24(E, E, E); + AmbientLuminance = MakeRGB24(E - 4, E - 4, E - 4); + } + else + { + SunLightEmitation = 0; + AmbientLuminance = NightAmbientLuminance; + } + } + else + return; + + SunLightDirection = game::GetSunLightDirectionVector(); + ChangeSunLight(); +} + +void level::ChangeSunLight() +{ + truth SunSet = game::IsDark(SunLightEmitation); + ulong c; + + for(c = 0; c < XSizeTimesYSize; ++c) + Map[0][c]->RemoveSunLight(); + + if(!SunSet) + EmitSunBeams(); + + for(c = 0; c < XSizeTimesYSize; ++c) + { + lsquare* Square = Map[0][c]; + + if(Square->Flags & IS_TRANSPARENT) + Square->CalculateSunLightLuminance(EMITTER_SQUARE_PART_BITS); + + if(!Square->IsInside()) + Square->AmbientLuminance = AmbientLuminance; + + Square->SendSunLightSignals(); + } + + for(c = 0; c < XSizeTimesYSize; ++c) + Map[0][c]->CheckIfIsSecondarySunLightEmitter(); +} + +void level::InitSquarePartEmitationTicks() +{ + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + Map[x][y]->SquarePartEmitationTick = 0; +} + +truth level::GenerateWindows(int X, int Y) const +{ + olterrain* Terrain = Map[X][Y]->GetOLTerrain(); + + if(Terrain && Terrain->CreateWindowConfigurations() && !(RAND() % 6)) + { + Terrain->SetConfig(Terrain->GetConfig() | WINDOW); + Map[X][Y]->CalculateIsTransparent(); + return true; + } + + return false; +} + +struct sunbeamcontroller : public stackcontroller +{ + static truth Handler(int, int); + static void ProcessStack(); + static ulong ID; + static int SunLightBlockHeight; + static v2 SunLightBlockPos; + static truth ReSunEmitation; +}; + +ulong sunbeamcontroller::ID; +int sunbeamcontroller::SunLightBlockHeight; +v2 sunbeamcontroller::SunLightBlockPos; +truth sunbeamcontroller::ReSunEmitation; + +void level::ForceEmitterNoxify(const emittervector& Emitter) const +{ + for(emittervector::const_iterator i = Emitter.begin(); i != Emitter.end(); ++i) + { + ulong ID = i->ID; + lsquare* Square = GetLSquare(ExtractPosFromEmitterID(ID)); + + if(ID & SECONDARY_SUN_LIGHT) + Square->Noxify(Square->SecondarySunLightEmitation, SECONDARY_SUN_LIGHT); + else + Square->Noxify(Square->Emitation); + } +} + +void level::ForceEmitterEmitation(const emittervector& Emitter, const sunemittervector& SunEmitter, ulong IDFlags) const +{ + for(emittervector::const_iterator i = Emitter.begin(); i != Emitter.end(); ++i) + { + ulong ID = i->ID; + lsquare* Square = GetLSquare(ExtractPosFromEmitterID(ID)); + + if(ID & SECONDARY_SUN_LIGHT) + Square->Emitate(Square->SecondarySunLightEmitation, SECONDARY_SUN_LIGHT|IDFlags); + else + Square->Emitate(Square->Emitation, IDFlags); + } + + { + stackcontroller::Map = Map; + stackcontroller::Stack = SquareStack; + stackcontroller::StackIndex = 0; + stackcontroller::LevelXSize = XSize; + stackcontroller::LevelYSize = YSize; + sunbeamcontroller::ReSunEmitation = true; + + for(sunemittervector::const_iterator i = SunEmitter.begin(); i != SunEmitter.end(); ++i) + { + ulong ID = *i & ~(EMITTER_SHADOW_BITS|EMITTER_SQUARE_PART_BITS) | RE_SUN_EMITATED, SourceFlags; + int X, Y; + + if(ID & ID_X_COORDINATE) + { + X = (ID & EMITTER_IDENTIFIER_BITS) - (XSize << 3); + Y = ID & ID_BEGIN ? -1 : YSize; + SourceFlags = ID & ID_BEGIN ? SP_BOTTOM : SP_TOP; + } + else + { + X = ID & ID_BEGIN ? -1 : XSize; + Y = (ID & EMITTER_IDENTIFIER_BITS) - (YSize << 3); + SourceFlags = ID & ID_BEGIN ? SP_RIGHT : SP_LEFT; + } + + EmitSunBeam(v2(X, Y), ID, SourceFlags); + } + + sunbeamcontroller::ProcessStack(); + } +} + +struct loscontroller : public tickcontroller, public stackcontroller +{ + static truth Handler(int x, int y) + { + lsquare* Square = Map[x >> 1][y >> 1]; + culong SquareFlags = Square->Flags; + + if(SquareFlags & PERFECTLY_QUADRI_HANDLED) + return true; + + if(!(SquareFlags & IN_SQUARE_STACK)) + { + Square->Flags |= IN_SQUARE_STACK; + Stack[StackIndex++] = Square; + } + + if(SquareFlags & IS_TRANSPARENT) + { + Square->Flags |= PERFECTLY_QUADRI_HANDLED; + return true; + } + + cint SquarePartIndex = (x & 1) + ((y & 1) << 1); + Square->SquarePartLastSeen = Square->SquarePartLastSeen + & ~SquarePartTickMask[SquarePartIndex] + | ShiftedTick[SquarePartIndex]; + return false; + } + static ulong& GetTickReference(int X, int Y) + { + return Map[X][Y]->SquarePartLastSeen; + } + static void ProcessStack() + { + for(long c = 0; c < StackIndex; ++c) + Stack[c]->SignalSeen(Tick); + } +}; + +void level::UpdateLOS() +{ + game::RemoveLOSUpdateRequest(); + stackcontroller::Map = Map; + stackcontroller::Stack = SquareStack; + stackcontroller::StackIndex = 0; + tickcontroller::Tick = game::IncreaseLOSTick(); + tickcontroller::PrepareShiftedTick(); + int Radius = PLAYER->GetLOSRange(); + + for(int c = 0; c < PLAYER->GetSquaresUnder(); ++c) + mapmath::DoQuadriArea(PLAYER->GetPos(c).X, PLAYER->GetPos(c).Y, + Radius * Radius, XSize, YSize); + + loscontroller::ProcessStack(); + + if(PLAYER->StateIsActivated(INFRA_VISION)) + for(int c = 0; c < game::GetTeams(); ++c) + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled()) + (*i)->SendNewDrawRequest(); +} + +void level::EnableGlobalRain() +{ + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + Map[x][y]->EnableGlobalRain(); +} + +void level::DisableGlobalRain() +{ + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + Map[x][y]->DisableGlobalRain(); +} + +void level::InitLastSeen() +{ + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + Map[x][y]->InitLastSeen(); +} + +void level::EmitSunBeams() +{ + stackcontroller::Map = Map; + stackcontroller::LevelXSize = XSize; + stackcontroller::LevelYSize = YSize; + sunbeamcontroller::ReSunEmitation = false; + v2 Dir = SunLightDirection; + int x, y, X = 0, Y = 0, SourceFlags; + ulong IDFlags; + + /* Do not try to understand the logic behind the starting points of + sunbeams. I determined the formulas by trial and error since all + understandable loops produced strange shapes for the shadows of + either small of large objects probably due to rounding errors + made during line calculations. */ + + if(!Dir.X || (Dir.Y && abs(Dir.Y) < abs(Dir.X))) + { + if(Dir.Y > 0) + { + Y = -1; + SourceFlags = SP_BOTTOM; + IDFlags = ID_X_COORDINATE|ID_BEGIN; + } + else + { + Y = YSize; + SourceFlags = SP_TOP; + IDFlags = ID_X_COORDINATE; + } + } + else + { + if(Dir.X > 0) + { + X = -1; + SourceFlags = SP_RIGHT; + IDFlags = ID_BEGIN; + } + else + { + X = XSize; + SourceFlags = SP_LEFT; + IDFlags = 0; + } + } + + if(!Dir.X) + { + int Index = XSize << 3; + + for(x = 0; x < XSize; ++x, ++Index) + EmitSunBeam(v2(x, Y), Index | IDFlags, SourceFlags); + } + else if(!Dir.Y) + { + int Index = YSize << 3; + + for(y = 0; y < YSize; ++y, ++Index) + EmitSunBeam(v2(X, y), Index | IDFlags, SourceFlags); + } + else if(abs(Dir.Y) < abs(Dir.X)) + { + int Index = Dir.X > 0 ? 0 : XSize << 3; + int StartX = Dir.X > 0 ? -XSize << 3 : 0; + int EndX = Dir.X > 0 ? XSize : (XSize << 3) + XSize; + + for(x = StartX; x < EndX; ++x, ++Index) + EmitSunBeam(v2(x, Y), Index | IDFlags, SourceFlags); + } + else + { + int Index = Dir.Y > 0 ? 0 : YSize << 3; + int StartY = Dir.Y > 0 ? -YSize << 3 : 0; + int EndY = Dir.Y > 0 ? YSize : (YSize << 3) + YSize; + + for(y = StartY; y < EndY; ++y, ++Index) + EmitSunBeam(v2(X, y), Index | IDFlags, SourceFlags); + } +} + +void level::EmitSunBeam(v2 S, ulong ID, int SourceFlags) const +{ + S <<= 1; + v2 D = S + SunLightDirection; + sunbeamcontroller::ID = ID; + + if(SourceFlags & SP_TOP_LEFT) + { + sunbeamcontroller::SunLightBlockHeight = 0; + mapmath::DoLine(S.X, S.Y, D.X, D.Y, SKIP_FIRST); + } + + if(SourceFlags & SP_TOP_RIGHT) + { + sunbeamcontroller::SunLightBlockHeight = 0; + mapmath::DoLine(S.X + 1, S.Y, D.X + 1, D.Y, SKIP_FIRST); + } + + if(SourceFlags & SP_BOTTOM_LEFT) + { + sunbeamcontroller::SunLightBlockHeight = 0; + mapmath::DoLine(S.X, S.Y + 1, D.X, D.Y + 1, SKIP_FIRST); + } + + if(SourceFlags & SP_BOTTOM_RIGHT) + { + sunbeamcontroller::SunLightBlockHeight = 0; + mapmath::DoLine(S.X + 1, S.Y + 1, D.X + 1, D.Y + 1, SKIP_FIRST); + } +} + +truth sunbeamcontroller::Handler(int x, int y) +{ + int X = x >> 1, Y = y >> 1; + + if(X < 0 || Y < 0 || X >= LevelXSize || Y >= LevelYSize) + return (X >= -1 && X <= LevelXSize) || (Y >= -1 && Y <= LevelYSize); + + lsquare* Square = Map[X][Y]; + int SquarePartIndex = (x & 1) + ((y & 1) << 1); + + if(SunLightBlockHeight && !Square->IsInside() + && HypotSquare(x - SunLightBlockPos.X, y - SunLightBlockPos.Y) > SunLightBlockHeight) + SunLightBlockHeight = 0; + + if(!SunLightBlockHeight) + { + ulong Flag = 1 << EMITTER_SQUARE_PART_SHIFT << SquarePartIndex; + Square->AddSunLightEmitter(ID | Flag); + } + else + { + ulong Flags = ((1 << EMITTER_SQUARE_PART_SHIFT) + | (1 << EMITTER_SHADOW_SHIFT)) + << SquarePartIndex; + + Square->AddSunLightEmitter(ID | Flags); + } + + if(ReSunEmitation) + { + if(!(Square->Flags & IN_SQUARE_STACK)) + Stack[StackIndex++] = Square; + + Square->Flags |= IN_SQUARE_STACK|CHECK_SUN_LIGHT_NEEDED; + + for(int d = 0; d < 8; ++d) + { + lsquare* Neighbour = Square->GetNeighbourLSquare(d); + + if(Neighbour && !(Neighbour->Flags & IN_SQUARE_STACK)) + { + Neighbour->Flags |= IN_SQUARE_STACK; + Stack[StackIndex++] = Neighbour; + } + } + } + + if(!(Square->Flags & IS_TRANSPARENT) || (SunLightBlockHeight && Square->IsInside())) + { + /* This should depend on the square */ + SunLightBlockHeight = 81; + SunLightBlockPos = v2(x, y); + } + + return true; +} + +void sunbeamcontroller::ProcessStack() +{ + long c; + + for(c = 0; c < StackIndex; ++c) + { + lsquare* Square = Stack[c]; + + if(Square->Flags & CHECK_SUN_LIGHT_NEEDED) + { + if(Square->Flags & IS_TRANSPARENT) + Square->CalculateSunLightLuminance(EMITTER_SQUARE_PART_BITS); + + Square->SendSunLightSignals(); + Square->ZeroReSunEmitatedFlags(); + } + + Square->Flags &= ~(IN_SQUARE_STACK|CHECK_SUN_LIGHT_NEEDED); + } + + for(c = 0; c < StackIndex; ++c) + Stack[c]->CheckIfIsSecondarySunLightEmitter(); +} + +int level::DetectMaterial(cmaterial* Material) +{ + ulong Tick = game::IncreaseLOSTick(); + int Squares = 0; + + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + { + lsquare* Square = Map[x][y]; + + if(Square->DetectMaterial(Material)) + { + Square->Reveal(Tick, true); + ++Squares; + } + } + + return Squares; +} + +void level::BlurMemory() +{ + int x, y, SquareStackSize = 0; + + for(x = 0; x < XSize; ++x) + for(y = 0; y < YSize; ++y) + { + lsquare* Square = Map[x][y]; + + if(Square->HasNoBorderPartners()) + SquareStack[SquareStackSize++] = Square; + } + + for(x = 0; x < XSize; ++x) + for(y = 0; y < YSize; ++y) + { + lsquare* Square = Map[x][y]; + Square->Flags |= STRONG_NEW_DRAW_REQUEST + | MEMORIZED_UPDATE_REQUEST + | DESCRIPTION_CHANGE; + + if(Square->HasNoBorderPartners() + && RAND() & 1 + && SquareStackSize) + Square->SwapMemorized(SquareStack[RAND() % SquareStackSize]); + else if(RAND() & 1) + Square->DestroyMemorized(); + } +} + +void level::CalculateLuminances() +{ + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + { + lsquare* Square = Map[x][y]; + Square->CalculateLuminance(); + Square->Flags |= MEMORIZED_UPDATE_REQUEST + | DESCRIPTION_CHANGE; + } +} + +struct areacontroller : public stackcontroller +{ + static truth Handler(int x, int y) + { + if(x >= 0 && y >= 0 && x < LevelXSize && y < LevelYSize + && HypotSquare(x - Center.X, y - Center.Y) <= RadiusSquare) + { + lsquare* Square = Map[x][y]; + + if(!(Square->Flags & IN_SQUARE_STACK)) + { + Stack[StackIndex++] = Square; + Square->Flags |= IN_SQUARE_STACK; + return Square->IsFlyable(); + } + } + + return false; + } + static int GetStartX(int) { return Center.X; } + static int GetStartY(int) { return Center.Y; } + static long RadiusSquare; +}; + +long areacontroller::RadiusSquare; + +int level::AddRadiusToSquareStack(v2 Center, long RadiusSquare) const +{ + stackcontroller::Map = Map; + stackcontroller::Stack = SquareStack; + SquareStack[0] = GetLSquare(Center); + stackcontroller::StackIndex = 1; + stackcontroller::LevelXSize = XSize; + stackcontroller::LevelYSize = YSize; + stackcontroller::Center = Center; + areacontroller::RadiusSquare = RadiusSquare; + mapmath::DoArea(); + return stackcontroller::StackIndex; +} + +/* Any fountain is good that is not dry and is NOT Except */ + +olterrain* level::GetRandomFountainWithWater(olterrain* Except) const +{ + std::vector Found; + olterrain* OLTerrain; + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + { + OLTerrain = GetLSquare(x,y)->GetOLTerrain(); + if(OLTerrain && OLTerrain != Except && OLTerrain->IsFountainWithWater()) + Found.push_back(OLTerrain); + } + + if(Found.empty()) + return 0; + + return Found[RAND_N(Found.size())]; +} +/* //ghastly piece of code that does not work +olterrain* level::FindWard(const room* Room) const +{ + std::vector Found; + olterrain* OLTerrain; + + v2 RoomPos = Room->GetPos(); + v2 RoomSize = Room->GetSize(); + + for(int x = RoomPos.X; x < (RoomPos.X + RoomSize.X); ++x) + for(int y = RoomPos.Y; y < ( RoomPos.Y + RoomSize.Y); ++y) + { + OLTerrain = GetLSquare(x,y)->GetOLTerrain(); + if(OLTerrain && OLTerrain->IsWard()) + return OLTerrain;//Found.push_back(OLTerrain); + } + + //if(Found.empty()) + return 0; + + //return Found[RAND_N(Found.size())]; +}*/ + +void level::Amnesia(int Percentile) +{ + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + { + lsquare* Square = Map[x][y]; + + if(Square->HasNoBorderPartners() && RAND_N(100) < Percentile) + { + Square->Flags |= STRONG_NEW_DRAW_REQUEST + | MEMORIZED_UPDATE_REQUEST + | DESCRIPTION_CHANGE; + + Square->DestroyMemorized(); + } + } +} + +/* Returns how many of the monsters were seen */ + +spawnresult level::SpawnMonsters(characterspawner Spawner, team* Team, + v2 Pos, int Config, int Amount, + truth IgnoreWalkability) +{ + spawnresult SR = { 0, 0 }; + + for(int c = 0; c < Amount; ++c) + { + character* Char = Spawner(Config, 0); + + if(!c) + SR.Pioneer = Char; + + Char->SetTeam(Team); + + if(IgnoreWalkability) + Char->ForcePutNear(Pos); + else + Char->PutNear(Pos); + + if(Char->CanBeSeenByPlayer()) + ++SR.Seen; + } + + return SR; +} + +void level::AddSpecialCursors() +{ + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + Map[x][y]->AddSpecialCursors(); +} + +void level::GasExplosion(gas* GasMaterial, lsquare* Square) +{ + for(int d = 0; d < 9; ++d) + { + lsquare* Neighbour = Square->GetNeighbourLSquare(d); + + if(Neighbour && Neighbour->IsFlyable()) + Neighbour->AddSmoke(static_cast(GasMaterial->SpawnMore(1000))); + } +} diff --git a/Main/Source/levelset.cpp b/Main/Source/levelset.cpp new file mode 100644 index 0000000..ebe34c0 --- /dev/null +++ b/Main/Source/levelset.cpp @@ -0,0 +1,56 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#define __FILE_OF_STATIC_LTERRAIN_PROTOTYPE_DEFINITIONS__ + +#include "proto.h" +#include "lterra.h" +#include "database.h" + +EXTENDED_SYSTEM_SPECIALIZATIONS(glterrain)(0, 0, "glterrain"); +EXTENDED_SYSTEM_SPECIALIZATIONS(olterrain)(0, 0, "olterrain"); + +#include "lterras.h" + +#undef __FILE_OF_STATIC_LTERRAIN_PROTOTYPE_DEFINITIONS__ + +#include +#include + +#include "char.h" +#include "team.h" +#include "action.h" +#include "message.h" +#include "stack.h" +#include "iconf.h" +#include "miscitem.h" +#include "room.h" +#include "game.h" +#include "graphics.h" +#include "bitmap.h" +#include "god.h" +#include "bitmap.h" +#include "materias.h" +#include "confdef.h" +#include "fluid.h" +#include "nonhuman.h" +#include "smoke.h" +#include "save.h" +#include "allocate.h" +#include "whandler.h" +#include "rain.h" +#include "balance.h" + +#include "level.cpp" +#include "lsquare.cpp" +#include "lterra.cpp" +#include "lterras.cpp" diff --git a/Main/Source/lsquare.cpp b/Main/Source/lsquare.cpp new file mode 100644 index 0000000..1443e9b --- /dev/null +++ b/Main/Source/lsquare.cpp @@ -0,0 +1,2947 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through levelset.cpp */ + +lsquare*** eyecontroller::Map; + +lsquare*** pathcontroller::Map; +ccharacter* pathcontroller::Character; + +lsquare*** stackcontroller::Map; +lsquare** stackcontroller::Stack; +long stackcontroller::StackIndex; +int stackcontroller::LevelXSize, stackcontroller::LevelYSize; +v2 stackcontroller::Center; + +ulong tickcontroller::Tick; +ulong tickcontroller::ShiftedTick[4]; +ulong tickcontroller::ShiftedQuadriTick[4]; + +void tickcontroller::PrepareShiftedTick() +{ + for(int c = 0; c < 4; ++c) + { + ShiftedTick[c] = Tick << (c << 3); + ShiftedQuadriTick[c] = (Tick + 1) << (c << 3); + } +} + +truth lsquare::IsDipDestination() const { return GLTerrain->IsDipDestination() || (OLTerrain && OLTerrain->IsDipDestination()); } + +lsquare::lsquare(level* LevelUnder, v2 Pos) +: square(LevelUnder, Pos), + Fluid(0), Smoke(0), Rain(0), Trap(0), + GLTerrain(0), OLTerrain(0), + Memorized(0), FowMemorized(0), + Engraved(0), + GroundBorderPartnerTerrain(0), + GroundBorderPartnerInfo(0), + OverBorderPartnerTerrain(0), + OverBorderPartnerInfo(0), + SquarePartEmitationTick(0), + SquarePartLastSeen(0), + Emitation(0), + SmokeAlphaSum(0), + AmbientLuminance(0), + SunLightLuminance(0), + TemporaryEmitation(0), + SecondarySunLightEmitation(0), + LastExplosionID(0), + RoomIndex(0) +{ + Stack = new stack(this, 0); +} + +lsquare::~lsquare() +{ + delete GLTerrain; + delete OLTerrain; + delete Stack; + delete [] Engraved; + + for(fluid* F = Fluid; F;) + { + fluid* ToDel = F; + F = F->Next; + delete ToDel; + } + + delete Memorized; + delete FowMemorized; + delete StaticContentCache.Bitmap; + delete [] GroundBorderPartnerTerrain; + delete [] OverBorderPartnerTerrain; + + for(smoke* S = Smoke; S;) + { + smoke* ToDel = S; + S = S->Next; + delete ToDel; + } + + for(rain* R = Rain; R;) + { + rain* ToDel = R; + R = R->Next; + delete ToDel; + } + + for(trap* T = Trap; T;) + { + trap* ToDel = T; + T = T->Next; + delete ToDel; + } +} + +void lsquare::SignalEmitationIncrease(col24 EmitationUpdate) +{ + if(game::CompareLights(EmitationUpdate, Emitation) > 0 && !game::IsGenerating() && !(Flags & FREEZED)) + { + CalculateEmitation(); // could this be optimized? + Emitate(); + } +} + +void lsquare::SignalEmitationDecrease(col24 EmitationUpdate) +{ + if(game::CompareLights(EmitationUpdate, Emitation) >= 0 && Emitation && !game::IsGenerating() && !(Flags & FREEZED)) + { + col24 Backup = Emitation; + CalculateEmitation(); + + if(Backup != Emitation) + { + Noxify(Backup); + Emitate(Emitation, FORCE_ADD); + } + } +} + +void lsquare::CalculateEmitation() +{ + Emitation = Stack->GetEmitation(); + int c; + + for(c = 0; c < 4; ++c) + { + stack* Stack = GetStackOfAdjacentSquare(c); + + if(Stack) + game::CombineLights(Emitation, Stack->GetSideEmitation(3 - c)); + } + + if(Character) + game::CombineLights(Emitation, Character->GetEmitation()); + + game::CombineLights(Emitation, GLTerrain->GetEmitation()); + + if(OLTerrain) + game::CombineLights(Emitation, OLTerrain->GetEmitation()); + + game::CombineLights(Emitation, TemporaryEmitation); + + for(const fluid* F = Fluid; F; F = F->Next) + game::CombineLights(Emitation, F->GetEmitation()); + + for(const rain* R = Rain; R; R = R->Next) + game::CombineLights(Emitation, R->GetEmitation()); +} + +void lsquare::UpdateMemorized() +{ + if(Flags & MEMORIZED_UPDATE_REQUEST) + { + if(!IsDark() || CanBeFeltByPlayer()) + { + blitdata B = { Memorized, + { 0, 0 }, + { 0, 0 }, + { TILE_SIZE, TILE_SIZE }, + { NORMAL_LUMINANCE }, + TRANSPARENT_COLOR, + ALLOW_ALPHA }; + + DrawStaticContents(B); + Memorized->FastBlit(FowMemorized); + B.Bitmap = FowMemorized; + B.Flags = 0; + B.MaskColor = 0; + igraph::GetFOWGraphic()->NormalMaskedBlit(B); + } + else + { + Memorized->ClearToColor(0); + igraph::GetFOWGraphic()->FastBlit(FowMemorized); + } + + if(!StaticContentCache.Bitmap) + { + StaticContentCache.Bitmap = new bitmap(TILE_V2); + StaticContentCache.Bitmap->ActivateFastFlag(); + } + + UpdateStaticContentCache(Luminance); + Flags &= ~MEMORIZED_UPDATE_REQUEST; + } +} + +void lsquare::UpdateStaticContentCache(col24 Luminance) const +{ + blitdata B = { StaticContentCache.Bitmap, + { 0, 0 }, + { 0, 0 }, + { TILE_SIZE, TILE_SIZE }, + { Luminance }, + 0, + 0 }; + + Memorized->LuminanceBlit(B); + StaticContentCache.Luminance = Luminance; +} + +void lsquare::DrawStaticContents(blitdata& BlitData) const +{ + if(BlitData.CustomData & ALLOW_ANIMATE && !StaticAnimatedEntities && Memorized && !game::GetSeeWholeMapCheatMode()) + { + if(StaticContentCache.Luminance != BlitData.Luminance) + UpdateStaticContentCache(BlitData.Luminance); + + StaticContentCache.Bitmap->FastBlit(BlitData.Bitmap, BlitData.Dest); + return; + } + + if(!OLTerrain || OLTerrain->ShowThingsUnder()) + GLTerrain->Draw(BlitData); + + int c; + int GroundPartners = GroundBorderPartnerInfo >> 24 & 15; + + for(c = 0; c < GroundPartners; ++c) + { + BlitData.CustomData |= 8 - (GroundBorderPartnerInfo >> ((c << 1) + c) & 7); + GroundBorderPartnerTerrain[c]->Draw(BlitData); + BlitData.CustomData &= ~SQUARE_INDEX_MASK; + } + + truth StackDrawn = false; + + if(OLTerrain && !IsFlyable()) + { + if(OLTerrain->IsTransparent() && OLTerrain->ShowThingsUnder()) + { + StackDrawn = true; + DrawStacks(BlitData); + } + + OLTerrain->Draw(BlitData); + } + + for(const fluid* F = Fluid; F; F = F->Next) + F->SimpleDraw(BlitData); + + if(OLTerrain && IsFlyable()) + OLTerrain->Draw(BlitData); + + if(!StackDrawn && Flags & IS_TRANSPARENT) + DrawStacks(BlitData); + + for(const trap* T = Trap; T; T = T->Next) + T->Draw(BlitData); + + int OverPartners = OverBorderPartnerInfo >> 24 & 15; + + for(c = 0; c < OverPartners; ++c) + { + BlitData.CustomData |= 8 - (OverBorderPartnerInfo >> ((c << 1) + c) & 7); + OverBorderPartnerTerrain[c]->Draw(BlitData); + BlitData.CustomData &= ~SQUARE_INDEX_MASK; + } +} + +void lsquare::Draw(blitdata& BlitData) const +{ + if(Flags & NEW_DRAW_REQUEST || AnimatedEntities) + { + if(!IsDark() || game::GetSeeWholeMapCheatMode()) + { + if(game::GetSeeWholeMapCheatMode() == SHOW_MAP_IN_UNIFORM_LIGHT + || (game::GetSeeWholeMapCheatMode() + && !(Flags & IS_TRANSPARENT))) + BlitData.Luminance = ivanconfig::GetContrastLuminance(); + else + BlitData.Luminance = ivanconfig::ApplyContrastTo(Luminance); + + DrawStaticContents(BlitData); + + if(Character && (Character->CanBeSeenByPlayer() || game::GetSeeWholeMapCheatMode())) + { + BlitData.CustomData |= Character->GetSquareIndex(Pos); + + if(Character->IsFlying()) + { + for(const smoke* S = Smoke; S; S = S->Next) + S->Draw(BlitData); + + Character->Draw(BlitData); + } + else + { + Character->Draw(BlitData); + + for(const smoke* S = Smoke; S; S = S->Next) + S->Draw(BlitData); + } + + BlitData.CustomData &= ~SQUARE_INDEX_MASK; + } + else + for(const smoke* S = Smoke; S; S = S->Next) + S->Draw(BlitData); + + for(const rain* R = Rain; R; R = R->Next) + if(R->IsEnabled()) + R->Draw(BlitData); + } + else if(CanBeFeltByPlayer()) + { + col24 L = Luminance; + game::CombineLights(L, DIM_LUMINANCE); + BlitData.Luminance = ivanconfig::ApplyContrastTo(L); + DrawStaticContents(BlitData); + + for(const rain* R = Rain; R; R = R->Next) + if(R->IsEnabled()) + R->Draw(BlitData); + } + else + { + DOUBLE_BUFFER->Fill(BlitData.Dest, BlitData.Border, 0); + + if(Character && Character->CanBeSeenByPlayer()) + { + BlitData.CustomData |= Character->GetSquareIndex(Pos); + BlitData.Luminance = ivanconfig::GetContrastLuminance(); + Character->Draw(BlitData); + BlitData.CustomData &= ~SQUARE_INDEX_MASK; + } + } + + Flags &= ~STRONG_NEW_DRAW_REQUEST; + } +} + +struct emitationcontroller : public tickcontroller, public stackcontroller +{ + static truth Handler(int x, int y) + { + lsquare* Square = Map[x >> 1][y >> 1]; + culong SquareFlags = Square->Flags; + + if(SquareFlags & PERFECTLY_QUADRI_HANDLED) + return SquareFlags & ALLOW_EMITATION_CONTINUE; + + if(SquareFlags & IS_TRANSPARENT) + return ProcessSquare(x >> 1, y >> 1, Square); + + if(!(SquareFlags & IN_SQUARE_STACK)) + { + Square->Flags |= IN_SQUARE_STACK; + Stack[StackIndex++] = Square; + } + + cint SquarePartIndex = (x & 1) + ((y & 1) << 1); + Square->SquarePartEmitationTick = Square->SquarePartEmitationTick + & ~SquarePartTickMask[SquarePartIndex] + | ShiftedTick[SquarePartIndex]; + + return false; + } + static int ProcessSquare(int X, int Y, lsquare* Square) + { + Stack[StackIndex++] = Square; + culong SquareFlags = Square->Flags; + cint MaxE = MaxLuxTable[X - EmitterPosXMinus16][Y - EmitterPosYMinus16]; + + if(MaxE >= LIGHT_BORDER + && (SquareFlags & INSIDE + || (!(ID & SECONDARY_SUN_LIGHT) + && MaxE > MinNightAmbientLuminanceElement))) + { + Square->Flags |= ALLOW_EMITATION_CONTINUE | PERFECTLY_QUADRI_HANDLED; + return true; + } + else + { + Square->Flags = SquareFlags & ~ALLOW_EMITATION_CONTINUE | PERFECTLY_QUADRI_HANDLED; + return false; + } + } + static ulong& GetTickReference(int X, int Y) + { + return Map[X][Y]->SquarePartEmitationTick; + } + static void ProcessStack() + { + for(long c1 = 0; c1 < StackIndex; ++c1) + { + lsquare* Square = Stack[c1]; + culong SquareTick = Square->SquarePartEmitationTick; + ulong TempID = ID; + + for(int c2 = 0; c2 < 4; ++c2) + if((SquareTick & SquarePartTickMask[c2]) == ShiftedTick[c2]) + TempID |= 1 << EMITTER_SQUARE_PART_SHIFT << c2; + + Square->Flags &= ~(IN_SQUARE_STACK|PERFECTLY_QUADRI_HANDLED); + v2 Pos = Square->Pos; + int XVal = Pos.X - EmitterPosXMinus16; + int YVal = Pos.Y - EmitterPosYMinus16; + + if(MaxLuxTable[XVal][YVal] >= LIGHT_BORDER) + Square->AlterLuminance(TempID, MakeRGB24(RedLuxTable[XVal][YVal], + GreenLuxTable[XVal][YVal], + BlueLuxTable[XVal][YVal])); + } + } + static ulong ID; + static int MinNightAmbientLuminanceElement; + static int EmitterPosXMinus16; + static int EmitterPosYMinus16; + static uchar** MaxLuxTable; + static uchar** RedLuxTable; + static uchar** GreenLuxTable; + static uchar** BlueLuxTable; +}; + +ulong emitationcontroller::ID; +int emitationcontroller::MinNightAmbientLuminanceElement; +int emitationcontroller::EmitterPosXMinus16; +int emitationcontroller::EmitterPosYMinus16; +uchar** emitationcontroller::MaxLuxTable; +uchar** emitationcontroller::RedLuxTable; +uchar** emitationcontroller::GreenLuxTable; +uchar** emitationcontroller::BlueLuxTable; + +void lsquare::Emitate(col24 Emitation, ulong IDFlags) +{ + if(game::IsDark(Emitation)) + return; + + int Radius = game::CalculateMinimumEmitationRadius(Emitation); + + if(!Radius) + return; + + stackcontroller::Map = GetLevel()->GetMap(); + stackcontroller::Stack = GetLevel()->GetSquareStack(); + stackcontroller::StackIndex = 0; + tickcontroller::Tick = game::IncreaseSquarePartEmitationTicks(); + tickcontroller::PrepareShiftedTick(); + emitationcontroller::ID = MakeEmitterID(Pos) | IDFlags; + emitationcontroller::MinNightAmbientLuminanceElement = GetMinColor24(GetLevel()->GetNightAmbientLuminance()); + emitationcontroller::EmitterPosXMinus16 = Pos.X - 16; + emitationcontroller::EmitterPosYMinus16 = Pos.Y - 16; + emitationcontroller::MaxLuxTable = game::GetLuxTable()[GetMaxColor24(Emitation)]; + emitationcontroller::RedLuxTable = game::GetLuxTable()[GetRed24(Emitation)]; + emitationcontroller::GreenLuxTable = game::GetLuxTable()[GetGreen24(Emitation)]; + emitationcontroller::BlueLuxTable = game::GetLuxTable()[GetBlue24(Emitation)]; + mapmath::DoQuadriArea(Pos.X, Pos.Y, + Radius * Radius, + GetLevel()->GetXSize(), + GetLevel()->GetYSize()); + emitationcontroller::ProcessStack(); +} + +struct noxifycontroller : public stackcontroller +{ + static truth Handler(int x, int y) + { + if(x >= 0 && y >= 0 && x < LevelXSize && y < LevelYSize) + { + lsquare* Square = Map[x][y]; + + if(Square->SquarePartEmitationTick != Tick) + { + Square->SquarePartEmitationTick = Tick; + return Square->NoxifyEmitter(ID); + } + } + + return false; + } + static int GetStartX(int) { return Center.X; } + static int GetStartY(int) { return Center.Y; } + static ulong ID; + static ulong Tick; +}; + +ulong noxifycontroller::ID; +ulong noxifycontroller::Tick; + +void lsquare::Noxify(col24 Emitation, ulong IDFlags) +{ + if(game::IsDark(Emitation)) + return; + + int Radius = game::CalculateMinimumEmitationRadius(Emitation); + + if(!Radius) + return; + + stackcontroller::Map = GetLevel()->GetMap(); + stackcontroller::LevelXSize = GetLevel()->GetXSize(); + stackcontroller::LevelYSize = GetLevel()->GetYSize(); + stackcontroller::Center = Pos; + noxifycontroller::ID = MakeEmitterID(Pos) | IDFlags; + noxifycontroller::Tick = game::IncreaseSquarePartEmitationTicks(); + NoxifyEmitter(noxifycontroller::ID); + mapmath::DoArea(); +} + +truth lsquare::NoxifyEmitter(ulong ID) +{ + emittervector::iterator i, End = Emitter.end(); + + for(i = Emitter.begin(); i != End; ++i) + if(!((i->ID ^ ID) & (EMITTER_IDENTIFIER_BITS|SECONDARY_SUN_LIGHT))) + { + RemoveLuminance(i->Emitation); + Swap(*i, Emitter.back()); + Emitter.pop_back(); + return true; + } + + return false; +} + +void lsquare::AlterLuminance(ulong ID, col24 NewLuminance) +{ + emittervector::iterator i, End = Emitter.end(); + + if(!(ID & FORCE_ADD)) + for(i = Emitter.begin(); i != End; ++i) + if(!((i->ID ^ ID) & (EMITTER_IDENTIFIER_BITS|SECONDARY_SUN_LIGHT))) + { + i->ID |= ID; + + if(i->Emitation != NewLuminance) + ChangeLuminance(i->Emitation, NewLuminance); + + return; + } + + Emitter.push_back(emitter(ID, 0)); + ChangeLuminance(Emitter.back().Emitation, NewLuminance); +} + +void lsquare::AddSunLightEmitter(ulong ID) +{ + sunemittervector::iterator i, End = SunEmitter.end(); + + for(i = SunEmitter.begin(); i != End; ++i) + if(!((*i ^ ID) & EMITTER_IDENTIFIER_BITS)) + { + if(ID & ~*i & RE_SUN_EMITATED) + *i &= ~EMITTER_SHADOW_BITS; + + *i |= ID; + Swap(*i, SunEmitter.front()); + return; + } + + SunEmitter.push_back(ID); +} + +truth lsquare::Open(character* Opener) +{ + return GetStack()->Open(Opener) || (OLTerrain && OLTerrain->Open(Opener)); +} + +truth lsquare::Close(character* Closer) +{ + if(!GetStack()->GetItems() && !Character) + return OLTerrain && OLTerrain->Close(Closer); + else + { + ADD_MESSAGE("There's something in the way..."); + return false; + } +} + +void lsquare::Save(outputfile& SaveFile) const +{ + Stack->Save(SaveFile); // This must be before square::Save! (Note: This comment is years old. It's probably obsolete) + square::Save(SaveFile); + SaveFile << GLTerrain << OLTerrain; + SaveFile << Emitter << SunEmitter; + SaveFile << Emitation << Engraved << Luminance; + SaveFile << SmokeAlphaSum << (uchar)Flags << Memorized; + SaveFile << SecondarySunLightEmitation; + SaveFile << (uchar)RoomIndex; + SaveFile << SunLightLuminance; + SaveLinkedList(SaveFile, Fluid); + SaveLinkedList(SaveFile, Smoke); + SaveLinkedList(SaveFile, Rain); + SaveLinkedList(SaveFile, Trap); +} + +void lsquare::Load(inputfile& SaveFile) +{ + Stack->Load(SaveFile); // This must be before square::Load! (Note: This comment is years old. It's probably obsolete) + Stack->SetMotherSquare(this); + square::Load(SaveFile); + SaveFile >> GLTerrain >> OLTerrain; + SaveFile >> Emitter >> SunEmitter; + SaveFile >> Emitation >> Engraved >> Luminance; + SaveFile >> SmokeAlphaSum >> (uchar&)Flags >> Memorized; + Flags &= INSIDE|DESCRIPTION_CHANGE; //only these flags are loaded + Flags |= MEMORIZED_UPDATE_REQUEST; + SecondarySunLightEmitation = ReadType(SaveFile); + RoomIndex = ReadType(SaveFile); + SunLightLuminance = ReadType(SaveFile); + LoadLinkedList(SaveFile, Fluid); + LoadLinkedList(SaveFile, Smoke); + LoadLinkedList(SaveFile, Rain); + LoadLinkedList(SaveFile, Trap); + CalculateIsTransparent(); + + if(Memorized) + { + FowMemorized = new bitmap(TILE_V2); + FowMemorized->ActivateFastFlag(); + Memorized->FastBlit(FowMemorized); + blitdata B = { FowMemorized, + { 0, 0 }, + { 0, 0 }, + { TILE_SIZE, TILE_SIZE }, + { 0 }, + 0, + 0 }; + + igraph::GetFOWGraphic()->NormalMaskedBlit(B); + } +} + +void lsquare::CalculateLuminance() +{ + Luminance = AmbientLuminance; + emittervector::const_iterator i, End = Emitter.end(); + + if(Flags & IS_TRANSPARENT) + { + game::CombineLights(Luminance, SunLightLuminance); + + for(i = Emitter.begin(); i != End; ++i) + game::CombineLights(Luminance, i->Emitation); + } + else + { + ulong BitMask = 0, LOSTick = game::GetLOSTick(); + + for(int c = 0; c < 4; ++c) + if((SquarePartLastSeen >> (c << 3) & 0xFF) >= LOSTick) + BitMask |= 1 << EMITTER_SQUARE_PART_SHIFT << c; + + CalculateSunLightLuminance(BitMask); + game::CombineLights(Luminance, SunLightLuminance); + + for(i = Emitter.begin(); i != End; ++i) + if(BitMask & i->ID) + game::CombineLights(Luminance, i->Emitation); + } +} + +void lsquare::AddCharacter(character* Guy) +{ + if(Character) + ABORT("Overgrowth of square population detected!"); + + Character = Guy; + SignalEmitationIncrease(Guy->GetEmitation()); + Flags |= STRONG_NEW_DRAW_REQUEST; + + if(Guy->IsAnimated()) + IncAnimatedEntities(); + + SignalPossibleTransparencyChange(); + + if(Guy->IsPlayer() + || (Guy->CanBeSeenByPlayer(true) && CanBeSeenByPlayer())) + Guy->SignalSeen(); +} + +void lsquare::Clean() +{ + GetStack()->Clean(); +} + +void lsquare::RemoveCharacter() +{ + if(Character) + { + character* Backup = Character; + + if(Backup->IsAnimated()) + DecAnimatedEntities(); + + Character = 0; + SignalEmitationDecrease(Backup->GetEmitation()); + Flags |= STRONG_NEW_DRAW_REQUEST; + SignalPossibleTransparencyChange(); + } +} + +void lsquare::UpdateMemorizedDescription(truth Cheat) +{ + if(Flags & DESCRIPTION_CHANGE || Cheat) + { + if(!IsDark() || Cheat) + { + MemorizedDescription.Empty(); + + if(!OLTerrain || (OLTerrain->IsTransparent() && OLTerrain->ShowThingsUnder())) + { + truth Anything = false; + + if(OLTerrain && OLTerrain->GetNameSingular().GetSize()) + { + OLTerrain->AddName(MemorizedDescription, INDEFINITE); + Anything = true; + } + + if(Flags & IS_TRANSPARENT) + { + itemvectorvector PileVector; + GetStack()->Pile(PileVector, PLAYER, CENTER); + + if(PileVector.size()) + { + if(Anything) + MemorizedDescription << " and "; + + if(PileVector.size() == 1) + PileVector[0][0]->AddName(MemorizedDescription, INDEFINITE, PileVector[0].size()); + else + MemorizedDescription << "many items"; + + MemorizedDescription << " on "; + Anything = true; + } + else if(Anything) + MemorizedDescription << " on "; + + GLTerrain->AddName(MemorizedDescription, INDEFINITE); + festring SideItems; + GetSideItemDescription(SideItems, Cheat); + + if(!SideItems.IsEmpty()) + MemorizedDescription << " and " << SideItems; + } + else + { + if(Anything) + MemorizedDescription << " on "; + + GLTerrain->AddName(MemorizedDescription, INDEFINITE); + } + } + else + OLTerrain->AddName(MemorizedDescription, INDEFINITE); + + if(FluidIsVisible()) + DisplayFluidInfo(MemorizedDescription); + + DisplayTrapInfo(MemorizedDescription); + + if(Cheat) + MemorizedDescription << " (pos " << Pos.X << ':' << Pos.Y << ")"; + } + else if(CanBeFeltByPlayer()) + { + MemorizedDescription.Empty(); + OLTerrain->AddName(MemorizedDescription, INDEFINITE); + + if(FluidIsVisible()) + DisplayFluidInfo(MemorizedDescription); + + DisplayTrapInfo(MemorizedDescription); + } + else + MemorizedDescription = CONST_S("darkness"); + + Flags &= ~DESCRIPTION_CHANGE; + } +} + +void lsquare::GetSideItemDescription(festring& String, truth Cheat) const +{ + int Items = 0; + + for(int c = 0; c < 4; ++c) + { + stack* Stack = GetStackOfAdjacentSquare(c); + + if(Stack) + Items += Cheat + ? Stack->GetSideItems(3 - c) + : Stack->GetVisibleSideItems(PLAYER, 3 - c); + } + + if(Items > 1) + String << "many items on the wall"; + else if(Items == 1) + { + for(int c = 0; c < 4; ++c) + { + stack* Stack = GetStackOfAdjacentSquare(c); + + if(Stack + && ((Cheat && Stack->GetSideItems(3 - c)) + || (!Cheat && Stack->GetVisibleSideItems(PLAYER, 3 - c)))) + Stack->GetBottomSideItem(PLAYER, 3 - c, Cheat)->AddName(String, INDEFINITE); + } + + String << " on the wall"; + } +} + +truth lsquare::BeKicked(character* Kicker, item* Boot, bodypart* Leg, double KickDamage, double KickToHitValue, int Success, int Direction, truth Critical, truth ForceHit) +{ + truth Return; + + if(GetCharacter()) + { + GetCharacter()->BeKicked(Kicker, Boot, Leg, Pos, KickDamage, KickToHitValue, Success, Direction, Critical, ForceHit); + Return = true; + } + else + Return = false; + + if(RoomIndex) + GetLevel()->GetRoom(RoomIndex)->KickSquare(Kicker, this); + + GetStack()->BeKicked(Kicker, int(KickDamage), Direction); + + if(GetOLTerrain()) + GetOLTerrain()->BeKicked(Kicker, int(KickDamage * (100 + Success) / 100), Direction); + + return Return; +} + +truth lsquare::CanBeDug() const +{ + if((!GetPos().X || !GetPos().Y || GetPos().X == GetLevel()->GetXSize() - 1 || GetPos().Y == GetLevel()->GetYSize() - 1) && !*GetLevel()->GetLevelScript()->IsOnGround()) + { + ADD_MESSAGE("Somehow you feel that by digging this square you would collapse the whole dungeon."); + return false; + } + else + return true; +} + +void lsquare::ChangeLTerrain(glterrain* NewGround, olterrain* NewOver) +{ + ChangeGLTerrain(NewGround); + ChangeOLTerrain(NewOver); +} + +void lsquare::ChangeGLTerrain(glterrain* NewGround) +{ + if(GLTerrain->IsAnimated()) + DecStaticAnimatedEntities(); + + truth WasUsingBorderTiles = GLTerrain->UseBorderTiles(); + delete GLTerrain; + GLTerrain = NewGround; + NewGround->SetLSquareUnder(this); + Flags |= NEW_DRAW_REQUEST; + GetLevel()->SetWalkability(Pos, GetTheoreticalWalkability()); + CalculateGroundBorderPartners(); + SendMemorizedUpdateRequest(); + + if(WasUsingBorderTiles || NewGround->UseBorderTiles()) + RequestForGroundBorderPartnerUpdates(); + + if(NewGround->IsAnimated()) + IncStaticAnimatedEntities(); +} + +void lsquare::ChangeOLTerrain(olterrain* NewOver) +{ + if(OLTerrain && OLTerrain->IsAnimated()) + DecStaticAnimatedEntities(); + + truth WasUsingBorderTiles = OLTerrain && OLTerrain->UseBorderTiles(); + delete OLTerrain; + OLTerrain = NewOver; + Flags |= NEW_DRAW_REQUEST; + GetLevel()->SetWalkability(Pos, GetTheoreticalWalkability()); + CalculateOverBorderPartners(); + CalculateIsTransparent(); + SendMemorizedUpdateRequest(); + + if(WasUsingBorderTiles || (NewOver && NewOver->UseBorderTiles())) + RequestForOverBorderPartnerUpdates(); + + if(NewOver) + { + NewOver->SetLSquareUnder(this); + + if(NewOver->IsAnimated()) + IncStaticAnimatedEntities(); + } +} + +void lsquare::SetLTerrain(glterrain* NewGround, olterrain* NewOver) +{ + GLTerrain = NewGround; + NewGround->SetLSquareUnder(this); + + if(NewGround->IsAnimated()) + IncStaticAnimatedEntities(); + + OLTerrain = NewOver; + + if(NewOver) + { + NewOver->SetLSquareUnder(this); + + if(NewOver->IsAnimated()) + IncStaticAnimatedEntities(); + + if(!NewOver->IsTransparent()) + Flags &= ~IS_TRANSPARENT; + } + + GetLevel()->SetWalkability(Pos, GetTheoreticalWalkability()); +} + +void lsquare::ApplyScript(const squarescript* SquareScript, room* Room) +{ + if(SquareScript->AttachRequired()) + GetLevel()->AddToAttachQueue(Pos); + + int EntryIndex = SquareScript->GetEntryIndex(); + + if(EntryIndex != NO_ENTRY) + GetLevel()->SetEntryPos(EntryIndex, Pos); + + const contentscript* CharacterScript = SquareScript->GetCharacter(); + + if(CharacterScript) + { + character* Char = CharacterScript->Instantiate(); + Char->SetGenerationDanger(GetLevel()->GetDifficulty()); + + if(!Char->GetTeam()) + Char->SetTeam(game::GetTeam(*GetLevel()->GetLevelScript()->GetTeamDefault())); + + if(CharacterScript->GetFlags() & IS_LEADER) + Char->GetTeam()->SetLeader(Char); + + Char->PutToOrNear(Pos); + Char->CreateHomeData(); + + if(Room && CharacterScript->GetFlags() & IS_MASTER) + Room->SetMasterID(Char->GetID()); + } + + const fearray >* Items = SquareScript->GetItems(); + + if(Items) + for(uint c1 = 0; c1 < Items->Size; ++c1) + { + const interval* TimesPtr = Items->Data[c1].GetTimes(); + int Times = TimesPtr ? TimesPtr->Randomize() : 1; + + for(int c2 = 0; c2 < Times; ++c2) + { + item* Item = Items->Data[c1].Instantiate(); + + if(Item) + { + int SquarePosition = Items->Data[c1].GetSquarePosition(); + + if(SquarePosition != CENTER) + Item->SignalSquarePositionChange(SquarePosition); + + GetStack()->AddItem(Item); + Item->SpecialGenerationHandler(); + } + } + } + + const contentscript* GLTerrainScript = SquareScript->GetGTerrain(); + + if(GLTerrainScript) + { + GetLevel()->AddFlag(Pos, FORBIDDEN); + ChangeGLTerrain(GLTerrainScript->Instantiate()); + + if(GLTerrainScript->IsInside()) + if(*GLTerrainScript->IsInside()) + Flags |= INSIDE; + else + Flags &= ~INSIDE; + } + + const contentscript* OLTerrainScript = SquareScript->GetOTerrain(); + + if(OLTerrainScript) + { + GetLevel()->AddFlag(Pos, FORBIDDEN); + ChangeOLTerrain(OLTerrainScript->Instantiate()); + } +} + +truth lsquare::CanBeSeenByPlayer(truth IgnoreDarkness) const +{ + return (IgnoreDarkness || !IsDark()) && LastSeen == game::GetLOSTick(); +} + +truth lsquare::CanBeSeenFrom(v2 FromPos, long MaxDistance, truth IgnoreDarkness) const +{ + if((Pos - FromPos).GetLengthSquare() <= MaxDistance + && (IgnoreDarkness || !IsDark())) + { + if(Character && Character->IsPlayer() + && GetNearLSquare(FromPos)->CanBeSeenByPlayer(true)) + return true; + + eyecontroller::Map = GetLevel()->GetMap(); + return mapmath::DoLine(FromPos.X, FromPos.Y, GetPos().X, GetPos().Y, SKIP_FIRST); + } + + return false; +} + +void lsquare::StepOn(character* Stepper, lsquare** ComingFrom) +{ + if(RoomIndex) + { + truth WasInRoom = false; + + if(ComingFrom) + for(int c = 0; c < Stepper->GetSquaresUnder(); ++c) + if(ComingFrom[c]->GetRoomIndex() == RoomIndex) + { + WasInRoom = true; + break; + } + + if(!WasInRoom) + GetLevel()->GetRoom(RoomIndex)->Enter(Stepper); + } + + GLTerrain->StepOn(Stepper); + + if(OLTerrain) + { + OLTerrain->StepOn(Stepper); + + if(Stepper->DestroysWalls() && OLTerrain->WillBeDestroyedBy(Stepper)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s destroys %s.", Stepper->CHAR_NAME(DEFINITE), OLTerrain->CHAR_NAME(DEFINITE)); + + Stepper->EditAP(-100); + OLTerrain->BeDestroyed(); + } + } + + uint c; + std::vector TrapVector; + + for(trap* T = Trap; T; T = T->Next) + TrapVector.push_back(T); + + for(c = 0; c < TrapVector.size(); ++c) + if(TrapVector[c]->Exists()) + { + TrapVector[c]->StepOnEffect(Stepper); + + if(!Stepper->IsEnabled()) + return; + } + + if(!Stepper->IsFlying()) + { + std::vector FluidVector; + + for(fluid* F = Fluid; F; F = F->Next) + FluidVector.push_back(F); + + for(c = 0; c < FluidVector.size(); ++c) + if(FluidVector[c]->Exists()) + { + FluidVector[c]->StepOnEffect(Stepper); + + if(!Stepper->IsEnabled()) + return; + } + + GetStack()->CheckForStepOnEffect(Stepper); + } +} + +void lsquare::ReceiveVomit(character* Who, liquid* Liquid) +{ + if(!GetOLTerrain() || !GetOLTerrain()->ReceiveVomit(Who, Liquid)) + { + SpillFluid(Who, Liquid); + + if(RoomIndex) + GetRoom()->ReceiveVomit(Who); + } +} + +void lsquare::SetTemporaryEmitation(col24 What) +{ + col24 Old = TemporaryEmitation; + TemporaryEmitation = 0; + SignalEmitationDecrease(Old); + TemporaryEmitation = What; + SignalEmitationIncrease(What); +} + +void lsquare::ChangeOLTerrainAndUpdateLights(olterrain* NewTerrain) +{ + truth WasTransparent = Flags & IS_TRANSPARENT, Noxified = false; + emittervector EmitterBackup; + + if(WasTransparent && NewTerrain && !NewTerrain->IsTransparent()) + { + EmitterBackup = Emitter; + GetLevel()->ForceEmitterNoxify(EmitterBackup); + Noxified = true; + } + + long OldEmit = OLTerrain ? OLTerrain->GetEmitation() : 0; + ChangeOLTerrain(NewTerrain); + + if(NewTerrain) + SignalEmitationIncrease(NewTerrain->GetEmitation()); + + SignalEmitationDecrease(OldEmit); + GetStack()->DropSideItems(); + + if(!IsFlyable() && Smoke) + { + DecAnimatedEntities(); + + for(smoke* S = Smoke; S; S = S->Next) + S->SendToHell(); + + Smoke = 0; + SmokeAlphaSum = 0; + } + + if(!WasTransparent == !!CalculateIsTransparent()) + { + if(Noxified) + GetLevel()->ForceEmitterEmitation(EmitterBackup, SunEmitter, FORCE_ADD); + else + GetLevel()->ForceEmitterEmitation(Emitter, SunEmitter); + + CalculateLuminance(); + + if(LastSeen == game::GetLOSTick()) + game::SendLOSUpdateRequest(); + } +} + +void lsquare::DrawParticles(long Color, truth DrawHere) +{ + if(GetPos().X < game::GetCamera().X + || GetPos().Y < game::GetCamera().Y + || GetPos().X >= game::GetCamera().X + game::GetScreenXSize() + || GetPos().Y >= game::GetCamera().Y + game::GetScreenYSize() + || !CanBeSeenByPlayer(true) + || Color == TRANSPARENT_COLOR) + return; + + clock_t StartTime = clock(); + + if(DrawHere) + game::DrawEverythingNoBlit(); + + if(Color & RANDOM_COLOR) + Color = MakeRGB16(60 + RAND() % 190, 60 + RAND() % 190, 60 + RAND() % 190); + + v2 Pos = game::CalculateScreenCoordinates(GetPos()); + + for(int c = 0; c < 10; ++c) + DOUBLE_BUFFER->PutPixel(Pos + v2(1 + RAND() % 14, 1 + RAND() % 14), Color); + + Flags |= STRONG_NEW_DRAW_REQUEST; // Clean the pixels from the screen afterwards + + if(DrawHere) + { + graphics::BlitDBToScreen(); + while(clock() - StartTime < 0.02 * CLOCKS_PER_SEC); + } +} + +truth lsquare::DipInto(item* Thingy, character* Dipper) +{ + if(IsDipDestination()) + { + room* Room = GetRoom(); + + if(Room && Room->HasDipHandler() && !Room->Dip(Dipper)) + return false; + + return (GLTerrain->IsDipDestination() && GLTerrain->DipInto(Thingy, Dipper)) || (OLTerrain && OLTerrain->IsDipDestination() && OLTerrain->DipInto(Thingy, Dipper)); + } + else + { + if(Dipper->IsPlayer()) + ADD_MESSAGE("You cannot dip %s on that square!", Thingy->CHAR_NAME(DEFINITE)); + + return false; + } +} + +// return true if key fits someplace + +truth lsquare::TryKey(item* Key, character* Applier) +{ + if(GetOLTerrain() && GetOLTerrain()->TryKey(Key, Applier)) + return true; + + if((!GetOLTerrain() || !GetOLTerrain()->HasKeyHole()) && !GetStack()->TryKey(Key, Applier)) + { + ADD_MESSAGE("There's no place here to put the key in!"); + return false; + } + + return true; +} + +void lsquare::SignalSeen(ulong Tick) +{ + if(LastSeen < Tick - 2) + Flags |= STRONG_NEW_DRAW_REQUEST; + + Flags &= ~(IN_SQUARE_STACK|PERFECTLY_QUADRI_HANDLED); + LastSeen = Tick; + + if(!(Flags & IS_TRANSPARENT)) + { + col24 OldLuminance = Luminance; + CalculateLuminance(); + + if(OldLuminance != Luminance) + { + Flags |= NEW_DRAW_REQUEST; + + if(IsDark() != game::IsDark(OldLuminance)) + Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE; + } + } + + if(IsDark()) + { + v2 Dist = Pos - PLAYER->GetPos(); + + if(abs(Dist.X) > 1 || abs(Dist.Y) > 1) + { + LastSeen -= 2; + return; + } + } + + if(!Memorized) + CreateMemorized(); + + UpdateMemorized(); + UpdateMemorizedDescription(); + + if(Character) + Character->CheckIfSeen(); +} + +void lsquare::DrawMemorized(blitdata& BlitData) const +{ + LastSeen = 0; + Flags &= ~STRONG_NEW_DRAW_REQUEST; + BlitData.Luminance = ivanconfig::GetContrastLuminance(); + + if(FowMemorized) + FowMemorized->LuminanceBlit(BlitData); + else + DOUBLE_BUFFER->Fill(BlitData.Dest, BlitData.Border, 0); + + ccharacter* C = Character; + + if(C && C->CanBeSeenByPlayer()) + { + BlitData.CustomData |= C->GetSquareIndex(Pos); + C->Draw(BlitData); + BlitData.CustomData &= ~SQUARE_INDEX_MASK; + } +} + +void lsquare::DrawMemorizedCharacter(blitdata& BlitData) const +{ + BlitData.Luminance = ivanconfig::GetContrastLuminance(); + + if(FowMemorized) + FowMemorized->LuminanceBlit(BlitData); + else + DOUBLE_BUFFER->Fill(BlitData.Dest, BlitData.Border, 0); + + BlitData.CustomData |= Character->GetSquareIndex(Pos); + Character->Draw(BlitData); + BlitData.CustomData &= ~SQUARE_INDEX_MASK; + Flags |= STRONG_NEW_DRAW_REQUEST; +} + +truth lsquare::IsDangerous(ccharacter* Who) const +{ + return ((!Who->IsFlying() + && (Stack->IsDangerous(Who) + || HasDangerousFluids(Who))) + || IsDangerousToBreathe(Who) || HasDangerousTraps(Who)); +} + +truth lsquare::IsScary(ccharacter* Who) const +{ + return IsScaryToBreathe(Who); +} + +stack* lsquare::GetStackOfAdjacentSquare(int I) const +{ + lsquare* Square = 0; + + switch(I) + { + case LEFT: Square = NeighbourLSquare[3]; break; + case DOWN: Square = NeighbourLSquare[6]; break; + case UP: Square = NeighbourLSquare[1]; break; + case RIGHT: Square = NeighbourLSquare[4]; break; + } + + return Square ? Square->Stack : 0; +} + +void lsquare::SendMemorizedUpdateRequest() +{ + if(!(Flags & FREEZED)) + { + Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE; + + if(!game::IsGenerating() && (CanBeSeenByPlayer() || CanBeFeltByPlayer())) + { + if(!Memorized) + CreateMemorized(); + + UpdateMemorized(); + UpdateMemorizedDescription(); + } + } +} + +void lsquare::KickAnyoneStandingHereAway() +{ + if(Character) + { + character* Backup = Character; + Backup->Remove(); + Backup->PutNear(Pos); + } +} + +outputfile& operator<<(outputfile& SaveFile, const emitter& Emitter) +{ + SaveFile.Write(reinterpret_cast(&Emitter), sizeof(Emitter)); + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, emitter& Emitter) +{ + SaveFile.Read(reinterpret_cast(&Emitter), sizeof(Emitter)); + return SaveFile; +} + +void lsquare::AddItem(item* Item) +{ + Stack->AddItem(Item); +} + +v2 lsquare::DrawLightning(v2 StartPos, long Color, int Direction, truth DrawHere) +{ + if(GetPos().X < game::GetCamera().X + || GetPos().Y < game::GetCamera().Y + || GetPos().X >= game::GetCamera().X + game::GetScreenXSize() + || GetPos().Y >= game::GetCamera().Y + game::GetScreenYSize() + || !CanBeSeenByPlayer(true)) + switch(Direction) + { + case 1: return v2(RAND() & 15, 15); + case 3: return v2(15, RAND() & 15); + case 4: return v2(0, RAND() & 15); + case 6: return v2(RAND() & 15, 0); + default: return StartPos; + } + + clock_t StartTime = clock(); + bitmap Empty(TILE_V2, TRANSPARENT_COLOR); + Empty.ActivateFastFlag(); + + if(Color & RANDOM_COLOR) + Color = MakeRGB16(60 + RAND() % 190, 60 + RAND() % 190, 60 + RAND() % 190); + + if(Direction != YOURSELF) + { + while(!Empty.CreateLightning(StartPos, game::GetMoveVector(Direction), 16, Color)); + v2 EndPos(0, 0); + + switch(Direction) + { + case 0: EndPos = v2(0, 0); break; + case 1: EndPos = v2(RAND() & 15, 0); StartPos = v2(EndPos.X, 15); break; + case 2: EndPos = v2(15, 0); break; + case 3: EndPos = v2(0, RAND() & 15); StartPos = v2(15, EndPos.Y); break; + case 4: EndPos = v2(15, RAND() & 15); StartPos = v2(0, EndPos.Y); break; + case 5: EndPos = v2(0, 15); break; + case 6: EndPos = v2(RAND() & 15, 15); StartPos = v2(EndPos.X, 0); break; + case 7: EndPos = v2(15, 15); break; + } + + while(!Empty.CreateLightning(EndPos, -game::GetMoveVector(Direction), NO_LIMIT, Color)); + } + else + { + static v2 Dir[4] = { v2(0, -1), v2(-1, 0), v2(1, 0), v2(0, 1) }; + + for(int d = 0; d < 4; ++d) + while(!Empty.CreateLightning(StartPos + Dir[d], ZERO_V2, 10, Color)); + } + + if(DrawHere) + game::DrawEverythingNoBlit(); + + blitdata B = { DOUBLE_BUFFER, + { 0, 0 }, + { 0, 0 }, + { TILE_SIZE, TILE_SIZE }, + { 0 }, + TRANSPARENT_COLOR, + 0 }; + + B.Dest = game::CalculateScreenCoordinates(GetPos()); + Empty.NormalMaskedBlit(B); + Flags |= STRONG_NEW_DRAW_REQUEST; + + if(DrawHere) + { + graphics::BlitDBToScreen(); + while(clock() - StartTime < 0.02 * CLOCKS_PER_SEC); + } + + return StartPos; +} + +truth lsquare::Polymorph(const beamdata& Beam) +{ + GetStack()->Polymorph(Beam.Owner); + + if(GetOLTerrain()) + GetOLTerrain()->Polymorph(Beam.Owner); + + character* Character = GetCharacter(); + + if(Character) + { + if(Beam.Owner && Character->GetTeam() != Beam.Owner->GetTeam()) + Beam.Owner->Hostility(Character); + + Character->PolymorphRandomly(1, 999999, 5000 + RAND() % 5000); + } + + if(Engraved) + { + for(int c = 0; Engraved[c] != '\0'; ++c) + { + if(RAND_2) + { + Engraved[c] = 32 + RAND_N(95); + } + } + } + return false; +} + +truth lsquare::Strike(const beamdata& Beam) +{ + int Damage = 50 + RAND() % 21 - RAND() % 21; + GetStack()->ReceiveDamage(Beam.Owner, Damage, ENERGY, Beam.Direction); + ReceiveTrapDamage(Beam.Owner, Damage, ENERGY, Beam.Direction); + + character* Char = GetCharacter(); + + if(Char) + { + if(Char->IsPlayer()) + ADD_MESSAGE("You are hit by a burst of energy!"); + else if(Char->CanBeSeenByPlayer()) + ADD_MESSAGE("%s is hit by a burst of energy!", Char->CHAR_NAME(DEFINITE)); + + if(Beam.Owner) + Beam.Owner->Hostility(Char); + + Char->ReceiveDamage(Beam.Owner, Damage, ENERGY, ALL); + Char->CheckDeath(Beam.DeathMsg, Beam.Owner); + } + + if(GetOLTerrain()) + GetOLTerrain()->ReceiveDamage(Beam.Owner, Damage, ENERGY); + + return false; +} + +truth lsquare::FireBall(const beamdata& Beam) +{ + if(!IsFlyable() || GetCharacter()) + { + if(CanBeSeenByPlayer(true)) + ADD_MESSAGE("A magical explosion is triggered!"); + + GetLevel()->Explosion(Beam.Owner, Beam.DeathMsg, Pos, 75 + RAND() % 25 - RAND() % 25); + return true; + } + + return false; +} + +truth lsquare::Teleport(const beamdata& Beam) +{ + if(Character) + { + if(Beam.Owner && Character->GetTeam() != Beam.Owner->GetTeam()) + Beam.Owner->Hostility(GetCharacter()); + + if(Character->IsPlayer()) + ADD_MESSAGE("You experience a forced teleportation."); + else if(Character->CanBeSeenByPlayer()) + ADD_MESSAGE("%s disappears!", Character->CHAR_NAME(DEFINITE)); + + Character->TeleportRandomly(); + } + + if(RoomIndex) + GetLevel()->GetRoom(RoomIndex)->TeleportSquare(Beam.Owner, this); + + GetStack()->TeleportRandomly(); + return false; +} + +truth lsquare::Haste(const beamdata&) +{ + GetStack()->Haste(); + character* Dude = GetCharacter(); + + if(Dude) + Dude->Haste(); + + return false; +} + +truth lsquare::Slow(const beamdata& Beam) +{ + GetStack()->Slow(); + character* Dude = GetCharacter(); + + if(Dude) + { + if(Beam.Owner) + Beam.Owner->Hostility(Dude); + + Dude->Slow(); + } + + return false; +} + +truth lsquare::Confuse(const beamdata& Beam) +{ + character* Dude = GetCharacter(); + + if(Dude && Dude->CanBeConfused()) + { + if(Beam.Owner) + Beam.Owner->Hostility(Dude); + + Dude->BeginTemporaryState(CONFUSED, 50 + RAND() % 50); + } + + return false; +} + +truth lsquare::Parasitize(const beamdata& Beam) +{ + character* Dude = GetCharacter(); + + if(Dude) + { + if(Dude->GetTorso()->CanHaveParasite()) + { + if(Beam.Owner) + Beam.Owner->Hostility(Dude); + + Dude->GainIntrinsic(PARASITIZED); + } + } + + return false; +} + +truth lsquare::InstillPanic(const beamdata& Beam) +{ + character* Dude = GetCharacter(); + + if(Dude && Dude->CanPanic()) + { + if(Beam.Owner) + Beam.Owner->Hostility(Dude); + + Dude->BeginTemporaryState(PANIC, 50 + RAND() % 50); + } + + return false; +} + +truth lsquare::Resurrect(const beamdata& Beam) +{ + if(GetCharacter()) + return GetCharacter()->RaiseTheDead(Beam.Owner); + else + return GetStack()->RaiseTheDead(Beam.Owner); +} + +truth lsquare::Invisibility(const beamdata&) +{ + if(GetCharacter()) + GetCharacter()->BeginTemporaryState(INVISIBLE, 1000 + RAND() % 1001); + + return false; +} + +truth lsquare::Duplicate(const beamdata& Beam) +{ + truth DuplicatedSomething = false; + character* Character = GetCharacter(); + + if(Character) + DuplicatedSomething = truth(Character->DuplicateToNearestSquare(Beam.Owner, Beam.SpecialParameters)); + + if(GetStack()->Duplicate(DuplicatedSomething ? 4 : 5, Beam.SpecialParameters)) + DuplicatedSomething = true; + + return DuplicatedSomething; +} + +truth lsquare::Lightning(const beamdata& Beam) +{ + int Damage = 20 + RAND() % 6 - RAND() % 6; + GetStack()->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY, Beam.Direction); + ReceiveTrapDamage(Beam.Owner, Damage, ELECTRICITY, Beam.Direction); + + character* Char = GetCharacter(); + + if(Char) + { + if(Char->IsPlayer()) + ADD_MESSAGE("A massive burst of electricity runs through your body!"); + else if(Char->CanBeSeenByPlayer()) + ADD_MESSAGE("A massive burst of electricity runs through %s!", Char->CHAR_NAME(DEFINITE)); + + if(Beam.Owner) + Beam.Owner->Hostility(Char); + + Char->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY, ALL); + Char->CheckDeath(Beam.DeathMsg, Beam.Owner); + } + + if(GetOLTerrain()) + GetOLTerrain()->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY); + + return false; +} + +truth lsquare::DoorCreation(const beamdata& Beam) +{ + if((!GetOLTerrain() + || GetOLTerrain()->IsSafeToCreateDoor()) + && !GetCharacter() + && (GetLevel()->IsOnGround() + || (Pos.X > 0 && Pos.Y > 0 + && Pos.X < GetLevel()->GetXSize() - 1 && Pos.Y < GetLevel()->GetYSize() - 1))) + { + if(Beam.Owner && GetRoom()) + GetRoom()->HostileAction(Beam.Owner); + + door* Door = door::Spawn(0, NO_MATERIALS); + Door->InitMaterials(MAKE_MATERIAL(STEEL)); + + if(RAND() % 10) + Door->Lock(); + + ChangeOLTerrainAndUpdateLights(Door); + return true; + } + + return false; +} + +truth (lsquare::*BeamEffect[BEAM_EFFECTS])(const beamdata&) = +{ + &lsquare::Polymorph, + &lsquare::Strike, + &lsquare::FireBall, + &lsquare::Teleport, + &lsquare::Haste, + &lsquare::Slow, + &lsquare::Resurrect, + &lsquare::Invisibility, + &lsquare::Duplicate, + &lsquare::Lightning, + &lsquare::DoorCreation, + &lsquare::AcidRain, + &lsquare::Necromancy +}; + +truth (lsquare::*lsquare::GetBeamEffect(int I))(const beamdata&) +{ + return BeamEffect[I]; +} + +truth lsquare::CheckKick(ccharacter* Kicker) const +{ + if(Character && Kicker->CheckIfTooScaredToHit(Character)) + return false; + + if(RoomIndex && !GetLevel()->GetRoom(RoomIndex)->CheckKickSquare(Kicker, this)) + return false; + + return true; +} + +void lsquare::GetHitByExplosion(const explosion* Explosion) +{ + if(Explosion->ID == LastExplosionID) + return; + + LastExplosionID = Explosion->ID; + int DistanceSquare = (Pos - Explosion->Pos).GetLengthSquare(); + + if(DistanceSquare > Explosion->RadiusSquare) + return; + + int Damage = Explosion->Strength / (DistanceSquare + 1); + + if(Character && (Explosion->HurtNeutrals || (Explosion->Terrorist && Character->GetRelation(Explosion->Terrorist) == HOSTILE))) + if(Character->IsPlayer()) + game::SetPlayerWasHurtByExplosion(true); + else + Character->GetHitByExplosion(Explosion, Damage); + + GetStack()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, FIRE); + GetStack()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE); + + ReceiveTrapDamage(Explosion->Terrorist, Damage >> 1, FIRE); + ReceiveTrapDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE); + + if(GetOLTerrain()) + GetOLTerrain()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, FIRE); + + if(GetOLTerrain()) + GetOLTerrain()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE); +} + +int lsquare::GetSpoiledItems() const +{ + return GetStack()->GetSpoiledItems(); +} + +truth lsquare::LowerEnchantment(const beamdata& Beam) +{ + character* Char = GetCharacter(); + itemvector AllItems; + sortdata SortData(AllItems, Beam.Owner, true, &item::IsEnchantable); + SortAllItems(SortData); + item* RandomItem; + + if(!AllItems.empty()) + RandomItem = AllItems[RAND() % AllItems.size()]; + else + return false; + + if(Char) + { + if(Char->IsPlayer()) + ADD_MESSAGE("%s glows blue for a moment!", RandomItem->CHAR_NAME(DEFINITE)); + + if(Beam.Owner) + Beam.Owner->Hostility(Char); + } + + if(RandomItem->GetEnchantment() > -5) + RandomItem->EditEnchantment(-1); + + return true; +} + +truth lsquare::SoftenMaterial(const beamdata& Beam) +{ + character* Char = GetCharacter(); + itemvector AllItems; + sortdata SortData(AllItems, Beam.Owner, true, &item::IsEnchantable); + SortAllItems(SortData); + //sortdata SortData2(AllItems, Beam.Owner, true, &item::MaterialIsChangeable); + //SortAllItems(SortData2); + item* RandomItem; + + if(!AllItems.empty()) + RandomItem = AllItems[RAND() % AllItems.size()]; + else + return false; + + if(Char) + { + if(Char->IsPlayer()) + ADD_MESSAGE("Your %s glows yellow for a moment!", RandomItem->CHAR_NAME(UNARTICLED)); + + if(Beam.Owner) + Beam.Owner->Hostility(Char); + } + + truth Changed = 0; + festring Desc; + RandomItem->AddName(Desc, UNARTICLED); + material* OldMaterial = RandomItem->GetMainMaterial(); + int NewMaterial = RandomItem->GetMainMaterial()->GetSoftenedMaterial(RandomItem); + if(NewMaterial != NONE) /* Don't Forget! It is an ugly thing, I know, but removal = seg-fault since cannot have NONE material */ + { + RandomItem->ChangeMainMaterial(MAKE_MATERIAL(NewMaterial)); /*->SpawnMore()*/ + if(OldMaterial->GetConfig() != NewMaterial) + Changed = 1; + } + + if(Char) + { + if(Changed && Char->IsPlayer()) + { + ADD_MESSAGE("Your %s softens into %s!", Desc.CStr(), RandomItem->GetMainMaterial()->GetName(false, false).CStr()); + } + else if(Changed) + ADD_MESSAGE("%s's %s softens into %s!", Char->CHAR_DESCRIPTION(DEFINITE), Desc.CStr(), RandomItem->GetMainMaterial()->GetName(false, false).CStr()); + + if(!Changed) //may not need this message + if(Char->IsPlayer()) + { + ADD_MESSAGE("Your %s vibrates slightly but remains unchanged.", RandomItem->CHAR_NAME(UNARTICLED) ); + } + else + ADD_MESSAGE("%s's %s vibrates slightly but remains unchanged.", Char->CHAR_DESCRIPTION(DEFINITE), RandomItem->CHAR_NAME(UNARTICLED) ); + } + + return true; +} + +void lsquare::SortAllItems(const sortdata& SortData) +{ + if(GetCharacter()) + GetCharacter()->SortAllItems(SortData); + + GetStack()->SortAllItems(SortData); +} + +void lsquare::RemoveSmoke(smoke* ToBeRemoved) +{ + smoke* S = Smoke; + + if(S == ToBeRemoved) + { + Smoke = S->Next; + + if(!S) + DecAnimatedEntities(); + } + else + { + smoke* LS; + + do + { + LS = S; + S = S->Next; + } + while(S != ToBeRemoved); + + LS->Next = S->Next; + } +} + +void lsquare::AddSmoke(gas* ToBeAdded) +{ + smoke* S = Smoke; + + if(!S) + { + Smoke = new smoke(ToBeAdded, this); + IncAnimatedEntities(); + } + else + { + smoke* LS; + + do + { + if(ToBeAdded->IsSameAs(S->GetGas())) + { + S->Merge(ToBeAdded); + return; + } + + LS = S; + S = S->Next; + } + while(S); + + LS->Next = new smoke(ToBeAdded, this); + } +} + +void lsquare::ShowSmokeMessage() const +{ + for(const smoke* S = Smoke; S; S = S->Next) + S->AddBreatheMessage(); +} + +void lsquare::SignalSmokeAlphaChange(int What) +{ + SmokeAlphaSum += What; + SignalPossibleTransparencyChange(); +} + +int lsquare::GetDivineMaster() const +{ + return RoomIndex ? GetLevel()->GetRoom(RoomIndex)->GetDivineMaster() : 0; +} + +void lsquare::DisplaySmokeInfo(festring& Msg) const +{ + if(Smoke) + if(!Smoke->Next) + Msg << " A cloud of " << Smoke->GetGas()->GetName(false, false) << " surrounds the square."; + else + Msg << " A lot of gases hover over the square."; +} + +void lsquare::ReceiveEarthQuakeDamage() +{ + GetStack()->ReceiveDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE); + ReceiveTrapDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE); + /* Gum solution */ + + if(GetOLTerrain() && GetOLTerrain()->IsDoor()) + GetOLTerrain()->ReceiveDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE); +} + +truth lsquare::CanBeFeltByPlayer() const +{ + return OLTerrain && !PLAYER->CanMoveOn(this) && Pos.IsAdjacent(PLAYER->GetPos()); +} + +void lsquare::PreProcessForBone() +{ + DestroyMemorized(); + LastSeen = 0; + + if(OLTerrain) + OLTerrain->PreProcessForBone(); + + if(Smoke) + { + DecAnimatedEntities(); + + for(smoke* S = Smoke; S; S = S->Next) + S->SendToHell(); + + Smoke = 0; + SmokeAlphaSum = 0; + } + + if(Character && !Character->PreProcessForBone()) + { + Character->SendToHell(); + Character->Remove(); + } + + for(fluid* F = Fluid; F; F = F->Next) + F->PreProcessForBone(); + + for(trap* T = Trap; T; T = T->Next) + T->PreProcessForBone(); + + GetStack()->PreProcessForBone(); +} + +void lsquare::PostProcessForBone(double& DangerSum, int& Enemies) +{ + if(OLTerrain) + OLTerrain->PostProcessForBone(); + + if(Character && !Character->PostProcessForBone(DangerSum, Enemies)) + { + Character->SendToHell(); + Character->Remove(); + } + + for(fluid* F = Fluid; F; F = F->Next) + F->PostProcessForBone(); + + for(trap* T = Trap; T; T = T->Next) + T->PostProcessForBone(); + + GetStack()->PostProcessForBone(); +} + +void lsquare::FinalProcessForBone() +{ + if(OLTerrain) + OLTerrain->FinalProcessForBone(); + + if(Character) + Character->FinalProcessForBone(); + + GetStack()->FinalProcessForBone(); +} + +truth lsquare::EngravingsCanBeReadByPlayer() +{ + return PLAYER->CanRead(); // Might be a good idea to improve sometime in the distant future. +} + +void lsquare::DisplayEngravedInfo(festring& Buffer) const +{ + Buffer << " There is a message engraved here: \"" << Engraved << '\"'; +} + +truth lsquare::IsDangerousToBreathe(ccharacter* Who) const +{ + for(const smoke* S = Smoke; S; S = S->Next) + if(S->IsDangerousToBreathe(Who)) + return true; + + return false; +} + +truth lsquare::IsScaryToBreathe(ccharacter* Who) const +{ + for(const smoke* S = Smoke; S; S = S->Next) + if(S->IsScaryToBreathe(Who)) + return true; + + return false; +} + +struct groundborderpartner +{ + truth operator<(const groundborderpartner& P) const { return Terrain->GetBorderTilePriority() < P.Terrain->GetBorderTilePriority(); } + glterrain* Terrain; + int SquareIndex; +}; + +void lsquare::CalculateGroundBorderPartners() +{ + if(GroundBorderPartnerInfo & BORDER_PARTNER_ANIMATED) + DecStaticAnimatedEntities(); + + groundborderpartner BorderPartner[8]; + int Index = 0; + int Priority = GLTerrain->GetBorderTilePriority(); + + for(int d = 0; d < 8; ++d) + { + lsquare* Square = NeighbourLSquare[d]; + + if(Square) + { + glterrain* Terrain = Square->GetGLTerrain(); + + if(Terrain && Terrain->UseBorderTiles() + && Terrain->GetBorderTilePriority() > Priority) + { + BorderPartner[Index].Terrain = Terrain; + BorderPartner[Index].SquareIndex = 7 - d; + ++Index; + } + } + } + + GroundBorderPartnerInfo = 0; + + if(!Index) + { + delete [] GroundBorderPartnerTerrain; + GroundBorderPartnerTerrain = 0; + return; + } + + if(!GroundBorderPartnerTerrain) + GroundBorderPartnerTerrain = new glterrain*[8]; + + std::sort(BorderPartner, BorderPartner + Index); + truth Animated = false; + + for(int c = 0; c < Index; ++c) + { + glterrain* T = BorderPartner[c].Terrain; + GroundBorderPartnerTerrain[c] = T; + GroundBorderPartnerInfo |= BorderPartner[c].SquareIndex << ((c << 1) + c); + + if(T->IsAnimated()) + Animated = true; + } + + if(Animated) + { + GroundBorderPartnerInfo |= BORDER_PARTNER_ANIMATED; + IncStaticAnimatedEntities(); + } + + GroundBorderPartnerInfo |= Index << 24; +} + +struct overborderpartner +{ + truth operator<(const overborderpartner& P) const { return Terrain->GetBorderTilePriority() < P.Terrain->GetBorderTilePriority(); } + olterrain* Terrain; + int SquareIndex; +}; + +void lsquare::CalculateOverBorderPartners() +{ + if(OverBorderPartnerInfo & BORDER_PARTNER_ANIMATED) + DecStaticAnimatedEntities(); + + overborderpartner BorderPartner[8]; + int Index = 0; + int Priority = OLTerrain ? OLTerrain->GetBorderTilePriority() : 0; + + for(int d = 0; d < 8; ++d) + { + lsquare* Square = NeighbourLSquare[d]; + + if(Square) + { + olterrain* Terrain = Square->GetOLTerrain(); + + if(Terrain && Terrain->UseBorderTiles() + && Terrain->GetBorderTilePriority() > Priority) + { + BorderPartner[Index].Terrain = Terrain; + BorderPartner[Index].SquareIndex = 7 - d; + ++Index; + } + } + } + + OverBorderPartnerInfo = 0; + + if(!Index) + { + delete [] OverBorderPartnerTerrain; + OverBorderPartnerTerrain = 0; + return; + } + + if(!OverBorderPartnerTerrain) + OverBorderPartnerTerrain = new olterrain*[8]; + + std::sort(BorderPartner, BorderPartner + Index); + truth Animated = false; + + for(int c = 0; c < Index; ++c) + { + olterrain* T = BorderPartner[c].Terrain; + OverBorderPartnerTerrain[c] = T; + OverBorderPartnerInfo |= BorderPartner[c].SquareIndex << ((c << 1) + c); + + if(T->IsAnimated()) + Animated = true; + } + + if(Animated) + { + OverBorderPartnerInfo |= BORDER_PARTNER_ANIMATED; + IncStaticAnimatedEntities(); + } + + OverBorderPartnerInfo |= Index << 24; + + if(OverBorderPartnerInfo & BORDER_PARTNER_ANIMATED) + int esko = esko = 2; +} + +void lsquare::RequestForGroundBorderPartnerUpdates() +{ + if(!game::IsGenerating()) + for(int d = 0; d < 8; ++d) + { + lsquare* Square = NeighbourLSquare[d]; + + if(Square) + { + Square->CalculateGroundBorderPartners(); + Square->SendNewDrawRequest(); + Square->SendMemorizedUpdateRequest(); + } + } +} + +void lsquare::RequestForOverBorderPartnerUpdates() +{ + if(!game::IsGenerating()) + for(int d = 0; d < 8; ++d) + { + lsquare* Square = NeighbourLSquare[d]; + + if(Square) + { + Square->CalculateOverBorderPartners(); + Square->SendNewDrawRequest(); + Square->SendMemorizedUpdateRequest(); + } + } +} + +int lsquare::GetWalkability() const +{ + if(!GetLevel()->IsOnGround()) + { + if(Pos.X >= 1 && Pos.Y >= 1 && Pos.X < GetLevel()->GetXSize() - 1 && Pos.Y < GetLevel()->GetYSize() - 1) + return OLTerrain ? OLTerrain->GetWalkability() & GLTerrain->GetWalkability() : GLTerrain->GetWalkability(); + else + return 0; + } + else + return OLTerrain ? OLTerrain->GetWalkability() & GLTerrain->GetWalkability() : GLTerrain->GetWalkability(); +} + +void lsquare::RemoveFluid(fluid* ToRemove) +{ + fluid*& F = ListFind(Fluid, pointercomparer(ToRemove)); + F = F->Next; + SignalEmitationDecrease(ToRemove->GetEmitation()); +} + +struct fluidcomparer +{ + fluidcomparer(const liquid* Liquid) : Liquid(Liquid) { } + truth operator()(const fluid* F) const { return Liquid->IsSameAs(F->GetLiquid()); } + const liquid* Liquid; +}; + +fluid* lsquare::AddFluid(liquid* ToBeAdded) +{ + fluid*& F = ListFind(Fluid, fluidcomparer(ToBeAdded)); + + if(F) + { + F->AddLiquidAndVolume(ToBeAdded->GetVolume()); + delete ToBeAdded; + } + else + { + F = new fluid(ToBeAdded, this); + SignalEmitationIncrease(ToBeAdded->GetEmitation()); + } + + SendNewDrawRequest(); + SendMemorizedUpdateRequest(); + return F; +} + +void lsquare::DisplayFluidInfo(festring& Msg) const +{ + if(Fluid) + { + Msg << ". There is "; + fluid::AddFluidInfo(Fluid, Msg); + AddLocationDescription(Msg); + } +} + +void lsquare::SpillFluid(character* Spiller, liquid* Liquid, truth ForceHit, truth ShowMsg) +{ + if(!Liquid->GetVolume()) + { + delete Liquid; + return; + } + + if(IsFlyable()) + { + if(GetCharacter()) + { + if(Spiller && !GetCharacter()->IsAlly(Spiller)) + Spiller->Hostility(GetCharacter()); + + long CharVolume = GetCharacter()->GetVolume(); + double ChanceMultiplier = 1. / (300 + sqrt(GetStack()->GetVolume() + CharVolume)); + double Root = sqrt(CharVolume); + + if(ForceHit || Root > RAND() % 400 || Root > RAND() % 400) + { + long SpillVolume = long(Liquid->GetVolume() * Root * ChanceMultiplier); + + if(SpillVolume) + { + if(ShowMsg && (GetCharacter()->IsPlayer() || GetCharacter()->CanBeSeenByPlayer())) + ADD_MESSAGE("%s is spilled all over %s.", Liquid->GetName(false, false).CStr(), GetCharacter()->CHAR_DESCRIPTION(DEFINITE)); + + Liquid->EditVolume(-SpillVolume); + GetCharacter()->SpillFluid(Spiller, Liquid->SpawnMoreLiquid(SpillVolume), GetCharacter()->GetSquareIndex(GetPos())); + } + } + } + + GetStack()->SpillFluid(Spiller, Liquid, Liquid->GetVolume()); + } + + if(Liquid->GetVolume() && !Liquid->IsSameAs(GLTerrain->GetMainMaterial())) + { + fluid* F = AddFluid(Liquid); + + if(GetCharacter()) + F->StepOnEffect(GetCharacter()); + } + else + delete Liquid; +} + +void lsquare::DrawStacks(blitdata& BlitData) const +{ + Stack->Draw(PLAYER, BlitData, CENTER); + + for(int c = 0; c < 4; ++c) + { + stack* Stack = GetStackOfAdjacentSquare(c); + + if(Stack) + Stack->Draw(PLAYER, BlitData, 3 - c); + } +} + +void lsquare::RemoveRain(rain* ToBeRemoved) +{ + SendNewDrawRequest(); + rain* R = Rain; + + if(ToBeRemoved->IsEnabled()) + DecAnimatedEntities(); + + if(R == ToBeRemoved) + Rain = R->Next; + else + { + rain* LR; + + do + { + LR = R; + R = R->Next; + } + while(R != ToBeRemoved); + + LR->Next = R->Next; + } + + SignalEmitationDecrease(ToBeRemoved->GetEmitation()); +} + +void lsquare::AddRain(liquid* RainLiquid, v2 Speed, int Team, truth OwnLiquid) +{ + rain* R = Rain, * NewRain = new rain(RainLiquid, this, Speed, Team, OwnLiquid); + + if(NewRain->IsEnabled()) + IncAnimatedEntities(); + + if(!R) + Rain = NewRain; + else + { + rain* LR; + + do + { + LR = R; + R = R->Next; + } + while(R); + + LR->Next = NewRain; + } +} + +void lsquare::RemoveSunLight() +{ + SunLightLuminance = 0; + SunEmitter.clear(); +} + +void lsquare::CheckIfIsSecondarySunLightEmitter() +{ + col24 OldEmitation = SecondarySunLightEmitation; + + if(Flags & IS_TRANSPARENT && (!(Flags & INSIDE) || SunLightLuminance)) + for(int d = 0; d < 8; ++d) + { + lsquare* Neighbour = NeighbourLSquare[d]; + + if(Neighbour && Neighbour->Flags & INSIDE) + { + col24 NewEmitation = GetLevel()->GetAmbientLuminance(); + + if(OldEmitation != NewEmitation) + { + SecondarySunLightEmitation = NewEmitation; + + if(game::CompareLights(NewEmitation, OldEmitation) >= 0) + Emitate(NewEmitation, SECONDARY_SUN_LIGHT); + else + { + Noxify(OldEmitation, SECONDARY_SUN_LIGHT); + Emitate(NewEmitation, SECONDARY_SUN_LIGHT|FORCE_ADD); + } + } + + return; + } + } + + if(OldEmitation) + { + Noxify(OldEmitation, SECONDARY_SUN_LIGHT); + SecondarySunLightEmitation = 0; + } +} + +void lsquare::CalculateNeighbourLSquares() +{ + int XSize = GetLevel()->GetXSize(); + int YSize = GetLevel()->GetYSize(); + + for(int d = 0; d < 8; ++d) + { + v2 NPos = Pos + game::GetMoveVector(d); + + if(NPos.X >= 0 && NPos.Y >= 0 && NPos.X < XSize && NPos.Y < YSize) + NeighbourLSquare[d] = GetLevel()->GetLSquare(NPos); + else + NeighbourLSquare[d] = 0; + } +} + +void lsquare::RemoveLuminance(col24& Emitation) +{ + col24 OldLuminance = Luminance; + col24 OldEmitation = Emitation; + Emitation = 0; + + if(game::CompareLights(OldEmitation, OldLuminance) < 0) + return; + + if(!(Flags & IS_TRANSPARENT)) + { + Flags |= NEW_DRAW_REQUEST; + + if(LastSeen == game::GetLOSTick()) + game::SendLOSUpdateRequest(); + } + else + { + CalculateLuminance(); + + if(OldLuminance == Luminance) + return; + + Flags |= NEW_DRAW_REQUEST; + + if(!Luminance) + { + Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE; + + if(LastSeen == game::GetLOSTick()) + game::SendLOSUpdateRequest(); + } + } +} + +void lsquare::ChangeLuminance(col24& Emitation, col24 NewLuminance) +{ + col24 OldLuminance = Luminance; + + if(!(Flags & IS_TRANSPARENT)) + { + Emitation = NewLuminance; + Flags |= NEW_DRAW_REQUEST; + + if(LastSeen == game::GetLOSTick()) + game::SendLOSUpdateRequest(); + + return; + } + + truth EmitationInsignificant = !Emitation + || game::CompareLights(Emitation, OldLuminance) < 0; + Emitation = NewLuminance; + + if(game::CompareLights(NewLuminance, OldLuminance) > 0 + && EmitationInsignificant) + game::CombineLights(Luminance, NewLuminance); + else + { + if(EmitationInsignificant) + return; + + CalculateLuminance(); + + if(OldLuminance == Luminance) + return; + } + + Flags |= NEW_DRAW_REQUEST; + + if(!OldLuminance) + { + Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE; + + if(LastSeen == game::GetLOSTick()) + game::SendLOSUpdateRequest(); + } +} + +void lsquare::EnableGlobalRain() +{ + for(rain* R = Rain; R; R = R->Next) + if(!R->HasOwnLiquid()) + { + R->Enable(); + IncAnimatedEntities(); + } +} + +void lsquare::DisableGlobalRain() +{ + SendNewDrawRequest(); + + for(rain* R = Rain; R; R = R->Next) + if(!R->HasOwnLiquid()) + { + R->Disable(); + DecAnimatedEntities(); + } +} + +void lsquare::InitLastSeen() +{ + LastSeen = LastSeen == game::GetLOSTick() ? 2 : 0; + SquarePartLastSeen = 0; +} + +truth lsquare::Engrave(cfestring& What) +{ + if(Engraved) + delete [] Engraved; + + if(!What.IsEmpty()) + { + Engraved = new char[What.GetSize() + 1]; + strcpy(Engraved, What.CStr()); + } + else + Engraved = 0; + + return true; +} + +void lsquare::SendSunLightSignals() +{ + if(Flags & IS_TRANSPARENT) + { + col24 OldLuminance = Luminance; + CalculateLuminance(); + + if(Luminance != OldLuminance) + { + Flags |= NEW_DRAW_REQUEST; + + if(!Luminance != !OldLuminance) + { + Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE; + + if(LastSeen == game::GetLOSTick()) + game::SendLOSUpdateRequest(); + } + } + } + else + { + Flags |= NEW_DRAW_REQUEST; + + if(LastSeen == game::GetLOSTick()) + game::SendLOSUpdateRequest(); + } +} + +void lsquare::ZeroReSunEmitatedFlags() +{ + sunemittervector::iterator i, End = SunEmitter.end(); + + for(i = SunEmitter.begin(); i != End; ++i) + *i &= ~RE_SUN_EMITATED; +} + +truth lsquare::CalculateIsTransparent() +{ + if((!OLTerrain || OLTerrain->IsTransparent()) && SmokeAlphaSum < 175 + && (!Character || Character->IsTransparent())) + { + Flags |= IS_TRANSPARENT; + return true; + } + else + { + Flags &= ~IS_TRANSPARENT; + return false; + } +} + +void lsquare::CalculateSunLightLuminance(ulong SeenBitMask) +{ + sunemittervector::const_iterator i, SunEnd = SunEmitter.end(); + int S = 0, L = 0; + + for(i = SunEmitter.begin(); i != SunEnd; ++i) + { + ulong ShadowFlag = 1 << EMITTER_SHADOW_SHIFT; + ulong SquarePartFlag = 1 << EMITTER_SQUARE_PART_SHIFT; + + for(int c = 0; c < 4; ++c, ShadowFlag <<= 1, SquarePartFlag <<= 1) + if(SeenBitMask & *i & SquarePartFlag) + if(*i & ShadowFlag) + ++S; + else + ++L; + } + + if(!L) + SunLightLuminance = 0; + else if(!S) + SunLightLuminance = GetLevel()->GetSunLightEmitation(); + else + { + col24 ShadowColor = GetLevel()->GetAmbientLuminance(); + col24 LightColor = GetLevel()->GetSunLightEmitation(); + SunLightLuminance = MakeRGB24((GetRed24(LightColor) * L + + GetRed24(ShadowColor) * S) / (S + L), + (GetGreen24(LightColor) * L + + GetGreen24(ShadowColor) * S) / (S + L), + (GetBlue24(LightColor) * L + + GetBlue24(ShadowColor) * S) / (S + L)); + } +} + +void lsquare::CreateMemorized() +{ + Memorized = new bitmap(TILE_V2); + Memorized->ActivateFastFlag(); + FowMemorized = new bitmap(TILE_V2); + FowMemorized->ActivateFastFlag(); +} + +truth lsquare::AcidRain(const beamdata& Beam) +{ + if(!IsFlyable() || GetCharacter() || Beam.Direction == YOURSELF) + { + int StackSize = GetLevel()->AddRadiusToSquareStack(Pos, 9); + lsquare** Stack = GetLevel()->GetSquareStack(); + v2 Speed = v2(512, 512); + int Team = Beam.Owner ? Beam.Owner->GetTeam()->GetID() : MONSTER_TEAM; + + for(int c = 0; c < StackSize; ++c) + { + Stack[c]->AddRain(liquid::Spawn(SULPHURIC_ACID, 300), Speed, Team, true); + Stack[c]->Flags &= ~IN_SQUARE_STACK; + } + + if(Beam.Owner && Character && Character->GetTeam() != Beam.Owner->GetTeam()) + Beam.Owner->Hostility(Character); + + return true; + } + + return false; +} + +truth lsquare::DetectMaterial(cmaterial* Material) const +{ + if(GLTerrain->DetectMaterial(Material) + || OLTerrain && OLTerrain->DetectMaterial(Material) + || Stack->DetectMaterial(Material) + || Character && Character->DetectMaterial(Material)) + return true; + + for(const fluid* F = Fluid; F; F = F->Next) + if(F->GetLiquid()->IsSameAs(Material)) + return true; + + for(const smoke* S = Smoke; S; S = S->Next) + if(S->GetGas()->IsSameAs(Material)) + return true; + + for(const rain* R = Rain; R; R = R->Next) + if(R->GetLiquid()->IsSameAs(Material)) + return true; + + return false; +} + +void lsquare::Reveal(ulong Tick, truth IgnoreDarkness) +{ + if(!Memorized) + CreateMemorized(); + + LastSeen = Tick; + + if(IgnoreDarkness) + Luminance = NORMAL_LUMINANCE; + else + { + SquarePartLastSeen = 0; + + for(int c = 0; c < 4; ++c) + SquarePartLastSeen |= (Tick << (c << 3)); + + CalculateLuminance(); + } + + Flags |= NEW_DRAW_REQUEST + | MEMORIZED_UPDATE_REQUEST + | DESCRIPTION_CHANGE; + UpdateMemorized(); + UpdateMemorizedDescription(); +} + +void lsquare::DestroyMemorized() +{ + delete Memorized; + delete FowMemorized; + Memorized = 0; + FowMemorized = 0; +} + +void lsquare::SwapMemorized(lsquare* Square) +{ + Swap(Memorized, Square->Memorized); + Swap(FowMemorized, Square->FowMemorized); + MemorizedDescription.SwapData(Square->MemorizedDescription); +} + +truth lsquare::Necromancy(const beamdata& Beam) +{ + return GetStack()->Necromancy(Beam.Owner); +} + +// Returns 0 if fails + +lsquare* lsquare::GetRandomAdjacentSquare() const +{ + lsquare* OK[8]; + int Index = 0; + + for(int c = 0; c < 8; ++c) + { + lsquare* Square = NeighbourLSquare[c]; + + if(Square) + OK[Index++] = Square; + } + + if(Index) + return OK[RAND_N(Index)]; + else + return 0; +} + +truth pathcontroller::Handler(int x, int y) +{ + return Character->CanMoveOn(Map[x][y]); +} + +void lsquare::SignalPossibleTransparencyChange() +{ + truth WasTransparent = IsTransparent(); + CalculateIsTransparent(); + + if(WasTransparent && !IsTransparent()) + { + Flags |= IS_TRANSPARENT; + emittervector EmitterBackup = Emitter; + GetLevel()->ForceEmitterNoxify(EmitterBackup); + Flags &= ~IS_TRANSPARENT; + GetLevel()->ForceEmitterEmitation(EmitterBackup, SunEmitter, FORCE_ADD); + CalculateLuminance(); + Flags |= DESCRIPTION_CHANGE|MEMORIZED_UPDATE_REQUEST; + + if(LastSeen == game::GetLOSTick()) + game::SendLOSUpdateRequest(); + } + else if(!WasTransparent && IsTransparent()) + { + GetLevel()->ForceEmitterEmitation(Emitter, SunEmitter); + CalculateLuminance(); + Flags |= DESCRIPTION_CHANGE|MEMORIZED_UPDATE_REQUEST; + + if(LastSeen == game::GetLOSTick()) + game::SendLOSUpdateRequest(); + } +} + +void lsquare::RemoveTrap(trap* ToRemove) +{ + trap*& T = ListFind(Trap, pointercomparer(ToRemove)); + T = T->Next; + SendNewDrawRequest(); + SendMemorizedUpdateRequest(); +} + +struct trapcomparer +{ + trapcomparer(int Type) : Type(Type) { } + truth operator()(const trap* T) const { return T->GetType() == Type; } + int Type; +}; + +truth lsquare::AddTrap(trap* ToBeAdded) +{ + trap*& T = ListFind(Trap, trapcomparer(ToBeAdded->GetType())); + + if(T) + { + delete ToBeAdded; + return false; + } + else + T = ToBeAdded; + + ToBeAdded->SetLSquareUnder(this); + SendNewDrawRequest(); + SendMemorizedUpdateRequest(); + return true; +} + +void lsquare::DisplayTrapInfo(festring& Msg) const +{ + for(const trap* T = Trap; T; T = T->Next) + T->AddDescription(Msg); +} + +void lsquare::FillTrapVector(std::vector& TrapVector) const +{ + for(trap* T = Trap; T; T = T->Next) + TrapVector.push_back(T); +} + +void lsquare::ReceiveTrapDamage(character* Damager, int Damage, int Type, int Direction) +{ + std::vector TrapVector; + FillTrapVector(TrapVector); + + for(uint c = 0; c < TrapVector.size(); ++c) + TrapVector[c]->ReceiveDamage(Damager, Damage, Type, Direction); +} + +truth lsquare::HasDangerousTraps(ccharacter* Who) const +{ + for(trap* T = Trap; T; T = T->Next) + if(T->IsDangerous(Who)) + return true; + + return false; +} + +truth lsquare::HasDangerousFluids(ccharacter* Who) const +{ + for(const fluid* F = Fluid; F; F = F->Next) + if(F->IsDangerous(Who)) + return true; + + return false; +} + +truth lsquare::HasNoBorderPartners() const +{ + return !(GroundBorderPartnerInfo >> 24) && !(OverBorderPartnerInfo >> 24); +} + +void lsquare::AddLocationDescription(festring& String) const +{ + if(IsFlyable()) + GLTerrain->AddLocationDescription(String); + else + OLTerrain->AddLocationDescription(String); +} + +truth lsquare::VomitingIsDangerous(ccharacter* Char) const +{ + return ((OLTerrain && OLTerrain->VomitingIsDangerous(Char)) + || (Character && Character->GetTeam() != Char->GetTeam() + && Character->GetRelation(Char) != HOSTILE)); +} + +bool lsquare::TeleportAllSmokeAway() +{ + return false; +} + +bool lsquare::TeleportAllFluidsAway() +{ + return false; +} + +bool lsquare::TeleportAllTrapsAway() +{ + for(trap* T = Trap; T; T = Trap) + { + T->Untrap(); + RemoveTrap(T); + v2 V, Pos = GetPos(); + for(V = GetLevel()->GetRandomSquare(); V != Pos; V = GetLevel()->GetRandomSquare()); + GetNearLSquare(V)->AddTrap(T); + } + + return false; +} + +void lsquare::AddSpecialCursors() +{ + if((FowMemorized || game::GetSeeWholeMapCheatMode()) && OLTerrain) + OLTerrain->AddSpecialCursors(); +} diff --git a/Main/Source/lterra.cpp b/Main/Source/lterra.cpp new file mode 100644 index 0000000..f8d8fab --- /dev/null +++ b/Main/Source/lterra.cpp @@ -0,0 +1,575 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through levelset.cpp */ + +glterrainprototype::glterrainprototype(const glterrainprototype* Base, glterrainspawner Spawner, cchar* ClassID) : Base(Base), Spawner(Spawner), ClassID(ClassID) { Index = protocontainer::Add(this); } +olterrainprototype::olterrainprototype(const olterrainprototype* Base, olterrainspawner Spawner, cchar* ClassID) : Base(Base), Spawner(Spawner), ClassID(ClassID) { Index = protocontainer::Add(this); } + +square* lterrain::GetSquareUnderEntity(int) const { return LSquareUnder; } +level* lterrain::GetLevel() const { return LSquareUnder->GetLevel(); } +lsquare* lterrain::GetNearLSquare(v2 Pos) const { return LSquareUnder->GetLevel()->GetLSquare(Pos); } +lsquare* lterrain::GetNearLSquare(int x, int y) const { return LSquareUnder->GetLevel()->GetLSquare(x, y); } +room* lterrain::GetRoom() const { return GetLSquareUnder()->GetRoom(); } +void lterrain::SetMainMaterial(material* NewMaterial, int SpecialFlags) { SetMaterial(MainMaterial, NewMaterial, 0, SpecialFlags); } +void lterrain::ChangeMainMaterial(material* NewMaterial, int SpecialFlags) { ChangeMaterial(MainMaterial, NewMaterial, 0, SpecialFlags); } +void lterrain::InitMaterials(const materialscript* M, const materialscript*, truth CUP) { InitMaterials(M->Instantiate(), CUP); } + +void glterrain::InstallDataBase(int NewConfig) { databasecreator::InstallDataBase(this, NewConfig); } +void olterrain::InstallDataBase(int NewConfig) { databasecreator::InstallDataBase(this, NewConfig); } +int glterrain::GetGraphicsContainerIndex() const { return GR_GLTERRAIN; } +int olterrain::GetGraphicsContainerIndex() const { return GR_OLTERRAIN; } + +truth olterraindatabase::AllowRandomInstantiation() const { return !(Config & S_LOCK_ID); } + +festring olterrain::GetText() const { return ""; } + +void lterrain::Load(inputfile& SaveFile) +{ + LSquareUnder = static_cast(game::GetSquareInLoad()); + object::Load(SaveFile); +} + +void glterrain::Save(outputfile& SaveFile) const +{ + SaveFile << (ushort)GetType(); + lterrain::Save(SaveFile); + SaveFile << (ushort)GetConfig(); +} + +void glterrain::Load(inputfile& SaveFile) +{ + lterrain::Load(SaveFile); + databasecreator::InstallDataBase(this, ReadType(SaveFile)); +} + +void olterrain::Save(outputfile& SaveFile) const +{ + SaveFile << (ushort)GetType(); + lterrain::Save(SaveFile); + SaveFile << (ushort)GetConfig(); + SaveFile << HP; +} + +void olterrain::Load(inputfile& SaveFile) +{ + lterrain::Load(SaveFile); + databasecreator::InstallDataBase(this, ReadType(SaveFile)); + SaveFile >> HP; +} + +v2 lterrain::GetPos() const +{ + return LSquareUnder->GetPos(); +} + +truth lterrain::SitOn(character* Sitter) +{ + if(GetSitMessage().GetSize()) + { + ADD_MESSAGE("%s", GetSitMessage().CStr()); + Sitter->EditAP(-1000); + return true; + } + else + return false; +} + +void olterrain::Break() +{ + lsquare* Square = GetLSquareUnder(); + const fearray >& ItemArray = GetLeftOverItems(); + + for(uint c1 = 0; c1 < ItemArray.Size; ++c1) + if(ItemArray[c1].IsValid()) + { + const interval* TimesPtr = ItemArray[c1].GetTimes(); + int Times = TimesPtr ? TimesPtr->Randomize() : 1; + + for(int c2 = 0; c2 < Times; ++c2) + { + item* Item = ItemArray[c1].InstantiateBasedOnMaterial(GetMainMaterial()->GetDigProductMaterial()); + + if(Item) + { + Square->AddItem(Item); + Item->SpecialGenerationHandler(); + } + } + } + + Square->ChangeOLTerrainAndUpdateLights(0); +} + +glterrain* glterrainprototype::SpawnAndLoad(inputfile& SaveFile) const +{ + glterrain* Terrain = Spawner(0, LOAD); + Terrain->Load(SaveFile); + Terrain->CalculateAll(); + return Terrain; +} + +olterrain* olterrainprototype::SpawnAndLoad(inputfile& SaveFile) const +{ + olterrain* Terrain = Spawner(0, LOAD); + Terrain->Load(SaveFile); + Terrain->CalculateAll(); + return Terrain; +} + +void lterrain::Initialize(int NewConfig, int SpecialFlags) +{ + if(!(SpecialFlags & LOAD)) + { + InstallDataBase(NewConfig); + RandomizeVisualEffects(); + + if(!(SpecialFlags & NO_MATERIALS)) + GenerateMaterials(); + } + + if(!(SpecialFlags & LOAD)) + { + PostConstruct(); + RestoreHP(); + } + + if(!(SpecialFlags & LOAD)) + { + if(!(SpecialFlags & NO_MATERIALS)) + { + CalculateAll(); + + if(!(SpecialFlags & NO_PIC_UPDATE)) + UpdatePictures(); + } + } +} + +truth lterrain::CanBeSeenByPlayer() const +{ + return LSquareUnder->CanBeSeenByPlayer(); +} + +truth lterrain::CanBeSeenBy(character* Who) const +{ + if(Who->IsPlayer()) + return CanBeSeenByPlayer(); + else + return LSquareUnder->CanBeSeenFrom(Who->GetPos(), Who->GetLOSRangeSquare()); +} + +void olterrain::ShowRestMessage(character*) const +{ + if(GetRestMessage().GetSize()) + ADD_MESSAGE("%s", GetRestMessage().CStr()); +} + +void lterrain::SignalEmitationIncrease(col24 EmitationUpdate) +{ + if(game::CompareLights(EmitationUpdate, Emitation) > 0) + { + game::CombineLights(Emitation, EmitationUpdate); + + if(LSquareUnder) + LSquareUnder->SignalEmitationIncrease(EmitationUpdate); + } +} + +void lterrain::SignalEmitationDecrease(col24 EmitationUpdate) +{ + if(game::CompareLights(EmitationUpdate, Emitation) >= 0 && Emitation) + { + col24 Backup = Emitation; + CalculateEmitation(); + + if(Backup != Emitation && LSquareUnder) + LSquareUnder->SignalEmitationDecrease(EmitationUpdate); + } +} + +truth olterrain::Enter(truth DirectionUp) const +{ + if(DirectionUp) + ADD_MESSAGE("You can't go up."); + else + ADD_MESSAGE("You can't go down."); + + return false; +} + +int olterrain::GetStrengthValue() const +{ + return GetMainMaterial()->GetStrengthValue() / 20; +} + +void olterrain::ReceiveDamage(character* Villain, int Damage, int) +{ + if(CanBeDestroyed() && Damage > GetStrengthValue()) + { + EditHP(GetStrengthValue() - Damage); + + if(HP <= 0) + { + room* Room = GetRoom(); + Break(); + + if(Room) + Room->DestroyTerrain(Villain); + } + } +} + +void olterrain::BeKicked(character* Kicker, int Damage, int) +{ + if(CanBeDestroyed() && Damage > GetMainMaterial()->GetStrengthValue() >> 1) + { + EditHP((GetMainMaterial()->GetStrengthValue() >> 1) - Damage); + + if(HP <= 0) + { + room* Room = GetRoom(); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s is shattered.", CHAR_NAME(DEFINITE)); + + Break(); + + if(Room) + Room->DestroyTerrain(Kicker); + } + } + else if(Kicker->IsPlayer()) + ADD_MESSAGE("Your kick has no effect on %s.", CHAR_NAME(DEFINITE)); +} + +int olterrain::CalculateMaxHP() +{ + if(GetMainMaterial()) + { + long SV = GetMainMaterial()->GetStrengthValue(); + return SV * SV * GetHPModifier() / 6000; + } + else + return 0; +} + +int glterrain::GetAttachedGod() const +{ + return DataBase->AttachedGod ? DataBase->AttachedGod : MainMaterial->GetAttachedGod(); +} + +int olterrain::GetAttachedGod() const +{ + return DataBase->AttachedGod ? DataBase->AttachedGod : MainMaterial->GetAttachedGod(); +} + +void olterrain::SetConfig(int NewConfig, int SpecialFlags) +{ + databasecreator::InstallDataBase(this, NewConfig); + + if(!(SpecialFlags & NO_PIC_UPDATE)) + UpdatePictures(); +} + +god* olterrain::GetMasterGod() const +{ + return game::GetGod(GetConfig()); +} + +truth olterrain::CanBeDestroyed() const +{ + return DataBase->CanBeDestroyed && ((GetPos().X && GetPos().Y && GetPos().X != GetLevel()->GetXSize() - 1 && GetPos().Y != GetLevel()->GetYSize() - 1) || GetLevel()->IsOnGround()); +} + +//extern itemprototype key_ProtoType; + +int olterrainprototype::CreateSpecialConfigurations(olterraindatabase** TempConfig, int Configs, int Level) +{ + if(Level) + return Configs; + + if(TempConfig[0]->CreateDivineConfigurations) + Configs = databasecreator::CreateDivineConfigurations(this, TempConfig, Configs); + + /* Gum solution */ + + if(TempConfig[0]->CreateLockConfigurations) + { + const item::database*const* KeyConfigData = key::ProtoType.GetConfigData(); + int KeyConfigSize = key::ProtoType.GetConfigSize(); + int OldConfigs = Configs; + + for(int c1 = 0; c1 < OldConfigs; ++c1) + if(!TempConfig[c1]->IsAbstract) + { + int BaseConfig = TempConfig[c1]->Config; + int NewConfig = BaseConfig | BROKEN_LOCK; + olterraindatabase* ConfigDataBase = new olterraindatabase(*TempConfig[c1]); + ConfigDataBase->InitDefaults(this, NewConfig); + ConfigDataBase->PostFix << "with a broken lock"; + TempConfig[Configs++] = ConfigDataBase; + + for(int c2 = 0; c2 < KeyConfigSize; ++c2) + { + NewConfig = BaseConfig | KeyConfigData[c2]->Config; + ConfigDataBase = new olterraindatabase(*TempConfig[c1]); + ConfigDataBase->InitDefaults(this, NewConfig); + ConfigDataBase->PostFix << "with "; + + if(KeyConfigData[c2]->UsesLongAdjectiveArticle) + ConfigDataBase->PostFix << "an "; + else + ConfigDataBase->PostFix << "a "; + + ConfigDataBase->PostFix << KeyConfigData[c2]->Adjective << " lock"; + TempConfig[Configs++] = ConfigDataBase; + } + } + } + + if(TempConfig[0]->CreateWindowConfigurations) + { + int OldConfigs = Configs; + + for(int c1 = 0; c1 < OldConfigs; ++c1) + if(!TempConfig[c1]->IsAbstract) + { + int NewConfig = TempConfig[c1]->Config | WINDOW; + olterraindatabase* ConfigDataBase = new olterraindatabase(*TempConfig[c1]); + ConfigDataBase->InitDefaults(this, NewConfig); + ConfigDataBase->PostFix << "with a window"; + ConfigDataBase->IsAlwaysTransparent = true; + ConfigDataBase->BitmapPos = ConfigDataBase->WindowBitmapPos; + TempConfig[Configs++] = ConfigDataBase; + } + } + + return Configs; +} + +truth olterrain::IsTransparent() const +{ + return IsAlwaysTransparent() || MainMaterial->IsTransparent(); +} + +void glterrain::ModifyAnimationFrames(int& AF) const +{ + if(UseBorderTiles()) + AF += AF << 3; +} + +v2 glterrain::GetBorderBitmapPos(v2 BasePos, int I) const +{ + if(UseBorderTiles()) + { + int Index = I ? 8 - (I + (I << 3)) / GraphicData.AnimationFrames : 8; + v2 MV = game::GetMoveVector(Index); + + if(VisualEffects & MIRROR) + MV.X = -MV.X; + + if(VisualEffects & FLIP) + MV.Y = -MV.Y; + + if(VisualEffects & ROTATE) + { + cint T = MV.Y; + MV.Y = -MV.X; + MV.X = T; + } + + return BasePos + (MV << 4); + } + else + return BasePos; +} + +void glterrain::Draw(blitdata& BlitData) const +{ + if(UseBorderTiles()) + { + cint TrueAF = GraphicData.AnimationFrames / 9; + int PictureIndex = (BlitData.CustomData & SQUARE_INDEX_MASK) * TrueAF; + + if(BlitData.CustomData & ALLOW_ANIMATE && TrueAF != 1) + PictureIndex += GET_TICK() & (TrueAF - 1); + + GraphicData.Picture[PictureIndex]->LuminanceMaskedBlit(BlitData); + } + else + { + cint AF = GraphicData.AnimationFrames; + cint F = !(BlitData.CustomData & ALLOW_ANIMATE) || AF == 1 ? 0 : GET_TICK() & (AF - 1); + GraphicData.Picture[F]->LuminanceBlit(BlitData); + } +} + +void olterrain::ModifyAnimationFrames(int& AF) const +{ + if(UseBorderTiles()) + AF += AF << 3; +} + +v2 olterrain::GetBorderBitmapPos(v2 BasePos, int I) const +{ + if(UseBorderTiles()) + { + int Index = I ? 8 - (I + (I << 3)) / GraphicData.AnimationFrames : 8; + v2 MV = game::GetMoveVector(Index); + + if(VisualEffects & MIRROR) + MV.X = -MV.X; + + if(VisualEffects & FLIP) + MV.Y = -MV.Y; + + if(VisualEffects & ROTATE) + { + cint T = MV.Y; + MV.Y = -MV.X; + MV.X = T; + } + + return BasePos + (MV << 4); + } + else + return BasePos; +} + +void olterrain::Draw(blitdata& BlitData) const +{ + if(UseBorderTiles()) + { + cint TrueAF = GraphicData.AnimationFrames / 9; + int PictureIndex = (BlitData.CustomData & SQUARE_INDEX_MASK) * TrueAF; + + if(BlitData.CustomData & ALLOW_ANIMATE && TrueAF != 1) + PictureIndex += GET_TICK() & (TrueAF - 1); + + GraphicData.Picture[PictureIndex]->AlphaLuminanceBlit(BlitData); + } + else + { + cint AF = GraphicData.AnimationFrames; + cint F = !(BlitData.CustomData & ALLOW_ANIMATE) || AF == 1 ? 0 : GET_TICK() & (AF - 1); + GraphicData.Picture[F]->AlphaLuminanceBlit(BlitData); + } +} + +void lterrain::SignalRustLevelChange() +{ + UpdatePictures(); + GetLSquareUnder()->SendMemorizedUpdateRequest(); + GetLSquareUnder()->SendNewDrawRequest(); +} + +void olterrain::SignalRustLevelChange() +{ + lterrain::SignalRustLevelChange(); + HP = Min(HP, CalculateMaxHP()); +} + +void lterrain::TryToRust(long LiquidModifier) +{ + if(MainMaterial->TryToRust(LiquidModifier * 10, 10000)) + { + if(CanBeSeenByPlayer()) + if(MainMaterial->GetRustLevel() == NOT_RUSTED) + ADD_MESSAGE("%s rusts.", CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("%s rusts more.", CHAR_NAME(DEFINITE)); + + MainMaterial->SetRustLevel(MainMaterial->GetRustLevel() + 1); + } +} + +void olterrain::ReceiveAcid(material*, long Modifier) +{ + if(GetMainMaterial()->GetInteractionFlags() & CAN_DISSOLVE) + { + int Damage = Modifier / 10000; + + if(Damage) + { + Damage += RAND() % Damage; + ReceiveDamage(0, Damage, ACID); + } + } +} + +void lterrain::InitMaterials(material* FirstMaterial, truth CallUpdatePictures) +{ + InitMaterial(MainMaterial, FirstMaterial, 0); + SignalVolumeAndWeightChange(); + + if(CallUpdatePictures) + UpdatePictures(); +} + +void lterrain::GenerateMaterials() +{ + int Chosen = RandomizeMaterialConfiguration(); + const fearray& MMC = GetMainMaterialConfig(); + InitMaterial(MainMaterial, + MAKE_MATERIAL(MMC.Data[MMC.Size == 1 ? 0 : Chosen]), + 0); +} + +void glterraindatabase::InitDefaults(const glterrainprototype* NewProtoType, int NewConfig) +{ + IsAbstract = false; + ProtoType = NewProtoType; + Config = NewConfig; +} + +void olterraindatabase::InitDefaults(const olterrainprototype* NewProtoType, int NewConfig) +{ + IsAbstract = false; + ProtoType = NewProtoType; + Config = NewConfig; +} + +truth olterrain::ShowThingsUnder() const +{ + return DataBase->ShowThingsUnder || IsTransparent(); +} + +truth olterrain::WillBeDestroyedBy(ccharacter* Char) const +{ + return IsWall() && CanBeDestroyed() && MainMaterial->GetStrengthValue() <= (Char->GetAttribute(ARM_STRENGTH) * 3); +} + +v2 glterrain::GetBitmapPos(int I) const +{ + return GetBorderBitmapPos(DataBase->BitmapPos, I); +} + +v2 olterrain::GetBitmapPos(int I) const +{ + return GetBorderBitmapPos(DataBase->BitmapPos, I); +} + +truth glterrain::IsAnimated() const +{ + return GraphicData.AnimationFrames > (UseBorderTiles() ? 9 : 1); +} + +truth olterrain::IsAnimated() const +{ + return GraphicData.AnimationFrames > (UseBorderTiles() ? 9 : 1); +} + +void lterrain::AddLocationDescription(festring& String) const +{ + String << " on the " << GetNameSingular(); +} diff --git a/Main/Source/lterras.cpp b/Main/Source/lterras.cpp new file mode 100644 index 0000000..7e61dbe --- /dev/null +++ b/Main/Source/lterras.cpp @@ -0,0 +1,1450 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through levelset.cpp */ + +truth door::CanBeOpenedByAI() { return !IsLocked() && CanBeOpened(); } +void door::HasBeenHitByItem(character* Thrower, item*, int Damage) { ReceiveDamage(Thrower, Damage, PHYSICAL_DAMAGE); } +v2 door::GetBitmapPos(int Frame) const { return Opened ? GetOpenBitmapPos(Frame) : olterrain::GetBitmapPos(Frame); } +int door::GetTheoreticalWalkability() const { return ANY_MOVE; } + +v2 portal::GetBitmapPos(int Frame) const { return v2(16 + (((Frame & 31) << 3)&~8), 0); } // gum solution, should come from script +v2 monsterportal::GetBitmapPos(int Frame) const { return v2(16 + (((Frame & 31) << 3)&~8), 0); } // gum solution, should come from script + +void fountain::SetSecondaryMaterial(material* What, int SpecialFlags) { SetMaterial(SecondaryMaterial, What, 0, SpecialFlags); } +void fountain::ChangeSecondaryMaterial(material* What, int SpecialFlags) { ChangeMaterial(SecondaryMaterial, What, 0, SpecialFlags); } +void fountain::InitMaterials(material* M1, material* M2, truth CUP) { ObjectInitMaterials(MainMaterial, M1, 0, SecondaryMaterial, M2, 0, CUP); } +v2 fountain::GetBitmapPos(int) const { return v2(GetSecondaryMaterial() ? 16 : 32, 288); } +void fountain::InitMaterials(const materialscript* M, const materialscript* C, truth CUP) { InitMaterials(M->Instantiate(), C->Instantiate(), CUP); } + +void brokendoor::HasBeenHitByItem(character* Thrower, item*, int Damage) { ReceiveDamage(Thrower, Damage, PHYSICAL_DAMAGE); } + +cchar* liquidterrain::SurviveMessage() const { return "you manage to get out of the pool"; } +cchar* liquidterrain::MonsterSurviveMessage() const { return "manages to get out of the pool"; } +cchar* liquidterrain::DeathMessage() const { return "you drown"; } +cchar* liquidterrain::MonsterDeathVerb() const { return "drowns"; } +cchar* liquidterrain::ScoreEntry() const { return "drowned"; } + +festring sign::GetText() const { return Text; } + +truth door::Open(character* Opener) +{ + if(!Opened) + { + if(IsLocked()) + { + if(Opener->IsPlayer()) + ADD_MESSAGE("The door is locked."); + + return false; + } + else if(RAND() % 20 < Opener->GetAttribute(ARM_STRENGTH)) + { + truth WasSeenByPlayer = CanBeSeenByPlayer(); // MakeWalkable() might destroy the door + MakeWalkable(); + + if(Opener->IsPlayer()) + ADD_MESSAGE("You open the door."); + else if(WasSeenByPlayer) + { + if(Opener->CanBeSeenByPlayer()) + ADD_MESSAGE("%s opens the door.", Opener->CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("Something opens the door."); + } + } + else + { + if(Opener->IsPlayer()) + ADD_MESSAGE("The door resists."); + else if(CanBeSeenByPlayer() && Opener->CanBeSeenByPlayer()) + ADD_MESSAGE("%s fails to open the door.", Opener->CHAR_NAME(DEFINITE)); + + ActivateBoobyTrap(); + } + + Opener->DexterityAction(Opener->OpenMultiplier() * 5); + return true; + } + else + { + if(Opener->IsPlayer()) + ADD_MESSAGE("The door is already open, %s.", game::Insult()); + + return false; + } +} + +truth door::Close(character* Closer) +{ + if(Closer->IsPlayer()) + if(Opened) + { + if(RAND() % 20 < Closer->GetAttribute(ARM_STRENGTH)) + { + ADD_MESSAGE("You close the door."); + MakeNotWalkable(); + } + else + ADD_MESSAGE("The door resists!"); + } + else + { + ADD_MESSAGE("The door is already closed, %s.", game::Insult()); + return false; + } + + Closer->DexterityAction(Closer->OpenMultiplier() * 5); + return true; +} + +void altar::Draw(blitdata& BlitData) const +{ + olterrain::Draw(BlitData); + BlitData.Src.X = GetConfig() << 4; + igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData); + BlitData.Src.X = BlitData.Src.Y = 0; +} + +void door::BeKicked(character* Kicker, int KickDamage, int) +{ + if(!Opened) + { + if(!IsLocked() && KickDamage > (RAND() & 3)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("The door opens."); + + MakeWalkable(); + return; + } + + if(KickDamage <= GetStrengthValue()) + { + if(CanBeSeenByPlayer() && (Kicker->CanBeSeenByPlayer() || Kicker->IsPlayer())) + ADD_MESSAGE("%s weak kick has no chance to affect this door.", Kicker->CHAR_POSSESSIVE_PRONOUN); + + return; + } + + EditHP(GetStrengthValue() - KickDamage); + int SV = Max(GetStrengthValue(), 1); + truth LockBreaks = IsLocked() && RAND() % (100 * KickDamage / SV) >= 100; + + if(LockBreaks) + SetIsLocked(false); + + if(GetHP() <= 0) + { + if(CanBeSeenByPlayer()) + { + if(LockBreaks) + ADD_MESSAGE("The lock breaks and the door is damaged."); + else + ADD_MESSAGE("The door is damaged."); + } + + room* Room = GetRoom(); + Break(); + + if(Room) + Room->DestroyTerrain(Kicker); + } + else if(CanBeSeenByPlayer()) + { + if(LockBreaks) + { + ADD_MESSAGE("The lock breaks."); + + if(GetRoom()) + GetRoom()->DestroyTerrain(Kicker); + } + else if(Kicker->CanBeSeenByPlayer() || Kicker->IsPlayer()) + ADD_MESSAGE("The door won't budge!"); + } + + // The door may have been destroyed here, so don't do anything! + } +} + +void door::Save(outputfile& SaveFile) const +{ + olterrain::Save(SaveFile); + SaveFile << Opened << Locked << BoobyTrap; +} + +void door::Load(inputfile& SaveFile) +{ + olterrain::Load(SaveFile); + SaveFile >> Opened >> Locked >> BoobyTrap; +} + +void door::MakeWalkable() +{ + SetIsOpened(true); + UpdatePictures(); + GetLSquareUnder()->CalculateIsTransparent(); + GetLSquareUnder()->SendNewDrawRequest(); + GetLSquareUnder()->SendMemorizedUpdateRequest(); + GetLevel()->ForceEmitterEmitation(GetLSquareUnder()->GetEmitter(), + GetLSquareUnder()->GetSunEmitter()); + GetLSquareUnder()->CalculateLuminance(); + + if(GetLSquareUnder()->GetLastSeen() == game::GetLOSTick()) + game::SendLOSUpdateRequest(); + + ActivateBoobyTrap(); +} + +void door::MakeNotWalkable() +{ + emittervector EmitterBackup = GetLSquareUnder()->GetEmitter(); + GetLevel()->ForceEmitterNoxify(EmitterBackup); + SetIsOpened(false); + UpdatePictures(); + GetLSquareUnder()->CalculateIsTransparent(); + GetLSquareUnder()->SendNewDrawRequest(); + GetLSquareUnder()->SendMemorizedUpdateRequest(); + GetLevel()->ForceEmitterEmitation(EmitterBackup, + GetLSquareUnder()->GetSunEmitter(), + FORCE_ADD); + GetLSquareUnder()->CalculateLuminance(); + + if(GetLSquareUnder()->GetLastSeen() == game::GetLOSTick()) + game::SendLOSUpdateRequest(); +} + +void altar::StepOn(character* Stepper) +{ + if(Stepper->IsPlayer() && !GetMasterGod()->IsKnown()) + { + ADD_MESSAGE("The ancient altar is covered with strange markings. You manage to decipher them. The altar is dedicated to %s, the %s. You now know the sacred rituals that allow you to contact this deity via prayers.", GetMasterGod()->GetName(), GetMasterGod()->GetDescription()); + game::LearnAbout(GetMasterGod()); + } +} + +truth throne::SitOn(character* Sitter) +{ + Sitter->EditAP(-1000); + + if(Sitter->HasPetrussNut() && Sitter->HasGoldenEagleShirt() && game::GetGod(VALPURUS)->GetRelation() != 1000) + { + ADD_MESSAGE("You have a strange vision of yourself becoming great ruler. The daydream fades in a whisper: \"Thou shalt be a Our Champion first!\""); + return true; + } + + if(Sitter->HasPetrussNut() && !Sitter->HasGoldenEagleShirt() && game::GetGod(VALPURUS)->GetRelation() == 1000) + { + ADD_MESSAGE("You have a strange vision of yourself becoming great ruler. The daydream fades in a whisper: \"Thou shalt wear Our shining armor first!\""); + return true; + } + + if(!Sitter->HasPetrussNut() && Sitter->HasGoldenEagleShirt() && game::GetGod(VALPURUS)->GetRelation() == 1000) + { + ADD_MESSAGE("You have a strange vision of yourself becoming great ruler. The daydream fades in a whisper: \"Thou shalt surpass thy predecessor first!\""); + return true; + } + + if(Sitter->HasPetrussNut() && Sitter->HasGoldenEagleShirt() && game::GetGod(VALPURUS)->GetRelation() == 1000) + { + game::TextScreen(CONST_S("A heavenly choir starts to sing Grandis Rana and a booming voice fills the air:\n\n\"Mortal! Thou hast surpassed Petrus, and pleased Us greatly during thy adventures!\nWe hereby title thee as Our new high priest!\"\n\nYou are victorious!")); + game::GetCurrentArea()->SendNewDrawRequest(); + game::DrawEverything(); + PLAYER->ShowAdventureInfo(); + festring Msg = CONST_S("became the new high priest of the Great Frog"); + PLAYER->AddScoreEntry(Msg, 5, false); + game::End(Msg); + return true; + } + + ADD_MESSAGE("You feel somehow out of place."); + return true; +} + +void altar::BeKicked(character* Kicker, int, int) +{ + if(Kicker->IsPlayer()) + ADD_MESSAGE("You feel like a sinner."); + else if(Kicker->CanBeSeenByPlayer()) + ADD_MESSAGE("%s looks like a sinner.", Kicker->CHAR_NAME(DEFINITE)); + + if(GetRoom()) + GetRoom()->DestroyTerrain(Kicker); + + if(Kicker->IsPlayer()) + { + GetMasterGod()->PlayerKickedAltar(); + + if(GetConfig() > 1) + game::GetGod(GetConfig() - 1)->PlayerKickedFriendsAltar(); + + if(GetConfig() < GODS) + game::GetGod(GetConfig() + 1)->PlayerKickedFriendsAltar(); + } +} + +truth altar::ReceiveVomit(character* Who, liquid* Liquid) +{ + if(Who->IsPlayer()) + { + if(GetRoom()) + GetRoom()->HostileAction(Who); + + return GetMasterGod()->PlayerVomitedOnAltar(Liquid); + } + else + return false; +} + +truth altar::VomitingIsDangerous(ccharacter*) const +{ + return !GetMasterGod()->LikesVomit(); +} + +truth door::AddAdjective(festring& String, truth Articled) const +{ + if(olterrain::AddAdjective(String, Articled)) + Articled = false; + + if(Articled) + String << (Opened ? "an open" : "a closed"); + else + String << (Opened ? "open" : "closed"); + + if(IsLocked()) + String << " locked "; + else + String << ' '; + + return true; +} + +truth fountain::SitOn(character* Sitter) +{ + if(GetSecondaryMaterial()) + { + ADD_MESSAGE("You sit on the fountain. Water falls on your head and you get quite wet. You feel like a moron."); + Sitter->EditAP(-1000); + return true; + } + else + return olterrain::SitOn(Sitter); +} + +truth fountain::Drink(character* Drinker) +{ + if(GetSecondaryMaterial()) + { + if(GetSecondaryMaterial()->GetConfig() == WATER) + { + room* Room = GetRoom(); + + if(Room && Room->HasDrinkHandler() && !Room->Drink(Drinker)) + return false; + else + { + if(!game::TruthQuestion(CONST_S("Do you want to drink from the fountain? [y/N]"))) + return false; + } + + Drinker->EditAP(-1000); + + switch(RAND() % 12) + { + case 0: + if(RAND_N(3)) + { + ADD_MESSAGE("The water is contaminated!"); + Drinker->EditNP(100); + + if(!RAND_4) + Drinker->PolymorphRandomly(0, 1000000, 2500 + RAND() % 2500); + else + { + Drinker->ChangeRandomAttribute(-1); + Drinker->CheckDeath(CONST_S("died of contaminated water")); + } + + break; + } + case 1: + ADD_MESSAGE("The water tasted very good."); + Drinker->EditNP(2500); + Drinker->ChangeRandomAttribute(1); + break; + case 2: + if(!(RAND() % 15)) + { + ADD_MESSAGE("You have freed a spirit that grants you a wish. You may wish for an item."); + game::Wish(Drinker, + "%s appears from nothing and the spirit flies happily away!", + "Two %s appear from nothing and the spirit flies happily away!"); + } + else + DryOut(); + + break; + case 4: + if(RAND() & 7) + { + ADD_MESSAGE("The water tastes normal, but there is an odd after taste."); + Drinker->ActivateRandomState(SRC_FOUNTAIN, 10000 + RAND() % 20000); + } + else + { + ADD_MESSAGE("This water tastes very odd."); + + if(!Drinker->GainRandomIntrinsic(SRC_FOUNTAIN)) + ADD_MESSAGE("You feel like a penguin."); /* This is rather rare, so no harm done */ + } + + break; + case 5: + { + characterspawner Spawner = 0; + int Config = 0, AddChance = 0; + truth ForceAdjacency = false; + + switch(RAND_N(5)) + { + case 0: + Spawner = (characterspawner)(&snake::Spawn); + AddChance = 66; + break; + case 1: + Spawner = (characterspawner)(&mommo::Spawn); + Config = RAND_2 ? CONICAL : FLAT; + AddChance = 50; + break; + case 2: + Spawner = (characterspawner)(&spider::Spawn); + + if(RAND_4) + { + Config = LARGE; + AddChance = 90; + } + else + { + Config = GIANT; + AddChance = 75; + } + + break; + case 3: + if(!RAND_N(50)) + { + Spawner = (characterspawner)(&dolphin::Spawn); + AddChance = 75; + ForceAdjacency = true; + } + else if(!RAND_N(50)) + { + Spawner = (characterspawner)(&mysticfrog::Spawn); + Config = DARK; + AddChance = 1; + } + else + { + Spawner = (characterspawner)(&frog::Spawn); + + if(RAND_N(5)) + { + Config = DARK; + AddChance = 10; + } + else if(RAND_N(5)) + { + Config = GREATER_DARK; + AddChance = 5; + } + else + { + Config = GIANT_DARK; + AddChance = 2; + } + } + + break; + case 4: + Spawner = (characterspawner)(&largerat::Spawn); + AddChance = 90; + break; + } + + int TeamIndex = RAND_N(3) ? MONSTER_TEAM : PLAYER_TEAM; + team* Team = game::GetTeam(TeamIndex); + int Amount = 1 + femath::LoopRoll(AddChance, 7); + spawnresult SR = GetLevel()->SpawnMonsters(Spawner, Team, GetPos(), Config, Amount, ForceAdjacency); + + msgsystem::EnterBigMessageMode(); + + if(SR.Seen == 1) + { + ADD_MESSAGE("%s appears from the fountain!", SR.Pioneer->CHAR_NAME(INDEFINITE)); + + if(TeamIndex == PLAYER_TEAM) + ADD_MESSAGE("%s seems to be friendly.", SR.Pioneer->CHAR_PERSONAL_PRONOUN); + + if(Amount > SR.Seen) + ADD_MESSAGE("Somehow you also sense %s isn't alone.", SR.Pioneer->CHAR_PERSONAL_PRONOUN); + } + else if(SR.Seen) + { + ADD_MESSAGE("%s appear from the fountain!", SR.Pioneer->GetName(PLURAL, SR.Seen).CStr()); + + if(TeamIndex == PLAYER_TEAM) + ADD_MESSAGE("They seem to be friendly."); + + if(Amount > SR.Seen) + ADD_MESSAGE("Somehow you also sense you haven't yet seen all of them."); + } + else + ADD_MESSAGE("You feel something moving near you."); + + msgsystem::LeaveBigMessageMode(); + break; + } + case 6: + if(!RAND_N(5)) + { + item* ToBeCreated = protosystem::BalancedCreateItem(0, MAX_PRICE, RING); + GetLSquareUnder()->AddItem(ToBeCreated); + + if(ToBeCreated->CanBeSeenByPlayer()) + ADD_MESSAGE("There's something sparkling in the water."); + + break; + } + case 7: + { + if(!RAND_N(2)) + { + olterrain* Found = GetLevel()->GetRandomFountainWithWater(this); + Drinker->RemoveTraps(); + + if(Found && RAND_N(3)) + { + ADD_MESSAGE("The fountain sucks you in. You are thrown through a network of tunnels and end up coming out from an other fountain."); + Found->GetLSquareUnder()->KickAnyoneStandingHereAway(); + Drinker->Move(Found->GetPos(), true); + } + else + { + int To = GetLSquareUnder()->GetDungeon()->GetLevelTeleportDestination(GetLevel()->GetIndex()); + int From = GetLevel()->GetIndex(); + + if(To == From) + game::TryTravel(game::GetCurrentDungeonIndex(), To, RANDOM, true, false); + else + game::TryTravel(game::GetCurrentDungeonIndex(), To, FOUNTAIN, true, false); + + olterrain* OLTerrain = Drinker->GetLSquareUnder()->GetOLTerrain(); + + if(OLTerrain && OLTerrain->IsFountainWithWater() && To != From) + ADD_MESSAGE("The fountain sucks you in. You are thrown through a network of tunnels and end up coming out from an other fountain."); + else + ADD_MESSAGE("The fountain sucks you in. You are thrown through a network of tunnels. Suddenly the wall of the tunnel bursts open and you fly out with the water."); + } + + Drinker->GetLSquareUnder()->SpillFluid(Drinker, liquid::Spawn(WATER, 1000 + RAND() % 501), false, false); + break; + } + } + default: + ADD_MESSAGE("The water tastes good."); + Drinker->EditNP(500); + break; + } + + // fountain might have dried out: don't do anything here. + + return true; + } + else + { + ADD_MESSAGE("You don't dare to drink from this fountain."); + return false; + } + } + else + { + ADD_MESSAGE("The fountain has dried out."); + return false; + } +} + +void fountain::DryOut() +{ + ADD_MESSAGE("%s dries out.", CHAR_NAME(DEFINITE)); + ChangeSecondaryMaterial(0); + + if(GetLSquareUnder()) + { + GetLSquareUnder()->SendNewDrawRequest(); + GetLSquareUnder()->SendMemorizedUpdateRequest(); + } +} + +void brokendoor::BeKicked(character* Kicker, int KickDamage, int) +{ + if(!Opened) + { + if(!IsLocked() && KickDamage > (RAND() & 3)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("The broken door opens."); + + MakeWalkable(); + return; + } + + if(IsLocked()) + { + int SV = Max(GetStrengthValue(), 1); + + if(KickDamage > SV && RAND() % (100 * KickDamage / SV) >= 100) + { + if(RAND() & 1) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("The broken door opens from the force of the kick."); + + MakeWalkable(); + } + else if(CanBeSeenByPlayer()) + { + ADD_MESSAGE("The lock breaks from the force of the kick."); + + if(GetRoom()) + GetRoom()->DestroyTerrain(Kicker); + } + + SetIsLocked(false); + return; + } + } + + if(CanBeSeenByPlayer() && (Kicker->CanBeSeenByPlayer() || Kicker->IsPlayer())) + ADD_MESSAGE("The broken door won't budge!"); + } +} + +truth altar::Polymorph(character*) +{ + room* Room = GetRoom(); + + if(Room && !Room->AllowAltarPolymorph()) + return false; + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s glows briefly.", CHAR_NAME(DEFINITE)); + + int OldGod = GetConfig(), NewGod = GetConfig(); + + while(NewGod == OldGod) + NewGod = 1 + RAND() % GODS; + + if(GetRoom()) + GetRoom()->SetDivineMaster(NewGod); + + SetConfig(NewGod); + GetLSquareUnder()->SendNewDrawRequest(); + GetLSquareUnder()->SendMemorizedUpdateRequest(); + return true; +} + +truth altar::SitOn(character* Sitter) +{ + ADD_MESSAGE("You kneel down and worship %s for a moment.", GetMasterGod()->GetName()); + + if(GetMasterGod()->GetRelation() < 500) + { + if(!(RAND() % 20)) + { + GetMasterGod()->AdjustRelation(2); + game::ApplyDivineAlignmentBonuses(GetMasterGod(), 1, true); + PLAYER->EditExperience(WISDOM, 75, 1 << 6); + } + } + else + if(!(RAND() % 2500)) + { + character* Angel = GetMasterGod()->CreateAngel(PLAYER->GetTeam()); + + if(Angel) + ADD_MESSAGE("%s seems to be very friendly towards you.", Angel->CHAR_NAME(DEFINITE)); + + GetMasterGod()->AdjustRelation(50); + game::ApplyDivineAlignmentBonuses(GetMasterGod(), 10, true); + PLAYER->EditExperience(WISDOM, 400, 1 << 11); + } + + Sitter->EditAP(-1000); + return true; +} + +void door::Break() +{ + if(BoobyTrap) + ActivateBoobyTrap(); + else + { + truth Open = Opened; + door* Temp = brokendoor::Spawn(GetConfig(), NO_MATERIALS); + Temp->InitMaterials(GetMainMaterial()->SpawnMore()); + Temp->SetIsLocked(IsLocked()); + Temp->SetBoobyTrap(0); + GetLSquareUnder()->ChangeOLTerrainAndUpdateLights(Temp); + + if(Open) + Temp->MakeWalkable(); + else + Temp->MakeNotWalkable(); + } +} + +void door::ActivateBoobyTrap() +{ + switch(BoobyTrap) + { + case 1: + // Explosion + if(LSquareUnder->CanBeSeenByPlayer(true)) + ADD_MESSAGE("%s is booby trapped!", CHAR_NAME(DEFINITE)); + + BoobyTrap = 0; + GetLevel()->Explosion(0, "killed by an exploding booby trapped door", GetPos(), 20 + RAND() % 5 - RAND() % 5); + break; + case 0: + break; + } +} + +void door::CreateBoobyTrap() +{ + SetBoobyTrap(1); +} + +truth fountain::DipInto(item* ToBeDipped, character* Who) +{ + ToBeDipped->DipInto(static_cast(GetSecondaryMaterial()->SpawnMore(100)), Who); + return true; +} + +void fountain::Save(outputfile& SaveFile) const +{ + olterrain::Save(SaveFile); + SaveFile << SecondaryMaterial; +} + +void fountain::Load(inputfile& SaveFile) +{ + olterrain::Load(SaveFile); + LoadMaterial(SaveFile, SecondaryMaterial); + +} + +material* fountain::GetMaterial(int I) const +{ + return !I ? MainMaterial : SecondaryMaterial; +} + +col16 fountain::GetMaterialColorB(int) const +{ + if(GetSecondaryMaterial()) + return GetSecondaryMaterial()->GetColor(); + else + return 0; +} + +void door::PostConstruct() +{ + /* Terrible gum solution! */ + + if(!(GetConfig() & LOCK_BITS)) + { + int NormalLockTypes = 0; + const database*const* ConfigData = GetProtoType()->GetConfigData(); + int c, ConfigSize = GetProtoType()->GetConfigSize(); + + for(c = 0; c < ConfigSize; ++c) + if(ConfigData[c]->Config & LOCK_BITS + && (ConfigData[c]->Config & ~LOCK_BITS) == GetConfig() + && !(ConfigData[c]->Config & S_LOCK_ID)) + ++NormalLockTypes; + + int ChosenLock = RAND() % NormalLockTypes; + + for(c = 0; c < ConfigSize; ++c) + if(ConfigData[c]->Config & LOCK_BITS + && (ConfigData[c]->Config & ~LOCK_BITS) == GetConfig() + && !(ConfigData[c]->Config & S_LOCK_ID) + && !ChosenLock--) + { + SetConfig(ConfigData[c]->Config, NO_PIC_UPDATE); + break; + } + } + + SetBoobyTrap(0); + SetIsOpened(false); + SetIsLocked(false); +} + +void door::SetParameters(int Param) +{ + SetIsLocked(Param & LOCKED); +} + +truth door::TryKey(item* Thingy, character* Applier) +{ + if(Opened) + return false; + + if(Thingy->CanOpenLockType(GetConfig()&LOCK_BITS)) + { + if(Applier->IsPlayer()) + { + if(IsLocked()) + ADD_MESSAGE("You unlock the door."); + else + ADD_MESSAGE("You lock the door."); + } + else if(Applier->CanBeSeenByPlayer()) + { + if(IsLocked()) + ADD_MESSAGE("%s unlocks the door.", Applier->CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("%s locks the door.", Applier->CHAR_NAME(DEFINITE)); + } + + SetIsLocked(!IsLocked()); + return true; + } + else + { + if(Applier->IsPlayer()) + ADD_MESSAGE("%s doesn't fit into the lock.", Thingy->CHAR_NAME(DEFINITE)); + + return false; + } +} + +void fountain::GenerateMaterials() +{ + int Chosen = RandomizeMaterialConfiguration(); + const fearray& MMC = GetMainMaterialConfig(); + InitMaterial(MainMaterial, + MAKE_MATERIAL(MMC.Data[MMC.Size == 1 ? 0 : Chosen]), + 0); + const fearray& SMC = GetSecondaryMaterialConfig(); + InitMaterial(SecondaryMaterial, + MAKE_MATERIAL(SMC.Data[SMC.Size == 1 ? 0 : Chosen]), + 0); +} + +truth fountain::AddAdjective(festring& String, truth Articled) const +{ + if(!GetSecondaryMaterial()) + { + String << (Articled ? "a dried out " : "dried out "); + return true; + } + else + return false; +} + +fountain::~fountain() +{ + delete SecondaryMaterial; +} + +int fountain::GetSparkleFlags() const +{ + return (MainMaterial->IsSparkling() ? SPARKLING_A : 0) + | (SecondaryMaterial && SecondaryMaterial->IsSparkling() ? SPARKLING_B : 0); +} + +void stairs::Save(outputfile& SaveFile) const +{ + olterrain::Save(SaveFile); + SaveFile << AttachedArea << AttachedEntry; +} + +void stairs::Load(inputfile& SaveFile) +{ + olterrain::Load(SaveFile); + SaveFile >> AttachedArea >> AttachedEntry; +} + +truth stairs::Enter(truth DirectionUp) const +{ + if(!DirectionUp != !IsUpLink()) + return olterrain::Enter(DirectionUp); + + /* "Temporary" gum solutions */ + + if(GetConfig() == OREE_LAIR_ENTRY) + { + ADD_MESSAGE("You sense terrible evil trembling very near under your feet. You feel you shouldn't wander any further. On the other hand you have little choice."); + + if(!game::TruthQuestion(CONST_S("Continue? [y/N]"))) + return false; + } + + if(GetConfig() == OREE_LAIR_EXIT) + if(PLAYER->HasGoldenEagleShirt()) + { + ADD_MESSAGE("Somehow you get the feeling you cannot return."); + + if(!game::TruthQuestion(CONST_S("Continue anyway? [y/N]"))) + return false; + } + else + { + ADD_MESSAGE("An unknown magical force pushes you back."); + PLAYER->EditAP(-1000); + return true; + } + + if(GetConfig() == DARK_LEVEL) + { + ADD_MESSAGE("This dark gate seems to be a one-way portal. You sense something distant but extremely dangerous on the other side. You feel you should think twice before entering."); + + if(!game::TruthQuestion(CONST_S("Continue? [y/N]"))) + return false; + } + + if(GetConfig() == SUMO_ARENA_ENTRY && !game::TryToEnterSumoArena()) + return false; + + if(GetConfig() == SUMO_ARENA_EXIT && !game::TryToExitSumoArena()) + return false; + + return game::TryTravel(game::GetCurrentDungeonIndex(), GetAttachedArea(), GetAttachedEntry(), GetAttachedArea() != WORLD_MAP); +} + +void stairs::StepOn(character* Stepper) +{ + if(Stepper->IsPlayer()) + ADD_MESSAGE("There is %s here.", CHAR_NAME(INDEFINITE)); +} + +void stairs::PostConstruct() +{ + if(GetConfig() == STAIRS_UP) + { + if(game::GetCurrentLevelIndex()) + { + AttachedArea = game::GetCurrentLevelIndex() - 1; + AttachedEntry = STAIRS_DOWN; + } + else + { + AttachedArea = WORLD_MAP; + AttachedEntry = game::GetCurrentDungeonIndex(); + } + } + else if(GetConfig() == STAIRS_DOWN) + { + AttachedArea = game::GetCurrentLevelIndex() + 1; + AttachedEntry = STAIRS_UP; + } +} + +void sign::AddPostFix(festring& String, int) const +{ + String << " with text \"" << Text << '\"'; +} + +void sign::StepOn(character* Stepper) +{ + if(Stepper->IsPlayer()) + ADD_MESSAGE("There's a sign here saying: \"%s\"", Text.CStr()); +} + +void sign::Save(outputfile& SaveFile) const +{ + olterrain::Save(SaveFile); + SaveFile << Text; +} + +void sign::Load(inputfile& SaveFile) +{ + olterrain::Load(SaveFile); + SaveFile >> Text; +} + +void olterraincontainer::Save(outputfile& SaveFile) const +{ + olterrain::Save(SaveFile); + Contained->Save(SaveFile); +} + +void olterraincontainer::Load(inputfile& SaveFile) +{ + olterrain::Load(SaveFile); + Contained->Load(SaveFile); +} + +olterraincontainer::olterraincontainer() +{ + Contained = new stack(0, this, HIDDEN); +} + +truth olterraincontainer::Open(character* Opener) +{ + if(!Opener->IsPlayer()) + return false; + + truth Success; + + switch(game::KeyQuestion(CONST_S("Do you want to (t)ake something from or (p)ut something in this container? [t,p]"), KEY_ESC, 3, 't', 'p', KEY_ESC)) + { + case 't': + case 'T': + Success = GetContained()->TakeSomethingFrom(Opener, GetName(DEFINITE)); + break; + case 'p': + case 'P': + Success = GetContained()->PutSomethingIn(Opener, GetName(DEFINITE), GetStorageVolume(), 0); + break; + default: + return false; + } + + if(Success) + Opener->DexterityAction(Opener->OpenMultiplier() * 5); + + return Success; +} + +void olterraincontainer::SetItemsInside(const fearray >& ItemArray, int SpecialFlags) +{ + GetContained()->Clean(); + + for(uint c1 = 0; c1 < ItemArray.Size; ++c1) + if(ItemArray[c1].IsValid()) + { + const interval* TimesPtr = ItemArray[c1].GetTimes(); + int Times = TimesPtr ? TimesPtr->Randomize() : 1; + + for(int c2 = 0; c2 < Times; ++c2) + { + item* Item = ItemArray[c1].Instantiate(SpecialFlags); + + if(Item) + { + Contained->AddItem(Item); + Item->SpecialGenerationHandler(); + } + } + } +} + +void door::ReceiveDamage(character* Villain, int Damage, int) +{ + if(!Opened && !IsLocked() && Damage > (RAND() & 3)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("The door opens."); + + MakeWalkable(); + return; + } + + if(CanBeDestroyed() && Damage > GetStrengthValue()) + { + EditHP(GetStrengthValue() - Damage); + int SV = Max(GetStrengthValue(), 1); + truth LockBreaks = IsLocked() && RAND() % (100 * Damage / SV) >= 100; + + if(LockBreaks) + SetIsLocked(false); + + if(HP <= 0) + { + if(CanBeSeenByPlayer()) + if(LockBreaks) + ADD_MESSAGE("The door breaks and its lock is destroyed."); + else + ADD_MESSAGE("The door breaks."); + + room* Room = GetRoom(); + Break(); + + if(Room) + Room->DestroyTerrain(Villain); + } + else if(LockBreaks && CanBeSeenByPlayer()) + { + ADD_MESSAGE("The door's lock is shattered."); + + if(GetRoom()) + GetRoom()->DestroyTerrain(Villain); + } + } +} + +void brokendoor::ReceiveDamage(character* Villain, int Damage, int) +{ + if(!Opened && !IsLocked() && Damage > (RAND() & 3)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("The broken door opens."); + + MakeWalkable(); + return; + } + + if(CanBeDestroyed() && Damage > GetStrengthValue()) + { + EditHP(GetStrengthValue() - Damage); + int SV = Max(GetStrengthValue(), 1); + truth LockBreaks = IsLocked() && RAND() % (100 * Damage / SV) >= 100; + + if(LockBreaks) + SetIsLocked(false); + + if(HP <= 0) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("The broken door is completely destroyed."); + + room* Room = GetRoom(); + Break(); + + if(Room) + Room->DestroyTerrain(Villain); + } + else if(LockBreaks && CanBeSeenByPlayer()) + { + ADD_MESSAGE("The broken door's lock is shattered."); + + if(GetRoom()) + GetRoom()->DestroyTerrain(Villain); + } + } +} + +olterraincontainer::~olterraincontainer() +{ + delete Contained; +} + +void olterraincontainer::Break() +{ + GetContained()->MoveItemsTo(GetLSquareUnder()->GetStack()); + olterrain::Break(); +} + +truth fountain::IsDipDestination() const +{ + return SecondaryMaterial && SecondaryMaterial->IsLiquid(); +} + +int door::GetWalkability() const +{ + return Opened ? ANY_MOVE : ANY_MOVE&~(WALK|FLY); +} + +truth door::IsTransparent() const +{ + return Opened || MainMaterial->IsTransparent() || DataBase->IsAlwaysTransparent; +} + +truth liquidterrain::DipInto(item* ToBeDipped, character* Who) +{ + ToBeDipped->DipInto(static_cast(GetMainMaterial()->SpawnMore(100)), Who); + return true; +} + +void earth::PostConstruct() +{ + PictureIndex = RAND() & 3; +} + +void earth::Save(outputfile& SaveFile) const +{ + olterrain::Save(SaveFile); + SaveFile << PictureIndex; +} + +void earth::Load(inputfile& SaveFile) +{ + olterrain::Load(SaveFile); + SaveFile >> PictureIndex; +} + +v2 earth::GetBitmapPos(int I) const +{ + return olterrain::GetBitmapPos(I) + v2(PictureIndex * 48, 0); +} + +void door::BeDestroyed() +{ + olterrain::Break(); +} + +truth fountain::IsFountainWithWater() const +{ + return truth(GetSecondaryMaterial()); +} + +void liquidterrain::SurviveEffect(character* Survivor) +{ + Survivor->GetLSquareUnder()->SpillFluid(Survivor, static_cast(GetMainMaterial()->SpawnMore(1000 + RAND_N(500))), false, false); +} + +monsterportal::monsterportal() +{ + game::SetMonsterPortal(this); +} + +void olterraincontainer::PreProcessForBone() +{ + olterrain::PreProcessForBone(); + Contained->PreProcessForBone(); +} + +void olterraincontainer::PostProcessForBone() +{ + olterrain::PostProcessForBone(); + Contained->PostProcessForBone(); +} + +void olterraincontainer::FinalProcessForBone() +{ + olterrain::FinalProcessForBone(); + Contained->FinalProcessForBone(); +} + +v2 liquidterrain::GetBitmapPos(int F) const +{ + /* gum solution, should come from script */ + return GetBorderBitmapPos(v2(64 + (F >> 1 & 3) * 48, 32 + (F >> 3 & 3) * 48), F); +} + +void liquidterrain::AddLocationDescription(festring& String) const +{ + String << " in the " << GetNameSingular(); +} + +void stairs::AddSpecialCursors() +{ + game::AddSpecialCursor(GetPos(), YELLOW_CURSOR|CURSOR_TARGET); +} + +truth coffin::Open(character* Opener) +{ + if(!Opener->IsPlayer()) + return false; + + + if(!game::TruthQuestion( + CONST_S("Disturbing the dead might not be wise... Continue? [y/N]"))) + return false; + truth Success = olterraincontainer::Open(Opener); + if(Success) + { + game::DoEvilDeed(25); + for(int c = 0; c < RAND_N(10); ++c) + { + v2 Pos = GetLevel()->GetRandomSquare(); + if(Pos != ERROR_V2) + GenerateGhost(GetLevel()->GetLSquare(Pos)); + } + } + return Success; +} + +void coffin::Break() +{ + for(int c = 0; c < 9; ++c) + { + lsquare* Neighbour = GetLSquareUnder()->GetNeighbourLSquare(c); + + if(!RAND_4 && Neighbour && Neighbour->IsFlyable()) + { + GenerateGhost(Neighbour); + } + } + olterraincontainer::Break(); +} + + +void coffin::GenerateGhost(lsquare* Square) +{ + v2 Pos = Square->GetPos(); + character* Char = ghost::Spawn(); + Char->SetTeam(game::GetTeam(MONSTER_TEAM)); + if((!Square->GetRoomIndex() + || !Square->GetRoom()->DontGenerateMonsters())) + { + Char->PutTo(Pos); + Char->SignalGeneration(); + + if(Char->CanBeSeenByPlayer()) + { + ADD_MESSAGE("%s appears.", Char->CHAR_NAME(DEFINITE)); + } + } +} + +void barwall::Break() +{ + if(GetConfig() == BROKEN_BARWALL) + { + olterrain::Break(); + } + else + { + barwall* Temp = barwall::Spawn(BROKEN_BARWALL, NO_MATERIALS); + Temp->InitMaterials(GetMainMaterial()->SpawnMore()); + GetLSquareUnder()->ChangeOLTerrainAndUpdateLights(Temp); + } +} + +void ironmaiden::Save(outputfile& SaveFile) const +{ + olterrain::Save(SaveFile); + SaveFile << Opened; +} + +void ironmaiden::Load(inputfile& SaveFile) +{ + olterrain::Load(SaveFile); + SaveFile >> Opened; +} + +v2 ironmaiden::GetBitmapPos(int) const +{ + return Opened ? v2(48,64) : v2(32,64); +} + +truth ironmaiden::Open(character* Opener) +{ + if(!Opened) + { + truth WasSeenByPlayer = CanBeSeenByPlayer(); + Opened = true; + UpdatePictures(); + GetLSquareUnder()->SendNewDrawRequest(); + GetLSquareUnder()->SendMemorizedUpdateRequest(); + + if(Opener->IsPlayer()) + ADD_MESSAGE("You open %s.", CHAR_NAME(DEFINITE)); + else if(WasSeenByPlayer) + { + if(Opener->CanBeSeenByPlayer()) + ADD_MESSAGE("%s opens %s.", Opener->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("Something opens %s.", CHAR_NAME(DEFINITE)); + } + return true; + } + else + { + if(Opener->IsPlayer()) + ADD_MESSAGE("%s is already open, %s.", CHAR_NAME(DEFINITE), + game::Insult()); + + return false; + } + Opener->DexterityAction(Opener->OpenMultiplier() * 5); + return true; +} + +truth ironmaiden::Close(character* Closer) +{ + if(Closer->IsPlayer()) + if(Opened) + { + ADD_MESSAGE("You close %s.", CHAR_NAME(DEFINITE)); + } + else + { + ADD_MESSAGE("%s is already closed, %s.", CHAR_NAME(DEFINITE), + game::Insult()); + return false; + } + + + Opened = false; + UpdatePictures(); + Closer->DexterityAction(Closer->OpenMultiplier() * 5); + return true; +} + +void coffin::Save(outputfile& SaveFile) const +{ + olterrain::Save(SaveFile); + Contained->Save(SaveFile); +} + +void coffin::Load(inputfile& SaveFile) +{ + olterrain::Load(SaveFile); + Contained->Load(SaveFile); +} + +coffin::coffin() +{ + Contained = new stack(0, this, HIDDEN); +} + +void coffin::SetItemsInside(const fearray >& ItemArray, int SpecialFlags) +{ + GetContained()->Clean(); + + for(uint c1 = 0; c1 < ItemArray.Size; ++c1) + if(ItemArray[c1].IsValid()) + { + const interval* TimesPtr = ItemArray[c1].GetTimes(); + int Times = TimesPtr ? TimesPtr->Randomize() : 1; + + for(int c2 = 0; c2 < Times; ++c2) + { + item* Item = ItemArray[c1].Instantiate(SpecialFlags); + + if(Item) + { + Contained->AddItem(Item); + Item->SpecialGenerationHandler(); + } + } + } +} + +coffin::~coffin() +{ + delete Contained; +} + +void coffin::PreProcessForBone() +{ + olterrain::PreProcessForBone(); + Contained->PreProcessForBone(); +} + +void coffin::PostProcessForBone() +{ + olterrain::PostProcessForBone(); + Contained->PostProcessForBone(); +} + +void coffin::FinalProcessForBone() +{ + olterrain::FinalProcessForBone(); + Contained->FinalProcessForBone(); +} diff --git a/Main/Source/main.cpp b/Main/Source/main.cpp new file mode 100644 index 0000000..881f109 --- /dev/null +++ b/Main/Source/main.cpp @@ -0,0 +1,130 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include + +#ifdef __DJGPP__ +#include +#include +#endif + +#include "game.h" +#include "database.h" +#include "feio.h" +#include "igraph.h" +#include "iconf.h" +#include "whandler.h" +#include "hscore.h" +#include "graphics.h" +#include "script.h" +#include "message.h" +#include "proto.h" + +int Main(int argc, char **argv) +{ + if(argc > 1 && festring(argv[1]) == "--version") + { + std::cout << "Iter Vehemens ad Necem version " << IVAN_VERSION << std::endl; + return 0; + } + +#ifdef __DJGPP__ + + /* Saves numlock state and toggles it off */ + + char ShiftByteState = _farpeekb(_dos_ds, 0x417); + _farpokeb(_dos_ds, 0x417, 0); + +#endif /* __DJGPP__ */ + + femath::SetSeed(time(0)); + game::InitGlobalValueMap(); + scriptsystem::Initialize(); + databasesystem::Initialize(); + game::InitLuxTable(); + ivanconfig::Initialize(); + igraph::Init(); + game::CreateBusyAnimationCache(); + globalwindowhandler::SetQuitMessageHandler(game::HandleQuitMessage); + msgsystem::Init(); + protosystem::Initialize(); + igraph::LoadMenu(); + + for(;;) + { + int Select = iosystem::Menu(igraph::GetMenuGraphic(), + v2(RES.X / 2, RES.Y / 2 - 20), + CONST_S("\r"), + CONST_S("Start Game\rContinue Game\r" + "Configuration\rHighscores\r" + "Quit\r"), + LIGHT_GRAY, + CONST_S("Released under the GNU\r" + "General Public License\r" + "More info: see COPYING\r"), + CONST_S("IVAN v" IVAN_VERSION "\r")); + + switch(Select) + { + case 0: + if(game::Init()) + { + igraph::UnLoadMenu(); + + game::Run(); + game::DeInit(); + igraph::LoadMenu(); + } + + break; + case 1: + { + festring LoadName = iosystem::ContinueMenu(WHITE, LIGHT_GRAY, game::GetSaveDir()); + + if(LoadName.GetSize()) + { + LoadName.Resize(LoadName.GetSize() - 4); + + if(game::Init(LoadName)) + { + igraph::UnLoadMenu(); + game::Run(); + game::DeInit(); + igraph::LoadMenu(); + } + } + + break; + } + case 2: + ivanconfig::Show(); + break; + case 3: + { + highscore HScore; + HScore.Draw(); + break; + } + case 4: + +#ifdef __DJGPP__ + + /* Loads numlock state */ + + _farpokeb(_dos_ds, 0x417, ShiftByteState); + +#endif + + return 0; + } + } +} diff --git a/Main/Source/materia.cpp b/Main/Source/materia.cpp new file mode 100644 index 0000000..b3d92e5 --- /dev/null +++ b/Main/Source/materia.cpp @@ -0,0 +1,468 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through materset.cpp */ + +materialprototype::materialprototype(const materialprototype* Base, + materialspawner Spawner, + materialcloner Cloner, + cchar* ClassID) +: Base(Base), Spawner(Spawner), Cloner(Cloner), ClassID(ClassID) +{ Index = protocontainer::Add(this); } + +long material::GetRawPrice() const +{ return GetPriceModifier() * GetWeight() / 10000; } +truth material::CanBeDug(material* ShovelMaterial) const +{ return ShovelMaterial->GetStrengthValue() > GetStrengthValue(); } +long material::GetTotalExplosivePower() const +{ return long(double(Volume) * GetExplosivePower() / 1000000); } +cchar* material::GetConsumeVerb() const { return "eating"; } + +materialpredicate TrueMaterialPredicate = &material::True; + +void material::AddName(festring& Name, truth Articled, truth Adjective) const +{ + if(Articled) + if(GetNameFlags() & USE_AN) + Name << "an "; + else + Name << "a "; + + Name << (Adjective ? GetAdjectiveStem() : GetNameStem()); +} + +festring material::GetName(truth Articled, truth Adjective) const +{ + static festring Name; + Name.Empty(); + AddName(Name, Articled, Adjective); + return Name; +} + +material* material::TakeDipVolumeAway() +{ + if(Volume > 500) + { + EditVolume(-500); + return SpawnMore(500); + } + else + return MotherEntity->RemoveMaterial(this); +} + +void material::Save(outputfile& SaveFile) const +{ + SaveFile << (ushort)GetType(); + SaveFile << Volume; + SaveFile << (ushort)GetConfig(); +} + +void material::Load(inputfile& SaveFile) +{ + SaveFile >> Volume; + databasecreator::InstallDataBase(this, ReadType(SaveFile)); +} + +truth material::Effect(character* Char, int BodyPart, long Amount) +{ + /* Receivexxx should return truth! */ + + Amount = Amount * GetEffectStrength() / 100; + + if(!Amount) + return false; + + switch(GetEffect()) + { + case EFFECT_POISON: Char->BeginTemporaryState(POISONED, Amount); break; + case EFFECT_DARKNESS: Char->ReceiveDarkness(Amount); break; + case EFFECT_OMMEL_URINE: Char->ReceiveOmmelUrine(Amount); break; + case EFFECT_PEPSI: Char->ReceivePepsi(Amount); break; + case EFFECT_KOBOLD_FLESH: Char->ReceiveKoboldFlesh(Amount); break; + case EFFECT_HEAL: Char->ReceiveHeal(Amount); break; + case EFFECT_LYCANTHROPY: + Char->BeginTemporaryState(LYCANTHROPY, Amount); + break; + case EFFECT_SCHOOL_FOOD: Char->ReceiveSchoolFood(Amount); break; + case EFFECT_ANTIDOTE: Char->ReceiveAntidote(Amount); break; + case EFFECT_CONFUSE: Char->BeginTemporaryState(CONFUSED, Amount); break; + case EFFECT_POLYMORPH: Char->BeginTemporaryState(POLYMORPH, Amount); break; + case EFFECT_ESP: Char->BeginTemporaryState(ESP, Amount); break; + case EFFECT_SKUNK_SMELL: Char->BeginTemporaryState(POISONED, Amount); break; + case EFFECT_MAGIC_MUSHROOM: + { + v2 Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos(); + Char->ActivateRandomState(SRC_MAGIC_MUSHROOM, Amount, + Volume % 250 + Pos.X + Pos.Y + 1); + break; + } + case EFFECT_TRAIN_PERCEPTION: + Char->EditExperience(PERCEPTION, Amount, 1 << 14); + break; + case EFFECT_HOLY_BANANA: Char->ReceiveHolyBanana(Amount); break; + case EFFECT_HOLY_MANGO: Char->ReceiveHolyMango(Amount); break; + case EFFECT_EVIL_WONDER_STAFF_VAPOUR: + { + v2 Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos(); + Char->ActivateRandomState(SRC_EVIL, Amount, + Volume % 250 + Pos.X + Pos.Y + 1); + break; + } + case EFFECT_GOOD_WONDER_STAFF_VAPOUR: + { + v2 Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos(); + Char->ActivateRandomState(SRC_GOOD, Amount, + Volume % 250 + Pos.X + Pos.Y + 1); + break; + } + case EFFECT_PEA_SOUP: Char->ReceivePeaSoup(Amount); break; + case EFFECT_BLACK_UNICORN_FLESH: Char->ReceiveBlackUnicorn(Amount); break; + case EFFECT_GRAY_UNICORN_FLESH: Char->ReceiveGrayUnicorn(Amount); break; + case EFFECT_WHITE_UNICORN_FLESH: Char->ReceiveWhiteUnicorn(Amount); break; + case EFFECT_TELEPORT_CONTROL: Char->BeginTemporaryState(TELEPORT_CONTROL, Amount); break; + case EFFECT_MUSHROOM: + { + v2 Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos(); + Char->ActivateRandomState(SRC_MUSHROOM, Amount, + Volume % 250 + Pos.X + Pos.Y + 1); + break; + } + case EFFECT_OMMEL_CERUMEN: Char->ReceiveOmmelCerumen(Amount); break; + case EFFECT_OMMEL_SWEAT: Char->ReceiveOmmelSweat(Amount); break; + case EFFECT_OMMEL_TEARS: Char->ReceiveOmmelTears(Amount); break; + case EFFECT_OMMEL_SNOT: Char->ReceiveOmmelSnot(Amount); break; + case EFFECT_OMMEL_BONE: Char->ReceiveOmmelBone(Amount); break; + case EFFECT_MUSTARD_GAS: Char->ReceiveMustardGas(BodyPart, Amount); break; + case EFFECT_MUSTARD_GAS_LIQUID: Char->ReceiveMustardGasLiquid(BodyPart, Amount); break; + case EFFECT_PANIC: Char->BeginTemporaryState(PANIC, Amount); break; + case EFFECT_TELEPORT: Char->BeginTemporaryState(TELEPORT, Amount); break; + case EFFECT_VAMPIRISM: + Char->BeginTemporaryState(VAMPIRISM, Amount); + break; + case EFFECT_DETECTING: + Char->BeginTemporaryState(DETECTING, Amount); + break; + default: return false; + } + + return true; +} + +material* material::EatEffect(character* Eater, long Amount) +{ + Amount = Volume > Amount ? Amount : Volume; + Eater->ReceiveNutrition(GetNutritionValue() * Amount / 50); + if(Amount && Volume) + { + if(DisablesPanicWhenConsumed() && Eater->TemporaryStateIsActivated(PANIC)) + { + if(Eater->IsPlayer()) + { + ADD_MESSAGE("You relax a bit."); + } + else if(Eater->CanBeSeenByPlayer()) + { + ADD_MESSAGE("%s relaxes a bit.", Eater->CHAR_NAME(DEFINITE)); + } + Eater->DeActivateTemporaryState(PANIC); + } + } + + if(GetInteractionFlags() & AFFECT_INSIDE) + { + head* Head = Eater->GetVirtualHead(); + long NewAmount = Amount; + + if(Head && Amount >= 8) + { + Head->AddFluid(static_cast(SpawnMore(Amount >> 3)), + CONST_S("throat"), 0, true); + NewAmount -= Amount >> 3; + } + + Eater->GetTorso()->AddFluid(static_cast(SpawnMore(NewAmount)), + CONST_S("stomach"), 0, true); + } + else + { + Effect(Eater, TORSO_INDEX, Amount); + + if(IsLiquid()) + Eater->EditStamina(int(50. * Amount * Eater->GetMaxStamina() + / Eater->GetBodyVolume()), + false); + } + + if(Volume != Amount) + { + EditVolume(-Amount); + return 0; + } + else + return MotherEntity->RemoveMaterial(this); +} + +truth material::HitEffect(character* Enemy, bodypart* BodyPart) +{ + if(!Volume) + return false; + + switch(GetHitMessage()) + { + case HM_SCHOOL_FOOD: Enemy->AddSchoolFoodHitMessage(); break; + case HM_FROG_FLESH: Enemy->AddFrogFleshConsumeEndMessage(); break; + case HM_OMMEL: Enemy->AddOmmelConsumeEndMessage(); break; + case HM_PEPSI: Enemy->AddPepsiConsumeEndMessage(); break; + case HM_KOBOLD_FLESH: Enemy->AddKoboldFleshHitMessage(); break; + case HM_HEALING_LIQUID: Enemy->AddHealingLiquidConsumeEndMessage(); break; + case HM_ANTIDOTE: Enemy->AddAntidoteConsumeEndMessage(); break; + case HM_CONFUSE: Enemy->AddConfuseHitMessage(); break; + case HM_HOLY_BANANA: Enemy->AddHolyBananaConsumeEndMessage(); break; + case HM_HOLY_MANGO: Enemy->AddHolyMangoConsumeEndMessage(); break; + } + + long Amount = Max(GetVolume() >> 1, 1); + truth Success; + + if(GetInteractionFlags() & AFFECT_INSIDE) + { + if(BodyPart) + { + BodyPart->AddFluid(static_cast(SpawnMore(Amount)), + CONST_S(""), 0, true); + Success = true; + } + else + Success = false; + } + else + { + int BPIndex = BodyPart ? BodyPart->GetBodyPartIndex() : NONE_INDEX; + Success = Effect(Enemy, BPIndex, Amount); + } + + if(Amount != Volume) + EditVolume(-Amount); + else + delete MotherEntity->RemoveMaterial(this); + + return Success; +} + +void material::AddConsumeEndMessage(character* Eater) const +{ + switch(GetConsumeEndMessage()) + { + case CEM_SCHOOL_FOOD: Eater->AddSchoolFoodConsumeEndMessage(); break; + case CEM_BONE: Eater->AddBoneConsumeEndMessage(); break; + case CEM_FROG_FLESH: Eater->AddFrogFleshConsumeEndMessage(); break; + case CEM_OMMEL: Eater->AddOmmelConsumeEndMessage(); break; + case CEM_PEPSI: Eater->AddPepsiConsumeEndMessage(); break; + case CEM_KOBOLD_FLESH: Eater->AddKoboldFleshConsumeEndMessage(); break; + case CEM_HEALING_LIQUID: Eater->AddHealingLiquidConsumeEndMessage(); break; + case CEM_ANTIDOTE: Eater->AddAntidoteConsumeEndMessage(); break; + case CEM_ESP: Eater->AddESPConsumeMessage(); break; + case CEM_HOLY_BANANA: Eater->AddHolyBananaConsumeEndMessage(); break; + case CEM_PEA_SOUP: Eater->AddPeaSoupConsumeEndMessage(); break; + case CEM_BLACK_UNICORN_FLESH: + Eater->AddBlackUnicornConsumeEndMessage(); + break; + case CEM_GRAY_UNICORN_FLESH: + Eater->AddGrayUnicornConsumeEndMessage(); + break; + case CEM_WHITE_UNICORN_FLESH: + Eater->AddWhiteUnicornConsumeEndMessage(); + break; + case CEM_OMMEL_BONE: Eater->AddOmmelBoneConsumeEndMessage(); break; + case CEM_LIQUID_HORROR: Eater->AddLiquidHorrorConsumeEndMessage(); break; + case CEM_HOLY_MANGO: Eater->AddHolyMangoConsumeEndMessage(); break; + } +} + +material* materialprototype::SpawnAndLoad(inputfile& SaveFile) const +{ + material* Material = Spawner(0, 0, true); + Material->Load(SaveFile); + return Material; +} + +material* material::MakeMaterial(int Config, long Volume) +{ + if(!Config) + return 0; + + switch(Config >> 12) + { + case SOLID_ID >> 12: return solid::Spawn(Config, Volume); + case ORGANIC_ID >> 12: return organic::Spawn(Config, Volume); + case GAS_ID >> 12: return gas::Spawn(Config, Volume); + case LIQUID_ID >> 12: return liquid::Spawn(Config, Volume); + case FLESH_ID >> 12: return flesh::Spawn(Config, Volume); + case POWDER_ID >> 12: return powder::Spawn(Config, Volume); + case IRON_ALLOY_ID >> 12: return ironalloy::Spawn(Config, Volume); + } + + ABORT("Odd material configuration number %d of volume %ld requested!", + Config, Volume); + return 0; +} + +void material::SetVolume(long What) +{ + Volume = What; + + if(MotherEntity) + MotherEntity->SignalVolumeAndWeightChange(); +} + +void material::Initialize(int NewConfig, long InitVolume, truth Load) +{ + if(!Load) + { + databasecreator::InstallDataBase(this, NewConfig); + Volume = InitVolume; + PostConstruct(); + } +} + +long material::GetTotalNutritionValue() const +{ + return GetNutritionValue() * GetVolume() / 50; +} + +truth material::CanBeEatenByAI(ccharacter* Eater) const +{ + return ((Eater->GetAttribute(WISDOM) < GetConsumeWisdomLimit() + || (Eater->IsAlcoholic() && (GetCategoryFlags() & IS_BEVERAGE))) + && !GetSpoilLevel() && !Eater->CheckCannibalism(this)); +} + +truth material::BreatheEffect(character* Enemy) +{ + return Effect(Enemy, TORSO_INDEX, Max(GetVolume() / 10, 50)); +} + +const materialdatabase* material::GetDataBase(int Config) +{ + const prototype* Proto = 0; + + switch(Config >> 12) + { + case SOLID_ID >> 12: Proto = &solid::ProtoType; break; + case ORGANIC_ID >> 12: Proto = &organic::ProtoType; break; + case GAS_ID >> 12: Proto = &gas::ProtoType; break; + case LIQUID_ID >> 12: Proto = &liquid::ProtoType; break; + case FLESH_ID >> 12: Proto = &flesh::ProtoType; break; + case POWDER_ID >> 12: Proto = &powder::ProtoType; break; + case IRON_ALLOY_ID >> 12: Proto = &ironalloy::ProtoType; break; + } + + const database* DataBase; + databasecreator::FindDataBase(DataBase, Proto, Config); + + if(DataBase) + return DataBase; + + ABORT("Odd material configuration number %d requested!", Config); + return 0; +} + +void material::FinishConsuming(character* Consumer) +{ + if(!Consumer->IsPlayer() && GetConsumeWisdomLimit() != NO_LIMIT) + Consumer->EditExperience(WISDOM, 150, 1 << 13); /** C **/ + + AddConsumeEndMessage(Consumer); +} + +void materialdatabase::InitDefaults(const materialprototype* NewProtoType, int NewConfig) +{ + ProtoType = NewProtoType; + DigProductMaterial = Config = NewConfig; + CommonFlags |= IS_ABSTRACT; // dummy value for configcontainer +} + +item* material::CreateNaturalForm(int Config, long Volume) +{ + item* Item = GetDataBase(Config)->NaturalForm.Instantiate(NO_MATERIALS + |NO_PIC_UPDATE); + Item->InitMaterials(MAKE_MATERIAL(Config, Volume)); + return Item; +} + +item* material::CreateNaturalForm(long Volume) const +{ + item* Item = GetNaturalForm().Instantiate(NO_MATERIALS|NO_PIC_UPDATE); + Item->InitMaterials(SpawnMore(Volume)); + return Item; +} + +int material::GetHardenedMaterial(citem* Item) const +{ + const materialdatabase* DB = DataBase; + + if(!Item->FlexibilityIsEssential()) + return DB->HardenedMaterial; + + while(DB->HardenedMaterial != NONE) + { + DB = material::GetDataBase(DB->HardenedMaterial); + + if(DataBase->Flexibility <= DB->Flexibility) + return DB->Config; + } + + return DB->HardenedMaterial; +} + +int material::GetSoftenedMaterial(citem* Item) const +{ + const materialdatabase* DB = DataBase; + + if(!Item->FlexibilityIsEssential()) + return DB->SoftenedMaterial; + + while(DB->SoftenedMaterial != NONE) + { + DB = material::GetDataBase(DB->SoftenedMaterial); + + if(DataBase->Flexibility <= DB->Flexibility) + return DB->Config; + } + + return DB->SoftenedMaterial; +} + +int material::GetHardenModifier(citem* Item) const +{ + int M = GetFlexibility() << 2; + + if(!Item || !Item->FlexibilityIsEssential()) + M += GetStrengthValue(); + + return M; +} + +truth material::IsExplosive() const +{ + return DataBase->InteractionFlags & CAN_EXPLODE; +} + +truth material::IsSparkling() const +{ + return DataBase->CategoryFlags & IS_SPARKLING; +} + +truth material::IsStuckTo(ccharacter* Char) const +{ + return MotherEntity->IsStuckTo(Char); +} diff --git a/Main/Source/materias.cpp b/Main/Source/materias.cpp new file mode 100644 index 0000000..050d409 --- /dev/null +++ b/Main/Source/materias.cpp @@ -0,0 +1,303 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through materset.cpp */ + +void organic::ResetSpoiling() { SpoilCounter = SpoilLevel = 0; } + +cchar* liquid::GetConsumeVerb() const { return "drinking"; } + +truth powder::IsExplosive() const { return !Wetness && material::IsExplosive(); } + +truth ironalloy::IsSparkling() const { return material::IsSparkling() && GetRustLevel() == NOT_RUSTED; } + +void organic::Be(ulong Flags) +{ + if(SpoilCheckCounter++ >= 50) + { + if(MotherEntity->AllowSpoil()) + { + if(Flags & HASTE) + SpoilCounter += 125; + else if(Flags & SLOW) + SpoilCounter += 5; + else + SpoilCounter += 25; + + if(SpoilCounter < GetSpoilModifier()) + { + if(SpoilCounter << 1 >= GetSpoilModifier()) + { + int NewSpoilLevel = ((SpoilCounter << 4) / GetSpoilModifier()) - 7; + + if(NewSpoilLevel != SpoilLevel) + { + SpoilLevel = NewSpoilLevel; + MotherEntity->SignalSpoilLevelChange(this); + } + } + } + else + { + SpoilLevel = 8; + MotherEntity->SignalSpoil(this); + } + } + + SpoilCheckCounter = 0; + } +} + +void organic::Save(outputfile& SaveFile) const +{ + material::Save(SaveFile); + SaveFile << SpoilCounter << SpoilCheckCounter << SpoilLevel; +} + +void organic::Load(inputfile& SaveFile) +{ + material::Load(SaveFile); + SaveFile >> SpoilCounter >> SpoilCheckCounter >> SpoilLevel; +} + +void organic::PostConstruct() +{ + SpoilLevel = SpoilCheckCounter = 0; + SpoilCounter = (RAND() % GetSpoilModifier()) >> 5; +} + +void flesh::PostConstruct() +{ + organic::PostConstruct(); + SkinColorSparkling = InfectedByLeprosy = false; + SkinColor = GetColor(); +} + +void flesh::Save(outputfile& SaveFile) const +{ + organic::Save(SaveFile); + SaveFile << SkinColor << SkinColorSparkling << InfectedByLeprosy; +} + +void flesh::Load(inputfile& SaveFile) +{ + organic::Load(SaveFile); + SaveFile >> SkinColor >> SkinColorSparkling >> InfectedByLeprosy; +} + +void powder::Be(ulong) +{ + if(Wetness > 0) + --Wetness; +} + +void powder::Save(outputfile& SaveFile) const +{ + material::Save(SaveFile); + SaveFile << Wetness; +} + +void powder::Load(inputfile& SaveFile) +{ + material::Load(SaveFile); + SaveFile >> Wetness; +} + +material* organic::EatEffect(character* Eater, long Amount) +{ + Amount = Volume > Amount ? Amount : Volume; + GetMotherEntity()->SpecialEatEffect(Eater, Amount); + Effect(Eater, TORSO_INDEX, Amount); + Eater->ReceiveNutrition(GetNutritionValue() * Amount * 20 / (1000 * (GetSpoilLevel() + 1))); + + if(IsInfectedByLeprosy() && Amount && !RAND_N(25000 / Amount)) + Eater->GainIntrinsic(LEPROSY); + + if(GetSpoilLevel() > 0) + { + Eater->BeginTemporaryState(CONFUSED, int(Amount * GetSpoilLevel() * sqrt(GetNutritionValue()) / 1000)); + + if(GetBodyFlags() & CAN_HAVE_PARASITE + && !(RAND() % (250 / GetSpoilLevel()))) + Eater->GainIntrinsic(PARASITIZED); + } + + if(GetSpoilLevel() > 4) + Eater->BeginTemporaryState(POISONED, int(Amount * (GetSpoilLevel() - 4) * sqrt(GetNutritionValue()) / 1000)); + + if(Volume != Amount) + { + EditVolume(-Amount); + return 0; + } + else + return MotherEntity->RemoveMaterial(this); +} + +void organic::AddConsumeEndMessage(character* Eater) const +{ + if(Eater->IsPlayer()) + { + if(GetSpoilLevel() > 0 && GetSpoilLevel() <= 4) + ADD_MESSAGE("Ugh. This stuff was slightly spoiled."); + else if(GetSpoilLevel() > 4) + ADD_MESSAGE("Ugh. This stuff was terribly spoiled!"); + } + + material::AddConsumeEndMessage(Eater); +} + +void organic::SetSpoilCounter(int What) +{ + SpoilCounter = What; + + if(SpoilCounter < GetSpoilModifier()) + { + if(SpoilCounter << 1 >= GetSpoilModifier()) + { + int NewSpoilLevel = ((SpoilCounter << 4) / GetSpoilModifier()) - 7; + + if(NewSpoilLevel != SpoilLevel) + { + SpoilLevel = NewSpoilLevel; + MotherEntity->SignalSpoilLevelChange(this); + } + } + } + else + MotherEntity->SignalSpoil(this); +} + +void ironalloy::SetRustLevel(int What) +{ + if(GetRustLevel() != What) + { + if(!RustData) + RustData = RAND() & 0xFC | What; + else if(!What) + RustData = 0; + else + RustData = RustData & 0xFC | What; + + if(MotherEntity) + MotherEntity->SignalRustLevelChange(); + } +} + +int ironalloy::GetStrengthValue() const +{ + int Base = material::GetStrengthValue(); + + switch(GetRustLevel()) + { + case NOT_RUSTED: return Base; + case SLIGHTLY_RUSTED: return ((Base << 3) + Base) / 10; + case RUSTED: return ((Base << 1) + Base) >> 2; + case VERY_RUSTED: return Base >> 1; + } + + return 0; /* not possible */ +} + +truth ironalloy::AddRustLevelDescription(festring& Name, truth Articled) const +{ + if(GetRustLevel() == NOT_RUSTED) + return false; + + if(Articled) + Name << "a "; + + switch(GetRustLevel()) + { + case SLIGHTLY_RUSTED: + Name << "slightly rusted "; + break; + case RUSTED: + Name << "rusted "; + break; + case VERY_RUSTED: + Name << "very rusted "; + break; + } + + return true; +} + +void ironalloy::Save(outputfile& SaveFile) const +{ + material::Save(SaveFile); + SaveFile << RustData; +} + +void ironalloy::Load(inputfile& SaveFile) +{ + material::Load(SaveFile); + SaveFile >> RustData; +} + +void liquid::TouchEffect(item* Item, cfestring& LocationName) +{ + if(GetRustModifier()) + Item->TryToRust(GetRustModifier() * GetVolume()); + + if(GetAcidicity()) + Item->ReceiveAcid(this, LocationName, Volume * GetAcidicity()); + + character* Char = Item->GetBodyPartMaster(); + + if(Char) + Effect(Char, Item->GetBodyPartIndex(), Volume >> 6); +} + +void liquid::TouchEffect(lterrain* Terrain) +{ + if(GetRustModifier()) + Terrain->TryToRust(GetRustModifier() * GetVolume()); + + if(GetAcidicity()) + Terrain->ReceiveAcid(this, Volume * GetAcidicity()); +} + +void liquid::TouchEffect(character* Char, int BodyPartIndex) +{ + if(GetRustModifier()) + Char->GetBodyPart(BodyPartIndex)->TryToRust(GetRustModifier() * GetVolume()); + + if(Char->IsEnabled() && GetAcidicity()) + Char->GetBodyPart(BodyPartIndex)->ReceiveAcid(this, CONST_S(""), Volume * GetAcidicity() >> 1); + + if(Char->IsEnabled()) + Effect(Char, BodyPartIndex, Volume >> 9); +} + +/* Doesn't do the actual rusting, just returns whether it should happen */ + +truth ironalloy::TryToRust(long Modifier, long Volume) +{ + if(GetRustLevel() != VERY_RUSTED) + { + if(!Volume) + Volume = GetVolume(); + + long Chance = long(30000000. * sqrt(Volume) / (Modifier * GetRustModifier())); + + if(Chance <= 1 || !(RAND() % Chance)) + return true; + } + + return false; +} + +int organic::GetSpoilPercentage() const +{ + return 100 * SpoilCounter / GetSpoilModifier(); +} diff --git a/Main/Source/materset.cpp b/Main/Source/materset.cpp new file mode 100644 index 0000000..848cde6 --- /dev/null +++ b/Main/Source/materset.cpp @@ -0,0 +1,41 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#define __FILE_OF_STATIC_MATERIAL_PROTOTYPE_DEFINITIONS__ + +#include "proto.h" +#include "materia.h" +#include "database.h" + +EXTENDED_SYSTEM_SPECIALIZATIONS(material)(0, 0, 0, "material"); + +#include "materias.h" + +#undef __FILE_OF_STATIC_MATERIAL_PROTOTYPE_DEFINITIONS__ + +#include "char.h" +#include "confdef.h" +#include "message.h" +#include "save.h" +#include "fluid.h" +#include "smoke.h" +#include "bitmap.h" +#include "game.h" +#include "rawbit.h" +#include "whandler.h" +#include "rain.h" + +#include "materia.cpp" +#include "materias.cpp" +#include "fluid.cpp" +#include "smoke.cpp" +#include "rain.cpp" diff --git a/Main/Source/message.cpp b/Main/Source/message.cpp new file mode 100644 index 0000000..5deba70 --- /dev/null +++ b/Main/Source/message.cpp @@ -0,0 +1,228 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include +#include + +#include "message.h" +#include "festring.h" +#include "felist.h" +#include "game.h" +#include "graphics.h" +#include "save.h" +#include "bitmap.h" +#include "igraph.h" + +felist msgsystem::MessageHistory(CONST_S("Message history"), WHITE, 128); +festring msgsystem::LastMessage; +festring msgsystem::BigMessage; +int msgsystem::Times; +v2 msgsystem::Begin, msgsystem::End; +truth msgsystem::Enabled = true; +truth msgsystem::BigMessageMode = false; +truth msgsystem::MessagesChanged = true; +bitmap* msgsystem::QuickDrawCache = 0; +int msgsystem::LastMessageLines; + +void msgsystem::AddMessage(cchar* Format, ...) +{ + if(!Enabled) + return; + + if(BigMessageMode && BigMessage.GetSize() >= 512) + LeaveBigMessageMode(); + + char Message[1024]; + + va_list AP; + va_start(AP, Format); + vsprintf(Message, Format, AP); + va_end(AP); + + festring Buffer(Message); + + if(!Buffer.GetSize()) + ABORT("Empty message request!"); + + Buffer.Capitalize(); + + /* Comment the first line and uncomment the second before the release! */ + + if(isalpha(Buffer[Buffer.GetSize() - 1])) + //Buffer << " (this sentence isn't terminated correctly because Hex doesn't know grammar rules)"; + Buffer << '.'; + + if(BigMessageMode) + { + if(BigMessage.GetSize()) + BigMessage << ' '; + + BigMessage << Buffer; + return; + } + + ivantime Time; + game::GetTime(Time); + + if(Buffer == LastMessage) + { + for(int c = 0; c < LastMessageLines; ++c) + MessageHistory.Pop(); + + ++Times; + End = v2(Time.Hour, Time.Min); + } + else + { + Times = 1; + Begin = End = v2(Time.Hour, Time.Min); + LastMessage = Buffer; + LastMessage.EnsureOwnsData(); + } + + festring Temp; + Temp << Begin.X << ':'; + + if(Begin.Y < 10) + Temp << '0'; + + Temp << Begin.Y; + + if(Begin != End) + { + Temp << '-' << End.X << ':'; + + if(End.Y < 10) + Temp << '0'; + + Temp << End.Y; + } + + if(Times != 1) + Temp << " (" << Times << "x)"; + + Temp << ' '; + int Marginal = Temp.GetSize(); + Temp << Buffer; + + std::vector Chapter; + festring::SplitString(Temp, Chapter, 78, Marginal); + + for(uint c = 0; c < Chapter.size(); ++c) + MessageHistory.AddEntry(Chapter[c], WHITE); + + MessageHistory.SetSelected(MessageHistory.GetLastEntryIndex()); + LastMessageLines = Chapter.size(); + MessagesChanged = true; +} + +void msgsystem::Draw() +{ + truth WasInBigMessageMode = BigMessageMode; + LeaveBigMessageMode(); + + if(MessagesChanged) + { + MessageHistory.QuickDraw(QuickDrawCache, 8); + MessagesChanged = false; + } + + v2 Size = QuickDrawCache->GetSize(); + int Y = RES.Y - 122; + blitdata B = { DOUBLE_BUFFER, + { 0, 0 }, + { 13, Y }, + { Size.X, Size.Y }, + { 0 }, + 0, + 0 }; + + QuickDrawCache->NormalBlit(B); + igraph::BlitBackGround(v2(13, Y), v2(1, 1)); + igraph::BlitBackGround(v2(12 + Size.X, Y), v2(1, 1)); + igraph::BlitBackGround(v2(13, Y + Size.Y - 1), v2(1, 1)); + igraph::BlitBackGround(v2(12 + Size.X, Y + Size.Y - 1), v2(1, 1)); + + if(WasInBigMessageMode) + EnterBigMessageMode(); +} + +void msgsystem::DrawMessageHistory() +{ + MessageHistory.Draw(); +} + +void msgsystem::Format() +{ + MessageHistory.Empty(); + LastMessage.Empty(); + MessagesChanged = true; + BigMessageMode = false; +} + +void msgsystem::Save(outputfile& SaveFile) +{ + MessageHistory.Save(SaveFile); + SaveFile << LastMessage << Times << Begin << End; +} + +void msgsystem::Load(inputfile& SaveFile) +{ + MessageHistory.Load(SaveFile); + SaveFile >> LastMessage >> Times >> Begin >> End; +} + +void msgsystem::ScrollDown() +{ + if(MessageHistory.GetSelected() < MessageHistory.GetLastEntryIndex()) + { + MessageHistory.EditSelected(1); + MessagesChanged = true; + } +} + +void msgsystem::ScrollUp() +{ + if(MessageHistory.GetSelected()) + { + MessageHistory.EditSelected(-1); + MessagesChanged = true; + } +} + +void msgsystem::LeaveBigMessageMode() +{ + BigMessageMode = false; + + if(BigMessage.GetSize()) + { + AddMessage("%s", BigMessage.CStr()); + BigMessage.Empty(); + } +} + +void msgsystem::Init() +{ + QuickDrawCache = new bitmap(v2((game::GetScreenXSize() << 4) + 6, 106)); + QuickDrawCache->ActivateFastFlag(); + game::SetStandardListAttributes(MessageHistory); + MessageHistory.AddFlags(INVERSE_MODE); +} + +void msgsystem::ThyMessagesAreNowOld() +{ + if(MessageHistory.GetColor(MessageHistory.GetLastEntryIndex()) == WHITE) + MessagesChanged = true; + + for(uint c = 0; c < MessageHistory.GetLength(); ++c) + MessageHistory.SetColor(c, LIGHT_GRAY); +} diff --git a/Main/Source/miscitem.cpp b/Main/Source/miscitem.cpp new file mode 100644 index 0000000..0aa8894 --- /dev/null +++ b/Main/Source/miscitem.cpp @@ -0,0 +1,3500 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through itemset.cpp */ + +void materialcontainer::SetSecondaryMaterial(material* What, int SpecialFlags) { SetMaterial(SecondaryMaterial, What, GetDefaultSecondaryVolume(), SpecialFlags); } +void materialcontainer::ChangeSecondaryMaterial(material* What, int SpecialFlags) { ChangeMaterial(SecondaryMaterial, What, GetDefaultSecondaryVolume(), SpecialFlags); } +void materialcontainer::InitMaterials(material* M1, material* M2, truth CUP) { ObjectInitMaterials(MainMaterial, M1, GetDefaultMainVolume(), SecondaryMaterial, M2, GetDefaultSecondaryVolume(), CUP); } +void materialcontainer::InitMaterials(const materialscript* M, const materialscript* C, truth CUP) { InitMaterials(M->Instantiate(), C->Instantiate(), CUP); } + +int holybanana::GetSpecialFlags() const { return ST_FLAME_1; } + +int holymango::GetSpecialFlags() const { return ST_FLAME_1; } + +col16 ullrbone::GetOutlineColor(int) const { return MakeRGB16(210, 210, 210); } + +col16 mangoseedling::GetOutlineColor(int) const { return MakeRGB16(118, 158, 226); } + +col16 lantern::GetMaterialColorA(int) const { return MakeRGB16(255, 255, 240); } +col16 lantern::GetMaterialColorB(int) const { return MakeRGB16(255, 255, 100); } +col16 lantern::GetMaterialColorC(int) const { return MakeRGB16(255, 255, 100); } +col16 lantern::GetMaterialColorD(int) const { return MakeRGB16(255, 255, 100); } + +truth can::AddAdjective(festring& String, truth Articled) const { return AddEmptyAdjective(String, Articled); } +v2 can::GetBitmapPos(int) const { return v2(16, SecondaryMaterial ? 288 : 304); } +truth can::IsDipDestination(ccharacter*) const { return SecondaryMaterial && SecondaryMaterial->IsLiquid(); } + +truth potion::IsExplosive() const { return GetSecondaryMaterial() && GetSecondaryMaterial()->IsExplosive(); } +truth potion::AddAdjective(festring& String, truth Articled) const { return AddEmptyAdjective(String, Articled); } +truth potion::EffectIsGood() const { return GetSecondaryMaterial() && GetSecondaryMaterial()->GetInteractionFlags() & EFFECT_IS_GOOD; } +truth potion::IsDipDestination(ccharacter*) const { return SecondaryMaterial && SecondaryMaterial->IsLiquid(); } + +truth kleinbottle::AddAdjective(festring& String, truth Articled) const { return AddEmptyAdjective(String, Articled); } + +truth cauldron::IsExplosive() const { return GetSecondaryMaterial() && GetSecondaryMaterial()->IsExplosive(); } +truth cauldron::AddAdjective(festring& String, truth Articled) const { return AddEmptyAdjective(String, Articled); } +truth cauldron::EffectIsGood() const { return GetSecondaryMaterial() && GetSecondaryMaterial()->GetInteractionFlags() & EFFECT_IS_GOOD; } +truth cauldron::IsDipDestination(ccharacter*) const { return SecondaryMaterial && SecondaryMaterial->IsLiquid(); } + +truth bananapeels::IsDangerous(ccharacter* Stepper) const { return Stepper->HasALeg(); } + +truth brokenbottle::IsDangerous(ccharacter* Stepper) const { return Stepper->HasALeg(); } + +long wand::GetPrice() const { return Charges > TimesUsed ? item::GetPrice() : 0; } + +truth backpack::IsExplosive() const { return GetSecondaryMaterial() && GetSecondaryMaterial()->IsExplosive(); } +long backpack::GetTotalExplosivePower() const { return GetSecondaryMaterial() ? GetSecondaryMaterial()->GetTotalExplosivePower() : 0; } + +long stone::GetTruePrice() const { return item::GetTruePrice() << 1; } + +col16 whistle::GetMaterialColorB(int) const { return MakeRGB16(80, 32, 16); } + +col16 itemcontainer::GetMaterialColorB(int) const { return MakeRGB16(80, 80, 80); } + +truth mine::AddAdjective(festring& String, truth Articled) const { return IsActive() && AddActiveAdjective(String, Articled); } + +truth beartrap::AddAdjective(festring& String, truth Articled) const { return (IsActive() && AddActiveAdjective(String, Articled)) || (!IsActive() && item::AddAdjective(String, Articled)); } + +col16 carrot::GetMaterialColorB(int) const { return MakeRGB16(80, 100, 16); } + +col16 charmlyre::GetMaterialColorB(int) const { return MakeRGB16(150, 130, 110); } + +int solstone::GetClassAnimationFrames() const { return !IsBroken() ? 128 : 1; } + +truth scroll::CanBeRead(character* Reader) const +{ + return Reader->CanRead() || game::GetSeeWholeMapCheatMode(); +} + +truth locationmap::CanBeRead(character* Reader) const +{ + return Reader->CanRead() || game::GetSeeWholeMapCheatMode(); +} + +void scrollofteleportation::FinishReading(character* Reader) +{ + if(!Reader->IsPlayer() && Reader->CanBeSeenByPlayer()) + ADD_MESSAGE("%s disappears!", Reader->CHAR_NAME(DEFINITE)); + + Reader->TeleportRandomly(true); + + if(Reader->IsPlayer()) + ADD_MESSAGE("Suddenly you realize you have teleported."); + + RemoveFromSlot(); + SendToHell(); + Reader->EditExperience(INTELLIGENCE, 150, 1 << 12); +} + +void scrolloffireballs::FinishReading(character* Reader) +{ + v2 FireBallPos = ERROR_V2; + + beamdata Beam + ( + Reader, + CONST_S("killed by the spells of ") + Reader->GetName(INDEFINITE), + YOURSELF, + 0 + ); + + ADD_MESSAGE("This could be loud..."); + + v2 Input = game::PositionQuestion(CONST_S("Where do you wish to send the fireball? [direction keys move cursor, space accepts]"), Reader->GetPos(), &game::TeleportHandler, 0, false); + + if(Input == ERROR_V2) // esc pressed + { + ADD_MESSAGE("You choose not to summon a fireball... phew!"); + return; + //Input = Reader->GetPos(); /*That would kill us, precious*/ + } + + lsquare* Square = GetNearLSquare(Input); + + if((Input - Reader->GetPos()).GetLengthSquare() <= Reader->GetTeleportRangeSquare()) + { + if(!Reader->IsFreeForMe(Square) || (Input == Reader->GetPos())) + { + //Reader->EditExperience(INTELLIGENCE, 100, 1 << 10); + FireBallPos = Input; + } + else + { + ADD_MESSAGE("The spell must target creatures, %s.", game::Insult()); + //FireBallPos = Reader->GetPos(); /*Better not. It's a little bit wtf for the player to understand*/ + return; + } + } + else + { + ADD_MESSAGE("You cannot concentrate yourself enough to send a fireball that far."); + //FireBallPos = ERROR_V2; + /* + if(Reader->GetNearestEnemy()) + FireBallPos = Reader->GetNearestEnemy()->GetPos(); + else*/ + if(!(RAND() % 3)) + { + ADD_MESSAGE("Something very wrong happens with the spell."); + FireBallPos = Reader->GetPos(); + Square = GetNearLSquare(Reader->GetPos()); + } + else + return; + } + + if(FireBallPos == ERROR_V2) + Square = GetNearLSquare(GetLevel()->GetRandomSquare(Reader)); + + if(Square->GetPos() == Reader->GetPos()) + { + ADD_MESSAGE("The scroll explodes in your face!"); + //Square->DrawParticles(RED); + //Square->FireBall(Beam); + //if(Reader->GetAction() && Reader->GetAction()->IsVoluntary()) + // Reader->GetAction()->Terminate(false); + //RemoveFromSlot(); + //SendToHell(); + //Reader->EditExperience(INTELLIGENCE, 150, 1 << 12); + //return; + } + else//(FireBallPos != ERROR_V2) + { + ADD_MESSAGE("A mighty fireball is called into existence."); + } + + RemoveFromSlot(); + SendToHell(); + Reader->EditExperience(INTELLIGENCE, 150, 1 << 12); + Square->DrawParticles(RED); + Square->FireBall(Beam); +} + +void maptotombofxinroch::FinishReading(character* Reader) +{ + if(!Reader->IsPlayer() && Reader->CanBeSeenByPlayer()) + ADD_MESSAGE("%s disappears!", Reader->CHAR_NAME(DEFINITE)); + + if(Reader->IsPlayer()) + { + //game::RevealXinrochTomb(); + game::LoadWorldMap(); + v2 XinrochTombPos = game::GetWorldMap()->GetEntryPos(0, XINROCH_TOMB); + game::GetWorldMap()->GetWSquare(XinrochTombPos)->ChangeOWTerrain(xinrochtomb::Spawn()); + game::GetWorldMap()->RevealEnvironment(XinrochTombPos, 1); + game::SaveWorldMap(); + ADD_MESSAGE("The map reveals to you the location of the Tomb of Xinroch and burns."); + } + + RemoveFromSlot(); + SendToHell(); + Reader->EditExperience(INTELLIGENCE, 150, 1 << 12); +} + +truth wand::Apply(character* Terrorist) +{ + if(Terrorist->IsPlayer() && !game::TruthQuestion(CONST_S("Are you sure you want to break ") + GetName(DEFINITE) + "? [y/N]")) + return false; + + if(Terrorist->IsPlayer()) + ADD_MESSAGE("You bend %s with all your strength.", CHAR_NAME(DEFINITE)); + else if(Terrorist->CanBeSeenByPlayer()) + ADD_MESSAGE("%s bends %s with all %s strength.", Terrorist->CHAR_NAME(DEFINITE), CHAR_NAME(INDEFINITE), Terrorist->CHAR_POSSESSIVE_PRONOUN); + + if(Terrorist->IsPlayer() || Terrorist->CanBeSeenByPlayer()) + ADD_MESSAGE("%s %s.", CHAR_NAME(DEFINITE), GetBreakMsg().CStr()); + + BreakEffect(Terrorist, CONST_S("killed by ") + GetName(INDEFINITE) + " broken @bk"); + Terrorist->DexterityAction(5); + return true; +} + +void wand::Save(outputfile& SaveFile) const +{ + item::Save(SaveFile); + SaveFile << TimesUsed << Charges; +} + +void wand::Load(inputfile& SaveFile) +{ + item::Load(SaveFile); + SaveFile >> TimesUsed >> Charges; +} + +void scrollofwishing::FinishReading(character* Reader) +{ + game::Wish(Reader, + "%s appears from nothing and the scroll burns!", + "Two %s appear from nothing and the scroll burns!"); + + RemoveFromSlot(); + SendToHell(); + Reader->EditExperience(INTELLIGENCE, 600, 1 << 12); +} + +void scrollofchangematerial::FinishReading(character* Reader) +{ + for(;;) + { + itemvector Item; + + if(!Reader->SelectFromPossessions(Item, CONST_S("What item do you wish to change?"), NO_MULTI_SELECT|SELECT_PAIR, &item::MaterialIsChangeable)) + { + ADD_MESSAGE("You notice you haven't got anything to alter."); + return; + } + + if(Item.empty()) + if(game::TruthQuestion(CONST_S("Really cancel read? [y/N]"))) + return; + else + continue; + + if(!Item[0]->IsMaterialChangeable()) + { + ADD_MESSAGE("You cast the spell, but the magic is not powerful enough to affect %s!", Item[0]->CHAR_NAME(DEFINITE|(Item.size() == 1 ? 0 : PLURAL))); + break; + } + + if(Item[0]->HandleInPairs() && Item.size() == 1) + { + ADD_MESSAGE("Only one %s will be altered.", Item[0]->CHAR_NAME(UNARTICLED)); + + if(!game::TruthQuestion(CONST_S("Still continue? [y/N]"))) + continue; + } + + festring Temp = game::DefaultQuestion(CONST_S("What material do you want to wish for?"), + game::GetDefaultChangeMaterial()); + material* TempMaterial = protosystem::CreateMaterial(Temp); + + if(!TempMaterial) + { + game::DrawEverythingNoBlit(); + continue; + } + + msgsystem::EnterBigMessageMode(); + + if(Item.size() == 1) + ADD_MESSAGE("Suddenly your %s is consumed in roaring magical flames.", Item[0]->CHAR_NAME(UNARTICLED)); + else + ADD_MESSAGE("Suddenly your %s are consumed in roaring magical flames.", Item[0]->CHAR_NAME(PLURAL)); + + if(TempMaterial->GetIntelligenceRequirement() + 5 > Reader->GetAttribute(INTELLIGENCE) && !game::WizardModeIsActive()) + { + ADD_MESSAGE("But your mind is not yet strong enough to summon enough %s for the change.", TempMaterial->GetName(false, false).CStr()); + delete TempMaterial; + msgsystem::LeaveBigMessageMode(); + continue; + } + + material* MainMaterial = Item[0]->GetMainMaterial(); + material* SecondaryMaterial = Item[0]->GetSecondaryMaterial(); + + if(Item.size() == 1) + { + if(!MainMaterial->IsSameAs(TempMaterial)) + { + ADD_MESSAGE("As the fire dies out it looks greatly altered."); + + if(SecondaryMaterial && SecondaryMaterial->IsSameAs(MainMaterial)) + Item[0]->ChangeSecondaryMaterial(TempMaterial->SpawnMore()); + + Item[0]->ChangeMainMaterial(TempMaterial); + } + else + ADD_MESSAGE("As the fire dies out it looks unchanged."); + } + else + { + if(!MainMaterial->IsSameAs(TempMaterial)) + { + ADD_MESSAGE("As the fire dies out they look greatly altered."); + + if(SecondaryMaterial && SecondaryMaterial->IsSameAs(MainMaterial)) + for(uint c = 0; c < Item.size(); ++c) + Item[c]->ChangeSecondaryMaterial(TempMaterial->SpawnMore()); + + Item[0]->ChangeMainMaterial(TempMaterial); + + for(uint c = 1; c < Item.size(); ++c) + Item[c]->ChangeMainMaterial(TempMaterial->SpawnMore()); + } + else + ADD_MESSAGE("As the fire dies out they look unchanged."); + } + + msgsystem::LeaveBigMessageMode(); + break; + } + + RemoveFromSlot(); + SendToHell(); + Reader->EditExperience(INTELLIGENCE, 500, 1 << 12); +} + +item* brokenbottle::BetterVersion() const +{ + return potion::Spawn(); +} + +void brokenbottle::StepOnEffect(character* Stepper) +{ + if(Stepper->HasALeg() && !(RAND() % 5)) + { + if(Stepper->IsPlayer()) + ADD_MESSAGE("Ouch. You step on sharp glass splinters."); + else if(Stepper->CanBeSeenByPlayer()) + ADD_MESSAGE("%s steps on sharp glass splinters.", Stepper->CHAR_NAME(DEFINITE)); + + Stepper->ReceiveDamage(0, 1 + RAND() % 3, PHYSICAL_DAMAGE, LEGS); + Stepper->CheckDeath(CONST_S("stepped on a broken bottle"), 0); + } +} + +liquid* can::CreateDipLiquid() +{ + return static_cast(GetSecondaryMaterial()->TakeDipVolumeAway()); +} + +liquid* potion::CreateDipLiquid() +{ + return static_cast(GetSecondaryMaterial()->TakeDipVolumeAway()); +} + +liquid* cauldron::CreateDipLiquid() +{ + return static_cast(GetSecondaryMaterial()->TakeDipVolumeAway()); +} + +void potion::DipInto(liquid* Liquid, character* Dipper) +{ + /* Add alchemy */ + + if(Dipper->IsPlayer()) + ADD_MESSAGE("%s is now filled with %s.", CHAR_NAME(DEFINITE), Liquid->GetName(false, false).CStr()); + + ChangeSecondaryMaterial(Liquid); + Dipper->DexterityAction(10); +} + +void cauldron::DipInto(liquid* Liquid, character* Dipper) +{ + /* Add alchemy */ + + if(Dipper->IsPlayer()) + ADD_MESSAGE("%s is now filled with %s.", CHAR_NAME(DEFINITE), Liquid->GetName(false, false).CStr()); + + ChangeSecondaryMaterial(Liquid); + Dipper->DexterityAction(10); +} + +liquid* kleinbottle::CreateDipLiquid() +{ + return static_cast(GetSecondaryMaterial()->TakeDipVolumeAway()); +} + +void kleinbottle::DipInto(liquid* Liquid, character* Dipper) +{ + /* Add alchemy */ + + if(Dipper->IsPlayer()) + ADD_MESSAGE("%s is now filled with %s.", CHAR_NAME(DEFINITE), Liquid->GetName(false, false).CStr()); + + ChangeSecondaryMaterial(Liquid); + Dipper->DexterityAction(100); +} + +void lantern::SignalSquarePositionChange(int SquarePosition) +{ + item::SignalSquarePositionChange(SquarePosition); + UpdatePictures(); +} + +item* potion::BetterVersion() const +{ + if(!GetSecondaryMaterial()) + return potion::Spawn(); + else + return 0; +} + +item* cauldron::BetterVersion() const +{ + if(!GetSecondaryMaterial()) + return cauldron::Spawn(); + else + return 0; +} + +item* can::BetterVersion() const +{ + if(!GetSecondaryMaterial()) + return can::Spawn(); + else + return 0; +} + +truth backpack::Apply(character* Terrorist) +{ + if(IsExplosive()) + { + if(Terrorist->IsPlayer()) + ADD_MESSAGE("You light your %s. It explodes!", CHAR_NAME(UNARTICLED)); + else if(Terrorist->CanBeSeenByPlayer()) + ADD_MESSAGE("%s lights %s. It explodes!", Terrorist->CHAR_NAME(DEFINITE), CHAR_NAME(INDEFINITE)); + else if(GetSquareUnder()->CanBeSeenByPlayer(true)) + ADD_MESSAGE("Something explodes!"); + + RemoveFromSlot(); + SendToHell(); + Terrorist->DexterityAction(5); + Terrorist->GetLevel()->Explosion(Terrorist, CONST_S("kamikazed @k"), Terrorist->GetLSquareUnder()->GetPos(), GetSecondaryMaterial()->GetTotalExplosivePower()); + return true; + } + else if(Terrorist->IsPlayer()) + ADD_MESSAGE("You are not able to explode yourself with this crummy %s!", CHAR_NAME(UNARTICLED)); + + return false; +} + +truth holybook::CanBeRead(character* Reader) const +{ + return Reader->CanRead() || game::GetSeeWholeMapCheatMode(); +} + +void holybook::FinishReading(character* Reader) +{ + if(Reader->IsPlayer()) + { + PLAYER->EditExperience(INTELLIGENCE, 75, 1 << 12); + PLAYER->EditExperience(WISDOM, 150, 1 << 12); + + //if(GetMasterGod()->GetConfig() == SOLICITU) // should be IsNotAGod() + // { + // ADD_MESSAGE("The pages in this holy book of %s are blank.", GetMasterGod()->GetName()); + // ADD_MESSAGE("It suddenly disappears!"); + // RemoveFromSlot(); + // SendToHell(); + // return; + // } + + if(GetMasterGod()->IsKnown()) + { + ADD_MESSAGE("The book reveals many divine secrets of %s to you.", GetMasterGod()->GetName()); + GetMasterGod()->AdjustRelation(75); + game::ApplyDivineAlignmentBonuses(GetMasterGod(), 15, true); + + if(!(RAND() % 3)) + { + ADD_MESSAGE("But then it disappears."); + RemoveFromSlot(); + SendToHell(); + } + } + else + { + game::LearnAbout(GetMasterGod()); + game::LearnAbout(GetMasterGod()); + ADD_MESSAGE("You feel you master the magical rites of %s.", GetMasterGod()->GetName()); + } + } +} + +truth wand::ReceiveDamage(character* Damager, int Damage, int Type, int) +{ + if(Type & (FIRE|ENERGY|PHYSICAL_DAMAGE) && Damage && (Damage > 125 || !(RAND() % (250 / Damage)))) + { + festring DeathMsg = CONST_S("killed by an explosion of "); + AddName(DeathMsg, INDEFINITE); + + if(Damager) + DeathMsg << " caused @bk"; + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s %s.", GetExtendedDescription().CStr(), GetBreakMsg().CStr()); + + BreakEffect(Damager, DeathMsg); + return true; + } + + return false; +} + +truth backpack::ReceiveDamage(character* Damager, int Damage, int Type, int) +{ + if(Type & (FIRE|ENERGY) && Damage && IsExplosive() && (Damage > 25 || !(RAND() % (50 / Damage)))) + { + festring DeathMsg = CONST_S("killed by an explosion of "); + AddName(DeathMsg, INDEFINITE); + + if(Damager) + DeathMsg << " caused @bk"; + + if(GetSquareUnder()->CanBeSeenByPlayer(true)) + ADD_MESSAGE("%s explodes!", GetExtendedDescription().CStr()); + + lsquare* Square = GetLSquareUnder(); + RemoveFromSlot(); + SendToHell(); + Square->GetLevel()->Explosion(Damager, DeathMsg, Square->GetPos(), GetSecondaryMaterial()->GetTotalExplosivePower()); + return true; + } + + return false; +} + +truth scroll::ReceiveDamage(character*, int Damage, int Type, int) +{ + if(Type & FIRE && Damage + && GetMainMaterial()->GetInteractionFlags() & CAN_BURN + && (Damage > 125 || !(RAND() % (250 / Damage)))) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s catches fire!", GetExtendedDescription().CStr()); + + RemoveFromSlot(); + SendToHell(); + return true; + } + + return false; +} + +truth locationmap::ReceiveDamage(character*, int Damage, int Type, int) +{ + if(Type & FIRE && Damage + && GetMainMaterial()->GetInteractionFlags() & CAN_BURN + && (Damage > 125 || !(RAND() % (250 / Damage)))) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s catches fire!", GetExtendedDescription().CStr()); + + RemoveFromSlot(); + SendToHell(); + return true; + } + + return false; +} + +truth holybook::ReceiveDamage(character*, int Damage, int Type, int) +{ + if(Type & FIRE && Damage + && GetMainMaterial()->GetInteractionFlags() & CAN_BURN + && (Damage > 125 || !(RAND() % (250 / Damage)))) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s catches fire!", GetExtendedDescription().CStr()); + + RemoveFromSlot(); + SendToHell(); + return true; + } + + return false; +} + +truth oillamp::Apply(character* Applier) +{ + if(Applier->IsPlayer()) + ADD_MESSAGE("You rub %s.", CHAR_NAME(DEFINITE)); + + if(GetInhabitedByGenie()) + { + genie* Genie = genie::Spawn(); + v2 TryToCreate; + truth FoundPlace = false; + + if(RAND_N(5)) + Genie->SetTeam(Applier->GetTeam()); + else + Genie->SetTeam(game::GetTeam(MONSTER_TEAM)); + + /* First try to create a genie nearby (10 tries - if all of them fail then stop trying) */ + + for(int c = 0; c < 10 && !FoundPlace; ++c) + { + TryToCreate = Applier->GetPos() + game::GetMoveVector(RAND() % DIRECTION_COMMAND_KEYS); + + if(GetArea()->IsValidPos(TryToCreate) && Genie->CanMoveOn(GetNearLSquare(TryToCreate)) && Genie->IsFreeForMe(GetNearLSquare(TryToCreate))) + { + Genie->PutTo(TryToCreate); + FoundPlace = true; + SetInhabitedByGenie(false); + break; + } + } + + if(FoundPlace) + { + Genie->GetLSquareUnder()->AddSmoke(gas::Spawn(SMOKE, 1000)); + + if(!Genie->IsPet()) + ADD_MESSAGE("You see a puff of smoke, and %s appears. \"For centuries I have been imprisoned in this lamp. But at last you have freed me! As a reward, I will kill you.\"", Genie->CHAR_NAME(INDEFINITE)); + else + { + if(Applier->IsPlayer()) + { + ADD_MESSAGE("You see a puff of smoke, and %s appears. \"For centuries I have been imprisoned in this lamp. But at last you have freed me! I am deeply grateful. You deserve a generous reward. I may serve you for 1001 nights or grant you a wish. It's your choice.\"", Genie->CHAR_NAME(INDEFINITE)); + + if(game::TruthQuestion(CONST_S("Do you want to wish? [Y/n]"), YES)) + { + ADD_MESSAGE("You may wish for an item."); + game::Wish(Applier, + "%s appears from nothing and the genie flies happily away!", + "Two %s appear from nothing and the genie flies happily away!"); + + Genie->Remove(); + delete Genie; + Applier->EditAP(-1000); + return true; + } + } + } + + meleeweapon* Weapon = meleeweapon::Spawn(TWO_HANDED_SCIMITAR, NO_MATERIALS); + Weapon->InitMaterials(MAKE_MATERIAL(ARCANITE), MAKE_MATERIAL(ARCANITE), true); + Genie->SetRightWielded(Weapon); + ADD_MESSAGE("%s wishes for a two-handed scimitar. Suddenly %s appears from nothing and %s wields it.", Genie->CHAR_NAME(DEFINITE), Genie->GetMainWielded()->CHAR_NAME(INDEFINITE), Genie->CHAR_NAME(DEFINITE)); + } + else + { + if(Applier->IsPlayer()) + ADD_MESSAGE("You feel that it is warmer."); + + delete Genie; + } + } + else + if(Applier->IsPlayer()) + ADD_MESSAGE("Nothing happens."); + + Applier->EditAP(-1000); + return true; +} + +void oillamp::Save(outputfile& SaveFile) const +{ + item::Save(SaveFile); + SaveFile << InhabitedByGenie; +} + +void oillamp::Load(inputfile& SaveFile) +{ + item::Load(SaveFile); + SaveFile >> InhabitedByGenie; +} + +col16 holybook::GetMaterialColorA(int) const +{ + return GetMasterGod()->GetColor(); +} + +void scrollofcharging::FinishReading(character* Reader) +{ + for(;;) + { + itemvector Item; + + if(!Reader->SelectFromPossessions(Item, CONST_S("Which item do you wish to charge?"), NO_MULTI_SELECT|SELECT_PAIR, &item::IsChargeable)) + { + ADD_MESSAGE("You notice you haven't got anything to charge."); + return; + } + + if(!Item.empty()) + { + if(Item[0]->HandleInPairs() && Item.size() == 1) + { + ADD_MESSAGE("Only one %s will be charged.", Item[0]->CHAR_NAME(UNARTICLED)); + + if(!game::TruthQuestion(CONST_S("Still continue? [y/N]"))) + continue; + } + + for(uint c = 0; c < Item.size(); ++c) + Item[c]->ChargeFully(Reader); + + ADD_MESSAGE("You charge %s and the scroll burns.", Item[0]->CHAR_NAME(DEFINITE|(Item.size() == 1 ? 0 : PLURAL))); + break; + } + else if(game::TruthQuestion(CONST_S("Really cancel read? [y/N]"))) + return; + } + + RemoveFromSlot(); + SendToHell(); + Reader->EditExperience(INTELLIGENCE, 300, 1 << 12); +} + +void bananapeels::StepOnEffect(character* Stepper) +{ + if(Stepper->HasALeg() && !(RAND() % 5)) + { + if(Stepper->IsPlayer()) + ADD_MESSAGE("Ouch. Your feet slip on %s and you fall down.", CHAR_NAME(INDEFINITE)); + else if(Stepper->CanBeSeenByPlayer()) + ADD_MESSAGE("%s steps on %s and falls down.", Stepper->CHAR_NAME(DEFINITE), CHAR_NAME(INDEFINITE)); + + /* Do damage against any random bodypart except legs */ + + Stepper->ReceiveDamage(0, 1 + (RAND() & 1), PHYSICAL_DAMAGE, ALL&~LEGS); + Stepper->CheckDeath(CONST_S("slipped on a banana peel"), 0); + Stepper->EditAP(-500); + } +} + +void scrolloftaming::FinishReading(character* Reader) +{ + // First find all tameable characters in the squares around Reader + + character* CharacterNear[8]; + int Index = 0; + + for(int c = 0; c < 8; ++c) + { + v2 Test = Reader->GetPos() + game::GetMoveVector(c); + + if(GetArea()->IsValidPos(Test)) + { + character* Char = GetNearSquare(Test)->GetCharacter(); + + if(Char && Char->CanTameWithScroll(Reader) && Char->GetTeam() != Reader->GetTeam()) + CharacterNear[Index++] = Char; + } + } + + // Then pick one of the characters and set it to the same team as Reader + + if(!Index) + { + if(Reader->IsPlayer() || Reader->CanBeSeenByPlayer()) + ADD_MESSAGE("The scroll burns, but nothing happens."); + } + else + { + character* ToBeTamed = CharacterNear[RAND() % Index]; + ToBeTamed->ChangeTeam(Reader->GetTeam()); + + if(Reader->IsPlayer()) + { + if(ToBeTamed->CanBeSeenByPlayer()) + ADD_MESSAGE("%s looks much friendlier.", ToBeTamed->CHAR_NAME(INDEFINITE)); + else + ADD_MESSAGE("You notice no effect."); + } + else if(Reader->CanBeSeenByPlayer()) + { + if(ToBeTamed->CanBeSeenByPlayer()) + ADD_MESSAGE("%s seems to like %s far more.", ToBeTamed->CHAR_NAME(INDEFINITE), Reader->CHAR_NAME(DEFINITE)); + } + else if(ToBeTamed->CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s looks like having fallen in love with something nearby.", ToBeTamed->CHAR_NAME(INDEFINITE)); + } + + RemoveFromSlot(); + SendToHell(); + Reader->EditExperience(INTELLIGENCE, 300, 1 << 12); +} + +truth mine::ReceiveDamage(character* Damager, int Damage, int Type, int) +{ + if((Type & (FIRE|ENERGY) && Damage && (Damage > 50 || !(RAND() % (100 / Damage)))) || (Type & (PHYSICAL_DAMAGE|SOUND) && WillExplode(0))) + { + festring DeathMsg = CONST_S("killed by an explosion of "); + AddName(DeathMsg, INDEFINITE); + + if(Damager) + DeathMsg << " caused @bk"; + + if(GetSquareUnder()->CanBeSeenByPlayer(true)) + ADD_MESSAGE("%s explodes!", GetExtendedDescription().CStr()); + + lsquare* Square = GetLSquareUnder(); + RemoveFromSlot(); + SendToHell(); + Square->GetLevel()->Explosion(Damager, DeathMsg, Square->GetPos(), GetSecondaryMaterial()->GetTotalExplosivePower()); + return true; + } + + return false; +} + +void mine::StepOnEffect(character* Stepper) +{ + if(!WillExplode(Stepper)) + return; + + if(Stepper->IsPlayer()) + { + cchar* SenseVerb = Stepper->CanHear() ? "hear" : "sense"; + + if(GetLSquareUnder()->IsDark()) + ADD_MESSAGE("You %s a faint thump. You try to look down, but it is too dark to see anything.", SenseVerb); + else + ADD_MESSAGE("You %s a faint thump. You look down. You see %s.", SenseVerb, CHAR_NAME(INDEFINITE)); + } + else if(Stepper->CanBeSeenByPlayer()) + ADD_MESSAGE("%s steps on %s.", Stepper->CHAR_NAME(DEFINITE), CHAR_NAME(INDEFINITE)); + else if(GetSquareUnder()->CanBeSeenByPlayer(true)) + ADD_MESSAGE("Something explodes!"); + + SetIsActive(false); + SendNewDrawAndMemorizedUpdateRequest(); + + if(Stepper->IsPlayer()) + game::AskForKeyPress(CONST_S("Trap activated! [press any key to continue]")); + + lsquare* Square = GetLSquareUnder(); + RemoveFromSlot(); + SendToHell(); + Square->GetLevel()->Explosion(0, "killed by a land mine", Square->GetPos(), GetSecondaryMaterial()->GetTotalExplosivePower()); +} + +truth key::Apply(character* User) +{ + if(User->IsPlayer()) + { + if(!User->CanOpen()) + { + ADD_MESSAGE("This monster type cannot use keys."); + return false; + } + int Key; + truth OpenableItems = User->GetStack()->SortedItems(User, &item::HasLock); + + if(OpenableItems) + Key = game::AskForKeyPress(CONST_S("What do you wish to lock or unlock? [press a direction key, space or i]")); + else + Key = game::AskForKeyPress(CONST_S("What do you wish to lock or unlock? [press a direction key or space]")); + + if(Key == 'i' && OpenableItems) + { + item* Item = User->GetStack()->DrawContents(User, CONST_S("What do you want to lock or unlock?"), 0, &item::IsOpenable); + return Item && Item->TryKey(this, User); + } + + v2 DirVect = game::GetDirectionVectorForKey(Key); + + if(DirVect != ERROR_V2 && User->GetArea()->IsValidPos(User->GetPos() + DirVect)) + return GetLevel()->GetLSquare(User->GetPos() + DirVect)->TryKey(this, User); + } + return true; +} + +void materialcontainer::Save(outputfile& SaveFile) const +{ + item::Save(SaveFile); + SaveFile << SecondaryMaterial; +} + +void materialcontainer::Load(inputfile& SaveFile) +{ + item::Load(SaveFile); + LoadMaterial(SaveFile, SecondaryMaterial); +} + +material* materialcontainer::GetMaterial(int I) const +{ + return !I ? MainMaterial : SecondaryMaterial; +} + +col16 materialcontainer::GetMaterialColorB(int Frame) const +{ + if(GetSecondaryMaterial()) + return GetSecondaryMaterial()->GetColor(); + else + return GetMaterialColorA(Frame); +} + +alpha materialcontainer::GetAlphaB(int Frame) const +{ + if(GetSecondaryMaterial() && GetSecondaryMaterial()->GetAlpha() > GetAlphaA(Frame)) + return GetSecondaryMaterial()->GetAlpha(); + else + return GetAlphaA(Frame); +} + +void wand::PostConstruct() +{ + Charges = GetMinCharges() + RAND() % (GetMaxCharges() - GetMinCharges() + 1); + TimesUsed = 0; +} + +oillamp::oillamp() +{ + if(!game::IsLoading()) + InhabitedByGenie = RAND_2; +} + +truth whistle::Apply(character* Whistler) +{ + if(!Whistler->HasHead()) + { + ADD_MESSAGE("You must have a head to do this."); + return false; + } + + BlowEffect(Whistler); + Whistler->EditAP(-1000); + return true; +} + +void whistle::BlowEffect(character* Whistler) +{ + if(Whistler->IsPlayer()) + { + if(Whistler->CanHear()) + ADD_MESSAGE("You produce an interesting sound."); + else + ADD_MESSAGE("You blow %s", CHAR_NAME(DEFINITE)); + } + else if(Whistler->CanBeSeenByPlayer()) + { + if(PLAYER->CanHear()) + ADD_MESSAGE("%s blows %s and produces an interesting sound.", Whistler->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("%s blows %s.", Whistler->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + } + else if(PLAYER->CanHear()) + ADD_MESSAGE("You hear a whistle playing."); + + game::CallForAttention(GetPos(), 400); +} + +struct distancepair +{ + distancepair(long Distance, character* Char) : Distance(Distance), Char(Char) { } + bool operator<(const distancepair& D) const { return Distance > D.Distance; } + long Distance; + character* Char; +}; + +void magicalwhistle::BlowEffect(character* Whistler) +{ + if(LastUsed && game::GetTick() - LastUsed < 2000) + { + whistle::BlowEffect(Whistler); + return; + } + else + LastUsed = game::GetTick(); + + if(Whistler->IsPlayer()) + { + if(Whistler->CanHear()) + ADD_MESSAGE("You produce a peculiar sound."); + else + ADD_MESSAGE("You blow %s.", CHAR_NAME(DEFINITE)); + } + else if(Whistler->CanBeSeenByPlayer()) + { + if(PLAYER->CanHear()) + ADD_MESSAGE("%s blows %s and produces a peculiar sound.", Whistler->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("%s blows %s.", Whistler->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + } + else if(PLAYER->CanHear()) + ADD_MESSAGE("You hear a strange tune playing."); + + const std::list& Member = Whistler->GetTeam()->GetMember(); + std::vector ToSort; + v2 Pos = Whistler->GetPos(); + + for(std::list::const_iterator i = Member.begin(); i != Member.end(); ++i) + if((*i)->IsEnabled() && Whistler != *i) + ToSort.push_back(distancepair((Pos - (*i)->GetPos()).GetLengthSquare(), *i)); + + if(ToSort.size() > 5) + std::sort(ToSort.begin(), ToSort.end()); + + for(uint c = 0; c < 5 && c < ToSort.size(); ++c) + ToSort[c].Char->TeleportNear(Whistler); + + game::CallForAttention(GetPos(), 400); +} + +itemcontainer::itemcontainer() +{ + Contained = new stack(0, this, HIDDEN); +} + +void itemcontainer::PostConstruct() +{ + lockableitem::PostConstruct(); + SetIsLocked(RAND_N(3)); + long ItemNumber = RAND() % (GetMaxGeneratedContainedItems() + 1); + + for(int c = 0; c < ItemNumber; ++c) + { + item* NewItem = protosystem::BalancedCreateItem(); + long Volume = NewItem->GetVolume(); + + if(NewItem->HandleInPairs()) + Volume <<= 1; + + if(NewItem->CanBeGeneratedInContainer() + && (GetStorageVolume() - GetContained()->GetVolume()) >= Volume) + { + GetContained()->AddItem(NewItem); + NewItem->SpecialGenerationHandler(); + } + else + delete NewItem; + } +} + +void materialcontainer::GenerateMaterials() +{ + int Chosen = RandomizeMaterialConfiguration(); + const fearray& MMC = GetMainMaterialConfig(); + InitMaterial(MainMaterial, + MAKE_MATERIAL(MMC.Data[MMC.Size == 1 ? 0 : Chosen]), + GetDefaultMainVolume()); + const fearray& SMC = GetSecondaryMaterialConfig(); + InitMaterial(SecondaryMaterial, + MAKE_MATERIAL(SMC.Data[SMC.Size == 1 ? 0 : Chosen]), + GetDefaultSecondaryVolume()); +} + +/* Returns true if container opens fine else false */ + +truth itemcontainer::Open(character* Opener) +{ + if(IsLocked()) + { + ADD_MESSAGE("%s seems to be locked.", CHAR_NAME(DEFINITE)); + return false; + } + + festring Question = CONST_S("Do you want to (t)ake something from or (p)ut something in this container? [t,p]"); + truth Success; + + switch(game::KeyQuestion(Question, KEY_ESC, 3, 't', 'p', KEY_ESC)) + { + case 't': + case 'T': + Success = GetContained()->TakeSomethingFrom(Opener, GetName(DEFINITE)); + break; + case 'p': + case 'P': + Success = GetContained()->PutSomethingIn(Opener, GetName(DEFINITE), GetStorageVolume(), GetID()); + break; + default: + return false; + } + + if(Success) + Opener->DexterityAction(Opener->OpenMultiplier() * 5); + + return Success; +} + +void itemcontainer::Save(outputfile& SaveFile) const +{ + lockableitem::Save(SaveFile); + Contained->Save(SaveFile);} + +void itemcontainer::Load(inputfile& SaveFile) +{ + lockableitem::Load(SaveFile); + Contained->Load(SaveFile); +} + +truth itemcontainer::Polymorph(character* Polymorpher, stack* CurrentStack) +{ + GetContained()->MoveItemsTo(CurrentStack); + item::Polymorph(Polymorpher, CurrentStack); + return true; +} + +itemcontainer::~itemcontainer() +{ + delete Contained; +} + +beartrap::beartrap(const beartrap& Trap) : mybase(Trap) +{ + TrapData.TrapID = game::CreateNewTrapID(this); + TrapData.VictimID = 0; +} + +truth beartrap::TryToUnStick(character* Victim, v2) +{ + ulong TrapID = GetTrapID(); + int Modifier = GetBaseTrapDamage() * 40 / Max(Victim->GetAttribute(DEXTERITY) + Victim->GetAttribute(ARM_STRENGTH), 1); + + if(!RAND_N(Max(Modifier, 2))) + { + Victim->RemoveTrap(TrapID); + TrapData.VictimID = 0; + + if(Victim->IsPlayer()) + ADD_MESSAGE("You manage to free yourself from %s.", CHAR_NAME(DEFINITE)); + else if(Victim->CanBeSeenByPlayer()) + ADD_MESSAGE("%s manages to free %sself from %s.", Victim->CHAR_NAME(DEFINITE), Victim->CHAR_OBJECT_PRONOUN, CHAR_NAME(DEFINITE)); + + Victim->EditAP(-500); + return true; + } + + if(!RAND_N(Max(Modifier << 1, 2))) + { + Victim->RemoveTrap(TrapID); + TrapData.VictimID = 0; + Break(Victim); + + if(Victim->IsPlayer()) + ADD_MESSAGE("You are freed."); + else if(Victim->CanBeSeenByPlayer()) + ADD_MESSAGE("%s is freed.", Victim->CHAR_NAME(DEFINITE)); + + Victim->EditAP(-500); + return true; + } + + Modifier = Victim->GetAttribute(DEXTERITY) + Victim->GetAttribute(ARM_STRENGTH) * 3 / 20; + + if(!RAND_N(Max(Modifier, 2))) + { + int BodyPart = Victim->RandomizeHurtBodyPart(TrapData.BodyParts); + + if(Victim->IsPlayer()) + ADD_MESSAGE("You manage to hurt your %s even more.", Victim->GetBodyPartName(BodyPart).CStr()); + else if(Victim->CanBeSeenByPlayer()) + ADD_MESSAGE("%s hurts %s %s more with %s.", Victim->CHAR_NAME(DEFINITE), Victim->GetPossessivePronoun().CStr(), Victim->GetBodyPartName(BodyPart).CStr(), CHAR_NAME(DEFINITE)); + + Victim->ReceiveBodyPartDamage(0, GetBaseTrapDamage(), PHYSICAL_DAMAGE, BodyPart, YOURSELF, false, false, false); + Victim->CheckDeath(CONST_S("died while trying to escape from ") + GetName(INDEFINITE), 0, IGNORE_TRAPS); + Victim->EditAP(-1000); + return false; + } + + if(!RAND_N(Max(Modifier << 1, 2))) + { + int VictimBodyPart = Victim->RandomizeTryToUnStickBodyPart(ALL_BODYPART_FLAGS&~TrapData.BodyParts); + + if(VictimBodyPart != NONE_INDEX) + { + TrapData.BodyParts |= 1 << VictimBodyPart; + Victim->AddTrap(GetTrapID(), 1 << VictimBodyPart); + + if(Victim->IsPlayer()) + ADD_MESSAGE("You fail to free yourself from %s and your %s is stuck in it in the attempt.", CHAR_NAME(DEFINITE), Victim->GetBodyPartName(VictimBodyPart).CStr()); + else if(Victim->CanBeSeenByPlayer()) + ADD_MESSAGE("%s tries to free %sself from %s but is stuck more tightly in it in the attempt.", Victim->CHAR_NAME(DEFINITE), Victim->CHAR_OBJECT_PRONOUN, CHAR_NAME(DEFINITE)); + + Victim->ReceiveBodyPartDamage(0, GetBaseTrapDamage() << 1, PHYSICAL_DAMAGE, VictimBodyPart, YOURSELF, false, false, false); + Victim->CheckDeath(CONST_S("died while trying to escape from ") + GetName(INDEFINITE), 0, IGNORE_TRAPS); + Victim->EditAP(-1000); + return true; + } + } + + if(Victim->IsPlayer()) + ADD_MESSAGE("You are unable to escape from %s.", CHAR_NAME(DEFINITE)); + + Victim->EditAP(-1000); + return false; +} + +void beartrap::Load(inputfile& SaveFile) +{ + mybase::Load(SaveFile); + SaveFile >> TrapData; + game::AddTrapID(this, TrapData.TrapID); +} + +void beartrap::Save(outputfile& SaveFile) const +{ + mybase::Save(SaveFile); + SaveFile << TrapData; +} + +beartrap::beartrap() +{ + if(!game::IsLoading()) + { + TrapData.TrapID = game::CreateNewTrapID(this); + TrapData.VictimID = 0; + } +} + +beartrap::~beartrap() +{ + game::RemoveTrapID(TrapData.TrapID); +} + +void beartrap::StepOnEffect(character* Stepper) +{ + if(IsActive() && !IsBroken()) + { + int StepperBodyPart = Stepper->GetRandomStepperBodyPart(); + + if(StepperBodyPart == NONE_INDEX) + return; + + TrapData.VictimID = Stepper->GetID(); + TrapData.BodyParts = 1 << StepperBodyPart; + Stepper->AddTrap(GetTrapID(), 1 << StepperBodyPart); + + if(Stepper->IsPlayer()) + ADD_MESSAGE("You step in %s and it traps your %s.", CHAR_NAME(INDEFINITE), Stepper->GetBodyPartName(StepperBodyPart).CStr()); + else if(Stepper->CanBeSeenByPlayer()) + ADD_MESSAGE("%s is trapped in %s.", Stepper->CHAR_NAME(DEFINITE), CHAR_NAME(INDEFINITE)); + + SetIsActive(false); + SendNewDrawAndMemorizedUpdateRequest(); + + if(Stepper->IsPlayer()) + game::AskForKeyPress(CONST_S("Trap activated! [press any key to continue]")); + + Stepper->ReceiveBodyPartDamage(0, GetBaseTrapDamage() << 1, PHYSICAL_DAMAGE, StepperBodyPart, YOURSELF, false, false, false); + Stepper->CheckDeath(CONST_S("died by stepping to ") + GetName(INDEFINITE), 0, IGNORE_TRAPS); + } +} + +truth beartrap::CheckPickUpEffect(character* Picker) +{ + if(Picker->IsStuckToTrap(GetTrapID())) + { + if(Picker->IsPlayer()) + ADD_MESSAGE("You are tightly stuck in %s.", CHAR_NAME(DEFINITE)); + + return false; + } + + SetIsActive(false); + return true; +} + +int lantern::GetSpecialFlags() const +{ + switch(GetSquarePosition()) + { + case LEFT: return ROTATE|MIRROR; + case DOWN: return FLIP; + case UP: return 0; + case RIGHT: return ROTATE; + } + + return 0; +} + +truth stethoscope::Apply(character* Doctor) +{ + if(!Doctor->CanUseStethoscope(true)) + return false; + + if(!Doctor->IsPlayer()) + ABORT("Doctor is not here, man, but these pills taste just as good anyway."); + + int Dir = game::DirectionQuestion(CONST_S("What do you want to inspect? [press a direction key]"), false,true); + + if(Dir == DIR_ERROR) + return false; + + Doctor->DexterityAction(2); + character* Char = GetNearSquare(GetPos() + game::GetMoveVector(Dir))->GetCharacter(); + + if(!Char) + { + ADD_MESSAGE("There's no-one here."); + return false; + } + + Char->DisplayStethoscopeInfo(Doctor); + return true; +} + +void itemcontainer::CalculateVolumeAndWeight() +{ + item::CalculateVolumeAndWeight(); + Volume += Contained->GetVolume(); + Weight += Contained->GetWeight(); +} + +materialcontainer::~materialcontainer() +{ + delete SecondaryMaterial; +} + +truth itemcontainer::ContentsCanBeSeenBy(ccharacter* Viewer) const +{ + return GetMainMaterial()->IsTransparent() && CanBeSeenBy(Viewer); +} + +truth mine::Apply(character* User) +{ + if(User->IsPlayer() && !game::TruthQuestion(CONST_S("Are you sure you want to plant ") + GetName(DEFINITE) + "? [y/N]")) + return false; + + room* Room = GetRoom(); + + if(Room) + Room->HostileAction(User); + + if(User->IsPlayer()) + ADD_MESSAGE("%s is now %sactive.", CHAR_NAME(DEFINITE), IsActive() ? "in" : ""); + + SetIsActive(!IsActive()); + User->DexterityAction(10); + + if(IsActive()) + { + Team = User->GetTeam()->GetID(); + RemoveFromSlot(); + User->GetStackUnder()->AddItem(this); + } + + return true; +} +/* +truth mangoseedling::Apply(character* Applier) +{ + + if((game::GetCurrentDungeonIndex() == NEW_ATTNAM)) + { + if(Applier->IsPlayer() && game::TweraifIsFree()) + { + Applier->EditAP(-1000); + + game::TextScreen(CONST_S( "You plant the seedling of the first new mango tree in New Attnam.\n" + "The people of your home village gather around you cheering! Tweraif is\n" + "now restored to its former glory and you remain there as honourary\n" + "spiritual leader and hero of the new republic. You ensure that free\n" + "and fair elections quickly ensue.\n\nYou are victorious!")); + game::GetCurrentArea()->SendNewDrawRequest(); + game::DrawEverything(); + PLAYER->ShowAdventureInfo(); + festring Msg = CONST_S("restored Tweraif to independence and continued to further adventures"); + Applier->AddScoreEntry(Msg, 1, false); + game::End(Msg); + } + + RemoveFromSlot(); + SendToHell(); + } + else + { + if(Applier->IsPlayer()) + ADD_MESSAGE("You feel that the climate is not quite right for growing mangoes."); + } + + Applier->EditAP(-1000); + return true; +}*/ + +truth beartrap::Apply(character* User) +{ + if(IsBroken()) + { + if(User->IsPlayer()) + ADD_MESSAGE("%s is useless.", CHAR_NAME(DEFINITE)); + + return false; + } + + if(User->IsPlayer() + && !game::TruthQuestion(CONST_S("Are you sure you want to plant ") + GetName(DEFINITE) + "? [y/N]")) + return false; + + room* Room = GetRoom(); + + if(Room) + Room->HostileAction(User); + + if(User->GetAttribute(DEXTERITY) < femath::LoopRoll(90, 1000)) + { + int UserBodyPart = User->GetRandomApplyBodyPart(); + + if(User->IsPlayer()) + ADD_MESSAGE("Somehow you manage to trap your %s in %s.", User->GetBodyPartName(UserBodyPart).CStr(), CHAR_NAME(DEFINITE)); + else if(User->CanBeSeenByPlayer()) + ADD_MESSAGE("%s somehow traps %sself in %s.", User->CHAR_NAME(DEFINITE), User->CHAR_OBJECT_PRONOUN, CHAR_NAME(DEFINITE)); + + RemoveFromSlot(); + User->GetStackUnder()->AddItem(this); + TrapData.VictimID = User->GetID(); + TrapData.BodyParts = 1 << UserBodyPart; + User->AddTrap(GetTrapID(), 1 << UserBodyPart); + SendNewDrawAndMemorizedUpdateRequest(); + + if(User->IsPlayer()) + game::AskForKeyPress(CONST_S("Trap activated! [press any key to continue]")); + + User->ReceiveBodyPartDamage(0, 1 + (RAND() & 1), PHYSICAL_DAMAGE, UserBodyPart, YOURSELF, false, false, false); + User->CheckDeath(CONST_S("died failing to set ") + GetName(INDEFINITE), 0, IGNORE_TRAPS); + } + else + { + if(User->IsPlayer()) + ADD_MESSAGE("%s is now %sactive.", CHAR_NAME(DEFINITE), IsActive() ? "in" : ""); + + SetIsActive(!IsActive()); + User->DexterityAction(10); + + if(IsActive()) + { + Team = User->GetTeam()->GetID(); + RemoveFromSlot(); + User->GetStackUnder()->AddItem(this); + } + } + + return true; +} + +v2 beartrap::GetBitmapPos(int Frame) const +{ + if(!IsBroken()) + return IsActive() ? v2(32, 304) : v2(32, 320); + else + return item::GetBitmapPos(Frame); +} + +truth mine::WillExplode(ccharacter* Stepper) const +{ + return IsActive() && GetSecondaryMaterial() && GetSecondaryMaterial()->IsExplosive() && (!Stepper || Stepper->GetWeight() > 5000); +} + +materialcontainer::materialcontainer(const materialcontainer& MC) : mybase(MC) +{ + CopyMaterial(MC.SecondaryMaterial, SecondaryMaterial); +} + +itemcontainer::itemcontainer(const itemcontainer& Container) : mybase(Container) +{ + Contained = new stack(0, this, HIDDEN); + CalculateAll(); +} + +oillamp::oillamp(const oillamp& Lamp) : mybase(Lamp), InhabitedByGenie(false) +{ +} + +truth wand::Zap(character* Zapper, v2, int Direction) +{ + if(Charges <= TimesUsed) + { + ADD_MESSAGE("Nothing happens."); + return true; + } + + Zapper->EditExperience(PERCEPTION, 150, 1 << 10); + + beamdata Beam + ( + Zapper, + CONST_S("killed by ") + GetName(INDEFINITE) + " zapped @bk", + Zapper->GetPos(), + GetBeamColor(), + GetBeamEffect(), + Direction, + GetBeamRange(), + GetSpecialParameters() + ); + + (GetLevel()->*level::GetBeam(GetBeamStyle()))(Beam); + ++TimesUsed; + return true; +} + +void wand::AddInventoryEntry(ccharacter*, festring& Entry, int, truth ShowSpecialInfo) const // never piled +{ + AddName(Entry, INDEFINITE); + + if(ShowSpecialInfo) + { + Entry << " [" << GetWeight(); + + if(TimesUsed == 1) + Entry << "g, used 1 time]"; + else if(TimesUsed) + Entry << "g, used " << TimesUsed << " times]"; + else + Entry << "g]"; + } +} + +void materialcontainer::SignalSpoil(material* Material) +{ + if(!Exists()) + return; + + if(Material == MainMaterial) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s becomes so spoiled that it cannot hold its contents anymore.", CHAR_NAME(DEFINITE)); + + RemoveMainMaterial(); + } + else + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("The contents of %s spoil completely.", CHAR_NAME(DEFINITE)); + + delete RemoveSecondaryMaterial(); + } +} + +truth materialcontainer::CanBePiledWith(citem* Item, ccharacter* Viewer) const +{ + if(!item::CanBePiledWith(Item, Viewer)) + return false; + + const materialcontainer* Weapon = static_cast(Item); + + if(!SecondaryMaterial && !Weapon->SecondaryMaterial) + return true; + + return SecondaryMaterial + && Weapon->SecondaryMaterial + && SecondaryMaterial->IsSameAs(Weapon->SecondaryMaterial) + && SecondaryMaterial->GetSpoilLevel() == Weapon->SecondaryMaterial->GetSpoilLevel(); +} + +long itemcontainer::GetTruePrice() const +{ + return GetContained()->GetTruePrice() + item::GetTruePrice(); +} + +void potion::Break(character* Breaker, int Dir) +{ + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s shatters to pieces.", GetExtendedDescription().CStr()); + else if(PLAYER->CanHear()) + ADD_MESSAGE("You hear something shattering."); + + if(Breaker && IsOnGround()) + { + room* Room = GetRoom(); + + if(Room) + Room->HostileAction(Breaker); + } + + item* Remains = brokenbottle::Spawn(0, NO_MATERIALS); + Remains->InitMaterials(GetMainMaterial()->SpawnMore()); + DonateFluidsTo(Remains); + DonateIDTo(Remains); + DonateSlotTo(Remains); + SendToHell(); + + if(GetSecondaryMaterial() && GetSecondaryMaterial()->IsLiquid()) + { + liquid* Liquid = static_cast(GetSecondaryMaterial()); + + if(Dir != YOURSELF) + { + v2 Pos = Remains->GetPos() + game::GetMoveVector(Dir); + + if(Remains->GetLevel()->IsValidPos(Pos)) + { + long HalfVolume = GetSecondaryMaterial()->GetVolume() >> 1; + Liquid->EditVolume(-HalfVolume); + Remains->GetNearLSquare(Pos)->SpillFluid(Breaker, Liquid->SpawnMoreLiquid(HalfVolume)); + } + } + + if(Remains->Exists()) + Remains->GetLSquareUnder()->SpillFluid(Breaker, Liquid->SpawnMoreLiquid(Liquid->GetVolume())); + } + + if(PLAYER->Equips(Remains)) + game::AskForKeyPress(CONST_S("Equipment broken! [press any key to continue]")); +} + +void materialcontainer::Be() +{ + item::Be(); + + if(Exists() && SecondaryMaterial) + SecondaryMaterial->Be(ItemFlags); +} + +int materialcontainer::GetSparkleFlags() const +{ + return (MainMaterial->IsSparkling() ? SPARKLING_A : 0) + | (SecondaryMaterial && SecondaryMaterial->IsSparkling() ? SPARKLING_B : 0); +} + +void scrollofenchantweapon::FinishReading(character* Reader) +{ + for(;;) + { + itemvector Item; + + if(!Reader->SelectFromPossessions(Item, CONST_S("Choose a weapon to enchant:"), NO_MULTI_SELECT|SELECT_PAIR, &item::IsWeapon)) + { + ADD_MESSAGE("You notice you haven't got anything to enchant."); + return; + } + + if(!Item.empty()) + { + if(!Item[0]->CanBeEnchanted()) + { + ADD_MESSAGE("You cast the spell, but the magic is not powerful enough to affect %s!", Item[0]->CHAR_NAME(DEFINITE|(Item.size() == 1 ? 0 : PLURAL))); + break; + } + + if(Item[0]->HandleInPairs() && Item.size() == 1) + { + ADD_MESSAGE("Only one %s will be enchanted.", Item[0]->CHAR_NAME(UNARTICLED)); + + if(!game::TruthQuestion(CONST_S("Still continue? [y/N]"))) + continue; + } + + if(Item[0]->GetEnchantment() >= 5 && RAND_GOOD(Item[0]->GetEnchantment() - 3)) + { + if(Item.size() == 1) + ADD_MESSAGE("Magic energies swirl around %s, but they fail to enchant it further!", Item[0]->CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("Magic energies swirl around %s, but they fail to enchant them further!", Item[0]->CHAR_NAME(DEFINITE|PLURAL)); + + break; + } + + if(Item.size() == 1) + ADD_MESSAGE("Your %s glows briefly red. It feels very warm now.", Item[0]->CHAR_NAME(UNARTICLED)); + else + ADD_MESSAGE("Your %s glow briefly red. They feel very warm now.", Item[0]->CHAR_NAME(PLURAL)); + + for(uint c = 0; c < Item.size(); ++c) + Item[c]->EditEnchantment(1); + + break; + } + else if(game::TruthQuestion(CONST_S("Really cancel read? [y/N]"))) + return; + } + + RemoveFromSlot(); + SendToHell(); + Reader->EditExperience(INTELLIGENCE, 300, 1 << 12); +} + +void scrollofenchantarmor::FinishReading(character* Reader) +{ + for(;;) + { + itemvector Item; + + if(!Reader->SelectFromPossessions(Item, CONST_S("Choose an armor to enchant:"), NO_MULTI_SELECT|SELECT_PAIR, &item::IsArmor)) + { + ADD_MESSAGE("You notice you haven't got anything to enchant."); + return; + } + + if(!Item.empty()) + { + if(!Item[0]->CanBeEnchanted()) + { + ADD_MESSAGE("You cast the spell, but the magic is not powerful enough to affect %s!", Item[0]->CHAR_NAME(DEFINITE|(Item.size() == 1 ? 0 : PLURAL))); + break; + } + + if(Item[0]->HandleInPairs() && Item.size() == 1) + { + ADD_MESSAGE("Only one %s will be enchanted.", Item[0]->CHAR_NAME(UNARTICLED)); + + if(!game::TruthQuestion(CONST_S("Still continue? [y/N]"))) + continue; + } + + if(Item[0]->GetEnchantment() >= 5 && RAND_GOOD(Item[0]->GetEnchantment() - 3)) + { + if(Item.size() == 1) + ADD_MESSAGE("Magic energies swirl around %s, but they fail to enchant it further!", Item[0]->CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("Magic energies swirl around %s, but they fail to enchant them further!", Item[0]->CHAR_NAME(DEFINITE|PLURAL)); + + break; + } + + if(Item.size() == 1) + ADD_MESSAGE("Your %s glows briefly blue. It feels very warm now.", Item[0]->CHAR_NAME(UNARTICLED)); + else + ADD_MESSAGE("Your %s glow briefly blue. They feel very warm now.", Item[0]->CHAR_NAME(PLURAL)); + + for(uint c = 0; c < Item.size(); ++c) + Item[c]->EditEnchantment(1); + + break; + } + else if(game::TruthQuestion(CONST_S("Really cancel read? [y/N]"))) + return; + } + + RemoveFromSlot(); + SendToHell(); + Reader->EditExperience(INTELLIGENCE, 300, 1 << 12); +} + +truth itemcontainer::ReceiveDamage(character* Damager, int Damage, int Type, int) +{ + if(Type & (PHYSICAL_DAMAGE|SOUND|ENERGY)) + { + Contained->ReceiveDamage(Damager, Damage / GetDamageDivider(), Type); + int SV = Max(GetStrengthValue(), 1); + + if(IsLocked() && Damage > SV && RAND() % (100 * Damage / SV) >= 100) + { + SetIsLocked(false); + SetConfig(GetConfig()&~LOCK_BITS|BROKEN_LOCK); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("The %s's lock shatters to pieces.", GetNameSingular().CStr()); + + return true; + } + else if(Damager && Damager->IsPlayer()) + ADD_MESSAGE("THUMP!"); + } + + return false; +} + +void itemcontainer::DrawContents(ccharacter* Char) +{ + festring Topic = CONST_S("Contents of your ") + GetName(UNARTICLED); + GetContained()->DrawContents(Char, Topic, NO_SELECT); + + for(stackiterator i = GetContained()->GetBottom(); i.HasItem(); ++i) + i->DrawContents(Char); +} + +void magicalwhistle::Save(outputfile& SaveFile) const +{ + whistle::Save(SaveFile); + SaveFile << LastUsed; +} + +void magicalwhistle::Load(inputfile& SaveFile) +{ + whistle::Load(SaveFile); + SaveFile >> LastUsed; +} + +int materialcontainer::GetSpoilLevel() const +{ + return Max(MainMaterial->GetSpoilLevel(), SecondaryMaterial ? SecondaryMaterial->GetSpoilLevel() : 0); +} + +void itemcontainer::SetItemsInside(const fearray >& ItemArray, int SpecialFlags) +{ + GetContained()->Clean(); + + for(uint c1 = 0; c1 < ItemArray.Size; ++c1) + if(ItemArray[c1].IsValid()) + { + const interval* TimesPtr = ItemArray[c1].GetTimes(); + int Times = TimesPtr ? TimesPtr->Randomize() : 1; + + for(int c2 = 0; c2 < Times; ++c2) + { + item* Item = ItemArray[c1].Instantiate(SpecialFlags); + + if(Item) + { + Contained->AddItem(Item); + Item->SpecialGenerationHandler(); + } + } + } +} + +truth mine::CheckPickUpEffect(character*) +{ + if(WillExplode(0)) + { + lsquare* Square = GetLSquareUnder(); + + if(Square->CanBeSeenByPlayer(true)) + ADD_MESSAGE("%s explodes!", GetExtendedDescription().CStr()); + + RemoveFromSlot(); + SendToHell(); + Square->GetLevel()->Explosion(0, "killed by a land mine", Square->GetPos(), GetSecondaryMaterial()->GetTotalExplosivePower()); + return false; + } + + return true; +} + +void scrollofrepair::FinishReading(character* Reader) +{ + for(;;) + { + itemvector Item; + + if(!Reader->SelectFromPossessions(Item, CONST_S("Which item do you wish to repair?"), NO_MULTI_SELECT|SELECT_PAIR, &item::IsRepairable)) + { + ADD_MESSAGE("You notice you haven't got anything to repair."); + return; + } + + if(!Item.empty()) + { + if(Item[0]->HandleInPairs() && Item.size() == 1) + { + ADD_MESSAGE("Only one %s will be repaired.", Item[0]->CHAR_NAME(UNARTICLED)); + + if(!game::TruthQuestion(CONST_S("Still continue? [y/N]"))) + continue; + } + + if(Item.size() == 1) + ADD_MESSAGE("As you read the scroll, %s glows green and %s.", Item[0]->CHAR_NAME(DEFINITE), Item[0]->IsBroken() ? "fixes itself" : "its rust vanishes"); + else + ADD_MESSAGE("As you read the scroll, %s glow green and %s.", Item[0]->CHAR_NAME(PLURAL), Item[0]->IsBroken() ? "fix themselves" : "their rust vanishes"); + + for(uint c = 0; c < Item.size(); ++c) + { + Item[c]->RemoveRust(); + Item[c]->Fix(); + } + + break; + } + else if(game::TruthQuestion(CONST_S("Really cancel read? [y/N]"))) + return; + } + + RemoveFromSlot(); + SendToHell(); + Reader->EditExperience(INTELLIGENCE, 300, 1 << 12); +} + +item* brokenbottle::Fix() +{ + potion* Potion = potion::Spawn(0, NO_MATERIALS); + Potion->InitMaterials(GetMainMaterial(), 0); + DonateFluidsTo(Potion); + DonateIDTo(Potion); + DonateSlotTo(Potion); + SetMainMaterial(0, NO_PIC_UPDATE|NO_SIGNALS); + SendToHell(); + return Potion; +} + +truth encryptedscroll::Read(character*) +{ + ADD_MESSAGE("You could never hope to decipher this top secret message. It is meant for Petrus's eyes only."); + return false; +} + +void mondedrpass::FinishReading(character* Reader) +{ + ADD_MESSAGE("This sheet of paper contains many interesting facts about Mondedr. After finishing reading the pass burns up."); + RemoveFromSlot(); + SendToHell(); + Reader->EditExperience(INTELLIGENCE, 3000, 1 << 12); +} + +truth horn::Apply(character* Blower) +{ + if(!Blower->HasHead()) + { + if(Blower->IsPlayer()) + ADD_MESSAGE("You need a head to do this."); + + return false; + } + + if(!LastUsed || game::GetTick() - LastUsed >= 2500) + { + LastUsed = game::GetTick(); + //cchar* SoundDescription = GetConfig() == BRAVERY ? "loud but calming" : "frightening, almost scream-like"; + + cchar* SoundDescription; + + switch(GetConfig()) + { + case BRAVERY: SoundDescription = "loud but calming"; break; + case FEAR: SoundDescription = "frightening, almost scream-like"; break; + case CONFUSION: SoundDescription = "strange and dissonant"; break; + default: SoundDescription = "never-before heard"; break; + } + + if(Blower->IsPlayer()) + { + if(Blower->CanHear()) + ADD_MESSAGE("You produce a %s sound.", SoundDescription); + else + ADD_MESSAGE("You blow %s.", CHAR_NAME(DEFINITE)); + } + else if(Blower->CanBeSeenByPlayer()) + { + if(PLAYER->CanHear()) + ADD_MESSAGE("%s blows %s and produces a %s sound.", Blower->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE), SoundDescription); + else + ADD_MESSAGE("%s blows %s.", Blower->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + } + else if(PLAYER->CanHear()) + ADD_MESSAGE("You hear a %s sound echoing everywhere.", SoundDescription); + + rect Rect; + femath::CalculateEnvironmentRectangle(Rect, GetLevel()->GetBorder(), GetPos(), 10); + + for(int x = Rect.X1; x <= Rect.X2; ++x) + for(int y = Rect.Y1; y <= Rect.Y2; ++y) + { + character* Audience = GetNearSquare(x, y)->GetCharacter(); + + if(Audience) + { + if(GetConfig() == BRAVERY && Audience->CanHear() && Audience->TemporaryStateIsActivated(PANIC) + && Blower->IsAlly(Audience)) + { + if(Audience->IsPlayer()) + ADD_MESSAGE("You calm down."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s calms down.", Audience->CHAR_NAME(DEFINITE)); + + Audience->DeActivateTemporaryState(PANIC); + } + else if(GetConfig() == FEAR && !Audience->TemporaryStateIsActivated(PANIC) + && Blower->GetRelation(Audience) == HOSTILE && Audience->HornOfFearWorks()) + Audience->BeginTemporaryState(PANIC, 500 + RAND() % 500); + else if(GetConfig() == CONFUSION && Blower->GetRelation(Audience) == HOSTILE && Audience->CanHear()) + Audience->BeginTemporaryState(CONFUSED, 500 + RAND() % 500); + } + } + + } + else + { + if(Blower->IsPlayer()) + { + if(Blower->CanHear()) + ADD_MESSAGE("You produce a mighty sound."); + else + ADD_MESSAGE("You blow %s.", CHAR_NAME(DEFINITE)); + } + else if(Blower->CanBeSeenByPlayer()) + { + if(PLAYER->CanHear()) + ADD_MESSAGE("%s blows %s and produces a mighty sound.", Blower->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("%s blows %s.", Blower->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + } + else if(PLAYER->CanHear()) + ADD_MESSAGE("You hear a horn being blown."); + } + + game::CallForAttention(GetPos(), 900); + Blower->EditAP(-1000); + return true; +} + +void horn::Save(outputfile& SaveFile) const +{ + item::Save(SaveFile); + SaveFile << LastUsed; +} + +void horn::Load(inputfile& SaveFile) +{ + item::Load(SaveFile); + SaveFile >> LastUsed; +} + +item* bananapeels::BetterVersion() const +{ + return banana::Spawn(); +} + +truth beartrap::IsPickable(character* Picker) const +{ + return !IsActive() && !Picker->IsStuckToTrap(GetTrapID()); +} + +void banana::Save(outputfile& SaveFile) const +{ + materialcontainer::Save(SaveFile); + SaveFile << TimesUsed << Charges << Jammed; +} + +void banana::Load(inputfile& SaveFile) +{ + materialcontainer::Load(SaveFile); + SaveFile >> TimesUsed >> Charges >> Jammed; +} + +truth banana::Zap(character*, v2, int) +{ + if(IsBroken()) + { + ADD_MESSAGE("This banana seems to be somehow disfunctional."); + return false; + } + + if(Jammed) + { + ADD_MESSAGE("Unfortunately, your banana is jammed!"); + return false; + } + + if(Charges > TimesUsed) + { + if(TimesUsed && !RAND_N(10)) + { + ADD_MESSAGE("Oh no! Your banana jams in the middle of the firefight!"); + Jammed = true; + } + else + { + ADD_MESSAGE("BANG! You zap %s!", CHAR_NAME(DEFINITE)); + ++TimesUsed; + } + } + else + ADD_MESSAGE("Click!"); + + return true; +} + +void banana::SignalSpoil(material* Material) +{ + if(!Exists()) + return; + + if(Material == GetSecondaryMaterial() && !GetMainMaterial()->IsVeryCloseToSpoiling()) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("The inside of %s spoils completely.", GetExtendedDescription().CStr()); + + delete RemoveSecondaryMaterial(); + } + else + item::SignalSpoil(Material); +} + +truth bone::DogWillCatchAndConsume(ccharacter* Doggie) const +{ + return GetConsumeMaterial(Doggie)->GetConfig() == BONE + && !GetConsumeMaterial(Doggie)->GetSpoilLevel(); +} + +int itemcontainer::GetOfferValue(int Receiver) const +{ + int Sum = 0; + + for(int c = 0; c < GetContained()->GetItems(); ++c) + Sum += GetContained()->GetItem(c)->GetOfferValue(Receiver); + + return item::GetOfferValue(Receiver) + Sum; +} + +truth itemcontainer::IsDestroyable(ccharacter* Char) const +{ + for(int c = 0; c < GetContained()->GetItems(); ++c) + if(!GetContained()->GetItem(c)->IsDestroyable(Char)) + return false; + + return true; +} + +alpha lantern::GetAlphaB(int Frame) const +{ + Frame &= 31; + return (Frame * (31 - Frame) >> 1); +} + +alpha lantern::GetAlphaC(int Frame) const +{ + Frame &= 31; + return (Frame * (31 - Frame) >> 2); +} + +alpha lantern::GetAlphaD(int Frame) const +{ + Frame &= 31; + return (Frame * (31 - Frame) >> 3); +} +/* +alpha christmaslight::GetAlphaD(int Frame) const +{ + Frame &= 31; + return (Frame * (31 - Frame) >> 3); +} + +void christmaslight::CalculateEmitation() const +{ + if(!IsBroken()) + switch(RAND() % 4) + { + case 0: Emitation = MakeRGB24(0, 0, 125); break; + case 1: Emitation = MakeRGB24(0, 125, 0); break; + case 2: Emitation = MakeRGB24(125, 0, 0); break; + case 3: Emitation = MakeRGB24(125, 125, 0); break; + } + else + Emitation = GetBaseEmitation(); + //object::CalculateEmitation(); + //Emitation = GetBaseEmitation(); + + if(MainMaterial) + game::CombineLights(Emitation, MainMaterial->GetEmitation()); + // + + if(Fluid) + for(int c = 0; c < SquaresUnder; ++c) + for(const fluid* F = Fluid[c]; F; F = F->Next) + game::CombineLights(Emitation, F->GetEmitation()); +} + +col16 christmaslight::GetEmitationColor(int Frame) const +{ + if(!IsBroken()) + switch((Frame&127) >> 5) + { + case 0: return BLUE; + case 1: return GREEN; + case 2: return RED; + case 3: return YELLOW; + } + + return TRANSPARENT_COLOR; +} +*/ +void itemcontainer::SortAllItems(const sortdata& SortData) const +{ + item::SortAllItems(SortData); + + if(SortData.Recurse) + GetContained()->SortAllItems(SortData); +} + +int materialcontainer::GetAttachedGod() const +{ + return DataBase->AttachedGod ? DataBase->AttachedGod : SecondaryMaterial ? SecondaryMaterial->GetAttachedGod() : MainMaterial->GetAttachedGod(); +} + +void wand::BreakEffect(character* Terrorist, cfestring& DeathMsg) +{ + v2 Pos = GetPos(); + level* Level = GetLevel(); + RemoveFromSlot(); + ulong StackSize = Level->AddRadiusToSquareStack(Pos, GetBreakEffectRangeSquare()); + lsquare** SquareStack = Level->GetSquareStack(); + ulong c; + + for(c = 0; c < StackSize; ++c) + SquareStack[c]->RemoveFlags(IN_SQUARE_STACK); + + fearray Stack(SquareStack, StackSize); + (Level->*level::GetBeamEffectVisualizer(GetBeamStyle()))(Stack, GetBeamColor()); + + beamdata Beam + ( + Terrorist, + DeathMsg, + YOURSELF, + GetSpecialParameters() + ); + + for(c = 0; c < Stack.Size; ++c) + (Stack[c]->*lsquare::GetBeamEffect(GetBeamEffect()))(Beam); + + SendToHell(); +} + +truth beartrap::ReceiveDamage(character* Damager, int Damage, int Type, int) +{ + if(!IsBroken() && Type & PHYSICAL_DAMAGE && Damage) + { + if(Damage > 125 || !(RAND() % (250 / Damage))) + { + SetIsActive(false); + Break(Damager); + return true; + } + else + { + if(IsActive()) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s snaps shut.", CHAR_NAME(DEFINITE)); + + SetIsActive(false); + SendNewDrawAndMemorizedUpdateRequest(); + return true; + } + } + } + + return false; +} + +truth potion::ReceiveDamage(character* Damager, int Damage, int Type, int Dir) +{ + if(Type & FIRE && Damage && IsExplosive() && (Damage > 50 || !(RAND() % (100 / Damage)))) + { + festring DeathMsg = CONST_S("killed by an explosion of "); + AddName(DeathMsg, INDEFINITE); + + if(Damager) + DeathMsg << " caused @bk"; + + if(GetSquareUnder()->CanBeSeenByPlayer(true)) + ADD_MESSAGE("%s explodes!", GetExtendedDescription().CStr()); + + lsquare* Square = GetLSquareUnder(); + RemoveFromSlot(); + SendToHell(); + Square->GetLevel()->Explosion(Damager, DeathMsg, Square->GetPos(), GetSecondaryMaterial()->GetTotalExplosivePower()); + return true; + } + + if(Type & THROW) + { + int StrengthValue = GetStrengthValue(); + + if(!StrengthValue) + StrengthValue = 1; + + if(Damage > StrengthValue << 2 && RAND() % (50 * Damage / StrengthValue) >= 100) + { + Break(Damager, Dir); + return true; + } + } + + return item::ReceiveDamage(Damager, Damage, Type, Dir); +} + +void beartrap::Fly(character* Thrower, int Direction, int Force) +{ + if(!IsStuck()) + item::Fly(Thrower, Direction, Force); +} + +void can::DipInto(liquid* Liquid, character* Dipper) +{ + /* Add alchemy */ + + if(Dipper->IsPlayer()) + ADD_MESSAGE("%s is now filled with %s.", CHAR_NAME(DEFINITE), Liquid->GetName(false, false).CStr()); + + ChangeSecondaryMaterial(Liquid); + Dipper->DexterityAction(10); +} + +truth holybanana::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = banana::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(Enemy->IsEnabled() && RAND() & 1) + { + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("%s banana burns %s.", Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_DESCRIPTION(DEFINITE)); + + return Enemy->ReceiveBodyPartDamage(Hitter, 2 + (RAND() & 3), FIRE, BodyPartIndex, Direction) || BaseSuccess; + } + else + return BaseSuccess; +} + +truth holybanana::Zap(character* Zapper, v2, int Direction) +{ + if(Charges > TimesUsed) + { + ADD_MESSAGE("BANG! You zap %s!", CHAR_NAME(DEFINITE)); + Zapper->EditExperience(PERCEPTION, 150, 1 << 10); + + beamdata Beam + ( + Zapper, + CONST_S("killed by ") + GetName(INDEFINITE), + Zapper->GetPos(), + YELLOW, + BEAM_FIRE_BALL, + Direction, + 50, + 0 + ); + + (GetLevel()->*level::GetBeam(PARTICLE_BEAM))(Beam); + ++TimesUsed; + } + else + ADD_MESSAGE("Click!"); + + return true; +} + +void holybanana::AddInventoryEntry(ccharacter* Viewer, festring& Entry, int, truth ShowSpecialInfo) const // never piled +{ + AddName(Entry, INDEFINITE); + + if(ShowSpecialInfo) + { + Entry << " [" << GetWeight() << "g, DAM " << GetBaseMinDamage() << '-' << GetBaseMaxDamage(); + Entry << ", " << GetBaseToHitValueDescription(); + + if(!IsBroken()) + Entry << ", " << GetStrengthValueDescription(); + + int CWeaponSkillLevel = Viewer->GetCWeaponSkillLevel(this); + int SWeaponSkillLevel = Viewer->GetSWeaponSkillLevel(this); + + if(CWeaponSkillLevel || SWeaponSkillLevel) + Entry << ", skill " << CWeaponSkillLevel << '/' << SWeaponSkillLevel; + + if(TimesUsed == 1) + Entry << ", used 1 time"; + else if(TimesUsed) + Entry << ", used " << TimesUsed << " times"; + + Entry << ']'; + } +} + +truth holybanana::ReceiveDamage(character* Damager, int Damage, int Type, int) +{ + if(TimesUsed != 6 && Type & (PHYSICAL_DAMAGE|FIRE|ENERGY) && Damage && (Damage > 50 || !(RAND() % (100 / Damage)))) + { + festring DeathMsg = CONST_S("killed by an explosion of "); + AddName(DeathMsg, INDEFINITE); + + if(Damager) + DeathMsg << " caused @bk"; + + if(GetSquareUnder()->CanBeSeenByPlayer(true)) + ADD_MESSAGE("%s explodes!", GetExtendedDescription().CStr()); + + lsquare* Square = GetLSquareUnder(); + RemoveFromSlot(); + SendToHell(); + Square->GetLevel()->Explosion(Damager, DeathMsg, Square->GetPos(), (6 - TimesUsed) * 50); + return true; + } + + return false; +} + +truth holymango::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = 1;//mango::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(Enemy->IsEnabled() && RAND() & 1) + { + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("%s mango burns %s.", Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_DESCRIPTION(DEFINITE)); + + return Enemy->ReceiveBodyPartDamage(Hitter, 2 + (RAND() & 3), FIRE, BodyPartIndex, Direction) || BaseSuccess; + } + else + return BaseSuccess; +} +/* +truth holymango::Zap(character* Zapper, v2, int Direction) +{ + if(Charges > TimesUsed) + { + ADD_MESSAGE("BANG! You zap %s!", CHAR_NAME(DEFINITE)); + Zapper->EditExperience(PERCEPTION, 150, 1 << 10); + + beamdata Beam + ( + Zapper, + CONST_S("killed by ") + GetName(INDEFINITE), + Zapper->GetPos(), + YELLOW, + BEAM_FIRE_BALL, + Direction, + 50, + 0 + ); + + (GetLevel()->*level::GetBeam(PARTICLE_BEAM))(Beam); + ++TimesUsed; + } + else + ADD_MESSAGE("Click!"); + + return true; +}*/ + +void holymango::AddInventoryEntry(ccharacter* Viewer, festring& Entry, int, truth ShowSpecialInfo) const // never piled +{ + AddName(Entry, INDEFINITE); + + if(ShowSpecialInfo) + { + Entry << " [" << GetWeight() << "g, DAM " << GetBaseMinDamage() << '-' << GetBaseMaxDamage(); + Entry << ", " << GetBaseToHitValueDescription(); + + if(!IsBroken()) + Entry << ", " << GetStrengthValueDescription(); + + int CWeaponSkillLevel = Viewer->GetCWeaponSkillLevel(this); + int SWeaponSkillLevel = Viewer->GetSWeaponSkillLevel(this); + + if(CWeaponSkillLevel || SWeaponSkillLevel) + Entry << ", skill " << CWeaponSkillLevel << '/' << SWeaponSkillLevel; + +// if(TimesUsed == 1) +// Entry << ", used 1 time"; +// else if(TimesUsed) +// Entry << ", used " << TimesUsed << " times"; + + Entry << ']'; + } +} + +truth holymango::ReceiveDamage(character* Damager, int Damage, int Type, int) +{ + if(Type & (PHYSICAL_DAMAGE|FIRE|ENERGY) && Damage && (Damage > 50 || !(RAND() % (100 / Damage)))) + { + festring DeathMsg = CONST_S("killed by an explosion of "); + AddName(DeathMsg, INDEFINITE); + + if(Damager) + DeathMsg << " caused @bk"; + + if(GetSquareUnder()->CanBeSeenByPlayer(true)) + ADD_MESSAGE("%s explodes!", GetExtendedDescription().CStr()); + + lsquare* Square = GetLSquareUnder(); + RemoveFromSlot(); + SendToHell(); + Square->GetLevel()->Explosion(Damager, DeathMsg, Square->GetPos(), (6 ) * 50); + return true; + } + + return false; +} + +void beartrap::PreProcessForBone() +{ + mybase::PreProcessForBone(); + game::RemoveTrapID(TrapData.TrapID); + TrapData.TrapID = 0; +} + +void beartrap::PostProcessForBone() +{ + mybase::PostProcessForBone(); + TrapData.TrapID = game::CreateNewTrapID(this); +} + +void itemcontainer::PreProcessForBone() +{ + item::PreProcessForBone(); + Contained->PreProcessForBone(); +} + +void itemcontainer::PostProcessForBone() +{ + item::PostProcessForBone(); + Contained->PostProcessForBone(); +} + +void itemcontainer::FinalProcessForBone() +{ + item::FinalProcessForBone(); + Contained->FinalProcessForBone(); +} + +void magicalwhistle::FinalProcessForBone() +{ + whistle::FinalProcessForBone(); + LastUsed = 0; +} + +void horn::FinalProcessForBone() +{ + item::FinalProcessForBone(); + LastUsed = 0; +} + +truth charmlyre::Apply(character* Charmer) +{ + if(LastUsed && game::GetTick() - LastUsed < 10000) + { + if(Charmer->IsPlayer()) + { + if(Charmer->CanHear()) + ADD_MESSAGE("You produce a highly alluring sound."); + else + ADD_MESSAGE("You try to play something with the %s, but it is hard when you can't hear.", CHAR_NAME(DEFINITE)); + } + else if(Charmer->CanBeSeenByPlayer()) + { + if(PLAYER->CanHear()) + ADD_MESSAGE("%s plays %s and produces a highly alluring sound.", Charmer->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("%s plays %s.", Charmer->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + } + else if(PLAYER->CanHear()) + ADD_MESSAGE("You hear a lyre playing."); + } + else + { + LastUsed = game::GetTick(); + if(Charmer->IsPlayer()) + { + if(Charmer->CanHear()) + ADD_MESSAGE("You produce a mesmerizing sound."); + else + ADD_MESSAGE("You try to play something with the %s, but it is hard when you can't hear.", CHAR_NAME(DEFINITE)); + } + else if(Charmer->CanBeSeenByPlayer()) + { + if(PLAYER->CanHear()) + ADD_MESSAGE("%s plays %s and produces a mesmerizing sound.", Charmer->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("%s plays %s.", Charmer->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + } + + for(int d = 0; d < Charmer->GetNeighbourSquares(); ++d) + { + square* Square = Charmer->GetNeighbourSquare(d); + + if(Square) + { + character* Char = Square->GetCharacter(); + + if(Char) + if(Char->CanHear()) + { + if(Char->CanTameWithLyre(Charmer)) + { + if(Char->GetTeam() == Charmer->GetTeam()) + ADD_MESSAGE("%s seems to be very happy.", Char->CHAR_NAME(DEFINITE)); + else if(Char->GetRelation(Charmer) == HOSTILE) + ADD_MESSAGE("%s stops fighting.", Char->CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("%s seems to be very friendly towards you.", Char->CHAR_NAME(DEFINITE)); + + Char->ChangeTeam(Charmer->GetTeam()); + } + else + ADD_MESSAGE("%s resists its charming call.", Char->CHAR_NAME(DEFINITE)); + } + else + ADD_MESSAGE("%s seems not affected.", Char->CHAR_NAME(DEFINITE)); + } + } + } + + Charmer->EditAP(-1000); + game::CallForAttention(GetPos(), 100); + return true; +} + +void charmlyre::Save(outputfile& SaveFile) const +{ + item::Save(SaveFile); + SaveFile << LastUsed; +} + +void charmlyre::Load(inputfile& SaveFile) +{ + item::Load(SaveFile); + SaveFile >> LastUsed; +} + +charmlyre::charmlyre() +{ + LastUsed = 0; +} + +void charmlyre::FinalProcessForBone() +{ + item::FinalProcessForBone(); + LastUsed = 0; +} + +truth carrot::BunnyWillCatchAndConsume(ccharacter* Bunny) const +{ + return GetConsumeMaterial(Bunny)->GetConfig() == CARROT_FLESH + && !GetConsumeMaterial(Bunny)->GetSpoilLevel(); +} + +int materialcontainer::GetRustDataB() const +{ + return SecondaryMaterial ? SecondaryMaterial->GetRustData() : GetRustDataA(); +} + +void backpack::SpillFluid(character* Spiller, liquid* Liquid, int SquareIndex) +{ + if(!Liquid->IsExplosive()) + { + GetSecondaryMaterial()->AddWetness(Liquid->GetVolume() * 25); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s gets wet.", CHAR_NAME(DEFINITE)); + } + + item::SpillFluid(Spiller, Liquid, SquareIndex); +} + +material* materialcontainer::GetConsumeMaterial(ccharacter* Consumer, materialpredicate Predicate) const +{ + if(SecondaryMaterial + && (SecondaryMaterial->*Predicate)() + && Consumer->CanConsume(SecondaryMaterial)) + return SecondaryMaterial; + else + return item::GetConsumeMaterial(Consumer, Predicate); +} + +material* kleinbottle::GetConsumeMaterial(ccharacter* Consumer, materialpredicate Predicate) const +{ + if(SecondaryMaterial + && (SecondaryMaterial->*Predicate)() + && Consumer->CanConsume(SecondaryMaterial) + && Consumer->StateIsActivated(TELEPORT)) + return SecondaryMaterial; + else + return item::GetConsumeMaterial(Consumer, Predicate); +} + +material* materialcontainer::RemoveMaterial(material* Material) +{ + if(Material == MainMaterial) + return RemoveMainMaterial(); + else + return RemoveSecondaryMaterial(); +} + +material* materialcontainer::RemoveMainMaterial() +{ + truth Equipped = PLAYER->Equips(this); + + if(!SecondaryMaterial) + RemoveFromSlot(); + else if(SecondaryMaterial->IsLiquid()) + { + if(!game::IsInWilderness()) + { + lsquare* Square = GetLSquareUnder(); + RemoveFromSlot(); + Square->SpillFluid(0, static_cast(SecondaryMaterial)); + SetSecondaryMaterial(0, NO_PIC_UPDATE|NO_SIGNALS); + } + else + RemoveFromSlot(); + } + else + { + item* Lump = lump::Spawn(0, NO_MATERIALS); + Lump->InitMaterials(SecondaryMaterial); + DonateFluidsTo(Lump); + DonateIDTo(Lump); + DonateSlotTo(Lump); + SetSecondaryMaterial(0, NO_PIC_UPDATE|NO_SIGNALS); + } + + SendToHell(); + + if(Equipped) + game::AskForKeyPress(CONST_S("Equipment destroyed! [press any key to continue]")); + + return 0; +} + +material* materialcontainer::RemoveSecondaryMaterial() +{ + material* Material = SecondaryMaterial; + SetSecondaryMaterial(0); + SendNewDrawAndMemorizedUpdateRequest(); + return Material; +} + +material* banana::RemoveSecondaryMaterial() +{ + item* Peel = bananapeels::Spawn(0, NO_MATERIALS); + Peel->InitMaterials(MainMaterial); + DonateSlotTo(Peel); + DonateIDTo(Peel); + SetMainMaterial(0, NO_PIC_UPDATE|NO_SIGNALS); + SendToHell(); + return 0; +} + +material* itemcontainer::RemoveMaterial(material* Material) +{ + Contained->MoveItemsTo(GetSlot()); + return item::RemoveMaterial(Material); +} + +void materialcontainer::CalculateEmitation() +{ + Emitation = GetBaseEmitation(); + + if(MainMaterial) + game::CombineLights(Emitation, MainMaterial->GetEmitation()); + + if(SecondaryMaterial) + game::CombineLights(Emitation, SecondaryMaterial->GetEmitation()); +} + +truth materialcontainer::CalculateHasBe() const +{ + return LifeExpectancy + || (MainMaterial && MainMaterial->HasBe()) + || (SecondaryMaterial && SecondaryMaterial->HasBe()); +} + +v2 lantern::GetBitmapPos(int Frame) const +{ + return GetSquarePosition() == CENTER + ? item::GetBitmapPos(Frame) + : item::GetWallBitmapPos(Frame); +} + +long materialcontainer::GetMaterialPrice() const +{ + return MainMaterial->GetRawPrice() + + (SecondaryMaterial ? SecondaryMaterial->GetRawPrice() : 0); +} + +void scrollofdetectmaterial::FinishReading(character* Reader) +{ + material* TempMaterial; + + for(;;) + { + festring Temp = game::DefaultQuestion(CONST_S("What material do you want to detect?"), + game::GetDefaultDetectMaterial()); + TempMaterial = protosystem::CreateMaterial(Temp); + + if(TempMaterial) + break; + else + game::DrawEverythingNoBlit(); + } + + level* Level = GetLevel(); + int Squares = Level->DetectMaterial(TempMaterial); + RemoveFromSlot(); + SendToHell(); + + if(Squares > Reader->GetAttribute(INTELLIGENCE) * (25 + RAND() % 51)) + { + ADD_MESSAGE("An enormous burst of geographical information overwhelms your consciousness. Your mind cannot cope with it and your memories blur."); + Level->BlurMemory(); + Reader->BeginTemporaryState(CONFUSED, 1000 + RAND() % 1000); + Reader->EditExperience(INTELLIGENCE, -100, 1 << 12); + } + else if(!Squares) + { + ADD_MESSAGE("You feel a sudden urge to imagine the dark void of a starless night sky."); + Reader->EditExperience(INTELLIGENCE, 200, 1 << 12); + } + else + { + ADD_MESSAGE("You feel attracted to all things made of %s.", TempMaterial->GetName(false, false).CStr()); + game::PositionQuestion(CONST_S("Detecting material [direction keys move cursor, space exits]"), Reader->GetPos(), 0, 0, false); + Reader->EditExperience(INTELLIGENCE, 300, 1 << 12); + } + + delete TempMaterial; + Level->CalculateLuminances(); + game::SendLOSUpdateRequest(); +} + +int beartrap::GetBaseTrapDamage() const +{ + int Modifier = GetMainMaterial()->GetStrengthValue() / 50; + Modifier *= Modifier; + Modifier >>= 1; + return Modifier ? Modifier + RAND_N(Modifier) : 1; +} + +void scrollofhardenmaterial::FinishReading(character* Reader) +{ + for(;;) + { + itemvector Item; + + if(!Reader->SelectFromPossessions(Item, CONST_S("What item do you wish to harden?"), NO_MULTI_SELECT|SELECT_PAIR, &item::CanBeHardened)) + { + ADD_MESSAGE("You notice you haven't got anything to harden."); + return; + } + + if(Item.empty()) + if(game::TruthQuestion(CONST_S("Really cancel read? [y/N]"))) + return; + else + continue; + + if(!Item[0]->IsMaterialChangeable()) + { + ADD_MESSAGE("You cast the spell, but the magic is not powerful enough to affect %s!", Item[0]->CHAR_NAME(DEFINITE|(Item.size() == 1 ? 0 : PLURAL))); + break; + } + + if(Item[0]->HandleInPairs() && Item.size() == 1) + { + ADD_MESSAGE("Only one %s will be altered.", Item[0]->CHAR_NAME(UNARTICLED)); + + if(!game::TruthQuestion(CONST_S("Still continue? [y/N]"))) + continue; + } + + msgsystem::EnterBigMessageMode(); + + if(Item.size() == 1) + ADD_MESSAGE("Suddenly your %s is consumed in roaring magical flames.", Item[0]->CHAR_NAME(UNARTICLED)); + else + ADD_MESSAGE("Suddenly your %s are consumed in roaring magical flames.", Item[0]->CHAR_NAME(PLURAL)); + + int Config = Item[0]->GetMainMaterial()->GetHardenedMaterial(Item[0]); + + if(!Config) + { + /* Should not be possible */ + + if(Item.size() == 1) + ADD_MESSAGE("But it is already as hard as it can get."); + else + ADD_MESSAGE("But they are already as hard as they can get."); + + msgsystem::LeaveBigMessageMode(); + break; + } + + material* TempMaterial = MAKE_MATERIAL(Config); + int Intelligence = Reader->GetAttribute(INTELLIGENCE); + + if(TempMaterial->GetIntelligenceRequirement() > Intelligence && !game::WizardModeIsActive()) + { + delete TempMaterial; + ADD_MESSAGE("But your mind is not yet strong enough to harden %s.", Item.size() == 1 ? "it" : "them"); + msgsystem::LeaveBigMessageMode(); + continue; + } + + for(int NewConfig = TempMaterial->GetHardenedMaterial(Item[0]), c = 1; + NewConfig; + NewConfig = TempMaterial->GetHardenedMaterial(Item[0]), ++c) + { + material* NewMaterial = MAKE_MATERIAL(NewConfig); + + if(NewMaterial->GetIntelligenceRequirement() + <= Intelligence - c * 5) + { + delete TempMaterial; + TempMaterial = NewMaterial; + } + else + break; + } + + material* MainMaterial = Item[0]->GetMainMaterial(); + material* SecondaryMaterial = Item[0]->GetSecondaryMaterial(); + + if(Item.size() == 1) + { + ADD_MESSAGE("As the fire dies out it looks much harder."); + + if(SecondaryMaterial && SecondaryMaterial->IsSameAs(MainMaterial)) + Item[0]->ChangeSecondaryMaterial(TempMaterial->SpawnMore()); + + Item[0]->ChangeMainMaterial(TempMaterial); + } + else + { + ADD_MESSAGE("As the fire dies out they look much harder."); + + if(SecondaryMaterial && SecondaryMaterial->IsSameAs(MainMaterial)) + for(uint c = 0; c < Item.size(); ++c) + Item[c]->ChangeSecondaryMaterial(TempMaterial->SpawnMore()); + + Item[0]->ChangeMainMaterial(TempMaterial); + + for(uint c = 1; c < Item.size(); ++c) + Item[c]->ChangeMainMaterial(TempMaterial->SpawnMore()); + } + + msgsystem::LeaveBigMessageMode(); + break; + } + + RemoveFromSlot(); + SendToHell(); + Reader->EditExperience(INTELLIGENCE, 300, 1 << 12); +} + +void itemcontainer::SetLifeExpectancy(int Base, int RandPlus) +{ + LifeExpectancy = RandPlus > 1 ? Base + RAND_N(RandPlus) : Base; + Enable(); + Contained->SetLifeExpectancy(Base, RandPlus); +} + +ulong wand::GetSpecialParameters() const +{ + switch(GetConfig()) + { + case WAND_OF_MIRRORING: + return MIRROR_IMAGE|(1000 << LE_BASE_SHIFT)|(1000 << LE_RAND_SHIFT); + } + + return 0; +} + +void scrollofgolemcreation::FinishReading(character* Reader) +{ + for(;;) + { + itemvector Item; + + if(!Reader->SelectFromPossessions(Item, CONST_S("Which item do you wish to use for golem creation?"), NO_MULTI_SELECT, &item::IsDestroyable)) + { + ADD_MESSAGE("You notice you haven't got anything to change into a golem."); + return; + } + + if(!Item.empty()) + { + material* Main = Item[0]->GetMainMaterial(); + material* Sec = Item[0]->GetSecondaryMaterial(); + truth MainPossible = Main->GetCategoryFlags() & IS_GOLEM_MATERIAL; + truth SecPossible = Sec && Sec->GetVolume() + && Sec->GetCategoryFlags() & IS_GOLEM_MATERIAL + && !Sec->IsSameAs(Main); + + if(!MainPossible && !SecPossible) + { + ADD_MESSAGE("You can't use that for golem creation."); + continue; + } + + if(MainPossible && SecPossible) + if(game::TruthQuestion(CONST_S("Use main material? [Y/n]"), YES)) + SecPossible = false; + else + MainPossible = false; + + int MaterialConfig = MainPossible ? Main->GetConfig() : Sec->GetConfig(); + golem* Golem = golem::Spawn(MaterialConfig); + long Volume = MainPossible ? Sec && Sec->IsSameAs(Main) + ? Main->GetVolume() + Sec->GetVolume() + : Main->GetVolume() : Sec->GetVolume(); + Golem->SetItemVolume(Volume); + v2 Where = GetLevel()->GetNearestFreeSquare(Golem, Reader->GetPos()); + Item[0]->RemoveFromSlot(); + Item[0]->SendToHell(); + + if(Where == ERROR_V2) + { + ADD_MESSAGE("You cast the spell and %s is sucked into a rainbow-coled magical vortex, but nothing happens.", Item[0]->CHAR_NAME(DEFINITE)); + delete Golem; + } + else + { + ADD_MESSAGE("You cast the spell and %s is sucked into a rainbow-coled magical vortex.", Item[0]->CHAR_NAME(DEFINITE)); + Golem->SetTeam(Reader->GetTeam()); + Golem->PutTo(Where); + + if(Golem->CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s materializes!", Golem->CHAR_NAME(INDEFINITE)); + + Golem->GetLSquareUnder()->DrawParticles(RED); + } + + break; + } + else if(game::TruthQuestion(CONST_S("Really cancel read? [y/N]"))) + return; + } + + RemoveFromSlot(); + SendToHell(); + Reader->EditExperience(INTELLIGENCE, 300, 1 << 12); +} + +void itemcontainer::CalculateEnchantment() +{ + Contained->CalculateEnchantments(); +} + +int itemcontainer::GetTeleportPriority() const +{ + long Priority = item::GetTeleportPriority(); + + for(stackiterator i = Contained->GetBottom(); i.HasItem(); ++i) + Priority += i->GetTeleportPriority(); + + return Priority; +} + +void itemcontainer::SetParameters(int Param) +{ + SetIsLocked(Param & LOCKED); +} + +truth bananapeels::RaiseTheDead(character*) +{ + GetSlot()->AddFriendItem(banana::Spawn()); + RemoveFromSlot(); + SendToHell(); + return true; +} + +void beartrap::RemoveFromSlot() +{ + character* Char = game::SearchCharacter(GetVictimID()); + + if(Char) + Char->RemoveTrap(GetTrapID()); + + TrapData.VictimID = 0; + item::RemoveFromSlot(); +} + +void beartrap::DonateSlotTo(item* Item) +{ + character* Char = game::SearchCharacter(GetVictimID()); + + if(Char) + Char->RemoveTrap(GetTrapID()); + + TrapData.VictimID = 0; + item::DonateSlotTo(Item); +} + +void itemcontainer::Disappear() +{ + Contained->MoveItemsTo(GetSlot()); + item::Disappear(); +} + +truth gasgrenade::ReceiveDamage(character* Damager, int Damage, int Type, int) +{ + if(Type & THROW || + (Type & (PHYSICAL_DAMAGE|FIRE|ENERGY) && Damage && (!(RAND_N(10 / Damage + 1))))) + { + if(GetSquareUnder()->CanBeSeenByPlayer(true)) + ADD_MESSAGE("%s explodes!", GetExtendedDescription().CStr()); + + material* GasMaterial = GetSecondaryMaterial(); + GetLevel()->GasExplosion(static_cast(GasMaterial), GetLSquareUnder()); + RemoveFromSlot(); + SendToHell(); + return true; + } + + return false; +} + +truth holyhandgrenade::Apply(character* Applier) +{ + if(PinPulledTick) + { + ADD_MESSAGE("The pin of %s has already been removed.", CHAR_NAME(DEFINITE)); + return false; + } + + if(Applier->IsPlayer()) { + ADD_MESSAGE("You pull the pin off the grenade."); + } + + PinPulledTick = game::GetTick(); + Count = 0; + PinPullerID = Applier->GetID(); + Enable(); + UpdatePictures(); + return true; +} + +truth holyhandgrenade::CalculateHasBe() const +{ + return PinPulledTick; +} + +void holyhandgrenade::Be() { + item::Be(); + if(3 * (game::GetTick() - PinPulledTick) > Count * 100) + { + ++Count; + festring Msg = "A voice loudly declares: \""; + switch(Count) + { + case 1: + Msg << "ONE"; + break; + case 2: + Msg << "TWO"; + break; + case 3: + Msg << "THREE"; + break; + } + Msg << "\"."; + ADD_MESSAGE(Msg.CStr()); + if(Count == 3) { + Explode(); + } + } +} + +void holyhandgrenade::Explode() +{ + if(game::IsInWilderness()) { + ADD_MESSAGE("You manage to dispose of %s.", CHAR_NAME(DEFINITE)); + RemoveFromSlot(); + SendToHell(); + return; + } + character* Damager = game::SearchCharacter(PinPullerID); + festring DeathMsg = CONST_S("killed by an explosion of "); + AddName(DeathMsg, INDEFINITE); + + if(Damager) + DeathMsg << " caused @bk"; + + if(GetSquareUnder()->CanBeSeenByPlayer(true)) + ADD_MESSAGE("%s explodes!", GetExtendedDescription().CStr()); + + lsquare* Square = GetLSquareUnder(); + RemoveFromSlot(); + SendToHell(); + Square->GetLevel()->Explosion(Damager, DeathMsg, Square->GetPos(), 300); +} + +v2 holyhandgrenade::GetBitmapPos(int Frame) const +{ + return PinPulledTick ? v2(96, 64) : v2(96, 32); +} + +int holyhandgrenade::GetClassAnimationFrames() const +{ + return 32; +} + +alpha holyhandgrenade::GetOutlineAlpha(int Frame) const +{ + if(!PinPulledTick) + return 0; + Frame &= 31; + return 50 + (Frame * (31 - Frame) >> 1); +} + +col16 holyhandgrenade::GetOutlineColor(int) const +{ + return MakeRGB16(0, 255, 0); +} + +void holyhandgrenade::Save(outputfile& SaveFile) const +{ + item::Save(SaveFile); + SaveFile << PinPulledTick << Count << PinPullerID; +} + +void holyhandgrenade::Load(inputfile& SaveFile) +{ + item::Load(SaveFile); + SaveFile >> PinPulledTick >> Count >> PinPullerID; +} + +void holyhandgrenade::PreProcessForBone() +{ + if(PinPulledTick) + { + RemoveFromSlot(); + SendToHell(); + } +} + +void holyhandgrenade::PostConstruct() +{ + PinPulledTick = 0; + Count = 0; + PinPullerID = 0; +} + +col16 holyhandgrenade::GetMaterialColorB(int) const +{ + return MakeRGB16(200, 10, 10); +} + +bool holyhandgrenade::WillExplodeSoon() const +{ + return PinPulledTick != 0; +} + +void pantheonbook::FinishReading(character* Reader) +{ + if(Reader->IsPlayer()) + { + PLAYER->EditExperience(INTELLIGENCE, 2000, 1 << 12); + PLAYER->EditExperience(WISDOM, 2000, 1 << 12); + + ADD_MESSAGE("The book reveals many divine secrets of the pantheon to you and disappears."); + game::AdjustRelationsToAllGods(75); + RemoveFromSlot(); + SendToHell(); + } +} + +col16 pantheonbook::GetMaterialColorA(int) const +{ + return MakeRGB16(200, 200, 200); +} + +void celestialmonograph::FinishReading(character* Reader) +{ + if(Reader->IsPlayer()) + { + PLAYER->EditExperience(INTELLIGENCE, 500, 1 << 12); + PLAYER->EditExperience(WISDOM, 250, 1 << 12); + game::SetRelationsToAllGods(0); + for(int c = 1; c <= GODS; ++c) + if(game::GetGod(c)->IsKnown()) + game::GetGod(c)->SetIsKnown(false); + + ADD_MESSAGE("The stimulating celestial monograph removes all your memory of the pantheon and disappears."); + RemoveFromSlot(); + SendToHell(); + } +} + +col16 celestialmonograph::GetMaterialColorA(int) const +{ + return MakeRGB16(40, 140, 40); +} + +void constitution::FinishReading(character* Reader) +{ + if(Reader->IsPlayer()) + { + game::TextScreen(CONST_S( "You see here an article on repelling an imperialist invasion.\n" + "It is difficult to read, being rather hastily handwritten, probably by the Tweraifian\n" + "lawmakers just before the invasion was complete. It reads:\n\n" + + "\"Article 62.5: In Case of Imperialist Invasion:\"\n\n" + + "\"Having freed the people of Tweraif of the Imperialist government, take the seedling\n" + "of the Holy Mango Tree, and plant the seedling upon that wretched banana drop area. \n" + "Then, thou wilt be pronounced the Liberator of Tweraif.\"")); + + GetArea()->SendNewDrawRequest(); + ADD_MESSAGE("You feel you know now what you must do. The constitution vanishes."); + + RemoveFromSlot(); + SendToHell(); + } +} + +col16 constitution::GetMaterialColorA(int) const +{ + return MakeRGB16(200, 200, 200); +} + +col16 gorovitscopyoflenin::GetMaterialColorB(int) const +{ + return MakeRGB16(30, 30, 30); +} + +bool firstbornchild::SpecialOfferEffect(int GodNumber) { + god* Receiver = game::GetGod(GodNumber); + Receiver->AdjustRelation(2000); // to the max + + int AmountOfAngelAppears = false; + for(int c = 0; c < 10; ++c) { + character* Angel = Receiver->CreateAngel(PLAYER->GetTeam(), 1000); + if(Angel) + { + ++AmountOfAngelAppears; + } + } + + if(AmountOfAngelAppears == 0) { + ADD_MESSAGE("You sacrifice %s. %s is very pleased.", + CHAR_NAME(DEFINITE), Receiver->GetName()); + } + else if(AmountOfAngelAppears == 1) { + ADD_MESSAGE("You sacrifice %s. %s is very pleased. An angel appears! ", + CHAR_NAME(DEFINITE), Receiver->GetName()); + } + else { + ADD_MESSAGE("You sacrifice %s. %s is very pleased. An army of angels appears! ", + CHAR_NAME(DEFINITE), Receiver->GetName()); + } + + return true; +} + +col16 firstbornchild::GetMaterialColorB(int) const { return MakeRGB16(160, 160, 160); } + +truth ullrbone::HitEffect(character* Enemy, character* Hitter, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = item::HitEffect(Enemy, Hitter, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(Enemy->IsEnabled() && RAND() & 1) + { + if(Enemy->IsPlayer() || Hitter->IsPlayer() || Enemy->CanBeSeenByPlayer() || Hitter->CanBeSeenByPlayer()) + ADD_MESSAGE("A burst of %s bone of Ullr's unholy energy fries %s.", Hitter->CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_DESCRIPTION(DEFINITE)); + + return Enemy->ReceiveBodyPartDamage(Hitter, 3 + (RAND() & 3), ENERGY, BodyPartIndex, Direction) || BaseSuccess; + } + else + return BaseSuccess; +} + +truth ullrbone::Zap(character* Zapper, v2, int Direction) +{ + if(Charges > TimesUsed) + { + ADD_MESSAGE("BANG! You zap %s!", CHAR_NAME(DEFINITE)); + Zapper->EditExperience(PERCEPTION, 150, 1 << 10); + + beamdata Beam + ( + Zapper, + CONST_S("killed by ") + GetName(INDEFINITE), + Zapper->GetPos(), + YELLOW, + BEAM_LIGHTNING, + Direction, + 50, + 0 + ); + + (GetLevel()->*level::GetBeam(PARTICLE_BEAM))(Beam); + ++TimesUsed; + } + else + ADD_MESSAGE("Click!"); + + return true; +} + +/*truth theredlantern::Zap(character* Zapper, v2, int Direction) +{ + if(!IsBroken) + { + ADD_MESSAGE("The red lantern discharges a powerful stream of lightning!", CHAR_NAME(DEFINITE)); + Zapper->EditExperience(PERCEPTION, 150, 1 << 10); + + beamdata Beam + ( + Zapper, + CONST_S("killed by ") + GetName(INDEFINITE), + Zapper->GetPos(), + RED, + BEAM_LIGHTNING, + Direction, + 200, + 0 + ); + + (GetLevel()->*level::GetBeam(PARTICLE_BEAM))(Beam); + + } + else + ADD_MESSAGE("Nothing happens."); + + return true; +}*/ + +void ullrbone::AddInventoryEntry(const character* Viewer, festring& Entry, int, truth ShowSpecialInfo) const // never piled +{ + AddName(Entry, INDEFINITE); + + if(ShowSpecialInfo) + { + Entry << " [" << GetWeight() << "g, DAM " << GetBaseMinDamage() << '-' << GetBaseMaxDamage(); + Entry << ", " << GetBaseToHitValueDescription(); + + if(!IsBroken()) + Entry << ", " << GetStrengthValueDescription(); + + int CWeaponSkillLevel = Viewer->GetCWeaponSkillLevel(this); + int SWeaponSkillLevel = Viewer->GetSWeaponSkillLevel(this); + + if(CWeaponSkillLevel || SWeaponSkillLevel) + Entry << ", skill " << CWeaponSkillLevel << '/' << SWeaponSkillLevel; + + if(TimesUsed == 1) + Entry << ", used 1 time"; + else if(TimesUsed) + Entry << ", used " << TimesUsed << " times"; + + Entry << ']'; + } +} + +truth ullrbone::ReceiveDamage(character* Damager, int Damage, int Type, int) +{ + if(TimesUsed != 12 && Type & (PHYSICAL_DAMAGE|FIRE|ENERGY) && Damage && (Damage > 50 || !(RAND() % (100 / Damage)))) + { + festring DeathMsg = CONST_S("killed by an explosion of "); + AddName(DeathMsg, INDEFINITE); + + if(Damager) + DeathMsg << " caused @bk"; + + if(GetSquareUnder()->CanBeSeenByPlayer(true)) + ADD_MESSAGE("%s explodes!", GetExtendedDescription().CStr()); + + lsquare* Square = GetLSquareUnder(); + RemoveFromSlot(); + SendToHell(); + Square->GetLevel()->Explosion(Damager, DeathMsg, Square->GetPos(), (6 - TimesUsed) * 50); + return true; + } + + return false; +} + +alpha ullrbone::GetOutlineAlpha(int Frame) const +{ + Frame &= 31; + return 50 + (Frame * (31 - Frame) >> 1); +} + +alpha mangoseedling::GetOutlineAlpha(int Frame) const +{ + Frame &= 31; + return 50 + (Frame * (31 - Frame) >> 1); +} + +alpha solstone::GetOutlineAlpha(int Frame) const +{ + if(!IsBroken()) + { + Frame &= 31; + return Frame * (31 - Frame) >> 1; + } + else + return 255; +} + +col16 solstone::GetOutlineColor(int Frame) const +{ + if(!IsBroken()) + switch((Frame&127) >> 5) + { + case 0: return BLUE; + case 1: return GREEN; + case 2: return RED; + case 3: return YELLOW; + } + + return TRANSPARENT_COLOR; +} + diff --git a/Main/Source/nonhuman.cpp b/Main/Source/nonhuman.cpp new file mode 100644 index 0000000..7be1bae --- /dev/null +++ b/Main/Source/nonhuman.cpp @@ -0,0 +1,3107 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through charsset.cpp */ + +int nonhumanoid::GetUnarmedMinDamage() const { return int(UnarmedDamage * 0.75); } +int nonhumanoid::GetUnarmedMaxDamage() const { return int(UnarmedDamage * 1.25 + 1); } +int nonhumanoid::GetKickMinDamage() const { return int(KickDamage * 0.75); } +int nonhumanoid::GetKickMaxDamage() const { return int(KickDamage * 1.25 + 1); } +int nonhumanoid::GetBiteMinDamage() const { return int(BiteDamage * 0.75); } +int nonhumanoid::GetBiteMaxDamage() const { return int(BiteDamage * 1.25 + 1); } +int nonhumanoid::GetCarryingStrength() const { return (Max(GetAttribute(LEG_STRENGTH), 1) << 1) + CarryingBonus; } +truth nonhumanoid::UseMaterialAttributes() const { return GetTorso()->UseMaterialAttributes(); } + +truth elpuri::SpecialEnemySightedReaction(character*) { return !(Active = true); } + +cchar* billswill::FirstPersonBiteVerb() const { return "emit psi waves at"; } +cchar* billswill::FirstPersonCriticalBiteVerb() const { return "emit powerful psi waves at"; } +cchar* billswill::ThirdPersonBiteVerb() const { return "emits psi waves at"; } +cchar* billswill::ThirdPersonCriticalBiteVerb() const { return "emits powerful psi waves at"; } +int billswill::GetBodyPartWobbleData(int) const { return WOBBLE_HORIZONTALLY|(2 << WOBBLE_FREQ_SHIFT); } + +int mommo::GetBodyPartWobbleData(int) const { return (GetConfig() == CONICAL ? WOBBLE_HORIZONTALLY : WOBBLE_VERTICALLY)|(2 << WOBBLE_FREQ_SHIFT); } + +bodypart* dog::MakeBodyPart(int) const { return dogtorso::Spawn(0, NO_MATERIALS); } + +bodypart* spider::MakeBodyPart(int) const { return spidertorso::Spawn(0, NO_MATERIALS); } + +int dolphin::GetSpecialBodyPartFlags(int) const { return RAND() & (MIRROR|ROTATE); } + +bodypart* bat::MakeBodyPart(int) const { return battorso::Spawn(0, NO_MATERIALS); } + +bodypart* fruitbat::MakeBodyPart(int) const { return battorso::Spawn(0, NO_MATERIALS); } + +col16 chameleon::GetSkinColor() const { return MakeRGB16(60 + RAND() % 190, 60 + RAND() % 190, 60 + RAND() % 190); } + +void floatingeye::SetWayPoints(const fearray& What) { ArrayToVector(What, WayPoints); } + +bodypart* eddy::MakeBodyPart(int) const { return eddytorso::Spawn(0, NO_MATERIALS); } +int eddy::GetBodyPartWobbleData(int) const { return WOBBLE_VERTICALLY|(2 << WOBBLE_FREQ_SHIFT); } + +bodypart* magicmushroom::MakeBodyPart(int) const { return magicmushroomtorso::Spawn(0, NO_MATERIALS); } + +bodypart* menatrixfusanga::MakeBodyPart(int) const { return menatrixtorso::Spawn(0, NO_MATERIALS); } + +cchar* ghost::FirstPersonBiteVerb() const { return "touch"; } +cchar* ghost::FirstPersonCriticalBiteVerb() const { return "awfully touch"; } +cchar* ghost::ThirdPersonBiteVerb() const { return "touches"; } +cchar* ghost::ThirdPersonCriticalBiteVerb() const { return "awfully touches"; } +truth ghost::SpecialEnemySightedReaction(character*) { return !(Active = true); } +int ghost::GetBodyPartWobbleData(int) const { return WOBBLE_HORIZONTALLY|(2 << WOBBLE_FREQ_SHIFT); } + +cchar* magpie::FirstPersonBiteVerb() const { return "peck"; } +cchar* magpie::FirstPersonCriticalBiteVerb() const { return "critically peck"; } +cchar* magpie::ThirdPersonBiteVerb() const { return "pecks"; } +cchar* magpie::ThirdPersonCriticalBiteVerb() const { return "critically pecks"; } + +cchar* haastseagle::FirstPersonBiteVerb() const { return "peck"; } +cchar* haastseagle::FirstPersonCriticalBiteVerb() const { return "critically peck"; } +cchar* haastseagle::ThirdPersonBiteVerb() const { return "pecks"; } +cchar* haastseagle::ThirdPersonCriticalBiteVerb() const { return "critically pecks"; } + +cchar* thunderbird::FirstPersonBiteVerb() const { return "peck"; } +cchar* thunderbird::FirstPersonCriticalBiteVerb() const { return "critically peck"; } +cchar* thunderbird::ThirdPersonBiteVerb() const { return "pecks"; } +cchar* thunderbird::ThirdPersonCriticalBiteVerb() const { return "critically pecks"; } + +bodypart* largecreature::MakeBodyPart(int) const { return largetorso::Spawn(0, NO_MATERIALS); } +lsquare* largecreature::GetNeighbourLSquare(int I) const { return static_cast(GetNeighbourSquare(I)); } +wsquare* largecreature::GetNeighbourWSquare(int I) const { return static_cast(GetNeighbourSquare(I)); } + +int hattifattener::GetSpecialBodyPartFlags(int) const { return ST_LIGHTNING; } +int hattifattener::GetBodyPartWobbleData(int) const { return WOBBLE_HORIZONTALLY|(1 << WOBBLE_SPEED_SHIFT)|(1 << WOBBLE_FREQ_SHIFT); } + +int firefox::GetSpecialBodyPartFlags(int) const { return ST_FLAMES; } + +int thunderbird::GetSpecialBodyPartFlags(int) const { return ST_LIGHTNING; } + +col16 vladimir::GetSkinColor() const { return MakeRGB16(60 + RAND() % 190, 60 + RAND() % 190, 60 + RAND() % 190); } + +col16 menatrixfusanga::GetSkinColor() const { return MakeRGB16(60 + RAND() % 190, 60 + RAND() % 190, 60 + RAND() % 190); } + +bodypart* blinkdog::MakeBodyPart(int) const { return blinkdogtorso::Spawn(0, NO_MATERIALS); } + +int mysticfrog::GetBodyPartWobbleData(int) const { return WOBBLE_HORIZONTALLY|(1 << WOBBLE_SPEED_SHIFT)|(3 << WOBBLE_FREQ_SHIFT); } +bodypart* mysticfrog::MakeBodyPart(int) const { return mysticfrogtorso::Spawn(0, NO_MATERIALS); } + +bodypart* lobhse::MakeBodyPart(int) const { return lobhsetorso::Spawn(0, NO_MATERIALS); } + +truth elpuri::Hit(character* Enemy, v2, int, int Flags) +{ + if(CheckIfTooScaredToHit(Enemy)) + return false; + + character* EnemyHit[MAX_NEIGHBOUR_SQUARES]; + int EnemiesHit = 0; + + for(int d = 0; d < GetExtendedNeighbourSquares(); ++d) + if(IsEnabled()) + { + lsquare* Square = GetNeighbourLSquare(d); + + if(Square) + { + character* ByStander = Square->GetCharacter(); + + if(ByStander && (ByStander == Enemy || GetRelation(ByStander) == HOSTILE)) + { + truth Abort = false; + + for(int c = 0; c < EnemiesHit; ++c) + if(EnemyHit[c] == ByStander) + Abort = true; + + if(!Abort) + { + nonhumanoid::Hit(ByStander, Square->GetPos(), YOURSELF, Flags); + ByStander->DamageAllItems(this, RAND() % 36 + RAND() % 36, PHYSICAL_DAMAGE); + EnemyHit[EnemiesHit++] = ByStander; + } + } + + Square->GetStack()->ReceiveDamage(this, RAND() % 36 + RAND() % 36, PHYSICAL_DAMAGE, game::GetLargeMoveDirection(d)); + } + } + + EditAP(-500); + return true; +} + +truth dog::Catches(item* Thingy) +{ + if(Thingy->DogWillCatchAndConsume(this)) + { + if(ConsumeItem(Thingy, CONST_S("eating"))) + { + if(IsPlayer()) + ADD_MESSAGE("You catch %s in mid-air and consume it.", Thingy->CHAR_NAME(DEFINITE)); + else + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s catches %s and eats it.", CHAR_NAME(DEFINITE), Thingy->CHAR_NAME(DEFINITE)); + + ChangeTeam(PLAYER->GetTeam()); + } + } + else if(IsPlayer()) + ADD_MESSAGE("You catch %s in mid-air.", Thingy->CHAR_NAME(DEFINITE)); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s catches %s.", CHAR_NAME(DEFINITE), Thingy->CHAR_NAME(DEFINITE)); + + return true; + } + else + return false; +} + +truth unicorn::SpecialEnemySightedReaction(character*) +{ + if(!(RAND() & 15)) + { + MonsterTeleport("neighs happily"); + return true; + } + + if(StateIsActivated(PANIC) || (RAND() & 1 && IsInBadCondition())) + { + MonsterTeleport("neighs"); + return true; + } + + if(!(RAND() % 3) && MoveRandomly()) + return true; + + return false; +} + +void nonhumanoid::Save(outputfile& SaveFile) const +{ + character::Save(SaveFile); + SaveFile << StrengthExperience << AgilityExperience; +} + +void nonhumanoid::Load(inputfile& SaveFile) +{ + character::Load(SaveFile); + SaveFile >> StrengthExperience >> AgilityExperience; +} + +void nonhumanoid::CalculateUnarmedDamage() +{ + UnarmedDamage = sqrt(5e-12 * GetAttribute(ARM_STRENGTH)) * GetBaseUnarmedStrength() * GetCWeaponSkill(UNARMED)->GetBonus(); +} + +void nonhumanoid::CalculateUnarmedToHitValue() +{ + UnarmedToHitValue = GetAttribute(DEXTERITY) * sqrt(2.5 * GetAttribute(PERCEPTION)) * GetCWeaponSkill(UNARMED)->GetBonus() * GetMoveEase() / 500000; +} + +void nonhumanoid::CalculateUnarmedAPCost() +{ + UnarmedAPCost = Max(long(10000000000. / (APBonus(GetAttribute(DEXTERITY)) * GetMoveEase() * GetCWeaponSkill(UNARMED)->GetBonus())), 100L); +} + +void nonhumanoid::CalculateKickDamage() +{ + KickDamage = sqrt(5e-12 * GetAttribute(LEG_STRENGTH)) * GetBaseKickStrength() * GetCWeaponSkill(KICK)->GetBonus(); +} + +void nonhumanoid::CalculateKickToHitValue() +{ + KickToHitValue = GetAttribute(AGILITY) * sqrt(2.5 * GetAttribute(PERCEPTION)) * GetCWeaponSkill(KICK)->GetBonus() * GetMoveEase() / 1000000; +} + +void nonhumanoid::CalculateKickAPCost() +{ + KickAPCost = Max(long(20000000000. / (APBonus(GetAttribute(AGILITY)) * GetMoveEase() * GetCWeaponSkill(KICK)->GetBonus())), 1000L); +} + +void nonhumanoid::CalculateBiteDamage() +{ + BiteDamage = sqrt(5e-12 * GetAttribute(ARM_STRENGTH)) * GetBaseBiteStrength() * GetCWeaponSkill(BITE)->GetBonus(); +} + +void nonhumanoid::CalculateBiteToHitValue() +{ + BiteToHitValue = GetAttribute(AGILITY) * sqrt(2.5 * GetAttribute(PERCEPTION)) * GetCWeaponSkill(BITE)->GetBonus() * GetMoveEase() / 1000000; +} + +void nonhumanoid::CalculateBiteAPCost() +{ + BiteAPCost = Max(long(10000000000. / (APBonus(GetAttribute(DEXTERITY)) * GetMoveEase() * GetCWeaponSkill(BITE)->GetBonus())), 100L); +} + +void nonhumanoid::InitSpecialAttributes() +{ + StrengthExperience = GetNaturalExperience(ARM_STRENGTH); + AgilityExperience = GetNaturalExperience(AGILITY); + LimitRef(StrengthExperience, MIN_EXP, MAX_EXP); + LimitRef(AgilityExperience, MIN_EXP, MAX_EXP); +} + +void nonhumanoid::Bite(character* Enemy, v2 HitPos, int Direction, truth ForceHit) +{ + EditNP(-50); + EditAP(-GetBiteAPCost()); + EditExperience(ARM_STRENGTH, 75, 1 << 8); + EditExperience(AGILITY, 150, 1 << 8); + EditStamina(-10000 / GetAttribute(ARM_STRENGTH), false); + Enemy->TakeHit(this, 0, GetTorso(), HitPos, GetBiteDamage(), GetBiteToHitValue(), RAND() % 26 - RAND() % 26, BITE_ATTACK, Direction, !(RAND() % GetCriticalModifier()), ForceHit); +} + +void nonhumanoid::Kick(lsquare* Square, int Direction, truth ForceHit) +{ + EditNP(-50); + EditAP(-GetKickAPCost()); + EditStamina(-10000 / GetAttribute(ARM_STRENGTH), false); + + if(Square->BeKicked(this, 0, GetTorso(), GetKickDamage(), GetKickToHitValue(), RAND() % 26 - RAND() % 26, Direction, !(RAND() % GetCriticalModifier()), ForceHit)) + { + EditExperience(LEG_STRENGTH, 150, 1 << 8); + EditExperience(AGILITY, 75, 1 << 8); + } +} + +truth nonhumanoid::Hit(character* Enemy, v2 HitPos, int Direction, int Flags) +{ + if(CheckIfTooScaredToHit(Enemy)) + return false; + + if(IsPlayer()) + { + if(!(Enemy->IsMasochist() && GetRelation(Enemy) == FRIEND) && GetRelation(Enemy) != HOSTILE && !game::TruthQuestion(CONST_S("This might cause a hostile reaction. Are you sure? [y/N]"))) + return false; + } + else if(GetAttribute(WISDOM) >= Enemy->GetAttackWisdomLimit()) + return false; + + if(GetBurdenState() == OVER_LOADED) + { + if(IsPlayer()) + ADD_MESSAGE("You cannot fight while carrying so much."); + + return false; + } + + /* Behold this Terrible Father of Gum Solutions! */ + + int AttackStyle = GetAttackStyle(); + + if(AttackStyle & USE_LEGS) + { + room* Room = GetNearLSquare(HitPos)->GetRoom(); + + if(Room && !Room->AllowKick(this, GetNearLSquare(HitPos))) + AttackStyle &= ~USE_LEGS; + } + + int c, AttackStyles; + + for(c = 0, AttackStyles = 0; c < 8; ++c) + if(AttackStyle & (1 << c)) + ++AttackStyles; + + int Chosen = RAND() % AttackStyles; + + for(c = 0, AttackStyles = 0; c < 8; ++c) + if(AttackStyle & (1 << c) && AttackStyles++ == Chosen) + { + Chosen = 1 << c; + break; + } + + switch(Chosen) + { + case USE_ARMS: + msgsystem::EnterBigMessageMode(); + Hostility(Enemy); + UnarmedHit(Enemy, HitPos, Direction, Flags & SADIST_HIT); + msgsystem::LeaveBigMessageMode(); + return true; + case USE_LEGS: + msgsystem::EnterBigMessageMode(); + Hostility(Enemy); + Kick(GetNearLSquare(HitPos), Direction, Flags & SADIST_HIT); + msgsystem::LeaveBigMessageMode(); + return true; + case USE_HEAD: + msgsystem::EnterBigMessageMode(); + Hostility(Enemy); + Bite(Enemy, HitPos, Direction, Flags & SADIST_HIT); + msgsystem::LeaveBigMessageMode(); + return true; + default: + ABORT("Strange alien attack style requested!"); + return false; + } +} + +void nonhumanoid::UnarmedHit(character* Enemy, v2 HitPos, int Direction, truth ForceHit) +{ + EditNP(-50); + EditAP(-GetUnarmedAPCost()); + EditStamina(-10000 / GetAttribute(ARM_STRENGTH), false); + + switch(Enemy->TakeHit(this, 0, GetTorso(), HitPos, GetUnarmedDamage(), GetUnarmedToHitValue(), RAND() % 26 - RAND() % 26, UNARMED_ATTACK, Direction, !(RAND() % GetCriticalModifier()), ForceHit)) + { + case HAS_HIT: + case HAS_BLOCKED: + case HAS_DIED: + case DID_NO_DAMAGE: + EditExperience(ARM_STRENGTH, 150, 1 << 8); + case HAS_DODGED: + EditExperience(DEXTERITY, 75, 1 << 8); + } +} + +/* Returns the average number of APs required to kill Enemy */ + +double nonhumanoid::GetTimeToKill(ccharacter* Enemy, truth UseMaxHP) const +{ + double Effectivity = 0; + int AttackStyles = 0; + + if(IsUsingArms()) + { + Effectivity += 1 / (Enemy->GetTimeToDie(this, int(GetUnarmedDamage()) + 1, GetUnarmedToHitValue(), AttackIsBlockable(UNARMED_ATTACK), UseMaxHP) * GetUnarmedAPCost()); + ++AttackStyles; + } + + if(IsUsingLegs()) + { + Effectivity += 1 / (Enemy->GetTimeToDie(this, int(GetKickDamage()) + 1, GetKickToHitValue(), AttackIsBlockable(KICK_ATTACK), UseMaxHP) * GetKickAPCost()); + ++AttackStyles; + } + + if(IsUsingHead()) + { + Effectivity += 1 / (Enemy->GetTimeToDie(this, int(GetBiteDamage()) + 1, GetBiteToHitValue(), AttackIsBlockable(BITE_ATTACK), UseMaxHP) * GetBiteAPCost()); + ++AttackStyles; + } + + if(StateIsActivated(HASTE)) + Effectivity *= 2; + + if(StateIsActivated(SLOW)) + Effectivity /= 2; + + return AttackStyles / Effectivity; +} + +int nonhumanoid::GetAttribute(int Identifier, truth AllowBonus) const +{ + if(Identifier < BASE_ATTRIBUTES) + return character::GetAttribute(Identifier, AllowBonus); + else if(Identifier == ARM_STRENGTH || Identifier == LEG_STRENGTH) + { + if(!UseMaterialAttributes()) + return int(StrengthExperience * EXP_DIVISOR); + else + return GetTorso()->GetMainMaterial()->GetStrengthValue(); + } + else if(Identifier == DEXTERITY || Identifier == AGILITY) + { + if(!UseMaterialAttributes()) + return int(AgilityExperience * EXP_DIVISOR); + else + return (GetTorso()->GetMainMaterial()->GetFlexibility() << 2); + } + else + { + ABORT("Illegal nonhumanoid attribute %d request!", Identifier); + return 0xABBE; + } +} + +truth nonhumanoid::EditAttribute(int Identifier, int Value) +{ + if(Identifier < BASE_ATTRIBUTES) + return character::EditAttribute(Identifier, Value); + else if(Identifier == ARM_STRENGTH || Identifier == LEG_STRENGTH) + return !UseMaterialAttributes() && RawEditAttribute(StrengthExperience, Value); + else if(Identifier == DEXTERITY || Identifier == AGILITY) + return !UseMaterialAttributes() && RawEditAttribute(AgilityExperience, Value); + else + { + ABORT("Illegal nonhumanoid attribute %d edit request!", Identifier); + return false; + } +} + +void nonhumanoid::EditExperience(int Identifier, double Value, double Speed) +{ + if(!AllowExperience()) + return; + + if(Identifier < BASE_ATTRIBUTES) + character::EditExperience(Identifier, Value, Speed); + else if(Identifier == ARM_STRENGTH || Identifier == LEG_STRENGTH) + { + if(!UseMaterialAttributes()) + { + int Change = RawEditExperience(StrengthExperience, + GetNaturalExperience(ARM_STRENGTH), + Value, Speed / 2); + + if(Change) + { + cchar* Adj = Change > 0 ? "stronger" : "weaker"; + + if(IsPlayer()) + ADD_MESSAGE("Your feel %s!", Adj); + else if(IsPet() && CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s looks %s.", CHAR_NAME(DEFINITE), Adj); + + CalculateBurdenState(); + CalculateBattleInfo(); + } + } + } + else if(Identifier == DEXTERITY || Identifier == AGILITY) + { + if(!UseMaterialAttributes()) + { + int Change = RawEditExperience(AgilityExperience, + GetNaturalExperience(AGILITY), + Value, Speed / 2); + + if(Change) + { + cchar* Adj = Change > 0 ? "very agile" : "sluggish"; + + if(IsPlayer()) + ADD_MESSAGE("Your feel %s!", Adj); + else if(IsPet() && CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s looks %s.", CHAR_NAME(DEFINITE), Adj); + + CalculateBattleInfo(); + } + } + } + else + ABORT("Illegal nonhumanoid attribute %d experience edit request!", Identifier); +} + +int nonhumanoid::DrawStats(truth AnimationDraw) const +{ + if(AnimationDraw) + return 3; + + int PanelPosX = RES.X - 96, PanelPosY = 3; + PrintAttribute("Str", ARM_STRENGTH, PanelPosX, PanelPosY++); + PrintAttribute("Agi", AGILITY, PanelPosX, PanelPosY++); + return PanelPosY; +} + +void nonhumanoid::CalculateBattleInfo() +{ + CalculateDodgeValue(); + CalculateUnarmedAttackInfo(); + CalculateKickAttackInfo(); + CalculateBiteAttackInfo(); +} + +void nonhumanoid::CalculateUnarmedAttackInfo() +{ + CalculateUnarmedDamage(); + CalculateUnarmedToHitValue(); + CalculateUnarmedAPCost(); +} + +void nonhumanoid::CalculateKickAttackInfo() +{ + CalculateKickDamage(); + CalculateKickToHitValue(); + CalculateKickAPCost(); +} + +void nonhumanoid::CalculateBiteAttackInfo() +{ + CalculateBiteDamage(); + CalculateBiteToHitValue(); + CalculateBiteAPCost(); +} + +void dog::BeTalkedTo() +{ + if(RAND_N(5)) + { + if(GetRelation(PLAYER) != HOSTILE) + { + static truth Last; + cchar* Reply; + + if(GetHP() << 1 > GetMaxHP()) + Reply = Last ? "barks happily" : "wags its tail happily"; + else + Reply = Last ? "yelps" : "howls"; + + ADD_MESSAGE("%s %s.", CHAR_NAME(DEFINITE), Reply); + Last = !Last; + } + else + character::BeTalkedTo(); + } + else if(RAND_N(5)) + ADD_MESSAGE("\"Can't you understand I can't speak?\""); + else + ADD_MESSAGE("\"Meow.\""); +} + +void solicitus::BeTalkedTo() +{ + if(GetRelation(PLAYER) == HOSTILE) + { + ADD_MESSAGE("Oh no. Now is for the figthing!!!"); + return; + } + + if(PLAYER->StateIsActivated(PANIC) && !game::PlayerIsSolicitusChampion()) + { + ADD_MESSAGE("Solicitus perks up. \"Well hullo there mortal! Would you care to be my Champion? I'll give you a free copy of my celestial monograph on Atheism!\""); + + if(game::TruthQuestion(CONST_S("Do you choose to become the Champion of Solicitus? [y/n]"), REQUIRES_ANSWER)) + { + game::TextScreen(CONST_S( "Solicitus speaks:\n" + "\"Becoming my champion involves my changing your sweat material into pure liquified fear.\"\n" + "\"Now, hold still while I administer to your body what powers I have left!\"\n")); + + game::TextScreen(CONST_S("You feel Solicitus changing your sweat glands. It feels disgusting.")); + + game::MakePlayerSolicitusChampion(); + PLAYER->EditCurrentSweatMaterial(LIQUID_HORROR); + (celestialmonograph::Spawn())->MoveTo(PLAYER->GetStack()); + + //pantheonbook* NewBook = pantheonbook::Spawn(); + //AddPlace->AddItem(NewBook); + + ADD_MESSAGE("\"Go forth, you are anointed! And here's your personal copy of my monograph, mortal. Enjoy!\""); + GetArea()->SendNewDrawRequest(); + } + else + { + ADD_MESSAGE("\"Not a problem, perhaps another time. It's not for everyone, you know.\""); + return; + } + } + else if(PLAYER->StateIsActivated(PANIC) && game::PlayerIsSolicitusChampion()) + { + ADD_MESSAGE("\"I suppose you want to hear my life story?\""); + } + else + { + ADD_MESSAGE("\"Maybe you should empathise with my situation first. Go drink some liquified fear and then we'll talk.\""); + } +} + +col16 wolf::GetSkinColor() const +{ + int Element = 40 + RAND() % 50; + return MakeRGB16(Element, Element, Element); +} + +void genetrixvesana::GetAICommand() +{ + ++TurnsExisted; + + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + if(!(RAND() % 60)) + { + int NumberOfPlants = RAND() % 3 + RAND() % 3 + RAND() % 3 + RAND() % 3; + + for(int c1 = 0; c1 < 50 && NumberOfPlants; ++c1) + { + for(int c2 = 0; c2 < game::GetTeams() && NumberOfPlants; ++c2) + if(GetTeam()->GetRelation(game::GetTeam(c2)) == HOSTILE) + for(std::list::const_iterator i = game::GetTeam(c2)->GetMember().begin(); i != game::GetTeam(c2)->GetMember().end() && NumberOfPlants; ++i) + if((*i)->IsEnabled()) + { + lsquare* LSquare = (*i)->GetNeighbourLSquare(RAND() % GetNeighbourSquares()); + + if(LSquare && (LSquare->GetWalkability() & WALK) && !LSquare->GetCharacter()) + { + character* NewPlant; + long RandomValue = RAND() % TurnsExisted; + + if(RandomValue < 250) + NewPlant = carnivorousplant::Spawn(); + else if(RandomValue < 1500) + NewPlant = carnivorousplant::Spawn(GREATER); + else + NewPlant = carnivorousplant::Spawn(GIANT); + + for(int c = 3; c < TurnsExisted / 500; ++c) + NewPlant->EditAllAttributes(1); + + NewPlant->SetGenerationDanger(GetGenerationDanger()); + NewPlant->SetTeam(GetTeam()); + NewPlant->PutTo(LSquare->GetPos()); + --NumberOfPlants; + + if(NewPlant->CanBeSeenByPlayer()) + { + if((*i)->IsPlayer()) + ADD_MESSAGE("%s sprouts from the ground near you.", NewPlant->CHAR_NAME(INDEFINITE)); + else if((*i)->CanBeSeenByPlayer()) + ADD_MESSAGE("%s sprouts from the ground near %s.", NewPlant->CHAR_NAME(INDEFINITE), (*i)->CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("%s sprouts from the ground.", NewPlant->CHAR_NAME(INDEFINITE)); + } + } + } + } + + EditAP(-2000); + return; + } + + if(AttackAdjacentEnemyAI()) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +void solicitus::GetAICommand() +{ + if(MoveRandomly()) + return; + + EditAP(-2000); + return; + +} + +col16 carnivorousplant::GetTorsoSpecialColor() const // the flower +{ + if(!GetConfig()) + return MakeRGB16(RAND() % 100, 125 + RAND() % 125, RAND() % 100); + else if(GetConfig() == GREATER) + return MakeRGB16(RAND() % 100, RAND() % 100, 125 + RAND() % 125); + else + return MakeRGB16(125 + RAND() % 125, 125 + RAND() % 125, RAND() % 100); +} + +col16 noxiousorchid::GetTorsoSpecialColor() const // the flower +{ + if(!GetConfig()) + return MakeRGB16(125 + RAND() % 100, RAND() % 125, RAND() % 100); + else if(GetConfig() == GREATER) + return MakeRGB16(100 + RAND() % 100, RAND() % 100, 155 + RAND() % 100); + else + return MakeRGB16(200 + RAND() % 55, RAND() % 60, 150); +} + +void noxiousorchid::PostConstruct() +{ + //GetTorso()->GetMainMaterial()->SetSpoilCounter(200 + RAND_N(100)); +} + +void ostrich::GetAICommand() +{ + if(game::TweraifIsFree()) + { + nonhumanoid::GetAICommand(); + return; + } + + if(CheckForEnemies(false, false, true, true)) + return; + + if(!IsEnabled()) + return; + + if(GetPos() == v2(45, 45)) + HasDroppedBananas = true; + + itemvector ItemVector; + GetStackUnder()->FillItemVector(ItemVector); + int BananasPicked = 0; + + for(uint c = 0; c < ItemVector.size(); ++c) + if(ItemVector[c]->IsBanana() && ItemVector[c]->CanBeSeenBy(this) + && ItemVector[c]->IsPickable(this) + && !MakesBurdened(GetCarriedWeight() + ItemVector[c]->GetWeight())) + { + ItemVector[c]->MoveTo(GetStack()); + ++BananasPicked; + } + + if(BananasPicked) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s picks up %s.", CHAR_NAME(DEFINITE), BananasPicked == 1 ? "the banana" : "some bananas"); + + return; + } + + if(!HasDroppedBananas) + { + SetGoingTo(v2(45, 45)); + + if(MoveTowardsTarget(true)) + return; + } + else if(GetPos().Y == 54) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s leaves the town.", CHAR_NAME(DEFINITE)); + + itemvector ItemVector; + GetStack()->FillItemVector(ItemVector); + + for(uint c = 0; c < ItemVector.size(); ++c) + { + ItemVector[c]->RemoveFromSlot(); + ItemVector[c]->SendToHell(); + } + + v2 Where = GetLevel()->GetNearestFreeSquare(this, v2(45, 0)); + + if(Where == ERROR_V2) + Where = GetLevel()->GetRandomSquare(this, NOT_IN_ROOM); // this is odd but at least it doesn't crash + + Move(Where, true); + RestoreHP(); + RestoreStamina(); + ResetStates(); + TemporaryState = 0; + GainIntrinsic(LEVITATION); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s enters the town.", CHAR_NAME(INDEFINITE)); + + HasDroppedBananas = false; + } + else + { + SetGoingTo(v2(45, 54)); + + if(MoveTowardsTarget(true)) + return; + } + + EditAP(-1000); +} + +void ostrich::Save(outputfile& SaveFile) const +{ + nonhumanoid::Save(SaveFile); + SaveFile << HasDroppedBananas; +} + +void ostrich::Load(inputfile& SaveFile) +{ + nonhumanoid::Load(SaveFile); + SaveFile >> HasDroppedBananas; +} + +truth ostrich::HandleCharacterBlockingTheWay(character* Char, v2 Pos, int Dir) +{ + return Char->GetPos() == v2(45, 45) && (Displace(Char, true) || Hit(Char, Pos, Dir)); +} + +void elpuri::Save(outputfile& SaveFile) const +{ + largecreature::Save(SaveFile); + SaveFile << Active; +} + +void elpuri::Load(inputfile& SaveFile) +{ + largecreature::Load(SaveFile); + SaveFile >> Active; +} + +void elpuri::GetAICommand() +{ + if(Active) + character::GetAICommand(); + else + { + if(CheckForEnemies(false, false, false)) + return; + + EditAP(-1000); + } +} + +int elpuri::ReceiveBodyPartDamage(character* Damager, int Damage, int Type, int BodyPartIndex, int Direction, truth PenetrateResistance, truth Critical, truth ShowNoDamageMsg, truth CaptureBodyPart) +{ + Active = true; + return character::ReceiveBodyPartDamage(Damager, Damage, Type, BodyPartIndex, Direction, PenetrateResistance, Critical, ShowNoDamageMsg, CaptureBodyPart); +} + +void mommo::CreateCorpse(lsquare* Square) +{ + for(int d = 0; d < GetExtendedNeighbourSquares(); ++d) + { + lsquare* NeighbourSquare = Square->GetNeighbourLSquare(d); + + if(NeighbourSquare) + NeighbourSquare->SpillFluid(0, static_cast(GetTorso()->GetMainMaterial()->SpawnMore(250 + RAND() % 250))); + } + + SendToHell(); +} + +void carnivorousplant::CreateCorpse(lsquare* Square) +{ + int Amount = !GetConfig() ? (RAND() % 7 ? 0 : 1) : GetConfig() == GREATER ? (RAND() & 1 ? 0 : (RAND() % 5 ? 1 : (RAND() % 5 ? 2 : 3))) : (!(RAND() % 3) ? 0 : (RAND() % 3 ? 1 : (RAND() % 3 ? 2 : 3))); + + for(int c = 0; c < Amount; ++c) + Square->AddItem(kiwi::Spawn()); + + nonhumanoid::CreateCorpse(Square); +} + +void genetrixvesana::CreateCorpse(lsquare* Square) +{ + for(int c = 0; c < 3; ++c) + Square->AddItem(pineapple::Spawn()); + + largecreature::CreateCorpse(Square); +} + +void noxiousorchid::CreateCorpse(lsquare* Square) +{ + //int Amount = !GetConfig() ? (RAND() % 7 ? 0 : 1) : GetConfig() == GREATER ? (RAND() & 1 ? 0 : (RAND() % 5 ? 1 : (RAND() % 5 ? 2 : 3))) : (!(RAND() % 3) ? 0 : (RAND() % 3 ? 1 : (RAND() % 3 ? 2 : 3))); + + //for(int c = 0; c < Amount; ++c) + // Square->AddItem(kiwi::Spawn()); + + nonhumanoid::CreateCorpse(Square); +} + +void solicitus::CreateCorpse(lsquare* Square) +{ + //for(int c = 0; c < 3; ++c) + // Square->AddItem(pineapple::Spawn()); + + //largecreature::CreateCorpse(Square); + ADD_MESSAGE("You hear a booming voice: \"No, mortal! This will not be done!\""); + game::GetCurrentLevel()->Explosion(this, CONST_S("killed by an explosion of the toppled-god Solicitus"), PLAYER->GetPos(), 1300 >> 3, false); + SendToHell(); +} + +void nonhumanoid::AddSpecialStethoscopeInfo(felist& Info) const +{ + Info.AddEntry(CONST_S("Strength: ") + GetAttribute(ARM_STRENGTH), LIGHT_GRAY); + Info.AddEntry(CONST_S("Agility: ") + GetAttribute(AGILITY), LIGHT_GRAY); +} + +void floatingeye::Save(outputfile& SaveFile) const +{ + nonhumanoid::Save(SaveFile); + SaveFile << WayPoints << NextWayPoint; +} + +void floatingeye::Load(inputfile& SaveFile) +{ + nonhumanoid::Load(SaveFile); + SaveFile >> WayPoints >> NextWayPoint; +} + +void floatingeye::GetAICommand() +{ + if(WayPoints.size() && !IsGoingSomeWhere()) + { + if(GetPos() == WayPoints[NextWayPoint]) + if(NextWayPoint < WayPoints.size() - 1) + ++NextWayPoint; + else + NextWayPoint = 0; + + GoingTo = WayPoints[NextWayPoint]; + } + + SeekLeader(GetLeader()); + + if(CheckForEnemies(false, false, true)) + return; + + if(FollowLeader(GetLeader())) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +truth floatingeye::Hit(character* Enemy, v2, int, int) +{ + if(IsPlayer()) + ADD_MESSAGE("You stare at %s.", Enemy->CHAR_DESCRIPTION(DEFINITE)); + else if(Enemy->IsPlayer() && CanBeSeenByPlayer()) + ADD_MESSAGE("%s stares at you.", CHAR_NAME(DEFINITE)); + + EditAP(-1000); + return true; +} + +int floatingeye::TakeHit(character* Enemy, item* Weapon, bodypart* EnemyBodyPart, v2 HitPos, double Damage, double ToHitValue, int Success, int Type, int Direction, truth Critical, truth ForceHit) +{ + if(CanBeSeenBy(Enemy) && Enemy->HasEyes() && RAND() % 3 && Enemy->LoseConsciousness(150 + RAND_N(150))) /* Changes for fainting 2 out of 3 */ + { + if(!Enemy->IsPlayer()) + Enemy->EditExperience(WISDOM, 75, 1 << 13); + + return HAS_FAILED; + } + else + return nonhumanoid::TakeHit(Enemy, Weapon, EnemyBodyPart, HitPos, Damage, ToHitValue, Success, Type, Direction, Critical, ForceHit); +} + +void elpuri::CreateCorpse(lsquare* Square) +{ + largecreature::CreateCorpse(Square); + Square->AddItem(headofelpuri::Spawn()); +} + +truth snake::SpecialBiteEffect(character* Char, v2, int, int, truth BlockedByArmour) +{ + if(!BlockedByArmour) + { + Char->BeginTemporaryState(POISONED, 400 + RAND_N(200)); + return true; + } + else + return false; +} + +truth spider::SpecialBiteEffect(character* Char, v2, int, int, truth BlockedByArmour) +{ + if(!BlockedByArmour) + { + Char->BeginTemporaryState(POISONED, GetConfig() == LARGE ? 80 + RAND_N(40) : 400 + RAND_N(200)); + return true; + } + else + return false; +} + +truth firefox::SpecialBiteEffect(character* Enemy, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + if(Enemy->IsEnabled() && RAND() & 1) + { + if(Enemy->CanBeSeenByPlayer() && IsPlayer()) + ADD_MESSAGE("Your bite burns %s.", Enemy->CHAR_DESCRIPTION(DEFINITE)); + else if(Enemy->IsPlayer() || Enemy->CanBeSeenByPlayer() || CanBeSeenByPlayer()) + ADD_MESSAGE("The bite of %s burns %s.", CHAR_DESCRIPTION(DEFINITE), Enemy->CHAR_DESCRIPTION(DEFINITE)); + + Enemy->ReceiveBodyPartDamage(this, 2 + (RAND() & 3), FIRE, BodyPartIndex, Direction); + Enemy->CheckDeath(CONST_S("killed by the firey bite of ") + GetName(INDEFINITE), this); + return true; + } + else + return false; +} + +truth thunderbird::SpecialBiteEffect(character* Enemy, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + if(Enemy->IsEnabled() && RAND() & 1) + { + if(Enemy->CanBeSeenByPlayer() && IsPlayer()) + ADD_MESSAGE("Your beak emits a thunderous peal directed at %s.", Enemy->CHAR_DESCRIPTION(DEFINITE)); + else if(Enemy->IsPlayer() || Enemy->CanBeSeenByPlayer() || CanBeSeenByPlayer()) + ADD_MESSAGE("Thunder is emitted from the beak of %s and the sound waves hit %s.", CHAR_DESCRIPTION(DEFINITE), Enemy->CHAR_DESCRIPTION(DEFINITE)); + + Enemy->ReceiveBodyPartDamage(this, 2 + (RAND() & 3), SOUND, BodyPartIndex, Direction); + Enemy->CheckDeath(CONST_S("killed by the thunderous attack of ") + GetName(INDEFINITE), this); + return true; + } + else + return false; +} + +/* +truth firefox::Bite(character* Enemy, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour) +{ + truth BaseSuccess = nonhumanoid::Bite(Enemy, HitPos, BodyPartIndex, Direction, BlockedByArmour); + + if(Enemy->IsEnabled() && RAND() & 1) + { + if(Enemy->CanBeSeenByPlayer() && IsPlayer()) + ADD_MESSAGE("Your bite burns %s.", Enemy->CHAR_DESCRIPTION(DEFINITE)); + else if(Enemy->IsPlayer() || Enemy->CanBeSeenByPlayer() || CanBeSeenByPlayer()) + ADD_MESSAGE("The bite of %s burns %s.", CHAR_DESCRIPTION(DEFINITE), Enemy->CHAR_DESCRIPTION(DEFINITE)); + return Enemy->ReceiveBodyPartDamage(this, 2 + (RAND() & 3), FIRE, BodyPartIndex, Direction) || BaseSuccess; + } + else + return BaseSuccess; +}*/ + +truth chameleon::SpecialEnemySightedReaction(character*) +{ + if(HP != MaxHP || !(RAND() % 3)) + { + character* NewForm = PolymorphRandomly(100, 1000, 500 + RAND() % 500); + NewForm->GainIntrinsic(POLYMORPH); + return true; + } + + return false; +} + +int chameleon::TakeHit(character* Enemy, item* Weapon, bodypart* EnemyBodyPart, v2 HitPos, double Damage, double ToHitValue, int Success, int Type, int Direction, truth Critical, truth ForceHit) +{ + int Return = nonhumanoid::TakeHit(Enemy, Weapon, EnemyBodyPart, HitPos, Damage, ToHitValue, Success, Type, Direction, Critical, ForceHit); + + if(Return != HAS_DIED) + { + character* NewForm = PolymorphRandomly(100, 1000, 500 + RAND() % 500); + NewForm->GainIntrinsic(POLYMORPH); + } + + return Return; +} + +truth eddy::Hit(character* Enemy, v2, int, int) +{ + if(IsPlayer()) + { + if(!(Enemy->IsMasochist() && GetRelation(Enemy) == FRIEND) && GetRelation(Enemy) != HOSTILE && !game::TruthQuestion(CONST_S("This might cause a hostile reaction. Are you sure? [y/N]"))) + return false; + } + + Hostility(Enemy); + + if(RAND() & 1) + { + if(IsPlayer()) + ADD_MESSAGE("You engulf %s.", Enemy->CHAR_DESCRIPTION(DEFINITE)); + else if(Enemy->IsPlayer() || CanBeSeenByPlayer() || Enemy->CanBeSeenByPlayer()) + ADD_MESSAGE("%s engulfs %s.", CHAR_DESCRIPTION(DEFINITE), Enemy->CHAR_DESCRIPTION(DEFINITE)); + + Enemy->TeleportRandomly(); + } + else if(IsPlayer()) + ADD_MESSAGE("You miss %s.", Enemy->CHAR_DESCRIPTION(DEFINITE)); + + EditAP(-500); + return true; +} +/* All about the spillfluid for the orchid */ +truth noxiousorchid::Hit(character* Enemy, v2 HitPos, int Direction, int Flags) +{ + if(!(RAND() & 2)) + { + if(IsPlayer()) + ADD_MESSAGE("You hit %s.", Enemy->CHAR_DESCRIPTION(DEFINITE)); + else if(Enemy->IsPlayer() || CanBeSeenByPlayer() || Enemy->CanBeSeenByPlayer()) + ADD_MESSAGE("%s hits %s.", CHAR_DESCRIPTION(DEFINITE), Enemy->CHAR_DESCRIPTION(DEFINITE)); + + liquid* Fluid = 0;// = liquid::Spawn(WATER, 25+RAND()%25); + + switch(GetConfig()) + { + case 0: + //{ + switch(RAND() % 48) + { + case 0: Fluid = liquid::Spawn(ANTIDOTE_LIQUID, 15+RAND()%25); break; + case 1: + case 2: + case 3: + case 4: Fluid = liquid::Spawn(POISON_LIQUID, 15+RAND()%25); break; + case 5: Fluid = liquid::Spawn(LIQUID_HORROR, 15+RAND()%25); break; + case 6: + case 7: + case 8: Fluid = liquid::Spawn(SULPHURIC_ACID, 15+RAND()%25); break; + case 9: + default: break; + }; + break; + //} + case GREATER: + //{ + switch(RAND() % 24) + { + case 0: Fluid = liquid::Spawn(ANTIDOTE_LIQUID, 25+RAND()%25); break; + case 1: + case 2: + case 3: + case 4: Fluid = liquid::Spawn(POISON_LIQUID, 25+RAND()%25); break; + case 5: Fluid = liquid::Spawn(LIQUID_HORROR, 25+RAND()%25); break; + case 6: + case 7: + case 8: Fluid = liquid::Spawn(SULPHURIC_ACID, 25+RAND()%25); break; + case 9: + default: break; + }; + break; + //} + case GIANT: + //{ + switch(RAND() % 24) + { + case 0: Fluid = liquid::Spawn(ANTIDOTE_LIQUID, 50+RAND()%50); break; + case 1: Fluid = liquid::Spawn(YELLOW_SLIME, 50+RAND()%50); break; + case 2: + case 3: + case 4: Fluid = liquid::Spawn(POISON_LIQUID, 50+RAND()%50); break; + case 5: Fluid = liquid::Spawn(LIQUID_HORROR, 50+RAND()%50); break; + case 6: + case 7: + case 8: Fluid = liquid::Spawn(SULPHURIC_ACID, 50+RAND()%50); break; + case 9: Fluid = liquid::Spawn(MUSTARD_GAS_LIQUID, 50+RAND()%50); break; + default: break; + }; + break; + //} + default: break; //Fluid = liquid::Spawn(WATER, 25+RAND()%25); break; + } + if(Fluid) + { + Enemy->SpillFluid(Enemy, Fluid); + if(IsPlayer()) + ADD_MESSAGE("You spill %s on %s.", Fluid->GetName(false, false).CStr(), Enemy->CHAR_DESCRIPTION(DEFINITE)); + else if(Enemy->IsPlayer() || CanBeSeenByPlayer() || Enemy->CanBeSeenByPlayer()) + ADD_MESSAGE("%s spills %s on %s.", CHAR_DESCRIPTION(DEFINITE), Fluid->GetName(false, false).CStr(), Enemy->CHAR_DESCRIPTION(DEFINITE)); + } + } + else if(nonhumanoid::Hit(Enemy, HitPos, Direction, Flags)) + return true; + else if(IsPlayer()) + ADD_MESSAGE("You miss %s.", Enemy->CHAR_DESCRIPTION(DEFINITE)); + + EditAP(-1000); + return true; +} + +void mushroom::Save(outputfile& SaveFile) const +{ + nonhumanoid::Save(SaveFile); + SaveFile << Species; +} + +void mushroom::Load(inputfile& SaveFile) +{ + nonhumanoid::Load(SaveFile); + SaveFile >> Species; +} + +void mushroom::GetAICommand() +{ + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + lsquare* CradleSquare = GetNeighbourLSquare(RAND() % 8); + + if(CradleSquare && !CradleSquare->GetCharacter() + && (CradleSquare->GetWalkability() & WALK)) + { + int SpoiledItems = 0; + int MushroomsNear = 0; + + for(int d = 0; d < 8; ++d) + { + lsquare* Square = CradleSquare->GetNeighbourLSquare(d); + + if(Square) + { + character* Char = Square->GetCharacter(); + + if(Char && Char->IsMushroom()) + ++MushroomsNear; + + SpoiledItems += Square->GetSpoiledItems(); + } + } + + if((SpoiledItems && MushroomsNear < 5 && !RAND_N(50)) || (MushroomsNear < 3 && !RAND_N((1 + MushroomsNear) * 100))) + { + mushroom* Child = static_cast(GetProtoType()->Spawn(GetConfig())); + Child->SetSpecies(Species); + Child->SetTeam(GetTeam()); + Child->SetGenerationDanger(GetGenerationDanger()); + Child->PutTo(CradleSquare->GetPos()); + + for(int c = 0; c < BASE_ATTRIBUTES; ++c) + Child->BaseExperience[c] = RandomizeBabyExperience(BaseExperience[c] * 4); + + if(Child->CanBeSeenByPlayer()) + ADD_MESSAGE("%s pops out from the ground.", Child->CHAR_NAME(INDEFINITE)); + } + } + + if(AttackAdjacentEnemyAI()) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +void mushroom::PostConstruct() +{ + switch(RAND() % 3) + { + case 0: SetSpecies(MakeRGB16(125 + RAND() % 125, RAND() % 100, RAND() % 100)); break; + case 1: SetSpecies(MakeRGB16(RAND() % 100, 125 + RAND() % 125, RAND() % 100)); break; + case 2: SetSpecies(MakeRGB16(RAND() % 100, RAND() % 100, 125 + RAND() % 125)); break; + } +} + +void magicmushroom::GetAICommand() +{ + if(!(RAND() % 750)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s disappears.", CHAR_NAME(DEFINITE)); + + TeleportRandomly(true); + EditAP(-1000); + } + else if(!(RAND() % 50)) + { + lsquare* Square = GetNeighbourLSquare(RAND() % 8); + + if(Square && Square->IsFlyable()) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s releases odd-looking gas.", CHAR_NAME(DEFINITE)); + + Square->AddSmoke(gas::Spawn(MAGIC_VAPOUR, 1000)); + EditAP(-1000); + } + } + else + mushroom::GetAICommand(); +} + +void mushroom::SetSpecies(int What) +{ + Species = What; + UpdatePictures(); +} + +truth twoheadedmoose::Hit(character* Enemy, v2 HitPos, int Direction, int Flags) +{ + if(CheckIfTooScaredToHit(Enemy)) + return false; + + if(IsPlayer()) + { + if(!(Enemy->IsMasochist() && GetRelation(Enemy) == FRIEND) && GetRelation(Enemy) != HOSTILE && !game::TruthQuestion(CONST_S("This might cause a hostile reaction. Are you sure? [y/N]"))) + return false; + } + else if(GetAttribute(WISDOM) >= Enemy->GetAttackWisdomLimit()) + return false; + + if(GetBurdenState() == OVER_LOADED) + { + if(IsPlayer()) + ADD_MESSAGE("You cannot fight while carrying so much."); + + return false; + } + + Hostility(Enemy); + msgsystem::EnterBigMessageMode(); + Bite(Enemy, HitPos, Direction, Flags & SADIST_HIT); + v2 Pos[MAX_NEIGHBOUR_SQUARES]; + character* Char[MAX_NEIGHBOUR_SQUARES]; + int Index = 0; + + for(int d = 0; d < GetNeighbourSquares(); ++d) + { + lsquare* LSquare = GetNeighbourLSquare(d); + + if(LSquare) + { + character* Enemy = LSquare->GetCharacter(); + + if(Enemy && GetRelation(Enemy) == HOSTILE && GetAttribute(WISDOM) < Enemy->GetAttackWisdomLimit()) + { + Pos[Index] = LSquare->GetPos(); + Char[Index++] = Enemy; + } + } + } + + if(Index) + { + int ChosenIndex = RAND() % Index; + Bite(Char[ChosenIndex], Pos[ChosenIndex], game::GetDirectionForVector(Pos[ChosenIndex] - GetPos()), Flags & SADIST_HIT); + } + + msgsystem::LeaveBigMessageMode(); + return true; +} + +truth magpie::IsRetreating() const +{ + if(nonhumanoid::IsRetreating()) + return true; + + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if((*i)->GetSparkleFlags()) + return true; + + return false; +} + +void magpie::GetAICommand() +{ + if(!IsRetreating()) + { + character* Char = GetRandomNeighbour(); + + if(Char) + { + itemvector Sparkling; + + for(stackiterator i = Char->GetStack()->GetBottom(); i.HasItem(); ++i) + { + if((*i)->GetSparkleFlags() && !MakesBurdened((*i)->GetWeight())) + Sparkling.push_back(*i); + } + + if(!Sparkling.empty()) + { + item* ToSteal = Sparkling[RAND() % Sparkling.size()]; + ToSteal->RemoveFromSlot(); + GetStack()->AddItem(ToSteal); + + if(Char->IsPlayer()) + ADD_MESSAGE("%s steals your %s.", CHAR_NAME(DEFINITE), ToSteal->CHAR_NAME(UNARTICLED)); + + EditAP(-500); + return; + } + } + } + + nonhumanoid::GetAICommand(); +} + +/* The Fruitbat */ +truth fruitbat::IsRetreating() const +{ + if(nonhumanoid::IsRetreating()) + return true; + + for(stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) + if( (*i)/*->GetMainMaterial()->IsFruit()*/->IsFood() ) + return true; + + return false; +} + +void fruitbat::GetAICommand() +{ + if(!IsRetreating()) + { + character* Char = GetRandomNeighbour(); + + if(Char) + { + itemvector Fruits; + + for(stackiterator i = Char->GetStack()->GetBottom(); i.HasItem(); ++i) + { + if( ((*i)->IsFood()/*->GetMainMaterial()->IsFruit())*/) && !MakesBurdened((*i)->GetWeight())) + Fruits.push_back(*i); + } + + if(!Fruits.empty()) + { + item* ToSteal = Fruits[RAND() % Fruits.size()]; + ToSteal->RemoveFromSlot(); + GetStack()->AddItem(ToSteal); + + if(Char->IsPlayer()) + ADD_MESSAGE("%s steals your %s.", CHAR_NAME(DEFINITE), ToSteal->CHAR_NAME(UNARTICLED)); + + EditAP(-500); + return; + } + } + } + + nonhumanoid::GetAICommand(); +} +/* end of fruitbat */ + +void eddy::GetAICommand() +{ + if(!GetLSquareUnder()->GetOLTerrain() && !(RAND() % 500)) + { + decoration* Couch = decoration::Spawn(RAND_N(5) ? COUCH : DOUBLE_BED); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s spits out %s.", CHAR_NAME(DEFINITE), Couch->CHAR_NAME(INDEFINITE)); + + GetLSquareUnder()->ChangeOLTerrainAndUpdateLights(Couch); + EditAP(-1000); + return; + } + + if(GetStackUnder()->GetItems() && !(RAND() % 10)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s engulfs something under it.", CHAR_NAME(DEFINITE)); + + GetStackUnder()->TeleportRandomly(3); + EditAP(-1000); + return; + } + + if(!(RAND() % 100)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s engulfs itself.", CHAR_NAME(DEFINITE)); + + TeleportRandomly(true); + EditAP(-1000); + return; + } + + nonhumanoid::GetAICommand(); +} + +void skunk::GetAICommand() +{ + if(!IsRetreating()) + { + if(!RAND_N(4)) + { + character* Char = GetRandomNeighbour(HOSTILE); + + if(Char) + { + int Amount = 500 / Char->GetSquaresUnder(); + truth Success = false; + + for(int c = 0; c < Char->GetSquaresUnder(); ++c) + if(Char->GetLSquareUnder(c)->IsFlyable()) + { + Success = true; + Char->GetLSquareUnder(c)->AddSmoke(gas::Spawn(SKUNK_SMELL, Amount)); + } + + if(Success) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s stinks.", CHAR_NAME(DEFINITE)); + + EditAP(-1000); + return; + } + } + } + } + else if(RAND_N(2)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s stinks.", CHAR_NAME(DEFINITE)); + + GetLSquareUnder()->AddSmoke(gas::Spawn(SKUNK_SMELL, 500)); + } + + nonhumanoid::GetAICommand(); +} + +truth elpuri::TryToRiseFromTheDead() +{ + character::TryToRiseFromTheDead(); + + for(int c = 0; c < GetSquaresUnder(); ++c) + for(stackiterator i = GetLSquareUnder(c)->GetStack()->GetBottom(); i.HasItem(); ++i) + if(i->IsHeadOfElpuri()) + { + i->SendToHell(); + i->RemoveFromSlot(); + return true; + } + + if(CanBeSeenByPlayer()) + { + ADD_MESSAGE("The headless body of %s vibrates violently.", CHAR_NAME(DEFINITE)); + ADD_MESSAGE("%s dies.", CHAR_NAME(DEFINITE)); + } + + return false; +} + +truth nonhumanoid::EditAllAttributes(int Amount) +{ + if(!Amount) + return true; + + LimitRef(StrengthExperience += Amount * EXP_MULTIPLIER, MIN_EXP, MAX_EXP); + LimitRef(AgilityExperience += Amount * EXP_MULTIPLIER, MIN_EXP, MAX_EXP); + return character::EditAllAttributes(Amount) + || (Amount < 0 + && (StrengthExperience != MIN_EXP || AgilityExperience != MIN_EXP)) + || (Amount > 0 + && (StrengthExperience != MAX_EXP || AgilityExperience != MAX_EXP)); +} + +#ifdef WIZARD + +void nonhumanoid::AddAttributeInfo(festring& Entry) const +{ + Entry.Resize(45); + Entry << GetAttribute(ARM_STRENGTH); + Entry.Resize(48); + Entry << "- - " << GetAttribute(AGILITY); + character::AddAttributeInfo(Entry); +} + +void nonhumanoid::AddAttackInfo(felist& List) const +{ + festring Entry; + + if(IsUsingArms()) + { + Entry = CONST_S(" unarmed attack"); + Entry.Resize(50); + Entry << GetUnarmedMinDamage() << '-' << GetUnarmedMaxDamage(); + Entry.Resize(60); + Entry << int(GetUnarmedToHitValue()); + Entry.Resize(70); + Entry << GetUnarmedAPCost(); + List.AddEntry(Entry, LIGHT_GRAY); + } + + if(IsUsingLegs()) + { + Entry = CONST_S(" kick attack"); + Entry.Resize(50); + Entry << GetKickMinDamage() << '-' << GetKickMaxDamage(); + Entry.Resize(60); + Entry << int(GetKickToHitValue()); + Entry.Resize(70); + Entry << GetKickAPCost(); + List.AddEntry(Entry, LIGHT_GRAY); + } + + if(IsUsingHead()) + { + Entry = CONST_S(" bite attack"); + Entry.Resize(50); + Entry << GetBiteMinDamage() << '-' << GetBiteMaxDamage(); + Entry.Resize(60); + Entry << int(GetBiteToHitValue()); + Entry.Resize(70); + Entry << GetBiteAPCost(); + List.AddEntry(Entry, LIGHT_GRAY); + } +} + +#else + +void nonhumanoid::AddAttributeInfo(festring&) const { } +void nonhumanoid::AddAttackInfo(felist&) const { } + +#endif + +truth elpuri::MustBeRemovedFromBone() const +{ + return !IsEnabled() || GetTeam()->GetID() != MONSTER_TEAM || GetDungeon()->GetIndex() != ELPURI_CAVE || GetLevel()->GetIndex() != DARK_LEVEL; +} + +truth genetrixvesana::MustBeRemovedFromBone() const +{ + return !IsEnabled() || GetTeam()->GetID() != MONSTER_TEAM || GetDungeon()->GetIndex() != UNDER_WATER_TUNNEL || GetLevel()->GetIndex() != VESANA_LEVEL; +} + +truth solicitus::MustBeRemovedFromBone() const +{ + return !IsEnabled() || GetTeam()->GetID() != MONSTER_TEAM || GetDungeon()->GetIndex() != UNDER_WATER_TUNNEL || GetLevel()->GetIndex() != VESANA_LEVEL; +} + +void ghost::AddName(festring& String, int Case) const +{ + if(OwnerSoul.IsEmpty() || Case & PLURAL) + character::AddName(String, Case); + else + { + character::AddName(String, (Case|ARTICLE_BIT)&~INDEFINE_BIT); + String << " of " << OwnerSoul; + } +} + +void ghost::Save(outputfile& SaveFile) const +{ + nonhumanoid::Save(SaveFile); + SaveFile << OwnerSoul << Active; +} + +void ghost::Load(inputfile& SaveFile) +{ + nonhumanoid::Load(SaveFile); + SaveFile >> OwnerSoul >> Active; +} + +truth ghost::RaiseTheDead(character* Summoner) +{ + itemvector ItemVector; + GetStackUnder()->FillItemVector(ItemVector); + + for(uint c = 0; c < ItemVector.size(); ++c) + if(ItemVector[c]->SuckSoul(this, Summoner)) + return true; + + if(IsPlayer()) + ADD_MESSAGE("You shudder."); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s shudders.", CHAR_NAME(DEFINITE)); + + return false; +} + +int ghost::ReceiveBodyPartDamage(character* Damager, int Damage, int Type, int BodyPartIndex, int Direction, truth PenetrateResistance, truth Critical, truth ShowNoDamageMsg, truth CaptureBodyPart) +{ + if(Type != SOUND) + { + Active = true; + return character::ReceiveBodyPartDamage(Damager, Damage, Type, BodyPartIndex, Direction, PenetrateResistance, Critical, ShowNoDamageMsg, CaptureBodyPart); + } + else + return 0; +} + +void ghost::GetAICommand() +{ + if(Active) + character::GetAICommand(); + else + { + if(CheckForEnemies(false, false, false)) + return; + + EditAP(-1000); + } +} + +int largecreature::GetSquareIndex(v2 Pos) const +{ + v2 RelativePos = Pos - GetPos(); + return RelativePos.X + (RelativePos.Y << 1); +} + +square* largecreature::GetNeighbourSquare(int I) const +{ + square* SquareUnder = GetSquareUnder(); + area* Area = SquareUnder->GetArea(); + v2 Pos = SquareUnder->GetPos() + game::GetLargeMoveVector(I); + return Area->IsValidPos(Pos) ? SquareUnder->GetArea()->GetSquare(Pos) : 0; +} + +int largecreature::CalculateNewSquaresUnder(lsquare** NewSquare, v2 Pos) const +{ + level* Level = GetLevel(); + + for(int c = 0; c < 4; ++c) + { + v2 SquarePos = Pos + game::GetLargeMoveVector(12 + c); + + if(Level->IsValidPos(SquarePos)) + NewSquare[c] = Level->GetLSquare(SquarePos); + else + return 0; + } + + return 4; +} + +truth largecreature::IsFreeForMe(square* Square) const +{ + v2 Pos = Square->GetPos(); + area* Area = Square->GetArea(); + + for(int c = 0; c < 4; ++c) + { + v2 SquarePos = Pos + game::GetLargeMoveVector(12 + c); + + if(!Area->IsValidPos(SquarePos) || (Area->GetSquare(SquarePos)->GetCharacter() && Area->GetSquare(SquarePos)->GetCharacter() != static_cast(this))) + return false; + } + + return true; +} + +truth largecreature::CanTheoreticallyMoveOn(const lsquare* LSquare) const +{ + v2 Pos = LSquare->GetPos(); + level* Level = LSquare->GetLevel(); + + for(int c = 0; c < 4; ++c) + { + v2 SquarePos = Pos + game::GetLargeMoveVector(12 + c); + + if(!Level->IsValidPos(SquarePos) || !(GetMoveType() & Level->GetLSquare(SquarePos)->GetTheoreticalWalkability())) + return false; + } + + return true; +} + +truth largecreature::CanMoveOn(const lsquare* LSquare) const +{ + v2 Pos = LSquare->GetPos(); + level* Level = LSquare->GetLevel(); + + for(int c = 0; c < 4; ++c) + { + v2 SquarePos = Pos + game::GetLargeMoveVector(12 + c); + + if(!Level->IsValidPos(SquarePos) || !PartCanMoveOn(Level->GetLSquare(SquarePos))) + return false; + } + + return true; +} + +truth largecreature::CanMoveOn(const square* Square) const +{ + v2 Pos = Square->GetPos(); + area* Area = Square->GetArea(); + + for(int c = 0; c < 4; ++c) + { + v2 SquarePos = Pos + game::GetLargeMoveVector(12 + c); + if(!Area->IsValidPos(SquarePos) || !(GetMoveType() & Area->GetSquare(SquarePos)->GetSquareWalkability())) + return false; + } + + return true; +} + +void largecreature::PutTo(v2 Pos) +{ + for(int c = 0; c < 4; ++c) + { + SquareUnder[c] = game::GetCurrentArea()->GetSquare(Pos + game::GetLargeMoveVector(12 + c)); + SquareUnder[c]->AddCharacter(this); + } +} + +void largecreature::Remove() +{ + for(int c = 0; c < 4; ++c) + { + SquareUnder[c]->RemoveCharacter(); + SquareUnder[c] = 0; + } +} + +void largecreature::CreateCorpse(lsquare* Square) +{ + if(!BodyPartsDisappearWhenSevered() && !game::AllBodyPartsVanish()) + { + corpse* Corpse = largecorpse::Spawn(0, NO_MATERIALS); + Corpse->SetDeceased(this); + Square->AddItem(Corpse); + Disable(); + } + else + SendToHell(); +} + +void largecreature::LoadSquaresUnder() +{ + for(int c = 0; c < 4; ++c) + SquareUnder[c] = game::GetSquareInLoad()->GetArea()->GetSquare(game::GetSquareInLoad()->GetPos() + game::GetLargeMoveVector(12 + c)); +} + +truth vladimir::MustBeRemovedFromBone() const +{ + return !IsEnabled() || GetTeam()->GetID() != IVAN_TEAM || GetDungeon()->GetIndex() != ELPURI_CAVE|| GetLevel()->GetIndex() != IVAN_LEVEL; +} + +void hattifattener::GetAICommand() +{ + if(!(RAND() % 7)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s emits a lightning bolt!", CHAR_DESCRIPTION(DEFINITE)); + + beamdata Beam + ( + this, + "killed by a hattifattener's lightning", + GetPos(), + WHITE, + BEAM_LIGHTNING, + RAND() & 7, + 1 + (RAND() & 7), + 0 + ); + + GetLevel()->LightningBeam(Beam); + EditAP(-1000); + return; + } + + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +void hattifattener::CreateCorpse(lsquare* Square) +{ + level* Level = Square->GetLevel(); + ulong StackSize = Level->AddRadiusToSquareStack(Square->GetPos(), 9); + lsquare** SquareStack = Level->GetSquareStack(); + ulong c; + + for(c = 0; c < StackSize; ++c) + SquareStack[c]->RemoveFlags(IN_SQUARE_STACK); + + fearray Stack(SquareStack, StackSize); + Level->LightningVisualizer(Stack, WHITE); + + for(c = 0; c < Stack.Size; ++c) + { + beamdata Beam + ( + this, + CONST_S("killed by electricity released by a dying hattifattener"), + YOURSELF, + 0 + ); + + Stack[c]->Lightning(Beam); + } + + SendToHell(); +} + +void hedgehog::SpecialBodyDefenceEffect(character* Enemy, bodypart* BodyPart, int Type) +{ + if(Type != WEAPON_ATTACK && RAND() & 1) + { + if(Enemy->IsPlayer()) + ADD_MESSAGE("%s spines jab your %s!", CHAR_POSSESSIVE_PRONOUN, BodyPart->GetBodyPartName().CStr()); + else if(CanBeSeenByPlayer() || Enemy->CanBeSeenByPlayer()) + ADD_MESSAGE("%s spines jab %s!", CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_NAME(DEFINITE)); + + Enemy->ReceiveBodyPartDamage(this, 1 + (RAND() & 1), PHYSICAL_DAMAGE, BodyPart->GetBodyPartIndex(), YOURSELF, false, false, true, false); + Enemy->CheckDeath(CONST_S("killed by the pointy spines of ") + GetName(INDEFINITE), this); + } +} + +void anvitas::SpecialBodyDefenceEffect(character* Enemy, bodypart* BodyPart, int Type) +{ + if(Type != WEAPON_ATTACK && RAND() & 1) + { + if(Enemy->IsPlayer()) + ADD_MESSAGE("%s spines jab your %s!", CHAR_POSSESSIVE_PRONOUN, BodyPart->GetBodyPartName().CStr()); + else if(CanBeSeenByPlayer() || Enemy->CanBeSeenByPlayer()) + ADD_MESSAGE("%s spines jab %s!", CHAR_POSSESSIVE_PRONOUN, Enemy->CHAR_NAME(DEFINITE)); + + Enemy->ReceiveBodyPartDamage(this, 10 + (RAND() & 6), PHYSICAL_DAMAGE, BodyPart->GetBodyPartIndex(), YOURSELF, false, false, true, false); + Enemy->CheckDeath(CONST_S("killed by the pointy spines of ") + GetName(INDEFINITE), this); + } +} + +void genetrixvesana::Save(outputfile& SaveFile) const +{ + nonhumanoid::Save(SaveFile); + SaveFile << TurnsExisted; +} + +void genetrixvesana::Load(inputfile& SaveFile) +{ + nonhumanoid::Load(SaveFile); + SaveFile >> TurnsExisted; +} + +void solicitus::Save(outputfile& SaveFile) const +{ + nonhumanoid::Save(SaveFile); +} + +void solicitus::Load(inputfile& SaveFile) +{ + nonhumanoid::Load(SaveFile); +} + +truth largecreature::CreateRoute() +{ + Route.clear(); + + if(GetAttribute(INTELLIGENCE) >= 10 && !StateIsActivated(CONFUSED)) + { + node* Node = GetLevel()->FindRoute(GetPos(), GoingTo, Illegal, 0, this); + + if(Node) + while(Node->Last) + { + Route.push_back(Node->Pos); + Node = Node->Last; + } + else + TerminateGoingTo(); + + IntelligenceAction(5); + return true; + } + else + return false; +} + +void bunny::GetAICommand() +{ + if(GetConfig() < 4 && GetNP() > (SATIATED_LEVEL + BLOATED_LEVEL) >> 1) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s looks more mature.", CHAR_NAME(DEFINITE)); + + GetTorso()->SetSize(GetTorso()->GetSize() << 1); + LimitRef(StrengthExperience *= 2, MIN_EXP, MAX_EXP); + LimitRef(AgilityExperience *= 2, MIN_EXP, MAX_EXP); + + for(int c = 0; c < BASE_ATTRIBUTES; ++c) + BaseExperience[c] = Limit(BaseExperience[c] * 2, MIN_EXP, MAX_EXP); + + GetTorso()->GetMainMaterial()->SetVolume(GetTorso()->GetMainMaterial()->GetVolume() << 1); + SetConfig(GetConfig() + 2); + RestoreHP(); + RestoreStamina(); + } + + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + if(CheckForEnemies(true, true, true)) + return; + + if(CheckForUsefulItemsOnGround()) + return; + + if(CheckForDoors()) + return; + + if(CheckForFood(5)) + return; + + if(CheckForMatePartner()) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +void bunny::SignalNaturalGeneration() +{ + character* Partner = bunny::Spawn(GetConfig()^1); + Partner->SetTeam(GetTeam()); + Partner->SetGenerationDanger(GetGenerationDanger()); + Partner->PutNear(GetPos()); +} + +truth bunny::CheckForMatePartner() +{ + if(GetConfig() == ADULT_MALE) + { + character* BestPartner = 0; + double BestPartnerDanger = 0; + + for(int c = 0; c < game::GetTeams(); ++c) + if(GetTeam()->GetRelation(game::GetTeam(c)) != HOSTILE) + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled() && (*i)->IsBunny() && (*i)->GetConfig() == ADULT_FEMALE && (*i)->GetNP() > SATIATED_LEVEL) + { + double Danger = (*i)->GetRelativeDanger(this, true); + + if(Danger > BestPartnerDanger) + { + BestPartner = *i; + BestPartnerDanger = Danger; + } + } + + if(BestPartner && !GetPos().IsAdjacent(BestPartner->GetPos())) + { + SetGoingTo(BestPartner->GetPos()); + MoveTowardsTarget(true); + return true; + } + } + + if(GetConfig() == ADULT_FEMALE && GetNP() > NOT_HUNGER_LEVEL + 10000) + { + for(int d = 0; d < GetNeighbourSquares(); ++d) + { + lsquare* Square = GetNeighbourLSquare(d); + + if(Square) + { + character* Father = Square->GetCharacter(); + + if(Father && Father->IsBunny() && Father->GetConfig() == ADULT_MALE && GetRelation(Father) != HOSTILE) + { + if(CanBeSeenByPlayer()) + { + if(Father->IsPlayer()) + ADD_MESSAGE("You have much fun with %s.", CHAR_NAME(DEFINITE)); + else if(Father->CanBeSeenByPlayer()) + ADD_MESSAGE("%s and %s seem to have much fun together.", Father->CHAR_NAME(DEFINITE), CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("%s seems to have much fun.", CHAR_NAME(DEFINITE)); + } + else + { + if(Father->IsPlayer()) + ADD_MESSAGE("You have much fun with something."); + else if(Father->CanBeSeenByPlayer()) + ADD_MESSAGE("%s seems to have much fun.", Father->CHAR_NAME(DEFINITE)); + } + + bunny* Baby = bunny::Spawn(BABY_MALE + (RAND() & 1)); + Baby->StrengthExperience = RandomizeBabyExperience(StrengthExperience + static_cast(Father)->StrengthExperience); + Baby->AgilityExperience = RandomizeBabyExperience(AgilityExperience + static_cast(Father)->AgilityExperience); + + if(Baby->GetConfig() == BABY_MALE) + { + Baby->StrengthExperience *= 4; + Baby->AgilityExperience *= 4; + } + else + { + Baby->StrengthExperience *= 2; + Baby->AgilityExperience *= 6; + } + + Baby->StrengthExperience /= 3; + Baby->AgilityExperience /= 5; + + for(int c = 0; c < BASE_ATTRIBUTES; ++c) + Baby->BaseExperience[c] = RandomizeBabyExperience(BaseExperience[c] + static_cast(Father)->BaseExperience[c]); + + Baby->CalculateAll(); + Baby->RestoreHP(); + Baby->RestoreStamina(); + Baby->SetTeam(GetTeam()); + Baby->SetGenerationDanger(GetGenerationDanger()); + Baby->PutNear(GetPos()); + + if(Baby->CanBeSeenByPlayer()) + ADD_MESSAGE("%s is born.", Baby->CHAR_NAME(INDEFINITE)); + + EditNP(-10000); + Father->EditAP(-3000); + EditAP(-5000); + EditStamina(-GetMaxStamina() >> 1, true); + Father->EditStamina(-(Father->GetMaxStamina() << 2) / 5, true); + return true; + } + } + } + } + + return false; +} + +truth bunny::Catches(item* Thingy) +{ + if(Thingy->BunnyWillCatchAndConsume(this)) + { + if(ConsumeItem(Thingy, CONST_S("eating"))) + { + if(IsPlayer()) + ADD_MESSAGE("You catch %s in mid-air and consume it.", Thingy->CHAR_NAME(DEFINITE)); + else + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s catches %s and eats it.", CHAR_NAME(DEFINITE), Thingy->CHAR_NAME(DEFINITE)); + + ChangeTeam(PLAYER->GetTeam()); + } + } + else if(IsPlayer()) + ADD_MESSAGE("You catch %s in mid-air.", Thingy->CHAR_NAME(DEFINITE)); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s catches %s.", CHAR_NAME(DEFINITE), Thingy->CHAR_NAME(DEFINITE)); + + return true; + } + else + return false; +} + +truth largecreature::PlaceIsIllegal(v2 Pos, v2 Illegal) const +{ + for(int c = 0; c < 4; ++c) + if(Pos + game::GetLargeMoveVector(12 + c) == Illegal) + return true; + + return false; +} + +truth mommo::Hit(character* Enemy, v2 Pos, int, int) +{ + if(CheckIfTooScaredToHit(Enemy)) + return false; + + if(IsPlayer()) + { + if(!(Enemy->IsMasochist() && GetRelation(Enemy) == FRIEND) && GetRelation(Enemy) != HOSTILE && !game::TruthQuestion(CONST_S("This might cause a hostile reaction. Are you sure? [y/N]"))) + return false; + } + else if(GetAttribute(WISDOM) >= Enemy->GetAttackWisdomLimit()) + return false; + + Hostility(Enemy); + + if(IsPlayer()) + ADD_MESSAGE("You spill acidous slime at %s.", Enemy->CHAR_DESCRIPTION(DEFINITE)); + else if(Enemy->IsPlayer() || CanBeSeenByPlayer() || Enemy->CanBeSeenByPlayer()) + ADD_MESSAGE("%s spills acidous slime at %s.", CHAR_DESCRIPTION(DEFINITE), Enemy->CHAR_DESCRIPTION(DEFINITE)); + + Vomit(Pos, 250 + RAND() % 250, false); + EditAP(-1000); + return true; +} + +void mommo::GetAICommand() +{ + SeekLeader(GetLeader()); + + if(CheckForEnemies(false, false, true)) + return; + + if(!(RAND() % 10)) + { + VomitAtRandomDirection(350 + RAND() % 350); + EditAP(-1000); + return; + } + + if(FollowLeader(GetLeader())) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +void dog::GetAICommand() +{ + if(!game::IsInWilderness() && !(RAND() & 7)) + GetLSquareUnder()->SpillFluid(this, liquid::Spawn(DOG_DROOL, 25 + RAND() % 50), false, false); + + character::GetAICommand(); +} + +truth blinkdog::SpecialEnemySightedReaction(character*) +{ + if(!(RAND() & 15) && SummonFriend()) + return true; + + if(!(RAND() & 31)) + { + MonsterTeleport("playful bark"); + return true; + } + + if((!(RAND() & 3) && StateIsActivated(PANIC)) + || (!(RAND() & 7) && IsInBadCondition())) + { + MonsterTeleport("frightened howl"); + return true; + } + + return false; +} + +void blinkdog::MonsterTeleport(cchar* BarkMsg) +{ + if(CanBeSeenByPlayer()) + ADD_MESSAGE("You hear a %s inside your head as %s vanishes!", BarkMsg, CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("You hear a %s inside your head.", BarkMsg); + + v2 Pos = GetPos(); + rect Border(Pos + v2(-5, -5), Pos + v2(5, 5)); + Pos = GetLevel()->GetRandomSquare(this, 0, &Border); + + if(Pos == ERROR_V2) + Pos = GetLevel()->GetRandomSquare(this); + + Move(Pos, true); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s materializes from nowhere!", CHAR_NAME(INDEFINITE)); + + EditAP(-1000); +} + +int unicorn::TakeHit(character* Enemy, item* Weapon, bodypart* EnemyBodyPart, v2 HitPos, double Damage, double ToHitValue, int Success, int Type, int Direction, truth Critical, truth ForceHit) +{ + int Return = nonhumanoid::TakeHit(Enemy, Weapon, EnemyBodyPart, HitPos, Damage, ToHitValue, Success, Type, Direction, Critical, ForceHit); + + if(Return != HAS_DIED + && (StateIsActivated(PANIC) + || (RAND() & 1 && IsInBadCondition()) + || !(RAND() & 7))) + MonsterTeleport("neighs in terror"); + + return Return; +} + +int blinkdog::TakeHit(character* Enemy, item* Weapon, bodypart* EnemyBodyPart, v2 HitPos, double Damage, double ToHitValue, int Success, int Type, int Direction, truth Critical, truth ForceHit) +{ + int Return = nonhumanoid::TakeHit(Enemy, Weapon, EnemyBodyPart, HitPos, Damage, ToHitValue, Success, Type, Direction, Critical, ForceHit); + + if(Return != HAS_DIED) + { + if(!(RAND() & 15) && SummonFriend()) + return Return; + + if((RAND() & 1 && StateIsActivated(PANIC)) + || (!(RAND() & 3) && IsInBadCondition()) + || !(RAND() & 15)) + MonsterTeleport("terrified yelp"); + } + + return Return; +} + +void unicorn::MonsterTeleport(cchar* NeighMsg) +{ + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s %s and disappears!", CHAR_NAME(DEFINITE), NeighMsg); + + Move(GetLevel()->GetRandomSquare(this), true); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("Suddenly %s appears from nothing!", CHAR_NAME(INDEFINITE)); + + EditAP(-1000); +} + +truth blinkdog::SummonFriend() +{ + if(!SummonModifier) + return false; + + --SummonModifier; + blinkdog* Buddy = blinkdog::Spawn(); + Buddy->SummonModifier = SummonModifier; + Buddy->SetTeam(GetTeam()); + Buddy->SetGenerationDanger(GetGenerationDanger()); + Buddy->PutNear(GetPos()); + + if(CanBeSeenByPlayer()) + { + ADD_MESSAGE("%s wags its tail in a mysterious pattern.", CHAR_NAME(DEFINITE)); + + if(Buddy->CanBeSeenByPlayer()) + ADD_MESSAGE("Another of its kin appears!"); + } + else if(Buddy->CanBeSeenByPlayer()) + ADD_MESSAGE("%s appears!", Buddy->CHAR_NAME(INDEFINITE)); + + EditAP(-1000); + return true; +} + +blinkdog::blinkdog() +{ + if(!game::IsLoading()) + SummonModifier = RAND_2 + RAND_2 + RAND_2 + RAND_2 + RAND_2 + RAND_2 + RAND_2; +} + +void blinkdog::Save(outputfile& SaveFile) const +{ + dog::Save(SaveFile); + SaveFile << SummonModifier; +} + +void blinkdog::Load(inputfile& SaveFile) +{ + dog::Load(SaveFile); + SaveFile >> SummonModifier; +} + +void genetrixvesana::FinalProcessForBone() +{ + largecreature::FinalProcessForBone(); + TurnsExisted = 0; +} + +void solicitus::FinalProcessForBone() +{ + largecreature::FinalProcessForBone(); +} + +void carnivorousplant::GetAICommand() +{ + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + if(AttackAdjacentEnemyAI()) + return; + + if(CheckForUsefulItemsOnGround()) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +void noxiousorchid::GetAICommand() +{ + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + if(AttackAdjacentEnemyAI()) + return; + + if(CheckForUsefulItemsOnGround()) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +void mysticfrog::GetAICommand() +{ + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + character* NearestEnemy = 0; + long NearestEnemyDistance = 0x7FFFFFFF; + character* RandomFriend = 0; + charactervector Friend; + v2 Pos = GetPos(); + truth Enemies = false; + + for(int c = 0; c < game::GetTeams(); ++c) + { + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + { + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled()) + { + Enemies = true; + long ThisDistance = Max(abs((*i)->GetPos().X - Pos.X), abs((*i)->GetPos().Y - Pos.Y)); + + if((ThisDistance < NearestEnemyDistance || (ThisDistance == NearestEnemyDistance && !(RAND() % 3))) && (*i)->CanBeSeenBy(this)) + { + NearestEnemy = *i; + NearestEnemyDistance = ThisDistance; + } + } + } + else if(GetTeam()->GetRelation(game::GetTeam(c)) == FRIEND) + { + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled() && (*i)->CanBeSeenBy(this)) + Friend.push_back(*i); + } + } + + if(NearestEnemy && NearestEnemy->GetPos().IsAdjacent(Pos)) + { + if(NearestEnemy->IsSmall() + && GetAttribute(WISDOM) < NearestEnemy->GetAttackWisdomLimit() + && !(RAND() % 5) + && Hit(NearestEnemy, NearestEnemy->GetPos(), game::GetDirectionForVector(NearestEnemy->GetPos() - GetPos()))) + return; + else if(!(RAND() & 3)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s invokes a spell and disappears.", CHAR_NAME(DEFINITE)); + + TeleportRandomly(true); + EditAP(-GetSpellAPCost()); + return; + } + } + + if(NearestEnemy && (NearestEnemyDistance < 10 || StateIsActivated(PANIC)) && RAND() & 3) + { + SetGoingTo((Pos << 1) - NearestEnemy->GetPos()); + + if(MoveTowardsTarget(true)) + return; + } + + if(Friend.size() && !(RAND() & 3)) + { + RandomFriend = Friend[RAND() % Friend.size()]; + NearestEnemy = 0; + } + + if(GetRelation(PLAYER) == HOSTILE && PLAYER->CanBeSeenBy(this) && !RAND_4) + NearestEnemy = PLAYER; + + beamdata Beam + ( + this, + CONST_S("killed by the spells of ") + GetName(INDEFINITE), + YOURSELF, + 0 + ); + + if(NearestEnemy) + { + lsquare* Square = NearestEnemy->GetLSquareUnder(); + EditAP(-GetSpellAPCost()); + + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s invokes a spell!", CHAR_NAME(DEFINITE)); + + switch(RAND() % 20) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: Square->DrawParticles(RED); if(NearestEnemy->TeleportRandomItem(GetConfig() == DARK)) break; + case 6: + case 7: + case 8: + case 9: + case 10: Square->DrawParticles(RED); Square->Teleport(Beam); break; + case 11: + case 12: + case 13: + case 14: Square->DrawParticles(RED); Square->Slow(Beam); break; + case 15: Square->DrawParticles(RED); Square->LowerEnchantment(Beam); break; + default: Square->DrawLightning(v2(8, 8), WHITE, YOURSELF); Square->Lightning(Beam); break; + } + + if(CanBeSeenByPlayer()) + NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell of ") + GetName(DEFINITE) + CONST_S(" interrupts you.")); + else + NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell interrupts you.")); + + return; + } + + if(RandomFriend && Enemies) + { + lsquare* Square = RandomFriend->GetLSquareUnder(); + EditAP(-GetSpellAPCost()); + Square->DrawParticles(RED); + + if(RAND() & 1) + Square->Invisibility(Beam); + else + Square->Haste(Beam); + + return; + } + + StandIdleAI(); +} + +truth largecreature::PartCanMoveOn(const lsquare* LSquare) const +{ + int Walkability = LSquare->GetWalkability(); + + if(GetMoveType() & Walkability) + return true; + + if(DestroysWalls() && Walkability & ETHEREAL) + { + olterrain* Terrain = LSquare->GetOLTerrain(); + + if(Terrain && Terrain->WillBeDestroyedBy(this)) + { + room* Room = LSquare->GetRoom(); + + if(!Room || Room->IsOKToDestroyWalls(this)) + return true; + } + } + + return false; +} + +void spider::GetAICommand() +{ + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + character* NearestChar = 0; + long NearestDistance = 0x7FFFFFFF; + v2 Pos = GetPos(); + int Hostiles = 0; + + for(int c = 0; c < game::GetTeams(); ++c) + if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) + for(std::list::const_iterator i = game::GetTeam(c)->GetMember().begin(); + i != game::GetTeam(c)->GetMember().end(); ++i) + if((*i)->IsEnabled() && GetAttribute(WISDOM) < (*i)->GetAttackWisdomLimit()) + { + long ThisDistance = Max(abs((*i)->GetPos().X - Pos.X), abs((*i)->GetPos().Y - Pos.Y)); + ++Hostiles; + + if((ThisDistance < NearestDistance + || (ThisDistance == NearestDistance && !(RAND() % 3))) + && (*i)->CanBeSeenBy(this, false, IsGoingSomeWhere()) + && (!IsGoingSomeWhere() || HasClearRouteTo((*i)->GetPos()))) + { + NearestChar = *i; + NearestDistance = ThisDistance; + } + } + + if(Hostiles && !RAND_N(Max(80 / Hostiles, 8))) + { + web* Web = web::Spawn(); + Web->SetStrength(GetConfig() == LARGE ? 10 : 25); + + if(GetLSquareUnder()->AddTrap(Web)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s spins a web.", CHAR_NAME(DEFINITE)); + + EditAP(-1000); + return; + } + } + + if(NearestChar) + { + if(NearestChar->IsStuck()) + SetGoingTo(NearestChar->GetPos()); + else + SetGoingTo((Pos << 1) - NearestChar->GetPos()); + + if(MoveTowardsTarget(true)) + return; + } + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +void largecat::Save(outputfile& SaveFile) const +{ + nonhumanoid::Save(SaveFile); + SaveFile << Lives; +} + +void largecat::Load(inputfile& SaveFile) +{ + nonhumanoid::Load(SaveFile); + SaveFile >> Lives; +} + +truth largecat::SpecialSaveLife() +{ + if(--Lives <= 0 || game::IsInWilderness()) + return false; + + if(IsPlayer()) + ADD_MESSAGE("But wait! You seem to have miraculously avoided certain death!"); + else if(CanBeSeenByPlayer()) + ADD_MESSAGE("But wait, %s seems to have miraculously avoided certain death!", GetPersonalPronoun().CStr()); + + v2 Pos = GetPos(); + rect Border(Pos + v2(-20, -20), Pos + v2(20, 20)); + Pos = GetLevel()->GetRandomSquare(this, 0, &Border); + + if(Pos == ERROR_V2) + Pos = GetLevel()->GetRandomSquare(this); + + Move(Pos, true); + + if(!IsPlayer() && CanBeSeenByPlayer()) + ADD_MESSAGE("%s appears!", CHAR_NAME(INDEFINITE)); + + if(IsPlayer()) + game::AskForKeyPress(CONST_S("Life saved! [press any key to continue]")); + + RestoreBodyParts(); + ResetSpoiling(); + RestoreHP(); + RestoreStamina(); + ResetStates(); + + if(GetNP() < SATIATED_LEVEL) + SetNP(SATIATED_LEVEL); + + SendNewDrawRequest(); + + if(GetAction()) + GetAction()->Terminate(false); + + return true; +} + +truth lobhse::MustBeRemovedFromBone() const +{ + return !IsEnabled() || GetTeam()->GetID() != MONSTER_TEAM || GetDungeon()->GetIndex() != UNDER_WATER_TUNNEL || GetLevel()->GetIndex() != SPIDER_LEVEL; +} + +truth lobhse::SpecialBiteEffect(character* Char, v2, int, int, truth BlockedByArmour) +{ + if(!BlockedByArmour) + { + Char->BeginTemporaryState(POISONED, 80 + RAND() % 40); + return true; + } + else + return false; +} + +void lobhse::GetAICommand() +{ + +StandIdleAI(); + + if(MoveRandomly()) + { + web* Web = web::Spawn(); + Web->SetStrength(GetConfig() == LARGE ? 50 : 100); + + if(GetLSquareUnder()->AddTrap(Web)) + { + if(CanBeSeenByPlayer()) + ADD_MESSAGE("%s spins a web.", CHAR_NAME(DEFINITE)); + + EditAP(-1000); + return; + } + } + + +} + +void lobhse::CreateCorpse(lsquare* Square) +{ + largecreature::CreateCorpse(Square); +} + +void mindworm::GetAICommand() +{ + character* NeighbourEnemy = GetRandomNeighbour(HOSTILE); + + + if(NeighbourEnemy && NeighbourEnemy->IsHumanoid() && NeighbourEnemy->HasHead() + && !NeighbourEnemy->IsInfectedByMindWorm()) + { + TryToImplantLarvae(NeighbourEnemy); + return; + } + + character* NearestEnemy = GetNearestEnemy(); + + if(NearestEnemy) + { + PsiAttack(NearestEnemy); + return; + } + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +void mindworm::TryToImplantLarvae(character* Victim) +{ + if(Victim->MindWormCanPenetrateSkull(this)) + { + Victim->SetCounterToMindWormHatch(100); + if(Victim->IsPlayer()) + { + ADD_MESSAGE("%s penetrates digs through your skull, lays %s eggs and jumps out.", + CHAR_NAME(DEFINITE), CHAR_POSSESSIVE_PRONOUN); + } + else if(Victim->CanBeSeenByPlayer()) + { + ADD_MESSAGE("%s penetrates digs through %s's skull, lays %s eggs and jumps out.", + CHAR_NAME(DEFINITE), Victim->CHAR_NAME(DEFINITE), CHAR_POSSESSIVE_PRONOUN); + } + MoveRandomly(); + } +} + +void mindworm::PsiAttack(character* Victim) +{ + if(Victim->IsPlayer()) + { + ADD_MESSAGE("Your brain is on fire."); + } + else if(Victim->CanBeSeenByPlayer() && PLAYER->GetAttribute(PERCEPTION) > RAND_N(20)) + { + ADD_MESSAGE("%s looks scared.", Victim->CHAR_NAME(DEFINITE)); + } + + + Victim->ReceiveDamage(this, 1 + RAND_N(5), PSI, ALL, 8, false, false, false, false); + Victim->CheckDeath(CONST_S("killed by ") + GetName(INDEFINITE) + "'s psi attack", this); +} + +void menatrixfusanga::GetAICommand() +{ +StandIdleAI(); + + ++TurnsExisted; + + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + if(!(RAND() % 10)) + { + int NumberOfPlants = RAND() % 3 + RAND() % 3 + RAND() % 3 + RAND() % 3; + + for(int c1 = 0; c1 < 50 && NumberOfPlants; ++c1) + { + for(int c2 = 0; c2 < game::GetTeams() && NumberOfPlants; ++c2) + if(GetTeam()->GetRelation(game::GetTeam(c2)) == HOSTILE) + for(std::list::const_iterator i = game::GetTeam(c2)->GetMember().begin(); i != game::GetTeam(c2)->GetMember().end() && NumberOfPlants; ++i) + if((*i)->IsEnabled()) + { + lsquare* LSquare = (*i)->GetNeighbourLSquare(RAND() % GetNeighbourSquares()); + + if(LSquare && (LSquare->GetWalkability() & WALK) && !LSquare->GetCharacter()) + { + character* NewPlant; + long RandomValue = RAND() % TurnsExisted; + +if(RandomValue < 250) + NewPlant = mushroom::Spawn(); + else if(RandomValue < 1500) + NewPlant = magicmushroom::Spawn(); + else + NewPlant = magicmushroom::Spawn(); + + for(int c = 3; c < TurnsExisted / 500; ++c) + NewPlant->EditAllAttributes(1); + + NewPlant->SetGenerationDanger(GetGenerationDanger()); + NewPlant->SetTeam(GetTeam()); + NewPlant->PutTo(LSquare->GetPos()); + --NumberOfPlants; + + if(NewPlant->CanBeSeenByPlayer()) + { + if((*i)->IsPlayer()) + ADD_MESSAGE("%s sprouts from the ground near you.", NewPlant->CHAR_NAME(INDEFINITE)); + else if((*i)->CanBeSeenByPlayer()) + ADD_MESSAGE("%s sprouts from the ground near %s.", NewPlant->CHAR_NAME(INDEFINITE), (*i)->CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("%s sprouts from the ground.", NewPlant->CHAR_NAME(INDEFINITE)); + } + } + } + { + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + lsquare* CradleSquare = GetNeighbourLSquare(RAND() % 8); + + if(CradleSquare && !CradleSquare->GetCharacter() + && (CradleSquare->GetWalkability() & WALK)) + { + int SpoiledItems = 0; + int MushroomsNear = 0; + + for(int d = 0; d < 8; ++d) + { + lsquare* Square = CradleSquare->GetNeighbourLSquare(d); + + if(Square) + { + character* Char = Square->GetCharacter(); + + if(Char && Char->IsMushroom()) + ++MushroomsNear; + + SpoiledItems += Square->GetSpoiledItems(); + } + } + + if((SpoiledItems && MushroomsNear < 1 && !RAND_N(2)) || (MushroomsNear < 3 && !RAND_N((1 + MushroomsNear) * 2))) + { + magicmushroom* Child = magicmushroom::Spawn(GetConfig()); + switch(RAND() % 3) + { + case 0: SetSpecies(MakeRGB16(125 + RAND() % 125, RAND() % 100, RAND() % 100)); break; + case 1: SetSpecies(MakeRGB16(RAND() % 100, 125 + RAND() % 125, RAND() % 100)); break; + case 2: SetSpecies(MakeRGB16(RAND() % 100, RAND() % 100, 125 + RAND() % 125)); break; + } + Child->SetSpecies(Species); + + Child->SetTeam(GetTeam()); + Child->SetGenerationDanger(GetGenerationDanger()); + Child->PutTo(CradleSquare->GetPos()); + + if(Child->CanBeSeenByPlayer()) + ADD_MESSAGE("%s pops out from the ground.", Child->CHAR_NAME(INDEFINITE)); + } + } + + if(AttackAdjacentEnemyAI()) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + } + + + + EditAP(-2000); + return; + } + + if(AttackAdjacentEnemyAI()) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +void menatrixfusanga::SetSpecies(int What) +{ + Species = What; + UpdatePictures(); +} + +void menatrixfusanga::CreateCorpse(lsquare* Square) +{ + for(int c = 0; c < 1; ++c) + Square->AddItem(wand::Spawn(WAND_OF_CLONING)); + Square->AddItem(wand::Spawn(WAND_OF_MIRRORING)); + Square->AddItem(solstone::Spawn()); + + largecreature::CreateCorpse(Square); +} + +void menatrixfusanga::Save(outputfile& SaveFile) const +{ + nonhumanoid::Save(SaveFile); + SaveFile << TurnsExisted; + SaveFile << Species; +} + +void menatrixfusanga::Load(inputfile& SaveFile) +{ + nonhumanoid::Load(SaveFile); + SaveFile >> TurnsExisted; + SaveFile >> Species; +} + +truth menatrixfusanga::MustBeRemovedFromBone() const +{ + return !IsEnabled() || GetTeam()->GetID() != MONSTER_TEAM || GetDungeon()->GetIndex() != UNDER_WATER_TUNNEL || GetLevel()->GetIndex() != VESANA_LEVEL; +} + +void menatrixfusanga::FinalProcessForBone() +{ + largecreature::FinalProcessForBone(); + TurnsExisted = 0; +} + +void genefourxvesana::GetAICommand() +{ +StandIdleAI(); + + ++TurnsExisted; + + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + if(!(RAND() % 10)) + { + int NumberOfPlants = RAND() % 3 + RAND() % 3 + RAND() % 3 + RAND() % 3; + + for(int c1 = 0; c1 < 50 && NumberOfPlants; ++c1) + { + for(int c2 = 0; c2 < game::GetTeams() && NumberOfPlants; ++c2) + if(GetTeam()->GetRelation(game::GetTeam(c2)) == HOSTILE) + for(std::list::const_iterator i = game::GetTeam(c2)->GetMember().begin(); i != game::GetTeam(c2)->GetMember().end() && NumberOfPlants; ++i) + if((*i)->IsEnabled()) + { + lsquare* LSquare = (*i)->GetNeighbourLSquare(RAND() % GetNeighbourSquares()); + + if(LSquare && (LSquare->GetWalkability() & WALK) && !LSquare->GetCharacter()) + { + character* NewPlant; + long RandomValue = RAND() % TurnsExisted; + +if(RandomValue < 250) + NewPlant = carnivorousplant::Spawn(GIANT); + else if(RandomValue < 1500) + NewPlant = carnivorousplant::Spawn(LILY); + else + NewPlant = carnivorousplant::Spawn(SHAMBLING); + + for(int c = 3; c < TurnsExisted / 500; ++c) + NewPlant->EditAllAttributes(1); + + NewPlant->SetGenerationDanger(GetGenerationDanger()); + NewPlant->SetTeam(GetTeam()); + NewPlant->PutTo(LSquare->GetPos()); + --NumberOfPlants; + + if(NewPlant->CanBeSeenByPlayer()) + { + if((*i)->IsPlayer()) + ADD_MESSAGE("%s sprouts from the ground near you.", NewPlant->CHAR_NAME(INDEFINITE)); + else if((*i)->CanBeSeenByPlayer()) + ADD_MESSAGE("%s sprouts from the ground near %s.", NewPlant->CHAR_NAME(INDEFINITE), (*i)->CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("%s sprouts from the ground.", NewPlant->CHAR_NAME(INDEFINITE)); + } + } + } + { + SeekLeader(GetLeader()); + + if(FollowLeader(GetLeader())) + return; + + lsquare* CradleSquare = GetNeighbourLSquare(RAND() % 8); + + if(CradleSquare && !CradleSquare->GetCharacter() + && (CradleSquare->GetWalkability() & WALK)) + { + int SpoiledItems = 0; + int MushroomsNear = 0; + + for(int d = 0; d < 8; ++d) + { + lsquare* Square = CradleSquare->GetNeighbourLSquare(d); + + if(Square) + { + character* Char = Square->GetCharacter(); + + if(Char && Char->IsMushroom()) + ++MushroomsNear; + + SpoiledItems += Square->GetSpoiledItems(); + } + } + + if((SpoiledItems && MushroomsNear < 1 && !RAND_N(2)) || (MushroomsNear < 3 && !RAND_N((1 + MushroomsNear) * 2))) + { + carnivorousplant* Child = carnivorousplant::Spawn(SHAMBLING); + + Child->SetTeam(GetTeam()); + Child->SetGenerationDanger(GetGenerationDanger()); + Child->PutTo(CradleSquare->GetPos()); + + if(Child->CanBeSeenByPlayer()) + ADD_MESSAGE("%s sprouts from the ground.", Child->CHAR_NAME(INDEFINITE)); + } + } + + if(AttackAdjacentEnemyAI()) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + } + + + + EditAP(-2000); + return; + } + + if(AttackAdjacentEnemyAI()) + return; + + if(MoveRandomly()) + return; + + EditAP(-1000); +} + +void genefourxvesana::CreateCorpse(lsquare* Square) +{ + for(int c = 0; c < 1; ++c) + Square->AddItem(solstone::Spawn()); + + largecreature::CreateCorpse(Square); +} + +void genefourxvesana::Save(outputfile& SaveFile) const +{ + nonhumanoid::Save(SaveFile); + SaveFile << TurnsExisted; + SaveFile << Species; +} + +void genefourxvesana::Load(inputfile& SaveFile) +{ + nonhumanoid::Load(SaveFile); + SaveFile >> TurnsExisted; + SaveFile >> Species; +} + +truth genefourxvesana::MustBeRemovedFromBone() const +{ + return !IsEnabled() || GetTeam()->GetID() != MONSTER_TEAM || GetDungeon()->GetIndex() != UNDER_WATER_TUNNEL || GetLevel()->GetIndex() != VESANA_LEVEL; +} + +void genefourxvesana::FinalProcessForBone() +{ + largecreature::FinalProcessForBone(); + TurnsExisted = 0; +} diff --git a/Main/Source/object.cpp b/Main/Source/object.cpp new file mode 100644 index 0000000..b0d88fb --- /dev/null +++ b/Main/Source/object.cpp @@ -0,0 +1,519 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "object.h" +#include "materia.h" +#include "festring.h" +#include "whandler.h" +#include "rawbit.h" +#include "proto.h" +#include "game.h" +#include "bitmap.h" +#include "save.h" + +v2 RightArmSparkleValidityArray[128]; +v2 LeftArmSparkleValidityArray[128]; +v2 GroinSparkleValidityArray[169]; +v2 RightLegSparkleValidityArray[42]; +v2 LeftLegSparkleValidityArray[45]; +v2 NormalSparkleValidityArray[256]; +v2 PossibleSparkleBuffer[256]; + +object::object() : entity(0), MainMaterial(0) { } +int object::GetSpecialFlags() const { return ST_NORMAL; } +col16 object::GetOutlineColor(int) const { return TRANSPARENT_COLOR; } +cbitmap*const* object::GetPicture() const { return GraphicData.Picture; } + +object::object(const object& Object) : entity(Object), id(Object), VisualEffects(Object.VisualEffects) +{ + CopyMaterial(Object.MainMaterial, MainMaterial); +} + +object::~object() +{ + delete MainMaterial; +} + +void object::CopyMaterial(material* const& Source, material*& Dest) +{ + if(Source) + { + Dest = Source->Duplicate(); + Dest->SetMotherEntity(this); + } + else + Dest = 0; +} + +void object::Save(outputfile& SaveFile) const +{ + SaveFile << GraphicData << (int)VisualEffects; + SaveFile << MainMaterial; +} + +void object::Load(inputfile& SaveFile) +{ + SaveFile >> GraphicData >> (int&)VisualEffects; + LoadMaterial(SaveFile, MainMaterial); +} + +void object::ObjectInitMaterials(material*& FirstMaterial, material* FirstNewMaterial, long FirstDefaultVolume, material*& SecondMaterial, material* SecondNewMaterial, long SecondDefaultVolume, truth CallUpdatePictures) +{ + InitMaterial(FirstMaterial, FirstNewMaterial, FirstDefaultVolume); + InitMaterial(SecondMaterial, SecondNewMaterial, SecondDefaultVolume); + SignalVolumeAndWeightChange(); + + if(CallUpdatePictures) + UpdatePictures(); +} + +void object::InitMaterial(material*& Material, material* NewMaterial, long DefaultVolume) +{ + Material = NewMaterial; + + if(Material) + { + if(Material->HasBe()) + Enable(); + + if(DefaultVolume && !Material->GetVolume()) + Material->SetVolume(DefaultVolume); + + Material->SetMotherEntity(this); + SignalEmitationIncrease(Material->GetEmitation()); + } +} + +void object::ChangeMaterial(material*& Material, material* NewMaterial, long DefaultVolume, int SpecialFlags) +{ + delete SetMaterial(Material, NewMaterial, DefaultVolume, SpecialFlags); +} + +material* object::SetMaterial(material*& Material, material* NewMaterial, long DefaultVolume, int SpecialFlags) +{ + material* OldMaterial = Material; + Material = NewMaterial; + + if((!OldMaterial || !OldMaterial->HasBe()) + && NewMaterial && NewMaterial->HasBe()) + Enable(); + else if(OldMaterial && OldMaterial->HasBe() + && (!NewMaterial || !NewMaterial->HasBe()) + && !CalculateHasBe()) + Disable(); + + if(NewMaterial) + { + if(!NewMaterial->GetVolume()) + { + if(OldMaterial) + NewMaterial->SetVolume(OldMaterial->GetVolume()); + else if(DefaultVolume) + NewMaterial->SetVolume(DefaultVolume); + else + ABORT("Singularity spawn detected!"); + } + + NewMaterial->SetMotherEntity(this); + + if(!(SpecialFlags & NO_SIGNALS)) + SignalEmitationIncrease(NewMaterial->GetEmitation()); + } + + if(!(SpecialFlags & NO_SIGNALS)) + { + if(OldMaterial) + SignalEmitationDecrease(OldMaterial->GetEmitation()); + + SignalVolumeAndWeightChange(); + SignalMaterialChange(); + } + + if(!(SpecialFlags & NO_PIC_UPDATE)) + UpdatePictures(); + + return OldMaterial; +} + +void object::UpdatePictures() +{ + static cv2 ZeroPos(0, 0); + UpdatePictures(GraphicData, ZeroPos, VisualEffects|GetSpecialFlags(), GetMaxAlpha(), GetGraphicsContainerIndex(), &object::GetBitmapPos); +} + +truth object::RandomizeSparklePos(v2& SparklePos, v2 BPos, int& SparkleTime, ulong SeedBase, int SpecialFlags, int GraphicsContainerIndex) const +{ + static int SeedModifier = 1; + femath::SaveSeed(); + femath::SetSeed(SeedBase + SeedModifier); + + if(++SeedModifier > 0x10) + SeedModifier = 1; + + v2* ValidityArray; + int ValidityArraySize; + + if((SpecialFlags & 0x38) == ST_RIGHT_ARM) + { + ValidityArray = RightArmSparkleValidityArray; + ValidityArraySize = 128; + } + else if((SpecialFlags & 0x38) == ST_LEFT_ARM) + { + ValidityArray = LeftArmSparkleValidityArray; + ValidityArraySize = 128; + } + else if((SpecialFlags & 0x38) == ST_GROIN) + { + ValidityArray = GroinSparkleValidityArray; + ValidityArraySize = 169; + } + else if((SpecialFlags & 0x38) == ST_RIGHT_LEG) + { + ValidityArray = RightLegSparkleValidityArray; + ValidityArraySize = 42; + } + else if((SpecialFlags & 0x38) == ST_LEFT_LEG) + { + ValidityArray = LeftLegSparkleValidityArray; + ValidityArraySize = 45; + } + else + { + ValidityArray = NormalSparkleValidityArray; + ValidityArraySize = 256; + } + + SparklePos = igraph::GetRawGraphic(GraphicsContainerIndex)->RandomizeSparklePos(ValidityArray, PossibleSparkleBuffer, BPos, TILE_V2, ValidityArraySize, GetSparkleFlags()); + + if(SparklePos != ERROR_V2) + { + SparkleTime = RAND() % 241; + femath::LoadSeed(); + return true; + } + else + { + femath::LoadSeed(); + return false; + } +} + +void object::UpdatePictures(graphicdata& GraphicData, v2 Position, int SpecialFlags, alpha MaxAlpha, int GraphicsContainerIndex, bposretriever BitmapPosRetriever) const +{ + int AnimationFrames = GetClassAnimationFrames(); + v2 SparklePos; + int SparkleTime = 0; + int Seed = 0; + int FlyAmount = GetSpoilLevel(); + truth Sparkling = false, FrameNeeded = false, SeedNeeded = false; + v2 BPos = (this->*BitmapPosRetriever)(0); + alpha Alpha; + + if(!(SpecialFlags & (ST_FLAMES|ST_LIGHTNING))) + { + if(AllowSparkling()) + { + int SparkleFlags = GetSparkleFlags(); + + if(SparkleFlags + && RandomizeSparklePos(SparklePos, BPos, SparkleTime, + BPos.X + BPos.Y + GetMaterialColorA(0), + SpecialFlags, GraphicsContainerIndex)) + { + Sparkling = true; + + if(AnimationFrames <= 256) + AnimationFrames = 256; + } + } + + if(FlyAmount) + { + SeedNeeded = true; + FrameNeeded = true; + + if(AnimationFrames <= 32) + AnimationFrames = 32; + } + } + else if(SpecialFlags & ST_FLAMES) + { + SeedNeeded = true; + FrameNeeded = true; + + if(AnimationFrames <= 16) + AnimationFrames = 16; + } + else if(SpecialFlags & ST_LIGHTNING) + { + SeedNeeded = true; + + if(AnimationFrames <= 128) + AnimationFrames = 128; + } + + if(SeedNeeded) + { + static int SeedModifier = 1; + Seed = BPos.X + BPos.Y + GetMaterialColorA(0) + SeedModifier + 0x42; + + if(++SeedModifier > 0x10) + SeedModifier = 1; + } + + int WobbleMask = 0, WobbleData = GetWobbleData(); + + if(WobbleData & WOBBLE) + { + int Speed = (WobbleData & WOBBLE_SPEED_RANGE) >> WOBBLE_SPEED_SHIFT; + int Freq = (WobbleData & WOBBLE_FREQ_RANGE) >> WOBBLE_FREQ_SHIFT; + int WobbleFrames = 512 >> (Freq + Speed); + WobbleMask = 7 >> Freq << (6 - Speed); + + if(AnimationFrames <= WobbleFrames) + AnimationFrames = WobbleFrames; + } + + ModifyAnimationFrames(AnimationFrames); + int c; + int OldAnimationFrames = GraphicData.AnimationFrames; + + for(c = 0; c < OldAnimationFrames; ++c) + igraph::RemoveUser(GraphicData.GraphicIterator[c]); + + if(OldAnimationFrames != AnimationFrames) + { + if(OldAnimationFrames) + { + delete [] GraphicData.Picture; + delete [] GraphicData.GraphicIterator; + } + + GraphicData.Picture = new bitmap*[AnimationFrames]; + GraphicData.GraphicIterator = new tilemap::iterator[AnimationFrames]; + } + + GraphicData.AnimationFrames = AnimationFrames; + + if(!AllowRegularColors()) + SpecialFlags |= ST_DISALLOW_R_COLORS; + + graphicid GI; + GI.BaseAlpha = MaxAlpha; + GI.FileIndex = GraphicsContainerIndex; + GI.SpecialFlags = SpecialFlags; + GI.Seed = Seed; + GI.FlyAmount = FlyAmount; + GI.Position = Position; + GI.RustData[0] = GetRustDataA(); + GI.RustData[1] = GetRustDataB(); + GI.RustData[2] = GetRustDataC(); + GI.RustData[3] = GetRustDataD(); + GI.WobbleData = WobbleData; + + for(c = 0; c < AnimationFrames; ++c) + { + GI.Color[0] = GetMaterialColorA(c); + GI.Color[1] = GetMaterialColorB(c); + GI.Color[2] = GetMaterialColorC(c); + GI.Color[3] = GetMaterialColorD(c); + Alpha = GetAlphaA(c); + GI.Alpha[0] = Alpha < MaxAlpha ? Alpha : MaxAlpha; + Alpha = GetAlphaB(c); + GI.Alpha[1] = Alpha < MaxAlpha ? Alpha : MaxAlpha; + Alpha = GetAlphaC(c); + GI.Alpha[2] = Alpha < MaxAlpha ? Alpha : MaxAlpha; + Alpha = GetAlphaD(c); + GI.Alpha[3] = Alpha < MaxAlpha ? Alpha : MaxAlpha; + v2 BPos = (this->*BitmapPosRetriever)(c); + GI.BitmapPosX = BPos.X; + GI.BitmapPosY = BPos.Y; + + if(Sparkling && c > SparkleTime && c < SparkleTime + 16) + { + GI.SparklePosX = SparklePos.X; + GI.SparklePosY = SparklePos.Y; + GI.SparkleFrame = c - SparkleTime; + } + else + { + GI.SparklePosX = SPARKLE_POS_X_ERROR; + GI.SparklePosY = 0; + GI.SparkleFrame = 0; + } + + GI.Frame = !c || FrameNeeded + || SpecialFlags & ST_LIGHTNING && !((c + 1) & 7) + || WobbleData & WOBBLE && !(c & WobbleMask) + ? c : 0; + + GI.OutlineColor = GetOutlineColor(c); + GI.OutlineAlpha = GetOutlineAlpha(c); + tilemap::iterator Iterator = igraph::AddUser(GI); + GraphicData.GraphicIterator[c] = Iterator; + GraphicData.Picture[c] = Iterator->second.Bitmap; + } +} + +col16 object::GetMaterialColorA(int) const +{ + return MainMaterial->GetColor(); +} + +truth object::AddRustLevelDescription(festring& String, truth Articled) const +{ + return MainMaterial->AddRustLevelDescription(String, Articled); +} + +truth object::AddMaterialDescription(festring& String, truth Articled) const +{ + MainMaterial->AddName(String, Articled); + String << ' '; + return true; +} + +void object::AddContainerPostFix(festring& String) const +{ + if(GetSecondaryMaterial()) + GetSecondaryMaterial()->AddName(String << " full of ", false, false); +} + +void object::AddLumpyPostFix(festring& String) const +{ + MainMaterial->AddName(String << " of ", false, false); +} + +alpha object::GetAlphaA(int) const +{ + return MainMaterial->GetAlpha(); +} + +void object::RandomizeVisualEffects() +{ + int AcceptedFlags = GetOKVisualEffects(); + + if(AcceptedFlags) + SetVisualEffects(RAND() & 0x7 & AcceptedFlags | GetForcedVisualEffects()); + else + SetVisualEffects(GetForcedVisualEffects()); +} + +void object::LoadMaterial(inputfile& SaveFile, material*& Material) +{ + SaveFile >> Material; + + if(Material) + { + if(Material->HasBe()) + Enable(); + + Material->SetMotherEntity(this); + game::CombineLights(Emitation, Material->GetEmitation()); + } +} + +int object::RandomizeMaterialConfiguration() +{ + const fearray& MCC = GetMaterialConfigChances(); + return MCC.Size > 1 + ? femath::WeightedRand(MCC.Data, GetMaterialConfigChanceSum()) + : 0; +} + +truth object::AddEmptyAdjective(festring& String, truth Articled) const +{ + if(GetSecondaryMaterial()) + return false; + else + { + String << (Articled ? "an empty " : "empty "); + return true; + } +} + +void object::CalculateEmitation() +{ + Emitation = GetBaseEmitation(); + + if(MainMaterial) + game::CombineLights(Emitation, MainMaterial->GetEmitation()); +} + +truth object::CalculateHasBe() const +{ + return MainMaterial && MainMaterial->HasBe(); +} + +int object::GetSparkleFlags() const +{ + return MainMaterial->IsSparkling() ? SPARKLING_A : 0; +} + +void object::InitSparkleValidityArrays() +{ + int y, x, Index = 0; + + for(y = 0; y < 16; ++y) + for(x = 0; x < 8; ++x) + RightArmSparkleValidityArray[Index++] = v2(x, y); + + Index = 0; + + for(y = 0; y < 16; ++y) + for(x = 8; x < 16; ++x) + LeftArmSparkleValidityArray[Index++] = v2(x, y); + + Index = 0; + + for(y = 0; y < 10; ++y) + for(x = 0; x < 16; ++x) + GroinSparkleValidityArray[Index++] = v2(x, y); + + for(y = 10; y < 13; ++y) + for(x = y - 5; x < 20 - y; ++x) + GroinSparkleValidityArray[Index++] = v2(x, y); + + Index = 0; + + for(y = 10; y < 16; ++y) + for(x = 0; x < 8; ++x) + if((y != 10 || x < 5) && (y != 11 || x < 6) && (y != 12 || x < 7)) + RightLegSparkleValidityArray[Index++] = v2(x, y); + + Index = 0; + + for(y = 10; y < 16; ++y) + for(x = 8; x < 16; ++x) + if((y != 10 || x > 9) && (y != 11 || x > 8)) + LeftLegSparkleValidityArray[Index++] = v2(x, y); + + Index = 0; + + for(y = 0; y < 16; ++y) + for(x = 0; x < 16; ++x) + NormalSparkleValidityArray[Index++] = v2(x, y); +} + +int object::GetRustDataA() const +{ + return MainMaterial->GetRustData(); +} + +truth object::DetectMaterial(cmaterial* Material) const +{ + for(int c = 0; c < GetMaterials(); ++c) + if(GetMaterial(c) && GetMaterial(c)->IsSameAs(Material)) + return true; + + return false; +} diff --git a/Main/Source/pool.cpp b/Main/Source/pool.cpp new file mode 100644 index 0000000..a04a154 --- /dev/null +++ b/Main/Source/pool.cpp @@ -0,0 +1,72 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through coreset.cpp */ + +entity* pool::FirstEntity = 0; +entity* pool::LastEntity = 0; +entity* pool::FirstDoomed = 0; +entity* pool::LastDoomed = 0; +entity* pool::CurrentEntity = 0; + +/* Calls the Be() function of each self-changeable entity during each tick, + * thus allowing acting characters, spoiling food etc. + * Also handles removal of entities marked as dead by calling SendToHell(). */ + +void pool::Be() +{ + CurrentEntity = FirstEntity; + + while(CurrentEntity) + { + CurrentEntity->Be(); + + if(CurrentEntity) + CurrentEntity = CurrentEntity->Next; + } +} + +void pool::BurnHell() +{ + entity* Entity = FirstDoomed; + + while(Entity) + { + entity* Next = Entity->Next; + delete Entity; + Entity = Next; + } + + FirstDoomed = 0; +} + +void pool::Add(entity* Entity) +{ + Entity->Last = LastEntity; + Entity->Next = 0; + LastEntity = (FirstEntity ? LastEntity->Next : FirstEntity) = Entity; +} + +void pool::Remove(entity* Entity) +{ + if(CurrentEntity == Entity) + CurrentEntity = Entity->Next; + + (Entity->Last ? Entity->Last->Next : FirstEntity) = Entity->Next; + (Entity->Next ? Entity->Next->Last : LastEntity) = Entity->Last; +} + +void pool::AddToHell(entity* Entity) +{ + Entity->Next = 0; + LastDoomed = (FirstDoomed ? LastDoomed->Next : FirstDoomed) = Entity; +} diff --git a/Main/Source/proto.cpp b/Main/Source/proto.cpp new file mode 100644 index 0000000..06c8eee --- /dev/null +++ b/Main/Source/proto.cpp @@ -0,0 +1,660 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through dataset.cpp */ + +itemdatabase** protosystem::ItemConfigData; +int protosystem::ItemConfigDataSize; +itemdatabase** protosystem::ItemCategoryData[ITEM_CATEGORIES]; +int protosystem::ItemCategorySize[ITEM_CATEGORIES]; +long protosystem::ItemCategoryPossibility[ITEM_CATEGORIES]; +long protosystem::TotalItemPossibility; + +character* protosystem::BalancedCreateMonster() +{ + for(int c = 0;; ++c) + { + double MinDifficulty = game::GetMinDifficulty(), MaxDifficulty = MinDifficulty * 25; + std::vector Possible; + + for(int Type = 1; Type < protocontainer::GetSize(); ++Type) + { + const character::prototype* Proto = protocontainer::GetProto(Type); + const character::database*const* ConfigData = Proto->GetConfigData(); + int ConfigSize = Proto->GetConfigSize(); + + for(int c = 0; c < ConfigSize; ++c) + { + const character::database* DataBase = ConfigData[c]; + + if(!DataBase->IsAbstract && DataBase->CanBeGenerated) + { + if(DataBase->IsUnique + && DataBase->Flags & HAS_BEEN_GENERATED) + continue; + + truth IsCatacomb = *game::GetCurrentLevel()->GetLevelScript()->IsCatacomb(); + + if((IsCatacomb && !DataBase->IsCatacombCreature) + || (!IsCatacomb && DataBase->CanBeGeneratedOnlyInTheCatacombs)) + continue; + + configid ConfigID(Type, ConfigData[c]->Config); + + if(c >= 100) + { + Possible.push_back(ConfigID); + continue; + } + + const dangerid& DangerID = game::GetDangerMap().find(ConfigID)->second; + + if(!DataBase->IgnoreDanger) + { + double Danger = DangerID.EquippedDanger; + + if(Danger > 99.0 + || Danger < 0.0011 + || (DataBase->IsUnique && Danger < 3.5)) + continue; + + double DangerModifier + = DataBase->DangerModifier == 100 + ? Danger + : Danger * 100 / DataBase->DangerModifier; + + if(DangerModifier < MinDifficulty + || DangerModifier > MaxDifficulty) + continue; + } + + ivantime Time; + game::GetTime(Time); + + if(PLAYER->GetMaxHP() < DataBase->HPRequirementForGeneration + && Time.Day < DataBase->DayRequirementForGeneration) + continue; + + Possible.push_back(ConfigID); + } + } + } + + if(Possible.empty()) + continue; + + for(int i = 0; i < 25; ++i) + { + configid Chosen = Possible[RAND_GOOD(Possible.size())]; + const character::prototype* Proto = protocontainer::GetProto(Chosen.Type); + character* Monster = Proto->Spawn(Chosen.Config); + + if(c >= 100 + || ((Monster->GetFrequency() == 10000 + || Monster->GetFrequency() > RAND_GOOD(10000)) + && (Monster->IsUnique() + || (Monster->GetTimeToKill(PLAYER, true) > 5000 + && PLAYER->GetTimeToKill(Monster, true) < 200000)))) + { + Monster->SetTeam(game::GetTeam(MONSTER_TEAM)); + return Monster; + } + else + delete Monster; + } + } + + /* This line is never reached, but it prevents warnings given by some (stupid) compilers. */ + + return 0; +} + +item* protosystem::BalancedCreateItem(long MinPrice, long MaxPrice, long RequiredCategory, int SpecialFlags, int ConfigFlags, int RequiredGod, truth Polymorph) +{ + typedef item::database database; + database** PossibleCategory[ITEM_CATEGORIES]; + int PossibleCategorySize[ITEM_CATEGORIES]; + long PartialCategoryPossibilitySum[ITEM_CATEGORIES]; + int PossibleCategories = 0; + long TotalPossibility = 0; + long database::*PartialPossibilitySumPtr; + + if(RequiredCategory == ANY_CATEGORY) + { + PartialPossibilitySumPtr = &database::PartialPossibilitySum; + PossibleCategory[0] = ItemConfigData; + PossibleCategorySize[0] = ItemConfigDataSize; + TotalPossibility = TotalItemPossibility; + PartialCategoryPossibilitySum[0] = TotalPossibility; + PossibleCategories = 1; + } + else + { + PartialPossibilitySumPtr = &database::PartialCategoryPossibilitySum; + + for(long CategoryIndex = 0, Category = 1; CategoryIndex < ITEM_CATEGORIES; ++CategoryIndex, Category <<= 1) + if(Category & RequiredCategory) + { + PossibleCategory[PossibleCategories] = ItemCategoryData[CategoryIndex]; + PossibleCategorySize[PossibleCategories] = ItemCategorySize[CategoryIndex]; + TotalPossibility += ItemCategoryPossibility[CategoryIndex]; + PartialCategoryPossibilitySum[PossibleCategories] = TotalPossibility; + ++PossibleCategories; + } + } + + for(int c0 = 0;; ++c0) + { + for(int c1 = 0; c1 < BALANCED_CREATE_ITEM_ITERATIONS; ++c1) + { + long Rand = RAND_GOOD(TotalPossibility); + int Category; + + if(RequiredCategory == ANY_CATEGORY) + Category = 0; + else + { + for(int c2 = 0;; ++c2) + if(PartialCategoryPossibilitySum[c2] > Rand) + { + Category = c2; + break; + } + + if(Category) + Rand -= PartialCategoryPossibilitySum[Category - 1]; + } + + const database*const* ChosenCategory = PossibleCategory[Category]; + const database* ChosenDataBase; + + if(ChosenCategory[0]->PartialCategoryPossibilitySum > Rand) + ChosenDataBase = ChosenCategory[0]; + else + { + long A = 0; + long B = PossibleCategorySize[Category] - 1; + + for(;;) + { + long C = (A + B) >> 1; + + if(A != C) + { + if(ChosenCategory[C]->*PartialPossibilitySumPtr > Rand) + B = C; + else + A = C; + } + else + { + ChosenDataBase = ChosenCategory[B]; + break; + } + } + } + + int Config = ChosenDataBase->Config; + + if((!(ConfigFlags & NO_BROKEN) + || !(Config & BROKEN)) + && (!Polymorph + || ChosenDataBase->IsPolymorphSpawnable)) + { + item* Item = ChosenDataBase->ProtoType->Spawn(Config, SpecialFlags); + truth GodOK = !RequiredGod || Item->GetAttachedGod() == RequiredGod; + + /* Optimization, GetTruePrice() may be rather slow */ + + if(((MinPrice == 0 && MaxPrice == MAX_PRICE) + || (Config & BROKEN && ConfigFlags & IGNORE_BROKEN_PRICE)) && GodOK) + return Item; + + long Price = Item->GetTruePrice(); + + if(Item->HandleInPairs()) + Price <<= 1; + + if(Price >= MinPrice && Price <= MaxPrice && GodOK) + return Item; + + delete Item; + } + } + + if(c0 == 25 && RequiredGod) + return 0; + + MinPrice = MinPrice * 7 >> 3; + MaxPrice = MaxPrice * 9 >> 3; + } +} + +character* protosystem::CreateMonster(int MinDanger, int MaxDanger, int SpecialFlags) +{ + std::vector Possible; + character* Monster = 0; + + for(int c = 0; !Monster; ++c) + { + for(int Type = 1; Type < protocontainer::GetSize(); ++Type) + { + const character::prototype* Proto = protocontainer::GetProto(Type); + const character::database*const* ConfigData = Proto->GetConfigData(); + int ConfigSize = Proto->GetConfigSize(); + + for(int c = 0; c < ConfigSize; ++c) + { + const character::database* DataBase = ConfigData[c]; + + if(!DataBase->IsAbstract + && DataBase->CanBeGenerated + && DataBase->CanBeWished + && !DataBase->IsUnique + && (DataBase->Frequency == 10000 + || DataBase->Frequency > RAND_GOOD(10000))) + { + configid ConfigID(Type, DataBase->Config); + + if((MinDanger > 0 || MaxDanger < 1000000) && c < 25) + { + const dangerid& DangerID = game::GetDangerMap().find(ConfigID)->second; + double RawDanger = SpecialFlags & NO_EQUIPMENT ? DangerID.NakedDanger : DangerID.EquippedDanger; + int Danger = int(DataBase->DangerModifier == 100 ? RawDanger * 1000 : RawDanger * 100000 / DataBase->DangerModifier); + + if(Danger < MinDanger || Danger > MaxDanger) + continue; + } + + Possible.push_back(ConfigID); + } + } + } + + if(Possible.empty()) + { + MinDanger = MinDanger > 0 ? Max(MinDanger * 3 >> 2, 1) : 0; + MaxDanger = MaxDanger < 1000000 ? Min(MaxDanger * 5 >> 2, 999999) : 1000000; + continue; + } + + configid Chosen = Possible[RAND_GOOD(Possible.size())]; + Monster = protocontainer::GetProto(Chosen.Type)->Spawn(Chosen.Config, SpecialFlags); + Monster->SignalGeneration(); + Monster->SetTeam(game::GetTeam(MONSTER_TEAM)); + } + + return Monster; +} + +template std::pair CountCorrectNameLetters(const typename type::database* DataBase, cfestring& Identifier) +{ + std::pair Result(0, 0); + + if(!DataBase->NameSingular.IsEmpty()) + ++Result.second; + + if(festring::IgnoreCaseFind(Identifier, " " + DataBase->NameSingular + ' ') != festring::NPos) + Result.first += DataBase->NameSingular.GetSize(); + + if(!DataBase->Adjective.IsEmpty()) + ++Result.second; + + if(DataBase->Adjective.GetSize() && festring::IgnoreCaseFind(Identifier, " " + DataBase->Adjective + ' ') != festring::NPos) + Result.first += DataBase->Adjective.GetSize(); + + if(!DataBase->PostFix.IsEmpty()) + ++Result.second; + + if(DataBase->PostFix.GetSize() && festring::IgnoreCaseFind(Identifier, " " + DataBase->PostFix + ' ') != festring::NPos) + Result.first += DataBase->PostFix.GetSize(); + + for(uint c = 0; c < DataBase->Alias.Size; ++c) + if(festring::IgnoreCaseFind(Identifier, " " + DataBase->Alias[c] + ' ') != festring::NPos) + Result.first += DataBase->Alias[c].GetSize(); + + return Result; +} + +template std::pair SearchForProto(cfestring& What, truth Output) +{ + typedef typename type::prototype prototype; + typedef typename type::database database; + + festring Identifier; + Identifier << ' ' << What << ' '; + truth Illegal = false, Conflict = false; + std::pair ID(0, 0); + std::pair Best(0, 0); + + for(int c = 1; c < protocontainer::GetSize(); ++c) + { + const prototype* Proto = protocontainer::GetProto(c); + const database*const* ConfigData = Proto->GetConfigData(); + int ConfigSize = Proto->GetConfigSize(); + + for(int c = 0; c < ConfigSize; ++c) + if(!ConfigData[c]->IsAbstract) + { + truth BrokenRequested = festring::IgnoreCaseFind(Identifier, " broken ") != festring::NPos; + + if(BrokenRequested == !(ConfigData[c]->Config & BROKEN)) + continue; + + std::pair Correct = CountCorrectNameLetters(ConfigData[c], Identifier); + + if(Correct == Best) + Conflict = true; + else if(Correct.first > Best.first || (Correct.first == Best.first && Correct.second < Best.second)) + if(ConfigData[c]->CanBeWished || game::WizardModeIsActive()) + { + ID.first = Proto; + ID.second = ConfigData[c]->Config; + Best = Correct; + Conflict = false; + } + else + Illegal = true; + } + } + + if(Output) + { + if(!Best.first) + { + if(Illegal) + ADD_MESSAGE("You hear a booming voice: \"No, mortal! This will not be done!\""); + else + ADD_MESSAGE("What a strange wish!"); + } + else if(Conflict) + { + ADD_MESSAGE("Be more precise!"); + return std::pair(0, 0); + } + } + + return ID; +} + +character* protosystem::CreateMonster(cfestring& What, int SpecialFlags, truth Output) +{ + std::pair ID = SearchForProto(What, Output); + + if(ID.first) + { + character* Char = ID.first->Spawn(ID.second, SpecialFlags); + + if(!Char->HasBeenSeen() && !game::WizardModeIsActive()) + { + ADD_MESSAGE("You have no idea what this creature is like."); + delete Char; + return 0; + } + else + return Char; + } + else + return 0; +} + +item* protosystem::CreateItem(cfestring& What, truth Output) +{ + std::pair ID = SearchForProto(What, Output); + + if(ID.first) + { + item* Item = ID.first->Spawn(ID.second); + festring Q = "Do you want to wish for "; + Item->AddName(Q, INDEFINITE|STRIPPED); + Q << "? [y/N]"; + + if(!game::TruthQuestion(Q)) + { + delete Item; + return 0; + } + + return Item; + } + else + return 0; +} + +material* protosystem::CreateMaterial(cfestring& What, long Volume, truth Output) +{ + for(int c1 = 1; c1 < protocontainer::GetSize(); ++c1) + { + const material::prototype* Proto = protocontainer::GetProto(c1); + const material::database*const* ConfigData = Proto->GetConfigData(); + int ConfigSize = Proto->GetConfigSize(); + + for(int c2 = 1; c2 < ConfigSize; ++c2) + if(ConfigData[c2]->NameStem == What) + if(ConfigData[c2]->CommonFlags & CAN_BE_WISHED + || game::WizardModeIsActive()) + return ConfigData[c2]->ProtoType->Spawn(ConfigData[c2]->Config, Volume); + else if(Output) + { + ADD_MESSAGE("You hear a booming voice: \"No, mortal! This will not be done!\""); + return 0; + } + } + + if(Output) + ADD_MESSAGE("There is no such material."); + + return 0; +} + +#ifdef WIZARD + +void protosystem::CreateEveryCharacter(charactervector& Character) +{ + for(int c1 = 1; c1 < protocontainer::GetSize(); ++c1) + { + const character::prototype* Proto = protocontainer::GetProto(c1); + const character::database*const* ConfigData = Proto->GetConfigData(); + int ConfigSize = Proto->GetConfigSize(); + + for(int c2 = 0; c2 < ConfigSize; ++c2) + if(!ConfigData[c2]->IsAbstract) + Character.push_back(Proto->Spawn(ConfigData[c2]->Config)); + } +} + +void protosystem::CreateEveryItem(itemvectorvector& Item) +{ + for(int c = 1; c < protocontainer::GetSize(); ++c) + { + const item::prototype* Proto = protocontainer::GetProto(c); + const item::database*const* ConfigData = Proto->GetConfigData(); + int ConfigSize = Proto->GetConfigSize(); + + for(int c2 = 0; c2 < ConfigSize; ++c2) + if(!ConfigData[c2]->IsAbstract && ConfigData[c2]->IsAutoInitializable) + Item.push_back(itemvector(1, Proto->Spawn(ConfigData[c2]->Config))); + } +} + +void protosystem::CreateEveryMaterial(std::vector& Material) +{ + for(int c1 = 1; c1 < protocontainer::GetSize(); ++c1) + { + const material::prototype* Proto = protocontainer::GetProto(c1); + const material::database*const* ConfigData = Proto->GetConfigData(); + int ConfigSize = Proto->GetConfigSize(); + + for(int c2 = 1; c2 < ConfigSize; ++c2) + Material.push_back(Proto->Spawn(ConfigData[c2]->Config)); + } +} + +#endif + +void protosystem::CreateEveryNormalEnemy(charactervector& EnemyVector) +{ + for(int c = 1; c < protocontainer::GetSize(); ++c) + { + const character::prototype* Proto = protocontainer::GetProto(c); + const character::database*const* ConfigData = Proto->GetConfigData(); + int ConfigSize = Proto->GetConfigSize(); + + for(int c2 = 0; c2 < ConfigSize; ++c2) + if(!ConfigData[c2]->IsAbstract + && !ConfigData[c2]->IsUnique + && ConfigData[c2]->CanBeGenerated) + EnemyVector.push_back(Proto->Spawn(ConfigData[c2]->Config, NO_PIC_UPDATE|NO_EQUIPMENT_PIC_UPDATE)); + } +} + +void protosystem::Initialize() +{ + typedef item::prototype prototype; + typedef item::database database; + int c; + + for(c = 1; c < protocontainer::GetSize(); ++c) + { + const prototype* Proto = protocontainer::GetProtoData()[c]; + ItemConfigDataSize += Proto->GetConfigSize(); + + if(Proto->GetConfigData()[0]->IsAbstract) + --ItemConfigDataSize; + } + + ItemConfigData = new database*[ItemConfigDataSize]; + int Index = 0; + + for(c = 1; c < protocontainer::GetSize(); ++c) + { + const prototype* Proto = protocontainer::GetProtoData()[c]; + const database*const* ProtoConfigData = Proto->GetConfigData(); + const database* MainDataBase = *ProtoConfigData; + + if(!MainDataBase->IsAbstract) + ItemConfigData[Index++] = const_cast(MainDataBase); + + int ConfigSize = Proto->GetConfigSize(); + + for(int c2 = 1; c2 < ConfigSize; ++c2) + ItemConfigData[Index++] = const_cast(ProtoConfigData[c2]); + } + + database** DataBaseBuffer = new database*[ItemConfigDataSize]; + + for(int CategoryIndex = 0, Category = 1; CategoryIndex < ITEM_CATEGORIES; ++CategoryIndex, Category <<= 1) + { + long TotalPossibility = 0; + int CSize = 0; + + for(int c = 0; c < ItemConfigDataSize; ++c) + { + database* DataBase = ItemConfigData[c]; + + if(DataBase->Category == Category) + { + DataBaseBuffer[CSize++] = DataBase; + TotalPossibility += DataBase->Possibility; + DataBase->PartialCategoryPossibilitySum = TotalPossibility; + } + } + + ItemCategoryData[CategoryIndex] = new database*[CSize]; + ItemCategorySize[CategoryIndex] = CSize; + ItemCategoryPossibility[CategoryIndex] = TotalPossibility; + memcpy(ItemCategoryData[CategoryIndex], DataBaseBuffer, CSize * sizeof(database*)); + } + + delete [] DataBaseBuffer; + + for(c = 0; c < ItemConfigDataSize; ++c) + { + database* DataBase = ItemConfigData[c]; + TotalItemPossibility += DataBase->Possibility; + DataBase->PartialPossibilitySum = TotalItemPossibility; + } +} + +void protosystem::InitCharacterDataBaseFlags() +{ + for(int c1 = 1; c1 < protocontainer::GetSize(); ++c1) + { + const character::prototype* Proto = protocontainer::GetProto(c1); + character::database** ConfigData = Proto->ConfigData; + int ConfigSize = Proto->GetConfigSize(); + + for(int c2 = 0; c2 < ConfigSize; ++c2) + if(!ConfigData[c2]->AutomaticallySeen) + ConfigData[c2]->Flags = 0; + else + ConfigData[c2]->Flags = HAS_BEEN_SEEN; + } +} + +void protosystem::SaveCharacterDataBaseFlags(outputfile& SaveFile) +{ + for(int c1 = 1; c1 < protocontainer::GetSize(); ++c1) + { + const character::prototype* Proto = protocontainer::GetProto(c1); + const character::database*const* ConfigData = Proto->ConfigData; + int ConfigSize = Proto->GetConfigSize(); + + for(int c2 = 0; c2 < ConfigSize; ++c2) + SaveFile << ConfigData[c2]->Flags; + } +} + +void protosystem::LoadCharacterDataBaseFlags(inputfile& SaveFile) +{ + for(int c1 = 1; c1 < protocontainer::GetSize(); ++c1) + { + const character::prototype* Proto = protocontainer::GetProto(c1); + character::database** ConfigData = Proto->ConfigData; + int ConfigSize = Proto->GetConfigSize(); + + for(int c2 = 0; c2 < ConfigSize; ++c2) + SaveFile >> ConfigData[c2]->Flags; + } +} + +void protosystem::CreateEverySeenCharacter(charactervector& Character) +{ + for(int c1 = 1; c1 < protocontainer::GetSize(); ++c1) + { + const character::prototype* Proto = protocontainer::GetProto(c1); + const character::database*const* ConfigData = Proto->GetConfigData(); + int ConfigSize = Proto->GetConfigSize(); + + for(int c2 = 0; c2 < ConfigSize; ++c2) + if(!ConfigData[c2]->IsAbstract && ConfigData[c2]->Flags & HAS_BEEN_SEEN) + { + character* Char = Proto->Spawn(ConfigData[c2]->Config); + Char->SetAssignedName(""); + Character.push_back(Char); + } + } +} + +void protosystem::CreateEveryMaterial(std::vector& Material, const god* God, ccharacter* Char) +{ + for(int c1 = 1; c1 < protocontainer::GetSize(); ++c1) + { + const material::prototype* Proto = protocontainer::GetProto(c1); + const material::database*const* ConfigData = Proto->GetConfigData(); + int ConfigSize = Proto->GetConfigSize(); + + for(int c2 = 1; c2 < ConfigSize; ++c2) + if(God->LikesMaterial(ConfigData[c2], Char)) + Material.push_back(Proto->Spawn(ConfigData[c2]->Config)); + } +} diff --git a/Main/Source/rain.cpp b/Main/Source/rain.cpp new file mode 100644 index 0000000..ef2d33b --- /dev/null +++ b/Main/Source/rain.cpp @@ -0,0 +1,194 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through materset.cpp */ + +rain::rain(liquid* Liquid, lsquare* LSquareUnder, v2 Speed, int Team, truth OwnLiquid) : entity(OwnLiquid ? HAS_BE : 0), Next(0), Drop(0), Liquid(Liquid), LSquareUnder(LSquareUnder), Speed(Speed), SpeedAbs(long(sqrt(Speed.GetLengthSquare()))), Drops(0), OwnLiquid(OwnLiquid), Team(Team) +{ + Emitation = Liquid->GetEmitation(); + BeCounter = RAND_N(50); +} + +rain::~rain() +{ + delete [] Drop; + + if(OwnLiquid) + delete Liquid; +} + +void rain::Draw(blitdata& BlitData) const +{ + long Volume = Liquid->GetVolume(); + int Drops = this->Drops; + + if(!Volume && !Drops) + return; + + int c, DropMax = Volume ? Limit(Volume / 50, 1, MAX_RAIN_DROPS) : 0; + + if(Drops < DropMax) + { + drop* OldDrop = Drop; + Drop = new drop[DropMax]; + + for(c = 0; c < Drops; ++c) + if(OldDrop[c].MaxAge) + Drop[c] = OldDrop[c]; + else + RandomizeDropPos(c); + + delete [] OldDrop; + + for(; Drops < DropMax; ++Drops) + RandomizeDropPos(Drops); + } + else + { + for(c = 0; c < DropMax; ++c) + if(!Drop[c].MaxAge) + RandomizeDropPos(c); + + for(; Drops > DropMax && !Drop[Drops - 1].MaxAge; --Drops); + + if(!Drops) + { + this->Drops = 0; + delete [] Drop; + Drop = 0; + return; + } + } + + col16 Color = Liquid->GetRainColor(); + + for(c = 0; c < Drops; ++c) + if(Drop[c].MaxAge) + { + ulong Age = ushort(GET_TICK()) - Drop[c].StartTick; + + if(Age > Drop[c].MaxAge) + { + Drop[c].MaxAge = 0; + continue; + } + + v2 DropPos = v2(Drop[c].StartPos) + (Speed * int(Age) >> 8); + + if(DropPos.X < 0 || DropPos.Y < 0 || DropPos.X >= 16 || DropPos.Y >= 16) + { + Drop[c].MaxAge = 0; + continue; + } + + BlitData.Bitmap->AlphaPutPixel(DropPos + BlitData.Dest, Color, BlitData.Luminance, 255); + } + + this->Drops = Drops; +} + +void rain::RandomizeDropPos(int I) const +{ + Drop[I].StartTick = GET_TICK() - (RAND() & 3); + v2 Pos; + + if(Speed.X && (!Speed.Y || RAND() & 1)) + { + Pos.X = Speed.X > 0 ? 0 : 15; + Pos.Y = RAND() & 15; + } + else + { + Pos.X = RAND() & 15; + Pos.Y = Speed.Y > 0 ? 0 : 15; + } + + Drop[I].StartPos = Pos; + int AgeModifier = 5000 / SpeedAbs; + Drop[I].MaxAge = AgeModifier > 1 ? 1 + RAND() % AgeModifier : 1; +} + +void rain::Be() +{ + if(++BeCounter < 50) + return; + + BeCounter = 0; + long Volume = Liquid->GetVolume(); + + if(Volume && !Liquid->IsPowder()) // gum + { + long Rand = 5000000 / (Volume * SpeedAbs); + + if(OwnLiquid) + Rand >>= 3; + + if(Rand < 1 || !(RAND() % Rand)) + { + long DropVolume = Min(Volume, 50L); + /* Gum */ + LSquareUnder->SpillFluid(Team == PLAYER_TEAM ? PLAYER : 0, Liquid->SpawnMoreLiquid(DropVolume), true, OwnLiquid); + + if(OwnLiquid) + if(Volume == DropVolume) + { + LSquareUnder->RemoveRain(this); + SendToHell(); + } + else + Liquid->EditVolume(-DropVolume); + } + } +} + +void rain::Save(outputfile& SaveFile) const +{ + SaveFile << Liquid << Speed << (uchar)Team; +} + +void rain::Load(inputfile& SaveFile) +{ + OwnLiquid = 1; + LSquareUnder = static_cast(game::GetSquareInLoad()); + Liquid = static_cast(ReadType(SaveFile)); + Liquid->SetMotherEntity(this); + Emitation = Liquid->GetEmitation(); + SaveFile >> Speed; + Team = ReadType(SaveFile); + SpeedAbs = long(sqrt(Speed.GetLengthSquare())); +} + +outputfile& operator<<(outputfile& SaveFile, const rain* Rain) +{ + if(Rain->HasOwnLiquid()) + { + SaveFile.Put(true); + Rain->Save(SaveFile); + } + else + SaveFile.Put(false); + + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, rain*& Rain) +{ + if(SaveFile.Get()) + { + Rain = new rain; + Rain->Load(SaveFile); + } + else + Rain = game::ConstructGlobalRain(); + + return SaveFile; +} diff --git a/Main/Source/room.cpp b/Main/Source/room.cpp new file mode 100644 index 0000000..759c304 --- /dev/null +++ b/Main/Source/room.cpp @@ -0,0 +1,163 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through roomset.cpp */ + +roomprototype::roomprototype(roomspawner Spawner, cchar* ClassID) : Spawner(Spawner), ClassID(ClassID) { Index = protocontainer::Add(this); } + +void room::Save(outputfile& SaveFile) const +{ + SaveFile << (ushort)GetType(); + SaveFile << Pos << Size << Index << DivineMaster << MasterID; +} + +void room::Load(inputfile& SaveFile) +{ + SaveFile >> Pos >> Size >> Index >> DivineMaster >> MasterID; +} + +room* roomprototype::SpawnAndLoad(inputfile& SaveFile) const +{ + room* Room = Spawner(); + Room->Load(SaveFile); + return Room; +} + +void room::DestroyTerrain(character* Who) +{ + if(Who && MasterIsActive()) + Who->Hostility(GetMaster()); + + if(Who && Who->IsPlayer() && DivineMaster) + game::GetGod(DivineMaster)->AdjustRelation(GetGodRelationAdjustment()); +} + +/* returns true if player agrees to continue */ + +truth room::CheckDestroyTerrain(character* Infidel) +{ + if(!MasterIsActive() || Infidel == GetMaster() || GetMaster()->GetRelation(Infidel) == HOSTILE) + return true; + + ADD_MESSAGE("%s might not like this.", GetMaster()->CHAR_NAME(DEFINITE)); + + if(game::TruthQuestion(CONST_S("Are you sure you want to do this? [y/N]"))) + { + DestroyTerrain(Infidel); + return true; + } + else + return false; +} + +truth room::MasterIsActive() const +{ + character* Master = GetMaster(); + return Master && Master->IsEnabled() && Master->IsConscious(); +} + +truth room::CheckKickSquare(ccharacter* Kicker, const lsquare* LSquare) const +{ + if(!AllowKick(Kicker, LSquare)) + { + ADD_MESSAGE("That would be vandalism."); + + if(!game::TruthQuestion(CONST_S("Do you still want to do this? [y/N]"))) + return false; + } + return true; +} + +character* room::GetMaster() const +{ + ulong Tick = game::GetTick(); + + if(LastMasterSearchTick == Tick) + return Master; + else + { + LastMasterSearchTick = Tick; + return Master = game::SearchCharacter(MasterID); + } +} + +truth room::WardIsActive() const +{ + olterrain* PossibleWard = GetWard(); + if(!PossibleWard) + return false; + else + return PossibleWard->IsWard(); //if it is broken, then it will return zero hopefully +} +/* +olterrain* room::GetWard() const +{ + ulong Tick = game::GetTick(); + + if(LastWardSearchTick == Tick) + return Ward; + else + { + LastWardSearchTick = Tick; + return Ward = level::FindWard(this); //hmm + } +}*/ + +olterrain* room::GetWard() const +{ + ulong Tick = game::GetTick(); + + if(LastWardSearchTick == Tick) + return Ward; + else + { + LastWardSearchTick = Tick; + + std::vector Found; + olterrain* OLTerrain; + + v2 RoomPos = /*Room->*/GetPos(); + v2 RoomSize = /*Room->*/GetSize(); + + for(int x = RoomPos.X; x < (RoomPos.X + RoomSize.X); ++x) + for(int y = RoomPos.Y; y < ( RoomPos.Y + RoomSize.Y); ++y) + { + OLTerrain = game::GetCurrentLevel()->GetLSquare(x,y)->GetOLTerrain(); + if(OLTerrain && OLTerrain->IsWard()) + return OLTerrain; + } + return 0; + } +} + +truth room::IsOKToTeleportInto() const +{ + return !WardIsActive(); +} + +truth room::IsOKToDestroyWalls(ccharacter* Infidel) const +{ + return !MasterIsActive() || Infidel == GetMaster() || GetMaster()->GetRelation(Infidel) == HOSTILE; +} + +void room::FinalProcessForBone() +{ + if(MasterID) + { + boneidmap::iterator BI = game::GetBoneCharacterIDMap().find(MasterID); + + if(BI != game::GetBoneCharacterIDMap().end()) + MasterID = BI->second; + else + MasterID = 0; + } +} diff --git a/Main/Source/rooms.cpp b/Main/Source/rooms.cpp new file mode 100644 index 0000000..c0cf2e5 --- /dev/null +++ b/Main/Source/rooms.cpp @@ -0,0 +1,1179 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through roomset.cpp */ + +void shop::Enter(character* Customer) +{ + if(Customer->IsPlayer()) + if(MasterIsActive()) + { + if(GetMaster()->GetRelation(Customer) != HOSTILE + && Customer->CanBeSeenBy(GetMaster())) + if(GetMaster()->CanBeSeenByPlayer()) + ADD_MESSAGE("%s welcomes you warmly to the shop.", + GetMaster()->CHAR_NAME(DEFINITE)); + else + ADD_MESSAGE("Something welcomes you warmly to the shop."); + } + else + ADD_MESSAGE("The shop appears to be deserted."); +} + +/* item* ForSale can also be in chest or other container, so don't assume + anything else in this function */ + +truth shop::PickupItem(character* Customer, item* ForSale, int Amount) +{ + if(!MasterIsActive() || Customer == GetMaster() + || GetMaster()->GetRelation(Customer) == HOSTILE) + return true; + + if(ForSale->IsLanternOnWall()) + { + ADD_MESSAGE("\"I'd appreciate it if you left my " + "light sources alone, thank you!\""); + return false; + } + + long Price = ForSale->GetTruePrice(); + + if(Price) + { + Price = Amount * (Price * 100 + / (100 + Customer->GetAttribute(CHARISMA)) + 1); + + if(GetMaster()->GetConfig() == NEW_ATTNAM) + if(ForSale->IsBanana()) + Price = (Price >> 2) + 1; + else if(ForSale->IsEatable(GetMaster())) + Price <<= 2; + else + Price = 0; + } + + if(!Customer->IsPlayer()) + if(Customer->CanBeSeenByPlayer() && Customer->GetMoney() >= Price) + { + if(Price) + { + ADD_MESSAGE("%s buys %s.", Customer->CHAR_NAME(DEFINITE), + ForSale->GetName(INDEFINITE, Amount).CStr()); + Customer->EditMoney(-Price); + GetMaster()->EditMoney(Price); + Customer->EditDealExperience(Price); + } + + return true; + } + else + return false; + + if(Customer->CanBeSeenBy(GetMaster())) + { + if(ForSale->IsHeadOfElpuri() || ForSale->IsGoldenEagleShirt() + || ForSale->IsPetrussNut() || ForSale->IsTheAvatar() + || ForSale->IsEncryptedScroll()) + { + ADD_MESSAGE("\"I think it is yours. Take it.\""); + return true; + } + + if(!Price) + { + ADD_MESSAGE("\"Thank you for cleaning that junk out of my floor.\""); + return true; + } + + if(Customer->GetMoney() >= Price) + { + if(Amount == 1) + ADD_MESSAGE("\"Ah! That %s costs %ld gold pieces. " + "No haggling, please.\"", + ForSale->CHAR_NAME(UNARTICLED), Price); + else + ADD_MESSAGE("\"Ah! Those %d %s cost %ld gold pieces. " + "No haggling, please.\"", + Amount, ForSale->CHAR_NAME(PLURAL), Price); + + if(game::TruthQuestion(CONST_S("Do you accept this deal? [y/N]"))) + { + Customer->EditMoney(-Price); + GetMaster()->EditMoney(+Price); + Customer->EditDealExperience(Price); + return true; + } + else + return false; + } + else + { + if(Amount == 1) + ADD_MESSAGE("\"Don't touch that %s, beggar! " + "It is worth %ld gold pieces!\"", + ForSale->CHAR_NAME(UNARTICLED), Price); + else + ADD_MESSAGE("\"Don't touch those %s, beggar! " + "They are worth %ld gold pieces!\"", + ForSale->CHAR_NAME(PLURAL), Price); + + return false; + } + } + else + if(game::TruthQuestion(CONST_S("Are you sure you want to " + "commit this thievery? [y/N]"))) + { + Customer->Hostility(GetMaster()); + return true; + } + else + return false; +} + +truth shop::DropItem(character* Customer, item* ForSale, int Amount) +{ + if(!MasterIsActive() || Customer == GetMaster() + || GetMaster()->GetRelation(Customer) == HOSTILE) + return true; + + if(GetMaster()->GetConfig() == NEW_ATTNAM) + { + ADD_MESSAGE("\"Sorry, I'm only allowed to buy from " + "Decos Bananas Co. if I wish to stay here.\""); + return false; + } + + long Price = ForSale->GetTruePrice() * Amount + * (100 + Customer->GetAttribute(CHARISMA)) / 400; + + if(!Customer->IsPlayer()) + if(Price && Customer->CanBeSeenByPlayer() + && GetMaster()->GetMoney() >= Price) + { + ADD_MESSAGE("%s sells %s.", Customer->CHAR_NAME(DEFINITE), + ForSale->GetName(INDEFINITE, Amount).CStr()); + Customer->EditMoney(Price); + GetMaster()->EditMoney(-Price); + Customer->EditDealExperience(Price); + return true; + } + else + return false; + + if(Customer->CanBeSeenBy(GetMaster())) + { + if(ForSale->IsHeadOfElpuri() || ForSale->IsGoldenEagleShirt() + || ForSale->IsPetrussNut() || ForSale->IsTheAvatar() + || ForSale->IsEncryptedScroll()) + { + ADD_MESSAGE("\"Oh no! You need it far more than I!\""); + return false; + } + + if(ForSale->WillExplodeSoon()) + { + ADD_MESSAGE("\"Hey that %s is primed! Take it out! OUT, I SAY!\"", + ForSale->CHAR_NAME(UNARTICLED)); + return false; + } + + if(!Price) + { + ADD_MESSAGE("\"Hah! I wouldn't take %s even " + "if you paid me for it!\"", + Amount == 1 ? "that" : "those"); + return false; + } + + if(GetMaster()->GetMoney()) + { + if(GetMaster()->GetMoney() < Price) + Price = GetMaster()->GetMoney(); + + if(Amount == 1) + ADD_MESSAGE("\"What a fine %s. I'll pay " + "%ld gold pieces for it.\"", + ForSale->CHAR_NAME(UNARTICLED), Price); + else + ADD_MESSAGE("\"What a fine pile of %d %s. I'll " + "pay %ld gold pieces for them.\"", + Amount, ForSale->CHAR_NAME(PLURAL), Price); + + if(game::TruthQuestion(CONST_S("Do you accept this deal? [y/N]"))) + { + Customer->SetMoney(Customer->GetMoney() + Price); + GetMaster()->SetMoney(GetMaster()->GetMoney() - Price); + Customer->EditDealExperience(Price); + return true; + } + else + return false; + } + else + { + ADD_MESSAGE("\"I would pay you %ld gold pieces " + "for %s, but I'm temporarily " + "short of cash. Sorry.\"", + Price, Amount == 1 ? "it" : "them"); + return false; + } + } + else + { + ADD_MESSAGE("The shopkeeper doesn't see you, " + "so you cannot trade with him."); + return game::TruthQuestion(CONST_S("Still drop ") + + (Amount == 1 ? "this item" : "these items") + + "? [y/N]"); + } +} + +void shop::KickSquare(character* Infidel, lsquare* Square) +{ + if(!AllowKick(Infidel, Square)) + { + ADD_MESSAGE("\"You infidel!\""); + Infidel->Hostility(GetMaster()); + } +} + +truth shop::ConsumeItem(character* Customer, item*, int) +{ + if(!MasterIsActive() || GetMaster()->GetRelation(Customer) == HOSTILE) + return true; + + if(!Customer->IsPlayer()) + return false; + + if(Customer->CanBeSeenBy(GetMaster())) + { + ADD_MESSAGE("\"Buy that first, please.\""); + return false; + } + else + if(game::TruthQuestion(CONST_S("It's illegal to eat property " + "of others. Are you sure you sure? [y/N]"))) + { + Customer->Hostility(GetMaster()); + return true; + } + else + return false; +} + +void cathedral::Enter(character* Visitor) +{ + if(Visitor->IsPlayer() && !Entered) + { + ADD_MESSAGE("The majestetic Cathedral of Valpurus looms " + "before you. You watch it with utter respect."); + Entered = true; + } +} + +/* Item can also be in a chest, so don't assume anything else... */ + +truth cathedral::PickupItem(character* Visitor, item* Item, int) +{ + if(game::GetStoryState() == 2 + || game::GetTeam(ATTNAM_TEAM)->GetRelation(Visitor->GetTeam()) == HOSTILE) + return true; + + if(Visitor->IsPlayer()) + { + if(Item->IsHeadOfElpuri() || Item->IsGoldenEagleShirt() + || Item->IsPetrussNut() || !Item->GetTruePrice() + || Item->IsEncryptedScroll()) + return true; + + ADD_MESSAGE("Picking up property of the Cathedral is prohibited."); + + if(game::TruthQuestion(CONST_S("Do you still want to do this? [y/N]"))) + { + Visitor->GetTeam()->Hostility(game::GetTeam(ATTNAM_TEAM)); + return true; + } + } + + return false; +} + +truth cathedral::DropItem(character* Visitor, item* Item, int) +{ + if(game::GetStoryState() == 2 + || game::GetTeam(ATTNAM_TEAM)->GetRelation(Visitor->GetTeam()) == HOSTILE) + return true; + + if(Visitor->IsPlayer()) + { + if(Item->IsHeadOfElpuri() || Item->IsGoldenEagleShirt() + || Item->IsPetrussNut() || Item->IsTheAvatar() + || Item->IsEncryptedScroll()) + { + ADD_MESSAGE("Donating this to the Cathedral wouldn't " + "be wise. You may still need it."); + return false; + } + + if(game::TruthQuestion(CONST_S("Do you wish to donate this " + "item to the Cathedral? [y/N]"))) + return true; + } + + return false; +} + +void cathedral::KickSquare(character* Kicker, lsquare* Square) +{ + if(!AllowKick(Kicker, Square) + && Kicker->IsPlayer() && game::GetStoryState() != 2 + && game::GetTeam(ATTNAM_TEAM)->GetRelation(Kicker->GetTeam()) != HOSTILE) + { + ADD_MESSAGE("You have harmed the property of the Cathedral!"); + Kicker->GetTeam()->Hostility(game::GetTeam(ATTNAM_TEAM)); + } +} + +truth cathedral::ConsumeItem(character* HungryMan, item*, int) +{ + if(game::GetStoryState() == 2 + || (game::GetTeam(ATTNAM_TEAM)->GetRelation(HungryMan->GetTeam()) + == HOSTILE)) + return true; + + if(HungryMan->IsPlayer()) + { + ADD_MESSAGE("Eating the property of the Cathedral is forbidden."); + + if(game::TruthQuestion(CONST_S("Do you still want to do this? [y/N]"))) + { + HungryMan->GetTeam()->Hostility(game::GetTeam(ATTNAM_TEAM)); + return true; + } + } + + return false; +} + +void cathedral::Save(outputfile& SaveFile) const +{ + room::Save(SaveFile); + SaveFile << Entered; +} + +void cathedral::Load(inputfile& SaveFile) +{ + room::Load(SaveFile); + SaveFile >> Entered; +} + +truth cathedral::Drink(character* Thirsty) const +{ + if(game::GetStoryState() == 2 + || game::GetTeam(ATTNAM_TEAM)->GetRelation(Thirsty->GetTeam()) == HOSTILE) + return game::TruthQuestion(CONST_S("Do you want to drink? [y/N]")); + + if(Thirsty->IsPlayer()) + { + ADD_MESSAGE("Drinking property of the Cathedral is prohibited."); + + if(game::TruthQuestion(CONST_S("Do you still want to do this? [y/N]"))) + { + Thirsty->GetTeam()->Hostility(game::GetTeam(ATTNAM_TEAM)); + return true; + } + } + + return false; +} + +void shop::TeleportSquare(character* Infidel, lsquare* Square) +{ + if(Square->GetStack()->GetItems() && MasterIsActive() + && Infidel && Infidel != GetMaster() + && GetMaster()->GetRelation(Infidel) != HOSTILE + && Square->CanBeSeenBy(GetMaster())) + { + ADD_MESSAGE("\"You infidel!\""); + Infidel->Hostility(GetMaster()); + } +} + +void cathedral::TeleportSquare(character* Teleporter, lsquare* Square) +{ + if(game::GetStoryState() == 2 || !Teleporter + || (game::GetTeam(ATTNAM_TEAM)->GetRelation(Teleporter->GetTeam()) + == HOSTILE)) + return; + + if(Teleporter->IsPlayer() && Square->GetStack()->GetItems()) + { + ADD_MESSAGE("You have done unnatural things to " + "the property of the Cathedral!"); + Teleporter->GetTeam()->Hostility(game::GetTeam(ATTNAM_TEAM)); + } +} + +truth cathedral::Dip(character* Thirsty) const +{ + if(game::GetStoryState() == 2 + || game::GetTeam(ATTNAM_TEAM)->GetRelation(Thirsty->GetTeam()) == HOSTILE) + return true; + + if(Thirsty->IsPlayer()) + { + /* What if it's not water? */ + + ADD_MESSAGE("Stealing the precious water of the Cathedral is prohibited."); + + if(game::TruthQuestion(CONST_S("Are you sure you want to dip? [y/N]"))) + { + Thirsty->GetTeam()->Hostility(game::GetTeam(ATTNAM_TEAM)); + return true; + } + } + + return false; +} + +cathedral::cathedral() +{ + SetEntered(false); +} + +void library::Enter(character* Customer) +{ + if(Customer->IsPlayer()) + if(MasterIsActive()) + { + if(GetMaster()->GetRelation(Customer) != HOSTILE + && Customer->CanBeSeenBy(GetMaster())) + if(GetMaster()->CanBeSeenByPlayer()) + ADD_MESSAGE("%s looks at you suspiciously. " + "\"Feel free to open the shelves, " + "but be quiet in the library!\" %s whispers.", + GetMaster()->CHAR_NAME(DEFINITE), + GetMaster()->GetPersonalPronoun().CStr()); + else + ADD_MESSAGE("You feel somebody staring at you."); + } + else + ADD_MESSAGE("The library appears to be deserted."); +} + +truth library::PickupItem(character* Customer, item* ForSale, int Amount) +{ + if(!MasterIsActive() || Customer == GetMaster() + || GetMaster()->GetRelation(Customer) == HOSTILE) + return true; + + if(ForSale->IsLanternOnWall()) + { + ADD_MESSAGE("\"I'd appreciate it if you left my " + "light sources alone, thank you!\""); + return false; + } + + long Price = ForSale->GetTruePrice() * Amount + * 100 / (100 + Customer->GetAttribute(CHARISMA)); + + if(!Customer->IsPlayer()) + { + if(Customer->CanBeSeenByPlayer() && Customer->GetMoney() >= Price) + { + ADD_MESSAGE("%s buys %s.", Customer->CHAR_NAME(DEFINITE), + ForSale->GetName(INDEFINITE, Amount).CStr()); + Customer->EditMoney(-Price); + GetMaster()->EditMoney(Price); + Customer->EditDealExperience(Price); + return true; + } + else + return false; + } + + if(Customer->CanBeSeenBy(GetMaster())) + { + if(ForSale->IsHeadOfElpuri() || ForSale->IsGoldenEagleShirt() + || ForSale->IsPetrussNut() || ForSale->IsTheAvatar() + || ForSale->IsEncryptedScroll()) + { + ADD_MESSAGE("\"I think it is yours. Take it.\""); + return true; + } + + if(!Price || !ForSale->CanBeSoldInLibrary(GetMaster())) + { + ADD_MESSAGE("\"Thank you for cleaning that junk out of my floor.\""); + return true; + } + + if(Customer->GetMoney() >= Price) + { + if(Amount == 1) + ADD_MESSAGE("\"Ah! That %s costs %ld gold " + "pieces. No haggling, please.\"", + ForSale->CHAR_NAME(UNARTICLED), Price); + else + ADD_MESSAGE("\"Ah! Those %d %s cost %ld gold " + "pieces. No haggling, please.\"", + Amount, ForSale->CHAR_NAME(PLURAL), Price); + + if(game::TruthQuestion(CONST_S("Do you accept this deal? [y/N]"))) + { + Customer->EditMoney(-Price); + GetMaster()->EditMoney(Price); + Customer->EditDealExperience(Price); + return true; + } + else + return false; + } + else + { + if(Amount == 1) + ADD_MESSAGE("\"Don't touch that %s, beggar! " + "It is worth %ld gold pieces!\"", + ForSale->CHAR_NAME(UNARTICLED), Price); + else + ADD_MESSAGE("\"Don't touch those %s, beggar! " + "They are worth %ld gold pieces!\"", + ForSale->CHAR_NAME(PLURAL), Price); + + return false; + } + } + else + if(game::TruthQuestion(CONST_S("Are you sure you want to " + "commit this thievery? [y/N]"))) + { + Customer->Hostility(GetMaster()); + return true; + } + else + return false; +} + +truth library::DropItem(character* Customer, item* ForSale, int Amount) +{ + if(!MasterIsActive() || Customer == GetMaster() + || GetMaster()->GetRelation(Customer) == HOSTILE) + return true; + + long Price = ForSale->GetTruePrice() * Amount + * (100 + Customer->GetAttribute(CHARISMA)) / 400; + + if(!Customer->IsPlayer()) + if(Price && Customer->CanBeSeenByPlayer() + && GetMaster()->GetMoney() >= Price) + { + ADD_MESSAGE("%s sells %s.", Customer->CHAR_NAME(DEFINITE), + ForSale->GetName(INDEFINITE, Amount).CStr()); + Customer->SetMoney(Customer->GetMoney() + Price); + GetMaster()->SetMoney(GetMaster()->GetMoney() - Price); + Customer->EditDealExperience(Price); + return true; + } + else + return false; + + if(Customer->CanBeSeenBy(GetMaster())) + { + if(ForSale->IsHeadOfElpuri() || ForSale->IsGoldenEagleShirt() + || ForSale->IsPetrussNut() || ForSale->IsTheAvatar() + || ForSale->IsEncryptedScroll()) + { + ADD_MESSAGE("\"Oh no! You need it far more than I!\""); + return false; + } + + if(!Price || !ForSale->CanBeSoldInLibrary(GetMaster())) + { + ADD_MESSAGE("\"Sorry, but I don't think %s into my collection.\"", + Amount == 1 ? "that fits" : "those fit"); + return false; + } + + if(GetMaster()->GetMoney()) + { + if(GetMaster()->GetMoney() < Price) + Price = GetMaster()->GetMoney(); + + if(Amount == 1) + ADD_MESSAGE("\"What an interesting %s. I'll " + "pay %ld gold pieces for it.\"", + ForSale->CHAR_NAME(UNARTICLED), Price); + else + ADD_MESSAGE("\"What an interesting collection of %d " + "%s. I'll pay %ld gold pieces for it.\"", + Amount, ForSale->CHAR_NAME(PLURAL), Price); + + if(game::TruthQuestion(CONST_S("Do you want to sell ") + + (Amount == 1 ? "this item" : "these items") + + "? [y/N]")) + { + Customer->EditMoney(Price); + GetMaster()->EditMoney(-Price); + Customer->EditDealExperience(Price); + return true; + } + else + return false; + } + else + { + ADD_MESSAGE("\"I would pay you %ld gold pieces for %s, " + "but I'm temporarily short of cash. Sorry.\"", + Price, Amount == 1 ? "it" : "them"); + return false; + } + } + else + { + ADD_MESSAGE("The librarian doesn't see you, " + "so you cannot trade with him."); + return game::TruthQuestion(CONST_S("Still drop ") + + (Amount == 1 ? "this item" : "these items") + + "? [y/N]"); + } +} + +void library::KickSquare(character* Infidel, lsquare* Square) +{ + if(!AllowKick(Infidel, Square)) + { + ADD_MESSAGE("\"You book vandal!\""); + Infidel->Hostility(GetMaster()); + } +} + +truth library::ConsumeItem(character*, item*, int) +{ + return true; +} + +void library::TeleportSquare(character* Infidel, lsquare* Square) +{ + if(Square->GetStack()->GetItems() && MasterIsActive() + && Infidel && Infidel != GetMaster() + && GetMaster()->GetRelation(Infidel) != HOSTILE + && Square->CanBeSeenBy(GetMaster())) + { + ADD_MESSAGE("\"You book hater!\""); + Infidel->Hostility(GetMaster()); + } +} + +truth bananadroparea::PickupItem(character* Hungry, item* Item, int) +{ + if(game::GetTeam(NEW_ATTNAM_TEAM)->GetRelation(Hungry->GetTeam()) == HOSTILE) + return true; + + if(Hungry->IsPlayer()) + { + if(!Item->IsBanana() && !Item->IsLanternOnWall()) + return true; + + ADD_MESSAGE("That would be stealing."); + + if(game::TruthQuestion(CONST_S("Do you still want to do this? [y/N]"))) + { + Hungry->GetTeam()->Hostility(game::GetTeam(NEW_ATTNAM_TEAM)); + return true; + } + } + + return false; +} + +truth bananadroparea::DropItem(character* Dropper, item* Item, int) +{ + if(Dropper->IsPlayer() && (Item->IsMangoSeedling())) + { + if(game::TruthQuestion(CONST_S("Do you wish to plant the mango seedling at this time? [y/N]")) && game::TweraifIsFree() && (game::GetTeam(NEW_ATTNAM_TEAM)->GetRelation(Dropper->GetTeam()) != HOSTILE)) + { + game::TextScreen(CONST_S( "You plant the seedling of the Holy Mango Tree in New Attnam.\n" + "The people of your home village gather around you cheering! Tweraif is\n" + "now restored to its former glory and you remain there as honourary\n" + "spiritual leader and hero of the new republic. You ensure that free\n" + "and fair elections quickly ensue.\n\nYou are victorious!")); + game::GetCurrentArea()->SendNewDrawRequest(); + game::DrawEverything(); + PLAYER->ShowAdventureInfo(); + festring Msg = CONST_S("restored Tweraif to independence and continued to further adventures"); + Dropper->AddScoreEntry(Msg, 1, false); + game::End(Msg); + } + else + { + if(Dropper->IsPlayer() && !game::TweraifIsFree()) + ADD_MESSAGE("You feel that the climate is not quite right for growing mangoes."); + else + ADD_MESSAGE("You choose not to plant the seedling."); + return false; + } + + } + return (game::GetTeam(NEW_ATTNAM_TEAM)->GetRelation(Dropper->GetTeam()) + == HOSTILE + || (Dropper->IsPlayer() + && ((!Item->IsBanana() && !Item->IsLanternOnWall()) + || game::TruthQuestion(CONST_S("Do you wish to " + "donate this item " + "to the town? [y/N]"))))); +} + +void bananadroparea::KickSquare(character* Kicker, lsquare* Square) +{ + if(AllowKick(Kicker, Square) && Kicker->IsPlayer() + && game::GetTeam(NEW_ATTNAM_TEAM)->GetRelation(Kicker->GetTeam()) + != HOSTILE) + { + for(stackiterator i = Square->GetStack()->GetBottom(); i.HasItem(); ++i) + if(i->IsBanana() || i->IsLanternOnWall()) + { + ADD_MESSAGE("You have harmed the property of the town!"); + Kicker->GetTeam()->Hostility(game::GetTeam(NEW_ATTNAM_TEAM)); + return; + } + } +} + +truth bananadroparea::ConsumeItem(character* HungryMan, item* Item, int) +{ + if(game::GetTeam(NEW_ATTNAM_TEAM)->GetRelation(HungryMan->GetTeam()) + == HOSTILE) + return true; + + if(HungryMan->IsPlayer()) + { + if(!Item->IsBanana() && !Item->IsLanternOnWall()) + return true; + + ADD_MESSAGE("Eating this is forbidden."); + + if(game::TruthQuestion(CONST_S("Do you still want to do this? [y/N]"))) + { + HungryMan->GetTeam()->Hostility(game::GetTeam(NEW_ATTNAM_TEAM)); + return true; + } + } + + return HungryMan->IsSumoWrestler(); +} + +void bananadroparea::TeleportSquare(character* Infidel, lsquare* Square) +{ + if(!Infidel + || game::GetTeam(NEW_ATTNAM_TEAM)->GetRelation(Infidel->GetTeam()) + == HOSTILE) + return; + + for(stackiterator i = Square->GetStack()->GetBottom(); i.HasItem(); ++i) + if(i->IsBanana() || i->IsLanternOnWall()) + { + Infidel->GetTeam()->Hostility(game::GetTeam(NEW_ATTNAM_TEAM)); + return; + } +} + +truth shop::AllowSpoil(citem* Item) const +{ + character* Master = GetMaster(); + return !Master || !Master->IsEnabled() || !Item->HasPrice(); +} + +/* Gum solution */ + +truth shop::AllowKick(ccharacter* Char, const lsquare* LSquare) const +{ + return (!LSquare->GetStack()->GetItems() || !MasterIsActive() + || Char == GetMaster() || GetMaster()->GetRelation(Char) == HOSTILE + || !LSquare->CanBeSeenBy(GetMaster())); +} + +truth cathedral::AllowKick(ccharacter* Char, const lsquare* LSquare) const +{ + return (game::GetTeam(ATTNAM_TEAM)->GetRelation(Char->GetTeam()) == HOSTILE + || !LSquare->GetStack()->GetItems()); +} + +truth library::AllowKick(ccharacter* Char, const lsquare* LSquare) const +{ + return (!LSquare->GetStack()->GetItems() + || !MasterIsActive() || Char == GetMaster() + || GetMaster()->GetRelation(Char) == HOSTILE + || LSquare->CanBeSeenBy(GetMaster())); +} + +truth bananadroparea::AllowKick(ccharacter* Char, const lsquare*) const +{ + return (!Char->IsPlayer() + || (game::GetTeam(NEW_ATTNAM_TEAM)->GetRelation(Char->GetTeam()) + == HOSTILE)); +} + +void shop::HostileAction(character* Guilty) const +{ + if(MasterIsActive() && Guilty && Guilty != GetMaster() + && GetMaster()->GetRelation(Guilty) != HOSTILE + && Guilty->CanBeSeenBy(GetMaster())) + { + ADD_MESSAGE("\"You infidel!\""); + Guilty->Hostility(GetMaster()); + } +} + +void cathedral::HostileAction(character* Guilty) const +{ + if(game::GetStoryState() != 2 && Guilty) + Guilty->GetTeam()->Hostility(game::GetTeam(ATTNAM_TEAM)); +} + +void library::HostileAction(character* Guilty) const +{ + if(MasterIsActive() && Guilty && Guilty != GetMaster() + && GetMaster()->GetRelation(Guilty) != HOSTILE + && Guilty->CanBeSeenBy(GetMaster())) + { + ADD_MESSAGE("\"You infidel!\""); + Guilty->Hostility(GetMaster()); + } +} + +void bananadroparea::HostileAction(character* Guilty) const +{ + if(Guilty) + Guilty->GetTeam()->Hostility(game::GetTeam(NEW_ATTNAM_TEAM)); +} + +void sumoarena::DestroyTerrain(character* Who) +{ + if(Who) + Who->GetTeam()->Hostility(game::GetTeam(NEW_ATTNAM_TEAM)); +} + +void sumoarena::HostileAction(character* Guilty) const +{ + if(Guilty) + Guilty->GetTeam()->Hostility(game::GetTeam(NEW_ATTNAM_TEAM)); +} + +truth sumoarena::CheckDestroyTerrain(character* Infidel) +{ + if(Infidel->GetTeam()->GetRelation(game::GetTeam(NEW_ATTNAM_TEAM)) + == HOSTILE) + return true; + + ADD_MESSAGE("The residents of New Attnam might not like this."); + + if(game::TruthQuestion(CONST_S("Are you sure you want to do this? [y/N]"))) + { + DestroyTerrain(Infidel); + return true; + } + else + return false; +} + +void shop::ReceiveVomit(character* Who) +{ + if(MasterIsActive() + && Who->IsPlayer() + && Who->GetRelation(GetMaster()) != HOSTILE + && Who->CanBeSeenBy(GetMaster())) + ADD_MESSAGE("\"Unfortunately I accept no returns.\""); +} + +void cathedral::AddItemEffect(item* Dropped) +{ + + truth SeenBeforeTeleport = Dropped->CanBeSeenByPlayer(); + character* KamikazeDwarf = FindRandomExplosiveReceiver(); + + if(!Dropped->IsKamikazeWeapon(KamikazeDwarf)) + return; + + if(KamikazeDwarf) + { + Dropped->MoveTo(KamikazeDwarf->GetStack()); + + if(KamikazeDwarf->CanBeSeenByPlayer()) + { + if(SeenBeforeTeleport) + ADD_MESSAGE("%s disappears and reappears in %s's inventory.", + Dropped->GetName(DEFINITE).CStr(), + KamikazeDwarf->GetName(DEFINITE).CStr()); + else + ADD_MESSAGE("%s appears in %s's inventory.", + Dropped->GetName(DEFINITE).CStr(), + KamikazeDwarf->GetName(DEFINITE).CStr()); + } + else if(SeenBeforeTeleport) + ADD_MESSAGE("%s disappears.", Dropped->GetName(DEFINITE).CStr()); + } + else + { + /* position is in kamikaze dwarf room */ + + Dropped->RemoveFromSlot(); + game::GetCurrentLevel()->GetLSquare(18,21) + ->GetStack()->AddItem(Dropped, false); + + if(Dropped->CanBeSeenByPlayer()) + { + if(SeenBeforeTeleport) + ADD_MESSAGE("%s disappears and reappears in the kamikaze dwarf room.", + Dropped->GetName(DEFINITE).CStr()); + else + ADD_MESSAGE("%s appears in the kamikaze dwarf room.", + Dropped->GetName(DEFINITE).CStr()); + } + else if(SeenBeforeTeleport) + ADD_MESSAGE("%s disappears.", Dropped->GetNameSingular().CStr()); + } +} + +character* cathedral::FindRandomExplosiveReceiver() const +{ + std::vector ListOfDwarfs; + + for(std::list::const_iterator i + = game::GetTeam(ATTNAM_TEAM)->GetMember().begin(); + i != game::GetTeam(ATTNAM_TEAM)->GetMember().end(); ++i) + if((*i)->IsEnabled() && (*i)->IsKamikazeDwarf()) + ListOfDwarfs.push_back(*i); + + if(ListOfDwarfs.empty()) + return 0; + else + return ListOfDwarfs[RAND_N(ListOfDwarfs.size())]; +} + +void vault::Enter(character* Visitor) +{ + if(Visitor->IsPlayer() && !Entered) + { + ADD_MESSAGE("You enter a well-guarded vault. Everyone eyes you like an animal looking at a fresh piece piece of meat."); + Entered = true; + } +} + +truth vault::PickupItem(character* Visitor, item* Item, int) +{ + if(game::GetStoryState() == 2 || game::GetTeam(KHARAZ_ARAD_TEAM)->GetRelation(Visitor->GetTeam()) == HOSTILE) + return true; + + if(Visitor->IsPlayer()) + { + if(Item->IsHeadOfElpuri() || Item->IsGoldenEagleShirt() || Item->IsPetrussNut() || !Item->GetTruePrice() || Item->IsEncryptedScroll()) + return true; + + ADD_MESSAGE("Picking up property of the Vault is prohibited."); + + if(game::TruthQuestion(CONST_S("Do you still want to do this? [y/N]"))) + { + Visitor->GetTeam()->Hostility(game::GetTeam(KHARAZ_ARAD_TEAM)); + return true; + } + } + + return false; +} + +truth vault::DropItem(character* Visitor, item* Item, int) +{ + if(game::GetStoryState() == 2 || game::GetTeam(KHARAZ_ARAD_TEAM)->GetRelation(Visitor->GetTeam()) == HOSTILE) + return true; + + if(Visitor->IsPlayer()) + { + if(Item->IsHeadOfElpuri() || Item->IsGoldenEagleShirt() || Item->IsPetrussNut() || Item->IsTheAvatar() || Item->IsEncryptedScroll()) + { + ADD_MESSAGE("Donating this to the Vault wouldn't be wise. You may still need it."); + return false; + } + + if(game::TruthQuestion(CONST_S("Do you wish to donate this item to the Vault? [y/N]"))) + return true; + } + + return false; +} + +void vault::KickSquare(character* Kicker, lsquare* Square) +{ + if(!AllowKick(Kicker, Square) && Kicker->IsPlayer() && game::GetStoryState() != 2 && game::GetTeam(KHARAZ_ARAD_TEAM)->GetRelation(Kicker->GetTeam()) != HOSTILE) + { + ADD_MESSAGE("You have harmed the property of the Vault!"); + Kicker->GetTeam()->Hostility(game::GetTeam(KHARAZ_ARAD_TEAM)); + } +} + +truth vault::ConsumeItem(character* HungryMan, item*, int) +{ + if(game::GetStoryState() == 2 || game::GetTeam(KHARAZ_ARAD_TEAM)->GetRelation(HungryMan->GetTeam()) == HOSTILE) + return true; + + if(HungryMan->IsPlayer()) + { + ADD_MESSAGE("Eating the property of the Vault is forbidden."); + + if(game::TruthQuestion(CONST_S("Do you still want to do this? [y/N]"))) + { + HungryMan->GetTeam()->Hostility(game::GetTeam(KHARAZ_ARAD_TEAM)); + return true; + } + } + + return false; +} + +void vault::Save(outputfile& SaveFile) const +{ + room::Save(SaveFile); + SaveFile << Entered; +} + +void vault::Load(inputfile& SaveFile) +{ + room::Load(SaveFile); + SaveFile >> Entered; +} + +truth vault::Drink(character* Thirsty) const +{ + if(game::GetStoryState() == 2 || game::GetTeam(KHARAZ_ARAD_TEAM)->GetRelation(Thirsty->GetTeam()) == HOSTILE) + return game::TruthQuestion(CONST_S("Do you want to drink? [y/N]")); + + if(Thirsty->IsPlayer()) + { + ADD_MESSAGE("Drinking property of the Vault is prohibited."); + + if(game::TruthQuestion(CONST_S("Do you still want to do this? [y/N]"))) + { + Thirsty->GetTeam()->Hostility(game::GetTeam(KHARAZ_ARAD_TEAM)); + return true; + } + } + + return false; +} + +void vault::TeleportSquare(character* Teleporter, lsquare* Square) +{ + if(game::GetStoryState() == 2 || !Teleporter || game::GetTeam(KHARAZ_ARAD_TEAM)->GetRelation(Teleporter->GetTeam()) == HOSTILE) + return; + + if(Teleporter->IsPlayer() && Square->GetStack()->GetItems()) + { + ADD_MESSAGE("You have done unnatural things to the property of the Vault!"); + Teleporter->GetTeam()->Hostility(game::GetTeam(KHARAZ_ARAD_TEAM)); + } +} + +truth vault::Dip(character* Thirsty) const +{ + if(game::GetStoryState() == 2 || game::GetTeam(KHARAZ_ARAD_TEAM)->GetRelation(Thirsty->GetTeam()) == HOSTILE) + return true; + + if(Thirsty->IsPlayer()) + { + /* What if it's not water? */ + + ADD_MESSAGE("Stealing the precious water of the Vault is prohibited."); + + if(game::TruthQuestion(CONST_S("Are you sure you want to dip? [y/N]"))) + { + Thirsty->GetTeam()->Hostility(game::GetTeam(KHARAZ_ARAD_TEAM)); + return true; + } + } + + return false; +} + +vault::vault() +{ + SetEntered(false); +} + +truth vault::AllowKick(ccharacter* Char, const lsquare* LSquare) const +{ + return game::GetTeam(KHARAZ_ARAD_TEAM)->GetRelation(Char->GetTeam()) == HOSTILE || !LSquare->GetStack()->GetItems(); +} + +void vault::HostileAction(character* Guilty) const +{ + if(game::GetStoryState() != 2 && Guilty) + Guilty->GetTeam()->Hostility(game::GetTeam(KHARAZ_ARAD_TEAM)); +} + +void vault::AddItemEffect(item* Dropped) +{ + if(!Dropped->IsExplosive()) + return; + + truth SeenBeforeTeleport = Dropped->CanBeSeenByPlayer(); + character* KamikazeDwarf = FindRandomExplosiveReceiver(); + + if(KamikazeDwarf) + { + Dropped->MoveTo(KamikazeDwarf->GetStack()); + + if(KamikazeDwarf->CanBeSeenByPlayer()) + { + if(SeenBeforeTeleport) + ADD_MESSAGE("%s disappears and reappears in %s's inventory.", Dropped->GetName(DEFINITE).CStr(), KamikazeDwarf->GetName(DEFINITE).CStr()); + else + ADD_MESSAGE("%s appears in %s's inventory.", Dropped->GetName(DEFINITE).CStr(), KamikazeDwarf->GetName(DEFINITE).CStr()); + } + else if(SeenBeforeTeleport) + ADD_MESSAGE("%s disappears.", Dropped->GetName(DEFINITE).CStr()); + } + else + { + /* position is in kamikaze dwarf room */ + + Dropped->RemoveFromSlot(); + game::GetCurrentLevel()->GetLSquare(18,21)->GetStack()->AddItem(Dropped, false); + + if(Dropped->CanBeSeenByPlayer()) + { + if(SeenBeforeTeleport) + ADD_MESSAGE("%s disappears and reappears in the kamikaze dwarf room.", + Dropped->GetName(DEFINITE).CStr()); + else + ADD_MESSAGE("%s appears in the kamikaze dwarf room.", + Dropped->GetName(DEFINITE).CStr()); + } + else if(SeenBeforeTeleport) + ADD_MESSAGE("%s disappears.", Dropped->GetNameSingular().CStr()); + } +} + +character* vault::FindRandomExplosiveReceiver() const +{ + std::vector ListOfDwarfs; + + for(std::list::const_iterator i = game::GetTeam(KHARAZ_ARAD_TEAM)->GetMember().begin(); + i != game::GetTeam(KHARAZ_ARAD_TEAM)->GetMember().end(); ++i) + if((*i)->IsEnabled() && (*i)->IsKamikazeDwarf()) + ListOfDwarfs.push_back(*i); + + if(ListOfDwarfs.empty()) + return 0; + else + return ListOfDwarfs[RAND_N(ListOfDwarfs.size())]; +} + diff --git a/Main/Source/roomset.cpp b/Main/Source/roomset.cpp new file mode 100644 index 0000000..ce666e0 --- /dev/null +++ b/Main/Source/roomset.cpp @@ -0,0 +1,33 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#define __FILE_OF_STATIC_ROOM_PROTOTYPE_DEFINITIONS__ + +#include "proto.h" +#include "room.h" + +SYSTEM_SPECIALIZATIONS(room) + +#include "rooms.h" + +#undef __FILE_OF_STATIC_ROOM_PROTOTYPE_DEFINITIONS__ + +#include "message.h" +#include "char.h" +#include "god.h" +#include "game.h" +#include "stack.h" +#include "team.h" +#include "save.h" + +#include "room.cpp" +#include "rooms.cpp" diff --git a/Main/Source/script.cpp b/Main/Source/script.cpp new file mode 100644 index 0000000..064f9f8 --- /dev/null +++ b/Main/Source/script.cpp @@ -0,0 +1,1225 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "script.h" +#include "save.h" +#include "game.h" +#include "materia.h" +#include "char.h" +#include "proto.h" +#include "allocate.h" + +script::datamap posscript::DataMap; +script::datamap materialscript::DataMap; +script::datamap basecontentscript::DataMap; +script::datamap contentscript::DataMap; +script::datamap contentscript::DataMap; +script::datamap contentscript::DataMap; +script::datamap contentscript::DataMap; +script::datamap squarescript::DataMap; +script::datamap roomscript::DataMap; +script::datamap levelscript::DataMap; +script::datamap dungeonscript::DataMap; +script::datamap teamscript::DataMap; +script::datamap gamescript::DataMap; +template script::datamap contentmap::DataMap; + +template void scriptmember::ReadFrom(inputfile& SaveFile) +{ + if(!Member) + Member = new type; + + ReadData(*Member, SaveFile); +} + +template void scriptmember::Replace(scriptmemberbase& Base) +{ + scriptmember& Data = static_cast&>(Base); + + if(Data.Member) + { + delete Member; + Member = Data.Member; + Data.Member = 0; + } +} + +template void scriptmember::Save(outputfile& SaveFile) const +{ + if(Member) + { + SaveFile.Put(1); + SaveFile << *Member; + } + else + SaveFile.Put(0); +} + +template void scriptmember::Load(inputfile& SaveFile) +{ + if(SaveFile.Get()) + { + Member = new type; + SaveFile >> *Member; + } +} + +#define INST_SCRIPT_MEMBER(type)\ +template void scriptmember< type >::ReadFrom(inputfile&);\ +template void scriptmember< type >::Replace(scriptmemberbase&);\ +template void scriptmember< type >::Save(outputfile&) const;\ +template void scriptmember< type >::Load(inputfile&) + +INST_SCRIPT_MEMBER(uchar); +INST_SCRIPT_MEMBER(short); +INST_SCRIPT_MEMBER(int); +INST_SCRIPT_MEMBER(long); +INST_SCRIPT_MEMBER(v2); +INST_SCRIPT_MEMBER(festring); +INST_SCRIPT_MEMBER(fearray); +INST_SCRIPT_MEMBER(rect); +INST_SCRIPT_MEMBER(interval); +INST_SCRIPT_MEMBER(region); +INST_SCRIPT_MEMBER(posscript); +INST_SCRIPT_MEMBER(materialscript); +INST_SCRIPT_MEMBER(squarescript); +INST_SCRIPT_MEMBER(roomscript); +INST_SCRIPT_MEMBER(levelscript); +INST_SCRIPT_MEMBER(contentscript); +INST_SCRIPT_MEMBER(fearray >); +INST_SCRIPT_MEMBER(contentscript); +INST_SCRIPT_MEMBER(contentscript); +INST_SCRIPT_MEMBER(charactercontentmap); +INST_SCRIPT_MEMBER(itemcontentmap); +INST_SCRIPT_MEMBER(glterraincontentmap); +INST_SCRIPT_MEMBER(olterraincontentmap); +INST_SCRIPT_MEMBER(fearray); + +template void fastscriptmember::ReadFrom(inputfile& SaveFile) +{ + ReadData(*&Member, SaveFile); // gcc 3.4.1 sucks +} + +template void fastscriptmember::Replace(scriptmemberbase& Base) +{ + fastscriptmember& Data = static_cast&>(Base); + Member = Data.Member; +} + +template void fastscriptmember::Save(outputfile& SaveFile) const +{ + SaveFile << Member; +} + +template void fastscriptmember::Load(inputfile& SaveFile) +{ + SaveFile >> *&Member; // gcc 3.4.1 sucks +} + +#define INST_FAST_SCRIPT_MEMBER(type)\ +template void fastscriptmember< type >::ReadFrom(inputfile&);\ +template void fastscriptmember< type >::Replace(scriptmemberbase&);\ +template void fastscriptmember< type >::Save(outputfile&) const;\ +template void fastscriptmember< type >::Load(inputfile&) + +INST_FAST_SCRIPT_MEMBER(char); +INST_FAST_SCRIPT_MEMBER(uchar); +INST_FAST_SCRIPT_MEMBER(int); +INST_FAST_SCRIPT_MEMBER(long); +INST_FAST_SCRIPT_MEMBER(ulong); +INST_FAST_SCRIPT_MEMBER(packv2); + +truth script::ReadMember(inputfile& SaveFile, cfestring& Word) +{ + scriptmemberbase* Data = GetData(Word.CStr()); + + if(Data) + { + Data->ReadFrom(SaveFile); + return true; + } + else + return false; +} + +scriptmemberbase* script::GetDataFromMap(const datamap& DataMap, cchar* Identifier) +{ + datamap::const_iterator i = DataMap.find(Identifier); + return i != DataMap.end() ? &(this->*i->second) : 0; +} + +void script::SaveDataMap(const datamap& DataMap, outputfile& SaveFile) const +{ + for(datamap::const_iterator i = DataMap.begin(); i != DataMap.end(); ++i) + (this->*i->second).Save(SaveFile); +} + +void script::LoadDataMap(const datamap& DataMap, inputfile& SaveFile) +{ + for(datamap::const_iterator i = DataMap.begin(); i != DataMap.end(); ++i) + (this->*i->second).Load(SaveFile); +} + +template void InitMember(script::datamap& DataMap, cchar* Identifier, scriptmemberptr DataMember) +{ + DataMap[Identifier] = reinterpret_cast(DataMember); +} + +#define INIT_ENTRY(name) InitMember(DataMap, #name, &scripttype::name##Holder) + +#define INIT(name, value) name##Holder(value) + +void posscript::InitDataMap() +{ + INIT_ENTRY(Vector); + INIT_ENTRY(Flags); + INIT_ENTRY(Borders); +} + +void posscript::ReadFrom(inputfile& SaveFile) +{ + static festring Word; + SaveFile.ReadWord(Word); + + if(Word == "Pos") + { + Random = false; + VectorHolder.ReadFrom(SaveFile); + } + + if(Word == "Random") + { + Random = true; + FlagsHolder.ReadFrom(SaveFile); + } + + if(Word == "BoundedRandom") + { + Random = true; + BordersHolder.ReadFrom(SaveFile); + FlagsHolder.ReadFrom(SaveFile); + } +} + +void posscript::Save(outputfile& SaveFile) const +{ + script::Save(SaveFile); + SaveFile << Random; +} + +void posscript::Load(inputfile& SaveFile) +{ + script::Load(SaveFile); + SaveFile >> Random; +} + +void materialscript::InitDataMap() +{ + INIT_ENTRY(Volume); +} + +void materialscript::ReadFrom(inputfile& SaveFile) +{ + static festring Word; + SaveFile.ReadWord(Word); + + if(Word == "=") + SaveFile.ReadWord(Word); + + if(Word == "0") + Config = 0; + else + { + valuemap::const_iterator i = game::GetGlobalValueMap().find(Word); + + if(i != game::GetGlobalValueMap().end()) + Config = i->second; + else + ABORT("Unconfigured material script detected at line %ld!", SaveFile.TellLine()); + } + + if(SaveFile.ReadWord() != "{") + return; + + for(SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) + if(!ReadMember(SaveFile, Word)) + ABORT("Odd script term %s encountered in material script line %ld!", Word.CStr(), SaveFile.TellLine()); +} + +material* materialscript::Instantiate() const +{ + return MAKE_MATERIAL(Config, GetVolume() ? GetVolume()->Randomize() : 0); +} + +void materialscript::Save(outputfile& SaveFile) const +{ + script::Save(SaveFile); + SaveFile << (ushort)Config; +} + +void materialscript::Load(inputfile& SaveFile) +{ + script::Load(SaveFile); + Config = 0; + SaveFile >> (ushort&)Config; +} + +void basecontentscript::InitDataMap() +{ + INIT_ENTRY(MainMaterial); + INIT_ENTRY(SecondaryMaterial); + INIT_ENTRY(Parameters); +} + +basecontentscript::basecontentscript() +: ContentType(0), Random(false), Config(0), + INIT(Parameters, NO_PARAMETERS) +{ } + +void basecontentscript::ReadFrom(inputfile& SaveFile) +{ + static festring Word; + SaveFile.ReadWord(Word); + + if(Word == "=" || Word == ",") + SaveFile.ReadWord(Word); + + valuemap::const_iterator i = game::GetGlobalValueMap().find(Word); + + if(i != game::GetGlobalValueMap().end()) + { + if(!GetMainMaterial()) + MainMaterialHolder.Member = new materialscript; + + MainMaterialHolder.Member->SetConfig(i->second); + SaveFile.ReadWord(Word); + i = game::GetGlobalValueMap().find(Word); + + if(i != game::GetGlobalValueMap().end()) + { + if(!GetSecondaryMaterial()) + SecondaryMaterialHolder.Member = new materialscript; + + SecondaryMaterialHolder.Member->SetConfig(i->second); + SaveFile.ReadWord(Word); + } + } + + if(Word == "NaturalMaterialForm") + { + Random = false; + ContentType = NATURAL_MATERIAL_FORM; + SaveFile.ReadWord(Word); + } + else if(Word == "Random") + { + Random = true; + SaveFile.ReadWord(Word); + } + else + { + Random = false; + ContentType = SearchCodeName(Word); + + if(ContentType || Word == "0") + SaveFile.ReadWord(Word); + else + ABORT("Odd script term %s encountered in %s content script, file %s line %ld!", Word.CStr(), GetClassID(), SaveFile.GetFileName().CStr(), SaveFile.TellLine()); + } + + if(Word == "(") + { + Config = SaveFile.ReadNumber(); + SaveFile.ReadWord(Word); + } + else + Config = 0; + + if(Word == "{") + for(SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) + { + if(!ReadMember(SaveFile, Word)) + ABORT("Odd script term %s encountered in %s content script, file %s line %ld!", Word.CStr(), GetClassID(), SaveFile.GetFileName().CStr(), SaveFile.TellLine()); + } + else + if(Word != ";" && Word != ",") + ABORT("Odd terminator %s encountered in %s content script, file %s line %ld!", Word.CStr(), GetClassID(), SaveFile.GetFileName().CStr(), SaveFile.TellLine()); +} + +scriptmemberbase* basecontentscript::GetData(cchar* String) +{ + scriptmemberbase* Return = GetDataFromMap(GetDataMap(), String); + return Return ? Return : GetDataFromMap(DataMap, String); +} + +void basecontentscript::Save(outputfile& SaveFile) const +{ + SaveDataMap(GetDataMap(), SaveFile); + SaveDataMap(DataMap, SaveFile); + SaveFile << ContentType; + SaveFile.Put(!!Random); + SaveFile << Config; +} + +void basecontentscript::Load(inputfile& SaveFile) +{ + LoadDataMap(GetDataMap(), SaveFile); + LoadDataMap(DataMap, SaveFile); + ContentType = ReadType(SaveFile); + Random = SaveFile.Get(); + SaveFile >> Config; +} + +template type* contentscripttemplate::BasicInstantiate(int SpecialFlags) const +{ + type* Instance = 0; + const typename type::prototype* Proto = protocontainer::GetProto(ContentType); + const typename type::database*const* ConfigData = Proto->GetConfigData(); + const materialscript* MainMaterial = GetMainMaterial(); + const materialscript* SecondaryMaterial = GetSecondaryMaterial(); + const typename type::database* DataBase = *ConfigData; + truth UseOverriddenMaterials = false; + + if(!Config && DataBase->IsAbstract) + { + while(!Instance) + { + DataBase = ConfigData[1 + RAND() % (Proto->GetConfigSize() - 1)]; + + if(DataBase->AllowRandomInstantiation()) + { + if(!(SpecialFlags & NO_MATERIALS) + && MainMaterial + && (!DataBase->HasSecondaryMaterial || SecondaryMaterial)) + { + SpecialFlags |= NO_MATERIALS; + UseOverriddenMaterials = true; + } + + Instance = Proto->Spawn(DataBase->Config, SpecialFlags|NO_PIC_UPDATE); + } + } + } + else + { + if(!(SpecialFlags & NO_MATERIALS) + && MainMaterial + && (!DataBase->HasSecondaryMaterial || SecondaryMaterial)) + { + SpecialFlags |= NO_MATERIALS; + UseOverriddenMaterials = true; + } + + Instance = Proto->Spawn(Config, SpecialFlags|NO_PIC_UPDATE); + } + + if(GetParameters() != NO_PARAMETERS) + Instance->SetParameters(GetParameters()); + + if(UseOverriddenMaterials) + Instance->InitMaterials(MainMaterial, SecondaryMaterial, false); + else + { + if(MainMaterial) + Instance->ChangeMainMaterial(MainMaterial->Instantiate(), SpecialFlags|NO_PIC_UPDATE); + + if(SecondaryMaterial) + Instance->ChangeSecondaryMaterial(SecondaryMaterial->Instantiate(), SpecialFlags|NO_PIC_UPDATE); + } + + if(!(SpecialFlags & NO_PIC_UPDATE)) + Instance->UpdatePictures(); + + return Instance; +} + +/* Called by an inline function in script.h... */ + +template glterrain* contentscripttemplate::BasicInstantiate(int) const; + +template int contentscripttemplate::SearchCodeName(cfestring& String) const +{ + return protocontainer::SearchCodeName(String); +} + +/* GCC 2.952 SUCKS!!! IT MUST BURN!!! */ + +template int contentscripttemplate::SearchCodeName(cfestring&) const; +template int contentscripttemplate::SearchCodeName(cfestring&) const; +template int contentscripttemplate::SearchCodeName(cfestring&) const; +template int contentscripttemplate::SearchCodeName(cfestring&) const; + +cchar* contentscript::GetClassID() const { return "character"; } +cchar* contentscript::GetClassID() const { return "item"; } +cchar* contentscript::GetClassID() const { return "glterrain"; } +cchar* contentscript::GetClassID() const { return "olterrain"; } + +void contentscript::InitDataMap() +{ + INIT_ENTRY(Inventory); + INIT_ENTRY(WayPoint); + INIT_ENTRY(Team); + INIT_ENTRY(Flags); +} + +contentscript::contentscript() +: INIT(Team, DEFAULT_TEAM), + INIT(Flags, 0) +{ } + +character* contentscript::Instantiate(int SpecialFlags) const +{ + character* Instance = contentscripttemplate::BasicInstantiate(SpecialFlags); + + if(GetTeam() != DEFAULT_TEAM) + Instance->SetTeam(game::GetTeam(GetTeam())); + + const fearray >* Inventory = GetInventory(); + + if(Inventory) + Instance->AddToInventory(*Inventory, SpecialFlags); + + const fearray* WayPoint = GetWayPoint(); + + if(WayPoint) + Instance->SetWayPoints(*WayPoint); + + Instance->RestoreHP(); + Instance->RestoreStamina(); + return Instance; +} + +contentscript::contentscript() +: INIT(Category, ANY_CATEGORY), + INIT(MinPrice, 0), + INIT(MaxPrice, MAX_PRICE), + INIT(Team, DEFAULT_TEAM), + INIT(SquarePosition, CENTER), + INIT(Chance, 100), + INIT(ConfigFlags, 0), + INIT(SpoilPercentage, 0), + INIT(Enchantment, 0), + INIT(IsActive, false) +{ } + +void contentscript::InitDataMap() +{ + INIT_ENTRY(ItemsInside); + INIT_ENTRY(Times); + INIT_ENTRY(MinPrice); + INIT_ENTRY(MaxPrice); + INIT_ENTRY(LifeExpectancy); + INIT_ENTRY(Team); + INIT_ENTRY(Category); + INIT_ENTRY(SquarePosition); + INIT_ENTRY(Chance); + INIT_ENTRY(ConfigFlags); + INIT_ENTRY(SpoilPercentage); + INIT_ENTRY(Enchantment); + INIT_ENTRY(IsActive); +} + +item* contentscript::InstantiateBasedOnMaterial(int MaterialConfig, int SpecialFlags) const +{ + if(ContentType == NATURAL_MATERIAL_FORM) + { + const materialscript* MainMaterial = GetMainMaterial(); + long Volume = MainMaterial && MainMaterial->GetVolume() + ? MainMaterial->GetVolume()->Randomize() : 0; + return material::CreateNaturalForm(MaterialConfig, Volume); + } + else + return Instantiate(SpecialFlags); +} + +item* contentscript::Instantiate(int SpecialFlags) const +{ + int Chance = GetChance(); + + if(Chance != 100 && Chance <= RAND_N(100)) + return 0; + + item* Instance; + + if(Random) + Instance = protosystem::BalancedCreateItem(GetMinPrice(), GetMaxPrice(), GetCategory(), SpecialFlags, GetConfigFlags()); + else + Instance = contentscripttemplate::BasicInstantiate(SpecialFlags); + + if(GetLifeExpectancy()) + Instance->SetLifeExpectancy(GetLifeExpectancy()->Min, (GetLifeExpectancy()->Max - GetLifeExpectancy()->Min) + 1); + + if(GetTeam() != DEFAULT_TEAM) + Instance->SetTeam(GetTeam()); + + if(IsActive()) + Instance->SetIsActive(true); + + if(GetEnchantment() != 0) + Instance->SetEnchantment(GetEnchantment()); + + const fearray >* ItemsInside = GetItemsInside(); + + if(ItemsInside) + Instance->SetItemsInside(*ItemsInside, SpecialFlags); + + if(GetSpoilPercentage() != 0) + Instance->SetSpoilPercentage(GetSpoilPercentage()); + + return Instance; +} + +truth IsValidScript(const fearray >* Array) +{ + for(uint c = 0; c < Array->Size; ++c) + if(IsValidScript(&Array->Data[c])) + return true; + + return false; +} + +void contentscript::InitDataMap() +{ + INIT_ENTRY(IsInside); +} + +contentscript::contentscript() +: INIT(VisualEffects, 0), + INIT(AttachedArea, DEFAULT_ATTACHED_AREA), + INIT(AttachedEntry, DEFAULT_ATTACHED_ENTRY) +{ } + +void contentscript::InitDataMap() +{ + INIT_ENTRY(ItemsInside); + INIT_ENTRY(Text); + INIT_ENTRY(VisualEffects); + INIT_ENTRY(AttachedArea); + INIT_ENTRY(AttachedEntry); +} + +olterrain* contentscript::Instantiate(int SpecialFlags) const +{ + if(!ContentType) + return 0; + + olterrain* Instance = contentscripttemplate::BasicInstantiate(SpecialFlags); + + if(GetVisualEffects()) + { + Instance->SetVisualEffects(GetVisualEffects()); + Instance->UpdatePictures(); + } + + if(GetAttachedArea() != DEFAULT_ATTACHED_AREA) + Instance->SetAttachedArea(GetAttachedArea()); + + if(GetAttachedEntry() != DEFAULT_ATTACHED_ENTRY) + Instance->SetAttachedEntry(GetAttachedEntry()); + + cfestring* Text = GetText(); + + if(Text) + Instance->SetText(*Text); + + const fearray >* ItemsInside = GetItemsInside(); + + if(ItemsInside) + Instance->SetItemsInside(*ItemsInside, SpecialFlags); + + return Instance; +} + +squarescript::squarescript() +: INIT(EntryIndex, NO_ENTRY), + INIT(AttachRequired, false) +{ } + +void squarescript::InitDataMap() +{ + INIT_ENTRY(Position); + INIT_ENTRY(Character); + INIT_ENTRY(Items); + INIT_ENTRY(GTerrain); + INIT_ENTRY(OTerrain); + INIT_ENTRY(Times); + INIT_ENTRY(EntryIndex); + INIT_ENTRY(AttachRequired); +} + +void squarescript::ReadFrom(inputfile& SaveFile) +{ + static festring Word; + SaveFile.ReadWord(Word); + + if(Word != "=") + { + PositionHolder.ReadFrom(SaveFile); + + if(SaveFile.ReadWord() != "{") + ABORT("Bracket missing in square script line %ld!", SaveFile.TellLine()); + + for(SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) + if(!ReadMember(SaveFile, Word)) + ABORT("Odd script term %s encountered in square script line %ld!", Word.CStr(), SaveFile.TellLine()); + } + else + { + GTerrainHolder.ReadFrom(SaveFile); + OTerrainHolder.ReadFrom(SaveFile); + } +} + +template contentmap::contentmap() : ContentMap(0) { } + +template contentmap::~contentmap() +{ + delete [] ContentMap; +} + +template void contentmap::InitDataMap() +{ + INIT_ENTRY(Size); + INIT_ENTRY(Pos); +} + +template void contentmap::ReadFrom(inputfile& SaveFile) +{ + typedef std::map maptype; + typedef typename maptype::iterator mapiterator; + + if(ContentMap) + ABORT("Illegal %s content map redefinition on line %ld!", protocontainer::GetMainClassID(), SaveFile.TellLine()); + + if(SaveFile.ReadWord() != "{") + ABORT("Bracket missing in %s content map script line %ld!", protocontainer::GetMainClassID(), SaveFile.TellLine()); + + SymbolMap.insert(std::pair('.', contenttype())); + static festring Word1, Word2; + + for(SaveFile.ReadWord(Word1); Word1 != "}"; Word1 = SaveFile.ReadWord()) + { + if(Word1 == "Types") + { + if(SaveFile.ReadWord() != "{") + ABORT("Missing bracket in %s content map script line %ld!", protocontainer::GetMainClassID(), SaveFile.TellLine()); + + for(SaveFile.ReadWord(Word2); Word2 != "}"; Word2 = SaveFile.ReadWord()) + { + std::pair Return = SymbolMap.insert(std::pair(Word2[0], contenttype())); + + if(Return.second) + ReadData(Return.first->second, SaveFile); + else + ABORT("Symbol %c defined again in %s content map script line %ld!", Word2[0], protocontainer::GetMainClassID(), SaveFile.TellLine()); + } + + continue; + } + + if(!ReadMember(SaveFile, Word1)) + ABORT("Odd script term %s encountered in %s content script line %ld!", Word1.CStr(), protocontainer::GetMainClassID(), SaveFile.TellLine()); + } + + v2 Size = *GetSize(); + Alloc2D(ContentMap, Size.X, Size.Y); + + if(SaveFile.ReadWord() != "{") + ABORT("Missing bracket in %s content map script line %ld!", protocontainer::GetMainClassID(), SaveFile.TellLine()); + + for(int y = 0; y < Size.Y; ++y) + for(int x = 0; x < Size.X; ++x) + { + int Char = SaveFile.ReadLetter(); + typename std::map::iterator i = SymbolMap.find(Char); + + if(i != SymbolMap.end()) + ContentMap[x][y] = std::make_pair(Char, &i->second); + else + ABORT("Illegal content %c in %s content map line %ld!", Char, protocontainer::GetMainClassID(), SaveFile.TellLine()); + } + + if(SaveFile.ReadWord() != "}") + ABORT("Missing bracket in %s content map script line %ld!", protocontainer::GetMainClassID(), SaveFile.TellLine()); +} + +template void contentmap::Save(outputfile& SaveFile) const +{ + script::Save(SaveFile); + SaveFile << SymbolMap; + v2 Size = *GetSize(); + + for(int y = 0; y < Size.Y; ++y) + for(int x = 0; x < Size.X; ++x) + SaveFile << char(ContentMap[x][y].first); +} + +template void contentmap::Load(inputfile& SaveFile) +{ + script::Load(SaveFile); + SaveFile >> SymbolMap; + v2 Size = *GetSize(); + Alloc2D(ContentMap, Size.X, Size.Y); + + for(int y = 0; y < Size.Y; ++y) + for(int x = 0; x < Size.X; ++x) + { + int Char = ReadType(SaveFile); + ContentMap[x][y] = std::make_pair(Char, &SymbolMap.find(Char)->second); + } +} + +const std::list& roomscript::GetSquare() const { return Square; } + +void roomscript::InitDataMap() +{ + INIT_ENTRY(CharacterMap); + INIT_ENTRY(ItemMap); + INIT_ENTRY(GTerrainMap); + INIT_ENTRY(OTerrainMap); + INIT_ENTRY(WallSquare); + INIT_ENTRY(FloorSquare); + INIT_ENTRY(DoorSquare); + INIT_ENTRY(Size); + INIT_ENTRY(Pos); + INIT_ENTRY(AltarPossible); + INIT_ENTRY(GenerateDoor); + INIT_ENTRY(GenerateTunnel); + INIT_ENTRY(DivineMaster); + INIT_ENTRY(GenerateLanterns); + INIT_ENTRY(Type); + INIT_ENTRY(GenerateFountains); + INIT_ENTRY(AllowLockedDoors); + INIT_ENTRY(AllowBoobyTrappedDoors); + INIT_ENTRY(Shape); + INIT_ENTRY(IsInside); + INIT_ENTRY(GenerateWindows); + INIT_ENTRY(UseFillSquareWalls); + INIT_ENTRY(Flags); + INIT_ENTRY(GenerateWards); +} + +void roomscript::ReadFrom(inputfile& SaveFile) +{ + if(SaveFile.ReadWord() != "{") + ABORT("Bracket missing in room script line %ld!", SaveFile.TellLine()); + + static festring Word; + + for(SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) + { + if(Word == "Square") + { + Square.push_back(squarescript()); + Square.back().ReadFrom(SaveFile); + continue; + } + + if(!ReadMember(SaveFile, Word)) + ABORT("Odd script term %s encountered in room script line %ld!", Word.CStr(), SaveFile.TellLine()); + } +} + +void roomscript::Save(outputfile& SaveFile) const +{ + script::Save(SaveFile); + SaveFile << Square; +} + +void roomscript::Load(inputfile& SaveFile) +{ + script::Load(SaveFile); + SaveFile >> Square; +} + +const std::list& levelscript::GetSquare() const { return Square; } +const std::list& levelscript::GetRoom() const { return Room; } + +void levelscript::InitDataMap() +{ + INIT_ENTRY(RoomDefault); + INIT_ENTRY(FillSquare); + INIT_ENTRY(TunnelSquare); + INIT_ENTRY(LevelMessage); + INIT_ENTRY(Size); + INIT_ENTRY(Items); + INIT_ENTRY(Rooms); + INIT_ENTRY(GenerateMonsters); + INIT_ENTRY(IsOnGround); + INIT_ENTRY(TeamDefault); + INIT_ENTRY(Description); + INIT_ENTRY(LOSModifier); + INIT_ENTRY(IgnoreDefaultSpecialSquares); + INIT_ENTRY(DifficultyBase); + INIT_ENTRY(DifficultyDelta); + INIT_ENTRY(MonsterAmountBase); + INIT_ENTRY(MonsterAmountDelta); + INIT_ENTRY(MonsterGenerationIntervalBase); + INIT_ENTRY(MonsterGenerationIntervalDelta); + INIT_ENTRY(AutoReveal); + INIT_ENTRY(ShortDescription); + INIT_ENTRY(CanGenerateBone); + INIT_ENTRY(ItemMinPriceBase); + INIT_ENTRY(ItemMinPriceDelta); + INIT_ENTRY(Type); + INIT_ENTRY(EnchantmentMinusChanceBase); + INIT_ENTRY(EnchantmentMinusChanceDelta); + INIT_ENTRY(EnchantmentPlusChanceBase); + INIT_ENTRY(EnchantmentPlusChanceDelta); + INIT_ENTRY(BackGroundType); + INIT_ENTRY(IsCatacomb); + INIT_ENTRY(EnterImage); + INIT_ENTRY(EnterTextDisplacement); +} + +void levelscript::ReadFrom(inputfile& SaveFile) +{ + if(SaveFile.ReadWord() != "{") + ABORT("Bracket missing in level script line %ld!", SaveFile.TellLine()); + + if(Base) + { + cv2* Size = static_cast(Base)->GetSize(); + + if(Size) + { + game::GetGlobalValueMap()["XSize"] = Size->X; + game::GetGlobalValueMap()["YSize"] = Size->Y; + } + } + + static festring Word; + + for(SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) + { + if(Word == "Square") + { + Square.push_back(squarescript()); + Square.back().ReadFrom(SaveFile); + continue; + } + + if(Word == "Room") + { + Room.push_back(roomscript()); + const roomscript* RoomDefault = GetRoomDefault(); + + if(RoomDefault) + Room.back().SetBase(RoomDefault); + + Room.back().ReadFrom(SaveFile); + continue; + } + + if(!ReadMember(SaveFile, Word)) + ABORT("Odd script term %s encountered in level script line %ld!", Word.CStr(), SaveFile.TellLine()); + + if(Word == "Size") + { + game::GetGlobalValueMap()["XSize"] = GetSize()->X; + game::GetGlobalValueMap()["YSize"] = GetSize()->Y; + } + } + + const levelscript* LevelBase = static_cast(Base); + + if(LevelBase && RoomDefaultHolder.Member) + RoomDefaultHolder.Member->SetBase(LevelBase->RoomDefaultHolder.Member); + + valuemap::iterator i = game::GetGlobalValueMap().find("XSize"); + + if(i != game::GetGlobalValueMap().end()) + game::GetGlobalValueMap().erase(i); + + i = game::GetGlobalValueMap().find("YSize"); + + if(i != game::GetGlobalValueMap().end()) + game::GetGlobalValueMap().erase(i); +} + +void levelscript::Combine(levelscript& Script) +{ + if(!Base) + Base = Script.Base; + + Square.splice(Square.end(), Script.Square); + Room.splice(Room.end(), Script.Room); + + for(std::list::iterator i1 = Room.begin(); i1 != Room.end(); ++i1) + i1->SetBase(GetRoomDefault()); + + for(datamap::const_iterator i2 = DataMap.begin(); i2 != DataMap.end(); ++i2) + (this->*i2->second).Replace(Script.*i2->second); +} + +void levelscript::SetBase(const scriptwithbase* What) +{ + const levelscript* LevelBase = static_cast(Base = What); + roomscript* BaseRoomDefault = LevelBase->RoomDefaultHolder.Member; + + if(BaseRoomDefault) + { + roomscript* ThisRoomDefault = RoomDefaultHolder.Member; + + if(!ThisRoomDefault) + for(std::list::iterator i = Room.begin(); i != Room.end(); ++i) + i->SetBase(BaseRoomDefault); + else + ThisRoomDefault->SetBase(BaseRoomDefault); + } +} + +void levelscript::Save(outputfile& SaveFile) const +{ + script::Save(SaveFile); + SaveFile << Square << Room; +} + +void levelscript::Load(inputfile& SaveFile) +{ + script::Load(SaveFile); + SaveFile >> Square >> Room; + const roomscript* RoomDefault = GetRoomDefault(); + + if(RoomDefault) + for(std::list::iterator i = Room.begin(); i != Room.end(); ++i) + i->SetBase(RoomDefault); +} + +dungeonscript::dungeonscript() { } +dungeonscript::~dungeonscript() { } +const std::map& dungeonscript::GetLevel() const { return Level; } + +void dungeonscript::InitDataMap() +{ + INIT_ENTRY(LevelDefault); + INIT_ENTRY(Levels); + INIT_ENTRY(Description); + INIT_ENTRY(ShortDescription); +} + +void dungeonscript::ReadFrom(inputfile& SaveFile) +{ + if(SaveFile.ReadWord() != "{") + ABORT("Bracket missing in dungeon script line %ld!", SaveFile.TellLine()); + + static festring Word; + + for(SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) + { + if(Word == "Level") + { + int Index = SaveFile.ReadNumber(); + std::pair::iterator, bool> Return = Level.insert(std::make_pair(Index, levelscript())); + + if(Return.second) + { + levelscript& LS = Return.first->second; + const levelscript* LevelDefault = GetLevelDefault(); + + if(LevelDefault) + LS.SetBase(LevelDefault); + + LS.ReadFrom(SaveFile); + } + else + ABORT("Level #%d defined again in dungeon script line %ld!", Index, SaveFile.TellLine()); + + continue; + } + + if(Word == "RandomLevel") + { + interval Interval; + ReadData(Interval, SaveFile); + RandomLevel.push_back(std::make_pair(Interval, levelscript())); + const levelscript* LevelDefault = GetLevelDefault(); + + if(LevelDefault) + RandomLevel.back().second.SetBase(LevelDefault); + + RandomLevel.back().second.ReadFrom(SaveFile); + continue; + } + + if(!ReadMember(SaveFile, Word)) + ABORT("Odd script term %s encountered in dungeon script line %ld!", Word.CStr(), SaveFile.TellLine()); + } +} + +void dungeonscript::RandomizeLevels() +{ + for(std::list >::iterator i = RandomLevel.begin(); i != RandomLevel.end(); ++i) + { + int Index = i->first.Randomize(); + Level[Index].Combine(i->second); + } + + RandomLevel.clear(); +} + +void dungeonscript::Save(outputfile& SaveFile) const +{ + script::Save(SaveFile); + SaveFile << Level << RandomLevel; +} + +void dungeonscript::Load(inputfile& SaveFile) +{ + script::Load(SaveFile); + SaveFile >> Level >> RandomLevel; + const levelscript* LevelDefault = GetLevelDefault(); + + if(LevelDefault) + { + for(std::map::iterator i1 = Level.begin(); i1 != Level.end(); ++i1) + i1->second.SetBase(LevelDefault); + + for(std::list >::iterator i2 = RandomLevel.begin(); i2 != RandomLevel.end(); ++i2) + i2->second.SetBase(LevelDefault); + } +} + +const std::vector >& teamscript::GetRelation() const { return Relation; } + +void teamscript::InitDataMap() +{ + INIT_ENTRY(KillEvilness); +} + +void teamscript::ReadFrom(inputfile& SaveFile) +{ + if(SaveFile.ReadWord() != "{") + ABORT("Bracket missing in team script line %ld!", SaveFile.TellLine()); + + static festring Word; + + for(SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) + { + if(Word == "Relation") + { + std::pair Rel; + Rel.first = SaveFile.ReadNumber(); + Rel.second = SaveFile.ReadNumber(); + Relation.push_back(Rel); + continue; + } + + if(!ReadMember(SaveFile, Word)) + ABORT("Odd script term %s encountered in team script line %ld!", Word.CStr(), SaveFile.TellLine()); + } +} + +void teamscript::Save(outputfile& SaveFile) const +{ + script::Save(SaveFile); + SaveFile << Relation; +} + +void teamscript::Load(inputfile& SaveFile) +{ + script::Load(SaveFile); + SaveFile >> Relation; +} + +const std::list >& gamescript::GetTeam() const { return Team; } +const std::map& gamescript::GetDungeon() const { return Dungeon; } + +void gamescript::InitDataMap() +{ + INIT_ENTRY(Dungeons); + INIT_ENTRY(Teams); +} + +void gamescript::ReadFrom(inputfile& SaveFile) +{ + static festring Word; + + for(SaveFile.ReadWord(Word, false); !SaveFile.Eof(); SaveFile.ReadWord(Word, false)) + { + if(Word == "Dungeon") + { + int Index = SaveFile.ReadNumber(); + std::pair::iterator, bool> Return = Dungeon.insert(std::make_pair(Index, dungeonscript())); + + if(Return.second) + Return.first->second.ReadFrom(SaveFile); + else + ABORT("Dungeon #%d defined again in game script line %ld!", Index, SaveFile.TellLine()); + + continue; + } + + if(Word == "Team") + { + int Index = SaveFile.ReadNumber(); + Team.push_back(std::pair(Index, teamscript())); + Team.back().second.ReadFrom(SaveFile); + continue; + } + + if(!ReadMember(SaveFile, Word)) + ABORT("Odd script term %s encountered in game script line %ld!", Word.CStr(), SaveFile.TellLine()); + } +} + +void gamescript::RandomizeLevels() +{ + for(std::map::iterator i = Dungeon.begin(); i != Dungeon.end(); ++i) + i->second.RandomizeLevels(); +} + +void gamescript::Save(outputfile& SaveFile) const +{ + script::Save(SaveFile); + SaveFile << Team << Dungeon; +} + +void gamescript::Load(inputfile& SaveFile) +{ + script::Load(SaveFile); + SaveFile >> Team >> Dungeon; +} + +outputfile& operator<<(outputfile& SaveFile, const gamescript* Script) +{ + Script->Save(SaveFile); + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, gamescript*& Script) +{ + Script = new gamescript; + Script->Load(SaveFile); + return SaveFile; +} + +void scriptsystem::Initialize() +{ + posscript::InitDataMap(); + materialscript::InitDataMap(); + basecontentscript::InitDataMap(); + contentscript::InitDataMap(); + contentscript::InitDataMap(); + contentscript::InitDataMap(); + contentscript::InitDataMap(); + squarescript::InitDataMap(); + itemcontentmap::InitDataMap(); + charactercontentmap::InitDataMap(); + glterraincontentmap::InitDataMap(); + olterraincontentmap::InitDataMap(); + roomscript::InitDataMap(); + levelscript::InitDataMap(); + dungeonscript::InitDataMap(); + teamscript::InitDataMap(); + gamescript::InitDataMap(); +} diff --git a/Main/Source/slot.cpp b/Main/Source/slot.cpp new file mode 100644 index 0000000..fec883d --- /dev/null +++ b/Main/Source/slot.cpp @@ -0,0 +1,263 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through slotset.cpp */ + +void slot::Save(outputfile& SaveFile) const +{ + SaveFile << Item; +} + +void slot::Load(inputfile& SaveFile) +{ + SaveFile >> Item; + + if(Item) + Item->SetMainSlot(this); +} + +void stackslot::Load(inputfile& SaveFile) +{ + SaveFile >> Item; + + if(Item) + Item->SignalStackAdd(this, &stack::AddElement); +} + +void stackslot::Empty() +{ + GetMotherStack()->RemoveItem(this); +} + +void bodypartslot::Empty() +{ + col24 Emitation = Item->GetEmitation(); + static_cast(Item)->SetMaster(0); + Item = 0; + GetMaster()->CalculateEquipmentState(); + SignalVolumeAndWeightChange(); + SignalEmitationDecrease(Emitation); + + if(!GetMaster()->IsInitializing()) + { + GetMaster()->CalculateHP(); + GetMaster()->CalculateMaxHP(); + } +} + +void gearslot::Empty() +{ + citem* Old = Item; + Item = 0; + col24 Emitation = Old->GetEmitation(); + SignalVolumeAndWeightChange(); + GetBodyPart()->SignalEquipmentRemoval(this, Old); + SignalEmitationDecrease(Emitation); +} + +void gearslot::Init(bodypart* BodyPart, int I) +{ + SetBodyPart(BodyPart); + SetEquipmentIndex(I); +} + +void stackslot::AddFriendItem(item* Item) const +{ + Item->RemoveFromSlot(); + GetMotherStack()->AddItem(Item); +} + +void bodypartslot::AddFriendItem(item* Item) const +{ + Item->RemoveFromSlot(); + + if(!game::IsInWilderness()) + GetMaster()->GetStackUnder()->AddItem(Item); + else + GetMaster()->GetStack()->AddItem(Item); +} + +void gearslot::AddFriendItem(item* Item) const +{ + Item->RemoveFromSlot(); + + if(!game::IsInWilderness()) + GetBodyPart()->GetLSquareUnder()->AddItem(Item); + else + GetBodyPart()->GetMaster()->GetStack()->AddItem(Item); +} + +truth stackslot::IsOnGround() const +{ + return GetMotherStack()->IsOnGround(); +} + +void stackslot::SignalVolumeAndWeightChange() +{ + GetMotherStack()->SignalVolumeAndWeightChange(); +} + +void bodypartslot::SignalVolumeAndWeightChange() +{ + GetMaster()->SignalVolumeAndWeightChange(); + GetMaster()->SignalBodyPartVolumeAndWeightChange(); +} + +void gearslot::SignalVolumeAndWeightChange() +{ + GetBodyPart()->SignalVolumeAndWeightChange(); +} + +void stackslot::PutInItem(item* What) +{ + Item = What; + + if(Item) + { + Item->SignalStackAdd(this, &stack::AddItem); + SignalVolumeAndWeightChange(); + SignalEmitationIncrease(Item->GetEmitation()); + } +} + +void bodypartslot::PutInItem(item* What) +{ + Item = What; + + if(Item) + { + Item->SetMainSlot(this); + static_cast(Item)->SetMaster(GetMaster()); + + if(!GetMaster()->IsInitializing()) + { + SignalVolumeAndWeightChange(); + SignalEmitationIncrease(Item->GetEmitation()); + static_cast(Item)->CalculateMaxHP(0); + GetMaster()->CalculateHP(); + GetMaster()->CalculateMaxHP(); + } + } +} + +void gearslot::PutInItem(item* What) +{ + Item = What; + + if(Item) + { + Item->SetMainSlot(this); + GetBodyPart()->SignalEquipmentAdd(this); + SignalVolumeAndWeightChange(); + SignalEmitationIncrease(Item->GetEmitation()); + } +} + +square* stackslot::GetSquareUnder(int) const +{ + return GetMotherStack()->GetSquareUnder(); +} + +square* bodypartslot::GetSquareUnder(int I) const +{ + return GetMaster()->GetSquareUnder(I); +} + +square* gearslot::GetSquareUnder(int) const +{ + return GetBodyPart()->GetSquareUnder(); +} + +void stackslot::SignalEmitationIncrease(col24 Emitation) +{ + GetMotherStack()->SignalEmitationIncrease(Item->GetSquarePosition(), Emitation); +} + +void bodypartslot::SignalEmitationIncrease(col24 Emitation) +{ + GetMaster()->SignalEmitationIncrease(Emitation); +} + +void gearslot::SignalEmitationIncrease(col24 Emitation) +{ + GetBodyPart()->SignalEmitationIncrease(Emitation); +} + +void stackslot::SignalEmitationDecrease(col24 Emitation) +{ + GetMotherStack()->SignalEmitationDecrease(Item->GetSquarePosition(), Emitation); +} + +void bodypartslot::SignalEmitationDecrease(col24 Emitation) +{ + GetMaster()->SignalEmitationDecrease(Emitation); +} + +void gearslot::SignalEmitationDecrease(col24 Emitation) +{ + GetBodyPart()->SignalEmitationDecrease(Emitation); +} + +void bodypartslot::Load(inputfile& SaveFile) +{ + slot::Load(SaveFile); + + if(Item) + static_cast(Item)->SetMaster(GetMaster()); +} + +void slot::DonateTo(item* Item) +{ + Empty(); + PutInItem(Item); +} + +void stackslot::DonateTo(item* Item) // could be optimized +{ + AddFriendItem(Item); + Empty(); +} + +truth stackslot::CanBeSeenBy(ccharacter* Viewer) const +{ + return GetMotherStack()->CanBeSeenBy(Viewer, Item->GetSquarePosition()); +} + +truth bodypartslot::CanBeSeenBy(ccharacter* Viewer) const +{ + return GetMaster()->CanBeSeenBy(Viewer); +} + +truth gearslot::CanBeSeenBy(ccharacter* Viewer) const +{ + return GetBodyPart()->CanBeSeenBy(Viewer); +} + +void gearslot::SignalEnchantmentChange() +{ + GetBodyPart()->SignalEnchantmentChange(); +} + +truth stackslot::IsVisible() const +{ + return GetMotherStack()->IsVisible(); +} + +ccharacter* stackslot::FindCarrier() const +{ + return GetMotherStack()->FindCarrier(); +} + +ccharacter* gearslot::FindCarrier() const +{ + return GetBodyPart()->FindCarrier(); +} diff --git a/Main/Source/slotset.cpp b/Main/Source/slotset.cpp new file mode 100644 index 0000000..b55a06c --- /dev/null +++ b/Main/Source/slotset.cpp @@ -0,0 +1,29 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include + +#include "iconf.h" +#include "char.h" +#include "stack.h" +#include "message.h" +#include "felist.h" +#include "room.h" +#include "game.h" +#include "bitmap.h" +#include "proto.h" +#include "action.h" +#include "save.h" +#include "materias.h" + +#include "slot.cpp" +#include "stack.cpp" diff --git a/Main/Source/smoke.cpp b/Main/Source/smoke.cpp new file mode 100644 index 0000000..2101502 --- /dev/null +++ b/Main/Source/smoke.cpp @@ -0,0 +1,148 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through materset.cpp */ + +smoke::smoke() : entity(HAS_BE), Next(0) { } +square* smoke::GetSquareUnderEntity(int) const { return LSquareUnder; } + +smoke::smoke(gas* Gas, lsquare* LSquareUnder) : entity(HAS_BE), Next(0), Gas(Gas), LSquareUnder(LSquareUnder), Alpha(Gas->GetAlpha()) +{ + Gas->SetMotherEntity(this); + Picture.resize(16); + packcol16 Color = Gas->GetColor(); + bitmap Temp(TILE_V2, TRANSPARENT_COLOR); + Temp.ActivateFastFlag(); + int Frame[16]; + int Flags[16]; + + for(int c = 0; c < 16; ++c) + { + Picture[c] = new bitmap(TILE_V2, TRANSPARENT_COLOR); + Picture[c]->ActivateFastFlag(); + Picture[c]->CreateAlphaMap(Alpha); + truth Correct = false; + + while(!Correct) + { + Frame[c] = RAND() & 3; + Flags[c] = RAND() & 7; + Correct = true; + + for(int i = 0; i < c; ++i) + if(Frame[c] == Frame[i] && Flags[c] == Flags[i]) + { + Correct = false; + break; + } + } + + igraph::GetRawGraphic(GR_EFFECT)->MaskedBlit(&Temp, v2(Frame[c] << 4, 32), ZERO_V2, TILE_V2, &Color); + Temp.NormalBlit(Picture[c], Flags[c]); + } + + LSquareUnder->SignalSmokeAlphaChange(Alpha); +} + +smoke::~smoke() +{ + for(uint c = 0; c < Picture.size(); ++c) + delete Picture[c]; + + delete Gas; +} + +void smoke::Be() +{ + if(!(RAND() & 7)) + { + LSquareUnder->SendNewDrawRequest(); + LSquareUnder->SignalSmokeAlphaChange(-1); + + if(!--Alpha) + { + LSquareUnder->RemoveSmoke(this); + SendToHell(); + return; + } + + for(int c = 0; c < 16; ++c) + Picture[c]->FillAlpha(Alpha); + + Gas->SetVolume(Gas->GetVolume() - Gas->GetVolume() / 50); + } + + character* Char = LSquareUnder->GetCharacter(); + + if(Char && !Char->StateIsActivated(GAS_IMMUNITY)) + Gas->BreatheEffect(Char); +} + +void smoke::Draw(blitdata& BlitData) const +{ + Picture[(GET_TICK() >> 1) & 3]->AlphaLuminanceBlit(BlitData); +} + +void smoke::Save(outputfile& SaveFile) const +{ + SaveFile << Picture << Gas << Alpha; +} + +void smoke::Load(inputfile& SaveFile) +{ + LSquareUnder = static_cast(game::GetSquareInLoad()); + SaveFile >> Picture >> Gas >> Alpha; + Gas->SetMotherEntity(this); +} + +outputfile& operator<<(outputfile& SaveFile, const smoke* Smoke) +{ + Smoke->Save(SaveFile); + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, smoke*& Smoke) +{ + Smoke = new smoke; + Smoke->Load(SaveFile); + return SaveFile; +} + +void smoke::AddBreatheMessage() const +{ + if(Gas->GetBreatheMessage().GetSize()) + ADD_MESSAGE("%s", Gas->GetBreatheMessage().CStr()); +} + +void smoke::Merge(gas* OtherGas) +{ + Gas->EditVolume(OtherGas->GetVolume()); + LSquareUnder->SignalSmokeAlphaChange(OtherGas->GetAlpha() - Alpha); + Alpha = OtherGas->GetAlpha(); + + for(int c = 0; c < 16; ++c) + Picture[c]->FillAlpha(Alpha); + + delete OtherGas; +} + +truth smoke::IsDangerousToBreathe(ccharacter* Who) const +{ + return (!Who->StateIsActivated(GAS_IMMUNITY) + && Who->GetAttribute(WISDOM) >= Gas->GetStepInWisdomLimit()); +} + +truth smoke::IsScaryToBreathe(ccharacter* Who) const +{ + return (!Who->StateIsActivated(GAS_IMMUNITY) + && Gas->GetCategoryFlags() & IS_SCARY); +} diff --git a/Main/Source/square.cpp b/Main/Source/square.cpp new file mode 100644 index 0000000..34f57ad --- /dev/null +++ b/Main/Source/square.cpp @@ -0,0 +1,145 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through areaset.cpp */ + +square::square(area* AreaUnder, v2 Pos) : AreaUnder(AreaUnder), Character(0), Pos(Pos), Luminance(0), Flags(IS_TRANSPARENT|MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE), StaticAnimatedEntities(0), AnimatedEntities(0), LastSeen(0) { } + +square::~square() +{ + character* Char = GetCharacter(); + + if(Char) + { + for(int c = 0; c < Char->GetSquaresUnder(); ++c) + Char->GetSquareUnder(c)->Character = 0; + + delete Char; + } +} + +void square::Save(outputfile& SaveFile) const +{ + if(!Character || Character->IsMainPos(Pos)) + SaveFile << Character; + + SaveFile << StaticAnimatedEntities << AnimatedEntities << MemorizedDescription; +} + +void square::Load(inputfile& SaveFile) +{ + if(!Character) + SaveFile >> Character; + + SaveFile >> StaticAnimatedEntities >> AnimatedEntities >> MemorizedDescription; +} + +void square::AddCharacter(character* Guy) +{ + Character = Guy; + Flags |= STRONG_NEW_DRAW_REQUEST; + + if(Guy->IsAnimated()) + IncAnimatedEntities(); + + Guy->CheckIfSeen(); +} + +void square::RemoveCharacter() +{ + if(Character && Character->IsAnimated()) + DecAnimatedEntities(); + + Character = 0; + Flags |= STRONG_NEW_DRAW_REQUEST; +} + +cchar* square::SurviveMessage(character* Char) const +{ + if(GetOTerrain() && !Char->CanMoveOn(GetOTerrain())) + return GetOTerrain()->SurviveMessage(); + else + return GetGTerrain()->SurviveMessage(); +} + +cchar* square::MonsterSurviveMessage(character* Char) const +{ + if(GetOTerrain() && !Char->CanMoveOn(GetOTerrain())) + return GetOTerrain()->MonsterSurviveMessage(); + else + return GetGTerrain()->MonsterSurviveMessage(); +} + +cchar* square::DeathMessage(character* Char) const +{ + if(GetOTerrain() && !Char->CanMoveOn(GetOTerrain())) + return GetOTerrain()->DeathMessage(); + else + return GetGTerrain()->DeathMessage(); +} + +cchar* square::MonsterDeathVerb(character* Char) const +{ + if(GetOTerrain() && !Char->CanMoveOn(GetOTerrain())) + return GetOTerrain()->MonsterDeathVerb(); + else + return GetGTerrain()->MonsterDeathVerb(); +} + +cchar* square::ScoreEntry(character* Char) const +{ + if(GetOTerrain() && !Char->CanMoveOn(GetOTerrain())) + return GetOTerrain()->ScoreEntry(); + else + return GetGTerrain()->ScoreEntry(); +} + +truth square::IsFatalToStay() const +{ + return GetGTerrain()->IsFatalToStay() || (GetOTerrain() && GetOTerrain()->IsFatalToStay()); +} + +int square::GetEntryDifficulty() const +{ + return GetGTerrain()->GetEntryDifficulty(); +} + +int square::GetRestModifier() const +{ + return GetOTerrain() ? GetOTerrain()->GetRestModifier() : 1; +} + +truth square::CanBeSeenBy(ccharacter* Who, truth IgnoreDarkness) const +{ + if(Who->IsPlayer()) + return CanBeSeenByPlayer(IgnoreDarkness); + else + return CanBeSeenFrom(Who->GetPos(), Who->GetLOSRangeSquare(), IgnoreDarkness); +} + +void square::SurviveEffect(character* Who) +{ + if(GetOTerrain()) + GetOTerrain()->SurviveEffect(Who); + + GetGTerrain()->SurviveEffect(Who); +} + +square* square::GetNeighbourSquare(int I) const +{ + return AreaUnder->GetNeighbourSquare(Pos, I); +} + +square* square::GetNearSquare(v2 Pos) const +{ + return AreaUnder->GetSquare(Pos); +} diff --git a/Main/Source/stack.cpp b/Main/Source/stack.cpp new file mode 100644 index 0000000..839c223 --- /dev/null +++ b/Main/Source/stack.cpp @@ -0,0 +1,1325 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through slotset.cpp */ + +/* If REMEMBER_SELECTED flag is used, DrawContents() will use this to determine + the initial selected item */ + +int stack::Selected; + +stack::stack(square* MotherSquare, entity* MotherEntity, ulong Flags) +: Bottom(0), Top(0), MotherSquare(MotherSquare), MotherEntity(MotherEntity), + Volume(0), Weight(0), Emitation(0), Flags(Flags), Items(0) { } +stack::~stack() { Clean(true); } +square* stack::GetSquareUnder() const +{ return !MotherEntity ? MotherSquare : MotherEntity->GetSquareUnderEntity(); } + +/* Modifies the square index bits of BlitData.CustomData */ + +void stack::Draw(ccharacter* Viewer, blitdata& BlitData, + int RequiredSquarePosition) const +{ + if(!Items) + return; + + int VisibleItems = 0; + v2 StackPos = GetPos(); + + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + if(i->GetSquarePosition() == RequiredSquarePosition + && (i->CanBeSeenBy(Viewer) || game::GetSeeWholeMapCheatMode())) + { + BlitData.CustomData |= i->GetSquareIndex(StackPos); + i->Draw(BlitData); + BlitData.CustomData &= ~SQUARE_INDEX_MASK; + ++VisibleItems; + } + + if(RequiredSquarePosition == CENTER) + { + truth PlusSymbol = VisibleItems > 1, Dangerous = NeedDangerSymbol(Viewer); + + if(PlusSymbol || Dangerous) + { + col24 L = BlitData.Luminance; + BlitData.Luminance = ivanconfig::GetContrastLuminance(); + BlitData.Src.Y = 16; + + if(PlusSymbol) + igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData); + + if(Dangerous) + { + BlitData.Src.X = 160; + igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData); + } + + BlitData.Src.X = BlitData.Src.Y = 0; /// check + BlitData.Luminance = L; + } + } +} + +void stack::AddItem(item* ToBeAdded, truth RunRoomEffects) +{ + if(!ToBeAdded) + return; + + AddElement(ToBeAdded); + + if(Flags & HIDDEN) + return; + + lsquare* SquareUnder = GetLSquareTrulyUnder(ToBeAdded->GetSquarePosition()); + + if(!SquareUnder) + return; + + if(ToBeAdded->IsAnimated()) + SquareUnder->IncStaticAnimatedEntities(); + + if(!game::IsGenerating()) + { + if(RunRoomEffects && GetLSquareUnder()->GetRoom()) + GetLSquareUnder()->GetRoom()->AddItemEffect(ToBeAdded); + + SquareUnder->SendNewDrawRequest(); + SquareUnder->SendMemorizedUpdateRequest(); + } +} + +void stack::RemoveItem(stackslot* Slot) +{ + item* Item = Slot->GetItem(); + truth WasAnimated = Item->IsAnimated(); + col24 Emit = Item->GetEmitation(); + RemoveElement(Slot); + SignalVolumeAndWeightChange(); + SignalEmitationDecrease(Item->GetSquarePosition(), Emit); + + if(Flags & HIDDEN) + return; + + lsquare* SquareUnder = GetLSquareTrulyUnder(Item->GetSquarePosition()); + + if(Item->GetSquarePosition() != CENTER) + Item->SignalSquarePositionChange(CENTER); + + if(!SquareUnder) + return; + + if(WasAnimated) + SquareUnder->DecStaticAnimatedEntities(); + + if(!game::IsGenerating()) + { + SquareUnder->SendNewDrawRequest(); + SquareUnder->SendMemorizedUpdateRequest(); + } +} + +/* Removes all items. LastClean should be true only if the stack is being + deleted (the default is false) */ + +void stack::Clean(truth LastClean) +{ + if(!Items) + return; + + stackslot* Slot = Bottom; + + if(!LastClean) + { + Bottom = Top = 0; + Volume = Weight = Items = 0; + SignalVolumeAndWeightChange(); + } + + while(Slot) + { + item* Item = Slot->GetItem(); + + if(!(Flags & HIDDEN) && Item->IsAnimated() && !LastClean) + { + lsquare* Square = GetLSquareTrulyUnder(Item->GetSquarePosition()); + + if(Square) + Square->DecStaticAnimatedEntities(); + } + + if(LastClean && Item->GetSquaresUnder() == 1) + delete Item; + else + Item->SendToHell(); + + stackslot* Rubbish = Slot; + Slot = Slot->Next; + delete Rubbish; + + if(!LastClean) + SignalEmitationDecrease(Item->GetSquarePosition(), Item->GetEmitation()); + } +} + +void stack::Save(outputfile& SaveFile) const +{ + if(!Items) + { + SaveFile << ushort(0); + return; + } + + int SavedItems = 0; + + for(stackiterator i1 = GetBottom(); i1.HasItem(); ++i1) + if(i1->IsMainSlot(&i1.GetSlot())) + ++SavedItems; + + SaveFile << (ushort)SavedItems; + + /* Save multitiled items only to one stack */ + + for(stackiterator i2 = GetBottom(); i2.HasItem(); ++i2) + if(i2->IsMainSlot(&i2.GetSlot())) + SaveFile << i2.GetSlot(); +} + +void stack::Load(inputfile& SaveFile) +{ + int SavedItems = 0; + SaveFile >> (ushort&)SavedItems; + + for(int c = 0; c < SavedItems; ++c) + { + if(!c && !Items) + Bottom = Top = new stackslot(this, 0); + else + Top = Top->Next = new stackslot(this, Top); + + SaveFile >> *Top; + Volume += (*Top)->GetVolume(); + Weight += (*Top)->GetWeight(); + + if((*Top)->GetSquarePosition() == CENTER) + Emitation = game::CombineConstLights(Emitation, (*Top)->GetEmitation()); + } + + Items += SavedItems; +} + +v2 stack::GetPos() const +{ + return GetSquareUnder()->GetPos(); +} + +/* Returns whether there are any items satisfying the sorter or any visible + items if it is zero */ + +truth stack::SortedItems(ccharacter* Viewer, sorter SorterFunction) const +{ + if(Items) + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + if((SorterFunction == 0 || ((*i)->*SorterFunction)(Viewer)) + && ((Flags & HIDDEN) || i->CanBeSeenBy(Viewer))) + return true; + + return false; +} + +void stack::BeKicked(character* Kicker, int KickDamage, int Direction) +{ + if(KickDamage) + { + ReceiveDamage(Kicker, KickDamage, PHYSICAL_DAMAGE, Direction); + + if(GetItems() && GetLSquareUnder()->IsFlyable())///&& SquarePosition == CENTER) + { + item* Item1 = *GetTop(); + item* Item2 = RAND() & 1 && GetItems() > 1 ? *--GetTop() : 0; + Item1->Fly(Kicker, Direction, KickDamage * 3); + + if(Item2) + { + if(!Item2->Exists() || Item2->GetPos() != GetPos()) + int esko = esko = 2; + + Item2->Fly(Kicker, Direction, KickDamage * 3); + } + } + } + else if(Kicker->IsPlayer() && GetNativeVisibleItems(Kicker)) + ADD_MESSAGE("Your weak kick has no effect."); +} + +void stack::Polymorph(character* Polymorpher) +{ + itemvector ItemVector; + FillItemVector(ItemVector); + int p = 0; + + for(uint c = 0; c < ItemVector.size(); ++c) + if(ItemVector[c]->Exists() + && ItemVector[c]->Polymorph(Polymorpher, this) + && ++p == 5) + break; +} + +void stack::CheckForStepOnEffect(character* Stepper) +{ + itemvector ItemVector; + FillItemVector(ItemVector); + + for(uint c = 0; c < ItemVector.size(); ++c) + if(ItemVector[c]->Exists()) + { + ItemVector[c]->StepOnEffect(Stepper); + + if(!Stepper->IsEnabled()) + return; + } +} + +lsquare* stack::GetLSquareTrulyUnder(int SquarePosition) const +{ + switch(SquarePosition) + { + case DOWN: + if(GetArea()->IsValidPos(GetPos() + v2(0, 1))) + return GetNearLSquare(GetPos() + v2(0, 1)); + else + return 0; + case LEFT: + if(GetArea()->IsValidPos(GetPos() + v2(-1, 0))) + return GetNearLSquare(GetPos() + v2(-1, 0)); + else + return 0; + case UP: + if(GetArea()->IsValidPos(GetPos() + v2(0, -1))) + return GetNearLSquare(GetPos() + v2(0, -1)); + else + return 0; + case RIGHT: + if(GetArea()->IsValidPos(GetPos() + v2(1, 0))) + return GetNearLSquare(GetPos() + v2(1, 0)); + else + return 0; + } + + return GetLSquareUnder(); +} + +void stack::ReceiveDamage(character* Damager, int Damage, + int Type, int Direction) +{ + itemvector ItemVector; + FillItemVector(ItemVector); + + for(uint c = 0; c < ItemVector.size(); ++c) + if(ItemVector[c]->Exists() + && AllowDamage(Direction, ItemVector[c]->GetSquarePosition())) + ItemVector[c]->ReceiveDamage(Damager, Damage, Type); +} + +void stack::TeleportRandomly(uint Amount) +{ + itemvector ItemVector; + FillItemVector(ItemVector); + + for(uint c = 0; c < ItemVector.size() && c < Amount; ++c) + if(ItemVector[c]->Exists()) + { + if(ItemVector[c]->CanBeSeenByPlayer()) + ADD_MESSAGE("%s disappears!", + ItemVector[c]->GetExtendedDescription().CStr()); + + ItemVector[c]->TeleportRandomly(); + } +} + +/* ItemVector receives all items in the stack */ + +void stack::FillItemVector(itemvector& ItemVector) const +{ + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + ItemVector.push_back(*i); +} + +/* Don't use; this function is only for gum solutions */ + +item* stack::GetItem(int I) const +{ + int c = 0; + + for(stackiterator i = GetBottom(); i.HasItem(); ++i, ++c) + if(c == I) + return *i; + + return 0; +} + +/* Don't use; this function is only for gum solutions */ + +int stack::SearchItem(item* ToBeSearched) const +{ + int c = 0; + + for(stackiterator i = GetBottom(); i.HasItem(); ++i, ++c) + if(*i == ToBeSearched) + return c; + + return -1; +} + +/* Flags for all DrawContents functions can be found in ivandef.h. + Those returning int return 0 on success and a felist error + otherwise (see felibdef.h) */ + +item* stack::DrawContents(ccharacter* Viewer, cfestring& Topic, + int Flags, sorter SorterFunction) const +{ + itemvector ReturnVector; + DrawContents(ReturnVector, 0, Viewer, Topic, CONST_S(""), CONST_S(""), + CONST_S(""), 0, Flags|NO_MULTI_SELECT, SorterFunction); + return ReturnVector.empty() ? 0 : ReturnVector[0]; +} + +int stack::DrawContents(itemvector& ReturnVector, + ccharacter* Viewer, + cfestring& Topic, int Flags, + sorter SorterFunction) const +{ + return DrawContents(ReturnVector, 0, Viewer, Topic, CONST_S(""), + CONST_S(""), CONST_S(""), 0, Flags, SorterFunction); +} + +/* MergeStack is used for showing two stacks together. Like when eating when + there are items on the ground and in the character's stack */ + +int stack::DrawContents(itemvector& ReturnVector, stack* MergeStack, + ccharacter* Viewer, cfestring& Topic, + cfestring& ThisDesc, cfestring& ThatDesc, + cfestring& SpecialDesc, col16 SpecialDescColor, + int Flags, sorter SorterFunction) const +{ + felist Contents(Topic); + lsquare* Square = GetLSquareUnder(); + stack* AdjacentStack[4] = { 0, 0, 0, 0 }; + int c; + + if(!(this->Flags & HIDDEN)) + for(c = 0; c < 4; ++c) + AdjacentStack[c] = Square->GetStackOfAdjacentSquare(c); + + if(!SpecialDesc.IsEmpty()) + { + Contents.AddDescription(CONST_S("")); + Contents.AddDescription(SpecialDesc.CapitalizeCopy(), SpecialDescColor); + } + + /*if(!(Flags & NO_SPECIAL_INFO)) + { + Contents.AddDescription(CONST_S("")); + long Weight = GetWeight(Viewer, CENTER); + + if(MergeStack) + Weight += MergeStack->GetWeight(Viewer, CENTER); + + for(c = 0; c < 4; ++c) + if(AdjacentStack[c]) + Weight += AdjacentStack[c]->GetWeight(Viewer, 3 - c); + + Contents.AddDescription(CONST_S("Overall weight: ") + Weight + " grams"); + }*/ + + if(Flags & NONE_AS_CHOICE) + { + int ImageKey = game::AddToItemDrawVector(itemvector()); + Contents.AddEntry(CONST_S("none"), LIGHT_GRAY, 0, ImageKey); + } + + if(MergeStack) + MergeStack->AddContentsToList(Contents, Viewer, ThatDesc, + Flags, CENTER, SorterFunction); + + AddContentsToList(Contents, Viewer, ThisDesc, Flags, CENTER, SorterFunction); + static cchar* WallDescription[] = { "western", "southern", + "nothern", "eastern" }; + + for(c = 0; c < 4; ++c) + if(AdjacentStack[c]) + AdjacentStack[c]->AddContentsToList(Contents, Viewer, + CONST_S("Items on the ") + + WallDescription[c] + " wall:", + Flags, 3 - c, SorterFunction); + + game::SetStandardListAttributes(Contents); + Contents.SetPageLength(12); + Contents.RemoveFlags(BLIT_AFTERWARDS); + Contents.SetEntryDrawer(game::ItemEntryDrawer); + + if(!(Flags & NO_SELECT)) + Contents.AddFlags(SELECTABLE); + + if(Flags & REMEMBER_SELECTED) + Contents.SetSelected(GetSelected()); + + game::DrawEverythingNoBlit(); //doesn't prevent mirage puppies + int Chosen = Contents.Draw(); + game::ClearItemDrawVector(); + + if(Chosen & FELIST_ERROR_BIT) + { + Selected = 0; + return Chosen; + } + else + Selected = Chosen; + + int Pos = 0; + + if(Flags & NONE_AS_CHOICE) + if(!Selected) + return 0; + else + ++Pos; + + if(MergeStack) + { + Pos = MergeStack->SearchChosen(ReturnVector, Viewer, Pos, Selected, + Flags, CENTER, SorterFunction); + + if(!ReturnVector.empty()) + return 0; + } + + Pos = SearchChosen(ReturnVector, Viewer, Pos, Selected, + Flags, CENTER, SorterFunction); + + if(!ReturnVector.empty()) + return 0; + + for(c = 0; c < 4; ++c) + if(AdjacentStack[c]) + { + AdjacentStack[c]->SearchChosen(ReturnVector, Viewer, Pos, Selected, + Flags, 3 - c, SorterFunction); + + if(!ReturnVector.empty()) + break; + } + + return 0; +} + +/* Internal function to fill Contents list */ + +void stack::AddContentsToList(felist& Contents, ccharacter* Viewer, + cfestring& Desc, int Flags, + int RequiredSquarePosition, + sorter SorterFunction) const +{ + itemvectorvector PileVector; + Pile(PileVector, Viewer, RequiredSquarePosition, SorterFunction); + + truth DrawDesc = Desc.GetSize(); + long LastCategory = 0; + festring Entry; + + for(uint p = 0; p < PileVector.size(); ++p) + { + if(DrawDesc) + { + if(!Contents.IsEmpty()) + Contents.AddEntry(CONST_S(""), WHITE, 0, NO_IMAGE, false); + + Contents.AddEntry(Desc, WHITE, 0, NO_IMAGE, false); + Contents.AddEntry(CONST_S(""), WHITE, 0, NO_IMAGE, false); + DrawDesc = false; + } + + item* Item = PileVector[p].back(); + + if(Item->GetCategory() != LastCategory) + { + LastCategory = Item->GetCategory(); + Contents.AddEntry(item::GetItemCategoryName(LastCategory), + LIGHT_GRAY, 0, NO_IMAGE, false); + } + + Entry.Empty(); + Item->AddInventoryEntry(Viewer, Entry, PileVector[p].size(), + !(Flags & NO_SPECIAL_INFO)); + int ImageKey = game::AddToItemDrawVector(PileVector[p]); + Contents.AddEntry(Entry, LIGHT_GRAY, 0, ImageKey); + } +} + +/* Internal function which fills ReturnVector according to Chosen, + which is given by felist::Draw, and possibly the user's additional + input about item amount. */ + +int stack::SearchChosen(itemvector& ReturnVector, + ccharacter* Viewer, + int Pos, int Chosen, int Flags, + int RequiredSquarePosition, + sorter SorterFunction) const +{ + /* Not really efficient... :( */ + + itemvectorvector PileVector; + Pile(PileVector, Viewer, RequiredSquarePosition, SorterFunction); + + for(uint p = 0; p < PileVector.size(); ++p) + if(Pos++ == Chosen) + if(Flags & NO_MULTI_SELECT) + { + int Amount = (Flags & SELECT_PAIR + && PileVector[p][0]->HandleInPairs() + && PileVector[p].size() >= 2 + ? 2 : 1); + ReturnVector.assign(PileVector[p].end() - Amount, PileVector[p].end()); + return -1; + } + else + { + int Amount = PileVector[p].size(); + + if(Amount > 1) + Amount = game::ScrollBarQuestion(CONST_S("How many ") + + PileVector[p][0]->GetName(PLURAL) + + '?', + Amount, 1, 0, Amount, 0, WHITE, + LIGHT_GRAY, DARK_GRAY); + + ReturnVector.assign(PileVector[p].end() - Amount, PileVector[p].end()); + return -1; + } + + return Pos; +} + +truth stack::RaiseTheDead(character* Summoner) +{ + itemvector ItemVector; + FillItemVector(ItemVector); + + for(uint c = 0; c < ItemVector.size(); ++c) + if(ItemVector[c]->RaiseTheDead(Summoner)) + return true; + + return false; +} + +/* Returns false if the Applier didn't try to use the key */ + +truth stack::TryKey(item* Key, character* Applier) +{ + if(!Applier->IsPlayer()) + return false; + + item* ToBeOpened = DrawContents(Applier, + CONST_S("Where do you wish to use the key?"), + 0, &item::HasLock); + + if(!ToBeOpened) + return false; + + return ToBeOpened->TryKey(Key, Applier); +} + +/* Returns false if the Applier didn't try to open anything */ + +truth stack::Open(character* Opener) +{ + if(!Opener->IsPlayer()) + return false; + + item* ToBeOpened = DrawContents(Opener, CONST_S("What do you wish to open?"), + 0, &item::IsOpenable); + return ToBeOpened ? ToBeOpened->Open(Opener) : false; +} + +int stack::GetSideItems(int RequiredSquarePosition) const +{ + int VisibleItems = 0; + + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + if(i->GetSquarePosition() == RequiredSquarePosition) + ++VisibleItems; + + return VisibleItems; +} + +int stack::GetVisibleItems(ccharacter* Viewer) const +{ + int VisibleItems = 0; + + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + if(i->GetSquarePosition() == CENTER && i->CanBeSeenBy(Viewer)) + ++VisibleItems; + + lsquare* Square = GetLSquareUnder(); + + for(int c = 0; c < 4; ++c) + { + stack* Stack = Square->GetStackOfAdjacentSquare(c); + + if(Stack) + VisibleItems += Stack->GetVisibleSideItems(Viewer, 3 - c); + } + + return VisibleItems; +} + +int stack::GetNativeVisibleItems(ccharacter* Viewer) const +{ + if(Flags & HIDDEN) + return Items; + + int VisibleItems = 0; + + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + if(i->CanBeSeenBy(Viewer)) + ++VisibleItems; + + return VisibleItems; +} + +int stack::GetVisibleSideItems(ccharacter* Viewer, + int RequiredSquarePosition) const +{ + int VisibleItems = 0; + + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + if(i->GetSquarePosition() == RequiredSquarePosition + && i->CanBeSeenBy(Viewer)) + ++VisibleItems; + + return VisibleItems; +} + +item* stack::GetBottomVisibleItem(ccharacter* Viewer) const +{ + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + if((Flags & HIDDEN) || i->CanBeSeenBy(Viewer)) + return *i; + + return 0; +} + +void stack::SignalVolumeAndWeightChange() +{ + if(!(Flags & FREEZED)) + { + CalculateVolumeAndWeight(); + + if(MotherEntity) + MotherEntity->SignalVolumeAndWeightChange(); + } +} + +void stack::CalculateVolumeAndWeight() +{ + Volume = Weight = 0; + + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + { + Volume += i->GetVolume(); + Weight += i->GetWeight(); + } +} + +void stack::SignalEmitationIncrease(int ItemSquarePosition, + col24 EmitationUpdate) +{ + if(ItemSquarePosition < CENTER) + { + stack* Stack = GetLSquareUnder() + ->GetStackOfAdjacentSquare(ItemSquarePosition); + + if(Stack) + Stack->SignalEmitationIncrease(CENTER, EmitationUpdate); + + return; + } + + if(!(Flags & FREEZED) && game::CompareLights(EmitationUpdate, Emitation) > 0) + { + Emitation = game::CombineConstLights(Emitation, EmitationUpdate); + + if(MotherEntity) + { + if(MotherEntity->AllowContentEmitation()) + MotherEntity->SignalEmitationIncrease(EmitationUpdate); + } + else + GetLSquareUnder()->SignalEmitationIncrease(EmitationUpdate); + } +} + +void stack::SignalEmitationDecrease(int ItemSquarePosition, + col24 EmitationUpdate) +{ + if(ItemSquarePosition < CENTER) + { + stack* Stack = GetLSquareUnder() + ->GetStackOfAdjacentSquare(ItemSquarePosition); + + if(Stack) + Stack->SignalEmitationDecrease(CENTER, EmitationUpdate); + + return; + } + + if(!(Flags & FREEZED) && Emitation + && game::CompareLights(EmitationUpdate, Emitation) >= 0) + { + col24 Backup = Emitation; + CalculateEmitation(); + + if(Backup != Emitation) + { + if(MotherEntity) + { + if(MotherEntity->AllowContentEmitation()) + MotherEntity->SignalEmitationDecrease(EmitationUpdate); + } + else + GetLSquareUnder()->SignalEmitationDecrease(EmitationUpdate); + } + } +} + +void stack::CalculateEmitation() +{ + Emitation = GetSideEmitation(CENTER); +} + +col24 stack::GetSideEmitation(int RequiredSquarePosition) +{ + col24 Emitation = 0; + + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + if(i->GetSquarePosition() == RequiredSquarePosition) + game::CombineLights(Emitation, i->GetEmitation()); + + return Emitation; +} + +truth stack::CanBeSeenBy(ccharacter* Viewer, int SquarePosition) const +{ + if(MotherEntity) + return MotherEntity->ContentsCanBeSeenBy(Viewer); + else + { + lsquare* Square = GetLSquareTrulyUnder(SquarePosition); + return Viewer->IsOver(Square->GetPos()) || Square->CanBeSeenBy(Viewer); + } +} + +truth stack::IsDangerous(ccharacter* Stepper) const +{ + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + if(i->IsDangerous(Stepper) && i->CanBeSeenBy(Stepper)) + return true; + + return false; +} + +/* Returns true if something was duplicated. + Max is the cap of items to be affected */ + +truth stack::Duplicate(int Max, ulong Flags) +{ + if(!GetItems()) + return false; + + itemvector ItemVector; + FillItemVector(ItemVector); + int p = 0; + + for(uint c = 0; c < ItemVector.size(); ++c) + if(ItemVector[c]->Exists() + && ItemVector[c]->DuplicateToStack(this, Flags) + && ++p == Max) + break; + + return p > 0; +} + +/* Adds the item without any external update requests */ + +void stack::AddElement(item* Item, truth) +{ + ++Items; + + /* "I love writing illegible code." - Guy who wrote this */ + + (Top = (Bottom ? Top->Next : Bottom) = new stackslot(this, Top))->PutInItem(Item); +} + +/* Removes the slot without any external update requests */ + +void stack::RemoveElement(stackslot* Slot) +{ + --Items; + (Slot->Last ? Slot->Last->Next : Bottom) = Slot->Next; + (Slot->Next ? Slot->Next->Last : Top) = Slot->Last; + delete Slot; +} + +void stack::MoveItemsTo(stack* Stack) +{ + while(Items) + GetBottom()->MoveTo(Stack); +} + +void stack::MoveItemsTo(slot* Slot) +{ + while(Items) + Slot->AddFriendItem(*GetBottom()); +} + +item* stack::GetBottomItem(ccharacter* Char, + truth ForceIgnoreVisibility) const +{ + if((Flags & HIDDEN) || ForceIgnoreVisibility) + return Bottom ? **Bottom : 0; + else + return GetBottomVisibleItem(Char); +} + +item* stack::GetBottomSideItem(ccharacter* Char, + int RequiredSquarePosition, + truth ForceIgnoreVisibility) const +{ + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + if(i->GetSquarePosition() == RequiredSquarePosition + && (Flags & HIDDEN) || ForceIgnoreVisibility || i->CanBeSeenBy(Char)) + return *i; + + return 0; +} + +truth CategorySorter(const itemvector& V1, const itemvector& V2) +{ + return (*V1.begin())->GetCategory() < (*V2.begin())->GetCategory(); +} + +/* Slow function which sorts the stack's contents to a vector of piles + (itemvectors) of which elements are similiar to each other, for instance + 4 bananas */ + +void stack::Pile(itemvectorvector& PileVector, ccharacter* Viewer, + int RequiredSquarePosition, sorter SorterFunction) const +{ + if(!Items) + return; + + std::list List; + + for(stackiterator s = GetBottom(); s.HasItem(); ++s) + if(s->GetSquarePosition() == RequiredSquarePosition + && (SorterFunction == 0 || ((*s)->*SorterFunction)(Viewer)) + && ((Flags & HIDDEN) || s->CanBeSeenBy(Viewer))) + List.push_back(*s); + + for(std::list::iterator i = List.begin(); i != List.end(); ++i) + { + PileVector.resize(PileVector.size() + 1); + itemvector& Pile = PileVector.back(); + Pile.push_back(*i); + + if((*i)->CanBePiled()) + { + std::list::iterator j = i; + + for(++j; j != List.end();) + if((*j)->CanBePiled() && (*i)->CanBePiledWith(*j, Viewer)) + { + Pile.push_back(*j); + std::list::iterator Dirt = j++; + List.erase(Dirt); + } + else + ++j; + } + } + + std::stable_sort(PileVector.begin(), PileVector.end(), CategorySorter); +} + +/* Total price of the stack */ + +long stack::GetTruePrice() const +{ + long Price = 0; + + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + Price += i->GetTruePrice(); + + return Price; +} + +/* GUI used for instance by chests and bookcases. + Returns whether anything was done. */ + +truth stack::TakeSomethingFrom(character* Opener, + cfestring& ContainerName) +{ + if(!GetItems()) + { + ADD_MESSAGE("There is nothing in %s.", ContainerName.CStr()); + return false; + } + + truth Success = false; + room* Room = GetLSquareUnder()->GetRoom(); + SetSelected(0); + + for(;;) + { + itemvector ToTake; + game::DrawEverythingNoBlit(); + DrawContents(ToTake, Opener, + CONST_S("What do you want to take from ") + + ContainerName + '?', + REMEMBER_SELECTED); + + if(ToTake.empty()) + break; + + if(!IsOnGround() || !Room + || Room->PickupItem(Opener, ToTake[0], ToTake.size())) + { + for(uint c = 0; c < ToTake.size(); ++c) + ToTake[c]->MoveTo(Opener->GetStack()); + + ADD_MESSAGE("You take %s from %s.", + ToTake[0]->GetName(DEFINITE, ToTake.size()).CStr(), + ContainerName.CStr()); + Success = true; + } + } + + return Success; +} + +/* GUI used for instance by chests and bookcases (use ContainerID == 0 if + the container isn't an item). Returns whether anything was done. */ + +truth stack::PutSomethingIn(character* Opener, cfestring& ContainerName, + long StorageVolume, ulong ContainerID) +{ + if(!Opener->GetStack()->GetItems()) + { + ADD_MESSAGE("You have nothing to put in %s.", ContainerName.CStr()); + return false; + } + + truth Success = false; + room* Room = GetLSquareUnder()->GetRoom(); + SetSelected(0); + + for(;;) + { + itemvector ToPut; + game::DrawEverythingNoBlit(); + Opener->GetStack()->DrawContents(ToPut, Opener, + CONST_S("What do you want to put in ") + + ContainerName + '?', + REMEMBER_SELECTED); + + if(ToPut.empty()) + break; + + if(ToPut[0]->GetID() == ContainerID) + { + ADD_MESSAGE("You can't put %s inside itself!", ContainerName.CStr()); + continue; + } + + uint Amount = Min((StorageVolume - GetVolume()) + / ToPut[0]->GetVolume(), + ToPut.size()); + + if(!Amount) + { + if(ToPut.size() == 1) + ADD_MESSAGE("%s doesn't fit in %s.", + ToPut[0]->CHAR_NAME(DEFINITE), + ContainerName.CStr()); + else + ADD_MESSAGE("None of the %d %s fit in %s.", int(ToPut.size()), + ToPut[0]->CHAR_NAME(PLURAL), ContainerName.CStr()); + + continue; + } + + if(Amount != ToPut.size()) + ADD_MESSAGE("Only %d of the %d %s fit%s in %s.", Amount, + int(ToPut.size()), ToPut[0]->CHAR_NAME(PLURAL), + Amount == 1 ? "s" : "", ContainerName.CStr()); + + if(!IsOnGround() || !Room || Room->DropItem(Opener, ToPut[0], Amount)) + { + for(uint c = 0; c < Amount; ++c) + ToPut[c]->MoveTo(this); + + ADD_MESSAGE("You put %s in %s.", + ToPut[0]->GetName(DEFINITE, Amount).CStr(), + ContainerName.CStr()); + Success = true; + } + } + + return Success; +} + +truth stack::IsOnGround() const +{ + return !MotherEntity || MotherEntity->IsOnGround(); +} + +int stack::GetSpoiledItems() const +{ + int Counter = 0; + + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + Counter += (i->GetSpoilLevel() > 0); // even though this is pretty unclear, it isn't mine but Hex's + + return Counter; +} + +/* Adds all items and recursively their contents + which satisfy the sorter to ItemVector */ + +void stack::SortAllItems(const sortdata& SortData) const +{ + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + i->SortAllItems(SortData); +} + +/* Search for traps and other secret items */ + +void stack::Search(ccharacter* Char, int Perception) +{ + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + i->Search(Char, Perception); +} + +/* Used to determine whether the danger symbol should be shown */ + +truth stack::NeedDangerSymbol(ccharacter* Viewer) const +{ + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + if(i->NeedDangerSymbol() && i->CanBeSeenBy(Viewer)) + return true; + + return false; +} + +void stack::PreProcessForBone() +{ + if(Items) + { + itemvector ItemVector; + FillItemVector(ItemVector); + + for(uint c = 0; c < ItemVector.size(); ++c) + ItemVector[c]->PreProcessForBone(); + } +} + +void stack::PostProcessForBone() +{ + if(Items) + { + itemvector ItemVector; + FillItemVector(ItemVector); + + for(uint c = 0; c < ItemVector.size(); ++c) + ItemVector[c]->PostProcessForBone(); + } +} + +void stack::FinalProcessForBone() +{ + /* Items can't be removed during the final processing stage */ + + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + i->FinalProcessForBone(); +} + +/* VolumeModifier increases the spilled liquid's volume. + Note that the original liquid isn't placed anywhere nor deleted, + but its volume is decreased (possibly to zero). */ + +void stack::SpillFluid(character* Spiller, liquid* Liquid, long VolumeModifier) +{ + if(!Items) + return; + + double ChanceMultiplier = 1. / (300 + sqrt(Volume)); + itemvector ItemVector; + FillItemVector(ItemVector); + + for(int c = ItemVector.size() - 1; c >= 0; --c) + if(ItemVector[c]->Exists() && ItemVector[c]->AllowFluids()) + { + long ItemVolume = ItemVector[c]->GetVolume(); + double Root = sqrt(ItemVolume); + + if(Root > RAND() % 200 || Root > RAND() % 200) + { + long SpillVolume = long(VolumeModifier * Root * ChanceMultiplier); + + if(SpillVolume) + { + Liquid->EditVolume(-Max(SpillVolume, Liquid->GetVolume())); + ItemVector[c]->SpillFluid(Spiller, + Liquid->SpawnMoreLiquid(SpillVolume), + ItemVector[c]->GetSquareIndex(GetPos())); + + if(!Liquid->GetVolume()) + return; + } + } + } +} + +void stack::AddItems(const itemvector& ItemVector) +{ + for(uint c = 0; c < ItemVector.size(); ++c) + AddItem(ItemVector[c]); +} + +void stack::MoveItemsTo(itemvector& ToVector, int RequiredSquarePosition) +{ + itemvector ItemVector; + FillItemVector(ItemVector); + + for(uint c = 0; c < ItemVector.size(); ++c) + if(ItemVector[c]->GetSquarePosition() == RequiredSquarePosition) + { + ItemVector[c]->RemoveFromSlot(); + ToVector.push_back(ItemVector[c]); + } +} + +void stack::DropSideItems() +{ + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + { + int SquarePosition = i->GetSquarePosition(); + + if(SquarePosition != CENTER) + { + if(i->IsAnimated()) + { + lsquare* Square = GetLSquareTrulyUnder(SquarePosition); + + if(Square) + Square->DecStaticAnimatedEntities(); + + GetLSquareUnder()->IncStaticAnimatedEntities(); + } + + i->SignalSquarePositionChange(CENTER); + SignalEmitationDecrease(SquarePosition, i->GetEmitation()); + SignalEmitationIncrease(CENTER, i->GetEmitation()); + } + } +} + +truth stack::AllowDamage(int Direction, int SquarePosition) +{ + if(SquarePosition == CENTER) + return true; + + switch(Direction) + { + case 0: return SquarePosition == DOWN || SquarePosition == RIGHT; + case 1: return SquarePosition == DOWN; + case 2: return SquarePosition == DOWN || SquarePosition == LEFT; + case 3: return SquarePosition == RIGHT; + case 4: return SquarePosition == LEFT; + case 5: return SquarePosition == UP || SquarePosition == RIGHT; + case 6: return SquarePosition == UP; + case 7: return SquarePosition == UP || SquarePosition == LEFT; + } + + return true; +} + +long stack::GetWeight(ccharacter* Viewer, int SquarePosition) const +{ + long Weight = 0; + + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + if(i->GetSquarePosition() == SquarePosition + && ((Flags & HIDDEN) || i->CanBeSeenBy(Viewer))) + Weight += i->GetWeight(); + + return Weight; +} + +truth stack::DetectMaterial(cmaterial* Material) const +{ + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + if(i->DetectMaterial(Material)) + return true; + + return false; +} + +void stack::SetLifeExpectancy(int Base, int RandPlus) +{ + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + i->SetLifeExpectancy(Base, RandPlus); +} + +truth stack::Necromancy(character* Necromancer) +{ + itemvector ItemVector; + FillItemVector(ItemVector); + + for(uint c = 0; c < ItemVector.size(); ++c) + if(ItemVector[c]->Necromancy(Necromancer)) + return true; + + return false; +} + +void stack::CalculateEnchantments() +{ + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + i->CalculateEnchantment(); +} + +ccharacter* stack::FindCarrier() const +{ + return MotherEntity ? MotherEntity->FindCarrier() : 0; +} + +void stack::Haste() +{ + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + i->Haste(); +} + +void stack::Slow() +{ + for(stackiterator i = GetBottom(); i.HasItem(); ++i) + i->Slow(); +} diff --git a/Main/Source/team.cpp b/Main/Source/team.cpp new file mode 100644 index 0000000..a99775b --- /dev/null +++ b/Main/Source/team.cpp @@ -0,0 +1,174 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through charset.cpp */ + +team::team() : Leader(0) { } +team::team(ulong ID) : Leader(0), ID(ID), KillEvilness(0) { } +std::list::iterator team::Add(character* Char) { return Member.insert(Member.end(), Char); } + +void team::SetRelation(team* AnotherTeam, int Relation) +{ + this->Relation[AnotherTeam->ID] = AnotherTeam->Relation[ID] = Relation; +} + +int team::GetRelation(const team* AnotherTeam) const +{ + if(AnotherTeam != this) + { + std::map::const_iterator Iterator = Relation.find(AnotherTeam->ID); + + if(Iterator != Relation.end()) + return Iterator->second; + else + ABORT("Team %lu dismissed!", AnotherTeam->ID); + } + + return FRIEND; +} + +void team::Hostility(team* Enemy) +{ + /* We're testing if the game works better this way... */ + + if(ID != PLAYER_TEAM) + return; + + if(this != Enemy && GetRelation(Enemy) != HOSTILE) + { + if(ID == PLAYER_TEAM && game::IsSumoWrestling()) + game::EndSumoWrestling(DISQUALIFIED); + + /* This is a gum solution. The behaviour should come from the script. */ + + /*if(ID == COLONIST_TEAM && Enemy->ID == NEW_ATTNAM_TEAM) + return;*/ + + game::Hostility(this, Enemy); + + if(ID == PLAYER_TEAM) + { + if(Enemy->ID == ATTNAM_TEAM) + { + /* This is a gum solution. The message should come from the script. */ + if(PLAYER->CanHear()) + ADD_MESSAGE("You hear an alarm ringing."); + + if(game::GetStoryState() != 2) + { + v2 AngelPos = game::GetPetrus() ? game::GetPetrus()->GetPos() : v2(28, 20); + int Seen = 0; + angel* Angel; + + for(int c = 0; c < 3; ++c) + { + if(!c) + Angel = archangel::Spawn(VALPURUS); + else + Angel = angel::Spawn(VALPURUS); + + v2 Where = game::GetCurrentLevel()->GetNearestFreeSquare(Angel, AngelPos); + + if(Where == ERROR_V2) + Where = game::GetCurrentLevel()->GetRandomSquare(Angel); + + Angel->SetTeam(Enemy); + Angel->PutTo(Where); + + if(Angel->CanBeSeenByPlayer()) + ++Seen; + } + + if(Seen == 1) + ADD_MESSAGE("%s materializes.", Angel->CHAR_NAME(INDEFINITE)); + else if(Seen == 2) + ADD_MESSAGE("Two %s materialize.", Angel->CHAR_NAME(PLURAL)); + else if(Seen == 3) + ADD_MESSAGE("Three %s materialize.", Angel->CHAR_NAME(PLURAL)); + + ADD_MESSAGE("\"We will defend the Holy Order!\""); + } + } + + ADD_MESSAGE("You have a feeling this wasn't a good idea..."); + } + + SetRelation(Enemy, HOSTILE); + } +} + +void team::Save(outputfile& SaveFile) const +{ + SaveFile << ID << Relation << KillEvilness; +} + +void team::Load(inputfile& SaveFile) +{ + SaveFile >> ID >> Relation >> KillEvilness; +} + +truth team::HasEnemy() const +{ + for(int c = 0; c < game::GetTeams(); ++c) + if(!game::GetTeam(c)->GetMember().empty() && GetRelation(game::GetTeam(c)) == HOSTILE) + return true; + + return false; +} + +int team::GetEnabledMembers() const +{ + int Amount = 0; + + for(std::list::const_iterator i = Member.begin(); i != Member.end(); ++i) + if((*i)->IsEnabled()) + ++Amount; + + return Amount; +} + +outputfile& operator<<(outputfile& SaveFile, const team* Team) +{ + Team->Save(SaveFile); + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, team*& Team) +{ + Team = new team; + Team->Load(SaveFile); + return SaveFile; +} + +void team::MoveMembersTo(charactervector& CVector) +{ + for(std::list::iterator i = Member.begin(); i != Member.end(); ++i) + if((*i)->IsEnabled()) + { + if((*i)->GetAction() && (*i)->GetAction()->IsVoluntary()) + (*i)->GetAction()->Terminate(false); + + if(!(*i)->GetAction()) + { + CVector.push_back(*i); + (*i)->Remove(); + } + } +} + +void team::Remove(std::list::iterator Iterator) +{ + if(*Iterator == Leader) + Leader = 0; + + Member.erase(Iterator); +} diff --git a/Main/Source/terra.cpp b/Main/Source/terra.cpp new file mode 100644 index 0000000..a352f38 --- /dev/null +++ b/Main/Source/terra.cpp @@ -0,0 +1,19 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through areaset.cpp */ + +cchar* terrain::SurviveMessage() const { return "somehow you survive"; } +cchar* terrain::MonsterSurviveMessage() const { return "somehow survives"; } +cchar* terrain::DeathMessage() const { return "strangely enough, you die"; } +cchar* terrain::MonsterDeathVerb() const { return "dies"; } +cchar* terrain::ScoreEntry() const { return "died on unfriendly terrain"; } diff --git a/Main/Source/trap.cpp b/Main/Source/trap.cpp new file mode 100644 index 0000000..3e23c5e --- /dev/null +++ b/Main/Source/trap.cpp @@ -0,0 +1,121 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through trapset.cpp */ + +trapprototype::trapprototype(trapspawner Spawner, cchar* ClassID) : Spawner(Spawner), ClassID(ClassID) { Index = protocontainer::Add(this); } + +trap::trap() : entity(HAS_BE), Next(0) { } +square* trap::GetSquareUnderEntity(int) const { return LSquareUnder; } + +trap::~trap() +{ +} + +void trap::Save(outputfile& SaveFile) const +{ + SaveFile << (ushort)GetType(); +} + +void trap::Load(inputfile&) +{ + LSquareUnder = static_cast(game::GetSquareInLoad()); +} + +outputfile& operator<<(outputfile& SaveFile, const trapdata* Data) +{ + SaveFile << *Data; + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, trapdata*& Data) +{ + Data = new trapdata; + SaveFile >> *Data; + return SaveFile; +} + +outputfile& operator<<(outputfile& SaveFile, const trapdata& Data) +{ + SaveFile << Data.TrapID << Data.VictimID << Data.BodyParts; + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, trapdata& Data) +{ + SaveFile >> Data.TrapID >> Data.VictimID >> Data.BodyParts; + return SaveFile; +} + +trap* trapprototype::SpawnAndLoad(inputfile& SaveFile) const +{ + trap* Trap = Spawner(); + Trap->Load(SaveFile); + return Trap; +} + +void itemtrapbase::Load(inputfile& SaveFile) +{ + SaveFile >> Active >> Team >> DiscoveredByTeam; +} + +void itemtrapbase::Save(outputfile& SaveFile) const +{ + SaveFile << Active << Team << DiscoveredByTeam; +} + +truth itemtrapbase::CanBeSeenBy(ccharacter* Viewer) const +{ + int ViewerTeam = Viewer->GetTeam()->GetID(); + return !Active || ViewerTeam == Team || DiscoveredByTeam.find(ViewerTeam) != DiscoveredByTeam.end(); +} + +void itemtrapbase::Search(ccharacter* Char, int Perception) +{ + int ViewerTeam = Char->GetTeam()->GetID(); + + if(Active && ViewerTeam != Team && DiscoveredByTeam.find(ViewerTeam) == DiscoveredByTeam.end() && !RAND_N(200 / Perception)) + { + DiscoveredByTeam.insert(ViewerTeam); + SendNewDrawAndMemorizedUpdateRequest(); + + if(Char->IsPlayer()) + { + game::AskForKeyPress(CONST_S("Trap found! [press any key to continue]")); + ADD_MESSAGE("You find %s.", CHAR_NAME(INDEFINITE)); + } + } +} + +void itemtrapbase::SetIsActive(truth What) +{ + Active = What; + UpdatePictures(); + DiscoveredByTeam.clear(); +} + +void itemtrapbase::FinalProcessForBone() +{ + if(Team == PLAYER_TEAM) + Team = MONSTER_TEAM; + + std::set::iterator i = DiscoveredByTeam.find(PLAYER_TEAM); + + if(i != DiscoveredByTeam.end()) + DiscoveredByTeam.erase(i); +} + +void itemtrapbase::TeleportRandomly() +{ + Team = NO_TEAM; + DiscoveredByTeam.clear(); +} diff --git a/Main/Source/traps.cpp b/Main/Source/traps.cpp new file mode 100644 index 0000000..8e17705 --- /dev/null +++ b/Main/Source/traps.cpp @@ -0,0 +1,219 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through trapset.cpp */ + +web::web() +{ + if(!game::IsLoading()) + { + TrapData.TrapID = game::CreateNewTrapID(this); + TrapData.VictimID = 0; + Picture = new bitmap(TILE_V2, TRANSPARENT_COLOR); + bitmap Temp(TILE_V2, TRANSPARENT_COLOR); + Temp.ActivateFastFlag(); + packcol16 Color = MakeRGB16(250, 250, 250); + const rawbitmap* Effect = igraph::GetRawGraphic(GR_EFFECT); + Effect->MaskedBlit(&Temp, v2(RAND_2 ? 64 : 80, 32), + ZERO_V2, TILE_V2, &Color); + Temp.NormalBlit(Picture, Flags); + } +} + +web::~web() +{ + game::RemoveTrapID(TrapData.TrapID); +} + +truth web::TryToUnStick(character* Victim, v2) +{ + ulong TrapID = GetTrapID(); + int Modifier = 7 * GetTrapBaseModifier() + / Max(Victim->GetAttribute(DEXTERITY) + + Victim->GetAttribute(ARM_STRENGTH), 1); + + if(!RAND_N(Max(Modifier, 2))) + { + Victim->RemoveTrap(TrapID); + TrapData.VictimID = 0; + + if(Victim->IsPlayer()) + ADD_MESSAGE("You manage to free yourself from the web."); + else if(Victim->CanBeSeenByPlayer()) + ADD_MESSAGE("%s manages to free %sself from the web.", + Victim->CHAR_NAME(DEFINITE), Victim->CHAR_OBJECT_PRONOUN); + + Victim->EditAP(-500); + return true; + } + + if(!RAND_N(Max(Modifier << 1, 2))) + { + Victim->RemoveTrap(TrapID); + TrapData.VictimID = 0; + GetLSquareUnder()->RemoveTrap(this); + SendToHell(); + + if(Victim->IsPlayer()) + ADD_MESSAGE("You tear the web down."); + else if(Victim->CanBeSeenByPlayer()) + ADD_MESSAGE("%s tears the web down.", Victim->CHAR_NAME(DEFINITE)); + + Victim->EditAP(-500); + return true; + } + + Modifier = GetTrapBaseModifier() + * (Victim->GetAttribute(DEXTERITY) + + Victim->GetAttribute(ARM_STRENGTH)) / 75; + + if(Victim->CanChokeOnWeb(this) && !RAND_N(Max(Modifier << 3, 2))) + { + if(Victim->IsPlayer()) + ADD_MESSAGE("You manage to choke yourself on the web."); + else if(Victim->CanBeSeenByPlayer()) + ADD_MESSAGE("%s chokes %sself on the web.", + Victim->CHAR_NAME(DEFINITE), Victim->CHAR_OBJECT_PRONOUN); + + Victim->LoseConsciousness(250 + RAND_N(250)); + Victim->EditAP(-1000); + return true; + } + + if(!RAND_N(Max(Modifier, 2))) + { + int VictimBodyPart = Victim->GetRandomBodyPart(ALL_BODYPART_FLAGS + &~TrapData.BodyParts); + + if(VictimBodyPart != NONE_INDEX) + { + TrapData.BodyParts |= 1 << VictimBodyPart; + Victim->AddTrap(GetTrapID(), 1 << VictimBodyPart); + + if(Victim->IsPlayer()) + ADD_MESSAGE("You fail to free yourself from the web " + "and your %s is stuck in it in the attempt.", + Victim->GetBodyPartName(VictimBodyPart).CStr()); + else if(Victim->CanBeSeenByPlayer()) + ADD_MESSAGE("%s tries to free %sself from the web " + "but is stuck more tightly in it in the attempt.", + Victim->CHAR_NAME(DEFINITE), + Victim->CHAR_OBJECT_PRONOUN); + + Victim->EditAP(-1000); + return true; + } + } + + if(Victim->IsPlayer()) + ADD_MESSAGE("You are unable to escape from the web."); + + Victim->EditAP(-1000); + return false; +} + +void web::Save(outputfile& SaveFile) const +{ + trap::Save(SaveFile); + SaveFile << TrapData << Strength << Picture; +} + +void web::Load(inputfile& SaveFile) +{ + trap::Load(SaveFile); + SaveFile >> TrapData >> Strength >> Picture; + game::AddTrapID(this, TrapData.TrapID); +} + +void web::StepOnEffect(character* Stepper) +{ + if(Stepper->IsImmuneToStickiness()) + return; + + int StepperBodyPart = Stepper->GetRandomBodyPart(); + + if(StepperBodyPart == NONE_INDEX) + return; + + TrapData.VictimID = Stepper->GetID(); + TrapData.BodyParts = 1 << StepperBodyPart; + Stepper->AddTrap(GetTrapID(), 1 << StepperBodyPart); + + if(Stepper->IsPlayer()) + ADD_MESSAGE("You try to step through the web " + "but your %s sticks in it.", + Stepper->GetBodyPartName(StepperBodyPart).CStr()); + else if(Stepper->CanBeSeenByPlayer()) + ADD_MESSAGE("%s gets stuck in the web.", Stepper->CHAR_NAME(DEFINITE)); +} + +void web::AddDescription(festring& Msg) const +{ + Msg << ". A web envelops the square"; +} + +void web::AddTrapName(festring& String, int) const +{ + String << "a spider web"; +} + +void web::Draw(blitdata& BlitData) const +{ + Picture->LuminanceMaskedBlit(BlitData); +} + +truth web::IsStuckToBodyPart(int I) const +{ + return 1 << I & TrapData.BodyParts; +} + +void web::ReceiveDamage(character*, int, int Type, int) +{ + if(Type & (ACID|FIRE|ELECTRICITY|ENERGY)) + Destroy(); +} + +void web::Destroy() +{ + Untrap(); + GetLSquareUnder()->RemoveTrap(this); + SendToHell(); +} + +truth web::CanBeSeenBy(ccharacter* Who) const +{ + return (GetLSquareUnder()->CanBeSeenBy(Who) + && Who->GetAttribute(WISDOM) > 4); +} + +void web::PreProcessForBone() +{ + trap::PreProcessForBone(); + game::RemoveTrapID(TrapData.TrapID); + TrapData.TrapID = 0; +} + +void web::PostProcessForBone() +{ + trap::PostProcessForBone(); + TrapData.TrapID = game::CreateNewTrapID(this); +} + +void web::Untrap() +{ + character* Char = game::SearchCharacter(GetVictimID()); + + if(Char) + Char->RemoveTrap(GetTrapID()); + + TrapData.VictimID = 0; +} diff --git a/Main/Source/trapset.cpp b/Main/Source/trapset.cpp new file mode 100644 index 0000000..f336d6a --- /dev/null +++ b/Main/Source/trapset.cpp @@ -0,0 +1,36 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#define __FILE_OF_STATIC_TRAP_PROTOTYPE_DEFINITIONS__ + +#include "trap.h" +#include "proto.h" + +SYSTEM_SPECIALIZATIONS(trap) + +#include "traps.h" + +#undef __FILE_OF_STATIC_TRAP_PROTOTYPE_DEFINITIONS__ + +#include "lsquare.h" +#include "game.h" +#include "char.h" +#include "message.h" +#include "lsquare.h" +#include "bitmap.h" +#include "rawbit.h" +#include "igraph.h" +#include "felibdef.h" +#include "team.h" + +#include "trap.cpp" +#include "traps.cpp" diff --git a/Main/Source/wmapset.cpp b/Main/Source/wmapset.cpp new file mode 100644 index 0000000..e8e1b97 --- /dev/null +++ b/Main/Source/wmapset.cpp @@ -0,0 +1,53 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#define __FILE_OF_STATIC_WTERRAIN_PROTOTYPE_DEFINITIONS__ + +#include "proto.h" +#include "wterra.h" + +SYSTEM_SPECIALIZATIONS(gwterrain) SYSTEM_SPECIALIZATIONS(owterrain) + +#include "wterras.h" + +#undef __FILE_OF_STATIC_WTERRAIN_PROTOTYPE_DEFINITIONS__ + +cint OceanType = ocean::ProtoType.GetIndex(); +cint SnowType = snow::ProtoType.GetIndex(); +cint GlacierType = glacier::ProtoType.GetIndex(); +cint EGForestType = evergreenforest::ProtoType.GetIndex(); +cint LForestType = leafyforest::ProtoType.GetIndex(); +cint SteppeType = steppe::ProtoType.GetIndex(); +cint DesertType = desert::ProtoType.GetIndex(); +cint JungleType = jungle::ProtoType.GetIndex(); + +#include + +#include "allocate.h" +#include "char.h" +#include "cont.h" +#include "game.h" +#include "cont.h" +#include "femath.h" +#include "iconf.h" +#include "graphics.h" +#include "whandler.h" +#include "message.h" +#include "igraph.h" +#include "bitmap.h" +#include "save.h" + +#include "cont.cpp" +#include "worldmap.cpp" +#include "wsquare.cpp" +#include "wterra.cpp" +#include "wterras.cpp" diff --git a/Main/Source/worldmap.cpp b/Main/Source/worldmap.cpp new file mode 100644 index 0000000..2f57c30 --- /dev/null +++ b/Main/Source/worldmap.cpp @@ -0,0 +1,724 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through wmapset.cpp */ + +#define MAX_TEMPERATURE 27 //increase for a warmer world +#define LATITUDE_EFFECT 40 //increase for more effect +#define ALTITUDE_EFFECT 0.02 + +#define COLD 10 +#define MEDIUM 12 +#define WARM 17 +#define HOT 19 + +int DirX[8] = { -1, -1, -1, 0, 0, 1, 1, 1 }; +int DirY[8] = { -1, 0, 1, -1, 1, -1, 0, 1 }; + +worldmap::worldmap() { } +continent* worldmap::GetContinentUnder(v2 Pos) const +{ return Continent[ContinentBuffer[Pos.X][Pos.Y]]; } +v2 worldmap::GetEntryPos(ccharacter*, int I) const +{ return EntryMap.find(I)->second; } +continent* worldmap::GetContinent(int I) const { return Continent[I]; } +int worldmap::GetAltitude(v2 Pos) { return AltitudeBuffer[Pos.X][Pos.Y]; } +charactervector& worldmap::GetPlayerGroup() { return PlayerGroup; } +character* worldmap::GetPlayerGroupMember(int c) { return PlayerGroup[c]; } + +worldmap::worldmap(int XSize, int YSize) : area(XSize, YSize) +{ + Map = reinterpret_cast(area::Map); + + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + { + Map[x][y] = new wsquare(this, v2(x, y)); + Map[x][y]->SetGWTerrain(ocean::Spawn()); + } + + Alloc2D(TypeBuffer, XSize, YSize); + Alloc2D(AltitudeBuffer, XSize, YSize); + Alloc2D(ContinentBuffer, XSize, YSize); + continent::TypeBuffer = TypeBuffer; + continent::AltitudeBuffer = AltitudeBuffer; + continent::ContinentBuffer = ContinentBuffer; +} + +worldmap::~worldmap() +{ + delete [] TypeBuffer; + delete [] AltitudeBuffer; + delete [] ContinentBuffer; + + uint c; + + for(c = 1; c < Continent.size(); ++c) + delete Continent[c]; + + for(c = 0; c < PlayerGroup.size(); ++c) + delete PlayerGroup[c]; +} + +void worldmap::Save(outputfile& SaveFile) const +{ + area::Save(SaveFile); + SaveFile.Write(reinterpret_cast(TypeBuffer[0]), + XSizeTimesYSize * sizeof(uchar)); + SaveFile.Write(reinterpret_cast(AltitudeBuffer[0]), + XSizeTimesYSize * sizeof(short)); + SaveFile.Write(reinterpret_cast(ContinentBuffer[0]), + XSizeTimesYSize * sizeof(uchar)); + + for(ulong c = 0; c < XSizeTimesYSize; ++c) + Map[0][c]->Save(SaveFile); + + SaveFile << Continent << PlayerGroup; +} + +void worldmap::Load(inputfile& SaveFile) +{ + area::Load(SaveFile); + Map = reinterpret_cast(area::Map); + Alloc2D(TypeBuffer, XSize, YSize); + Alloc2D(AltitudeBuffer, XSize, YSize); + Alloc2D(ContinentBuffer, XSize, YSize); + SaveFile.Read(reinterpret_cast(TypeBuffer[0]), + XSizeTimesYSize * sizeof(uchar)); + SaveFile.Read(reinterpret_cast(AltitudeBuffer[0]), + XSizeTimesYSize * sizeof(short)); + SaveFile.Read(reinterpret_cast(ContinentBuffer[0]), + XSizeTimesYSize * sizeof(uchar)); + continent::TypeBuffer = TypeBuffer; + continent::AltitudeBuffer = AltitudeBuffer; + continent::ContinentBuffer = ContinentBuffer; + int x, y; + + for(x = 0; x < XSize; ++x) + for(y = 0; y < YSize; ++y) + Map[x][y] = new wsquare(this, v2(x, y)); + + for(x = 0; x < XSize; ++x) + for(y = 0; y < YSize; ++y) + { + game::SetSquareInLoad(Map[x][y]); + Map[x][y]->Load(SaveFile); + } + + CalculateNeighbourBitmapPoses(); + SaveFile >> Continent >> PlayerGroup; +} + +void worldmap::Generate() +{ + Alloc2D(OldAltitudeBuffer, XSize, YSize); + Alloc2D(OldTypeBuffer, XSize, YSize); + + for(;;) + { + RandomizeAltitude(); + SmoothAltitude(); + GenerateClimate(); + SmoothClimate(); + CalculateContinents(); + std::vector PerfectForAttnam, PerfectForNewAttnam; + + for(uint c = 1; c < Continent.size(); ++c) + if(Continent[c]->GetSize() > 25 && Continent[c]->GetSize() < 700 // 1000 is the previous value. It was lowered to make continents smaller + && Continent[c]->GetGTerrainAmount(EGForestType) + && Continent[c]->GetGTerrainAmount(SnowType) + && Continent[c]->GetGTerrainAmount(SteppeType) + && Continent[c]->GetGTerrainAmount(LForestType)) + PerfectForAttnam.push_back(Continent[c]); + + if(!PerfectForAttnam.size()) + continue; + + v2 AttnamPos, ElpuriCavePos, NewAttnamPos, TunnelEntry, TunnelExit, MondedrPos, DragonTowerPos, DarkForestPos, MuntuoPos, XinrochTombPos; + truth Correct = false; + continent* PetrusLikes; + + for(int c1 = 0; c1 < 25; ++c1) + { + game::BusyAnimation(); + PetrusLikes = PerfectForAttnam[RAND() % PerfectForAttnam.size()]; + AttnamPos = PetrusLikes->GetRandomMember(EGForestType); + ElpuriCavePos = PetrusLikes->GetRandomMember(SnowType); + MondedrPos = PetrusLikes->GetRandomMember(SteppeType); + DragonTowerPos = PetrusLikes->GetRandomMember(SteppeType); + DarkForestPos = PetrusLikes->GetRandomMember(LForestType); + MuntuoPos = PetrusLikes->GetRandomMember(LForestType); + XinrochTombPos = PetrusLikes->GetRandomMember(SnowType); + + for(int c2 = 1; c2 < 50; ++c2) + { + TunnelExit = PetrusLikes->GetMember(RAND() % PetrusLikes->GetSize()); + + if(AttnamPos != TunnelExit && ElpuriCavePos != TunnelExit) + { + for(int d1 = 0; d1 < 8; ++d1) + { + v2 Pos = TunnelExit + game::GetMoveVector(d1); + + if(IsValidPos(Pos) && AltitudeBuffer[Pos.X][Pos.Y] <= 0) + { + int Distance = 3 + (RAND() & 3); + truth Error = false; + TunnelEntry = Pos; + + for(int c2 = 0; c2 < Distance; ++c2) + { + TunnelEntry += game::GetMoveVector(d1); + + if(!IsValidPos(TunnelEntry) + || AltitudeBuffer[TunnelEntry.X][TunnelEntry.Y] > 0) + { + Error = true; + break; + } + } + + if(Error) + continue; + + int x, y; + int Counter = 0; + + for(x = TunnelEntry.X - 3; x <= TunnelEntry.X + 3; ++x) + { + for(y = TunnelEntry.Y - 3; y <= TunnelEntry.Y + 3; + ++y, ++Counter) + if(Counter != 0 && Counter != 6 + && Counter != 42 && Counter != 48 + && (!IsValidPos(x, y) + || AltitudeBuffer[x][y] > 0 + || AltitudeBuffer[x][y] < -350)) + { + Error = true; + break; + } + + if(Error) + break; + } + + if(Error) + continue; + + Error = true; + + for(x = 0; x < XSize; ++x) + if(TypeBuffer[x][TunnelEntry.Y] == JungleType) + { + Error = false; + break; + } + + if(Error) + continue; + + Counter = 0; + + for(x = TunnelEntry.X - 2; x <= TunnelEntry.X + 2; ++x) + for(y = TunnelEntry.Y - 2; y <= TunnelEntry.Y + 2; + ++y, ++Counter) + if(Counter != 0 && Counter != 4 + && Counter != 20 && Counter != 24) + AltitudeBuffer[x][y] /= 2; + + AltitudeBuffer[TunnelEntry.X][TunnelEntry.Y] = 1 + RAND() % 50; + TypeBuffer[TunnelEntry.X][TunnelEntry.Y] = JungleType; + GetWSquare(TunnelEntry)->ChangeGWTerrain(jungle::Spawn()); + int NewAttnamIndex; + + for(NewAttnamIndex = RAND() & 7; + NewAttnamIndex == 7 - d1; + NewAttnamIndex = RAND() & 7); + + NewAttnamPos = TunnelEntry + + game::GetMoveVector(NewAttnamIndex); + static int DiagonalDir[4] = { 0, 2, 5, 7 }; + static int NotDiagonalDir[4] = { 1, 3, 4, 6 }; + static int AdjacentDir[4][2] = { { 0, 1 }, { 0, 2 }, + { 1, 3 }, { 2, 3 } }; + truth Raised[] = { false, false, false, false }; + int d2; + + for(d2 = 0; d2 < 4; ++d2) + if(NotDiagonalDir[d2] != 7 - d1 + && (NotDiagonalDir[d2] == NewAttnamIndex + || !(RAND() & 2))) + { + v2 Pos = TunnelEntry + + game::GetMoveVector(NotDiagonalDir[d2]); + AltitudeBuffer[Pos.X][Pos.Y] = 1 + RAND() % 50; + TypeBuffer[Pos.X][Pos.Y] = JungleType; + GetWSquare(Pos)->ChangeGWTerrain(jungle::Spawn()); + Raised[d2] = true; + } + + for(d2 = 0; d2 < 4; ++d2) + if(DiagonalDir[d2] != 7 - d1 + && (DiagonalDir[d2] == NewAttnamIndex + || (Raised[AdjacentDir[d2][0]] + && Raised[AdjacentDir[d2][1]] && !(RAND() & 2)))) + { + v2 Pos = TunnelEntry + + game::GetMoveVector(DiagonalDir[d2]); + AltitudeBuffer[Pos.X][Pos.Y] = 1 + RAND() % 50; + TypeBuffer[Pos.X][Pos.Y] = JungleType; + GetWSquare(Pos)->ChangeGWTerrain(jungle::Spawn()); + } + + Correct = true; + break; + } + } + + if(Correct) + break; + } + } + + if(Correct) + break; + } + + if(!Correct) + continue; + + GetWSquare(AttnamPos)->ChangeOWTerrain(attnam::Spawn()); + SetEntryPos(ATTNAM, AttnamPos); + RevealEnvironment(AttnamPos, 1); + GetWSquare(NewAttnamPos)->ChangeOWTerrain(newattnam::Spawn()); + SetEntryPos(NEW_ATTNAM, NewAttnamPos); + SetEntryPos(ELPURI_CAVE, ElpuriCavePos); + SetEntryPos(MONDEDR, MondedrPos); + GetWSquare(TunnelEntry)->ChangeOWTerrain(underwatertunnel::Spawn()); + SetEntryPos(UNDER_WATER_TUNNEL, TunnelEntry); + GetWSquare(TunnelExit)->ChangeOWTerrain(underwatertunnelexit::Spawn()); + SetEntryPos(UNDER_WATER_TUNNEL_EXIT, TunnelExit); + GetWSquare(DragonTowerPos)->ChangeOWTerrain(dragontower::Spawn()); + SetEntryPos(DRAGON_TOWER, DragonTowerPos); + GetWSquare(DarkForestPos)->ChangeOWTerrain(darkforest::Spawn()); + SetEntryPos(DARK_FOREST, DarkForestPos); + GetWSquare(MuntuoPos)->ChangeOWTerrain(muntuo::Spawn()); + SetEntryPos(MUNTUO, MuntuoPos); + SetEntryPos(XINROCH_TOMB, XinrochTombPos); + PLAYER->PutTo(NewAttnamPos); + CalculateLuminances(); + CalculateNeighbourBitmapPoses(); + break; + } + + delete [] OldAltitudeBuffer; + delete [] OldTypeBuffer; +} + +void worldmap::RandomizeAltitude() +{ + game::BusyAnimation(); + + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + AltitudeBuffer[x][y] = 4000 - RAND() % 8000; +} + +void worldmap::SmoothAltitude() +{ + for(int c = 0; c < 10; ++c) + { + game::BusyAnimation(); + int x, y; + + for(y = 0; y < YSize; ++y) + SafeSmooth(0, y); + + for(x = 1; x < XSize - 1; ++x) + { + SafeSmooth(x, 0); + + for(y = 1; y < YSize - 1; ++y) + FastSmooth(x, y); + + SafeSmooth(x, YSize - 1); + } + + for(y = 0; y < YSize; ++y) + SafeSmooth(XSize - 1, y); + } +} + +void worldmap::FastSmooth(int x, int y) +{ + long HeightNear = 0; + int d; + + for(d = 0; d < 4; ++d) + HeightNear += OldAltitudeBuffer[x + DirX[d]][y + DirY[d]]; + + for(d = 4; d < 8; ++d) + HeightNear += AltitudeBuffer[x + DirX[d]][y + DirY[d]]; + + OldAltitudeBuffer[x][y] = AltitudeBuffer[x][y]; + AltitudeBuffer[x][y] = HeightNear >> 3; +} + +void worldmap::SafeSmooth(int x, int y) +{ + long HeightNear = 0; + int d, SquaresNear = 0; + + for(d = 0; d < 4; ++d) + { + int X = x + DirX[d]; + int Y = y + DirY[d]; + + if(IsValidPos(X, Y)) + { + HeightNear += OldAltitudeBuffer[X][Y]; + ++SquaresNear; + } + } + + for(d = 4; d < 8; ++d) + { + int X = x + DirX[d]; + int Y = y + DirY[d]; + + if(IsValidPos(X, Y)) + { + HeightNear += AltitudeBuffer[X][Y]; + ++SquaresNear; + } + } + + OldAltitudeBuffer[x][y] = AltitudeBuffer[x][y]; + AltitudeBuffer[x][y] = HeightNear / SquaresNear; +} + +void worldmap::GenerateClimate() +{ + game::BusyAnimation(); + + for(int y = 0; y < YSize; ++y) + { + double DistanceFromEquator = fabs(double(y) / YSize - 0.5); + truth LatitudeRainy = DistanceFromEquator <= 0.05 + || (DistanceFromEquator > 0.25 + && DistanceFromEquator <= 0.45); + + for(int x = 0; x < XSize; ++x) + { + if(AltitudeBuffer[x][y] <= 0) + { + TypeBuffer[x][y] = OceanType; + continue; + } + + truth Rainy = LatitudeRainy; + + if(!Rainy) + for(int d = 0; d < 8; ++d) + { + v2 Pos = v2(x, y) + game::GetMoveVector(d); + + if(IsValidPos(Pos) && AltitudeBuffer[Pos.X][Pos.Y] <= 0) + { + Rainy = true; + break; + } + } + + int Temperature = int(MAX_TEMPERATURE + - DistanceFromEquator * LATITUDE_EFFECT + - AltitudeBuffer[x][y] * ALTITUDE_EFFECT); + int Type = 0; + + if(Temperature <= COLD) + Type = Rainy ? SnowType : GlacierType; + else if(Temperature <= MEDIUM) + Type = Rainy ? EGForestType : SnowType; + else if(Temperature <= WARM) + Type = Rainy ? LForestType : SteppeType; + else if(Temperature <= HOT) + Type = Rainy ? LForestType : DesertType; + else + Type = Rainy ? JungleType : DesertType; + + TypeBuffer[x][y] = Type; + } + } +} + +void worldmap::SmoothClimate() +{ + for(int c = 0; c < 3; ++c) + { + game::BusyAnimation(); + + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + if((OldTypeBuffer[x][y] = TypeBuffer[x][y]) != OceanType) + TypeBuffer[x][y] = WhatTerrainIsMostCommonAroundCurrentTerritorySquareIncludingTheSquareItself(x, y); + } + + game::BusyAnimation(); + + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + Map[x][y]->ChangeGWTerrain + (protocontainer::GetProto(TypeBuffer[x][y])->Spawn()); +} + +/* Evil... */ + +#define ANALYZE_TYPE(type)\ +{\ + int T = type;\ + \ + for(c = 0; c < u; ++c)\ + if(T == UsedType[c])\ + {\ + ++TypeAmount[c];\ + break;\ + }\ + \ + if(c == u)\ + {\ + UsedType[u] = T;\ + TypeAmount[u++] = 1;\ + }\ +} + +int worldmap::WhatTerrainIsMostCommonAroundCurrentTerritorySquareIncludingTheSquareItself(int x, int y) +{ + int UsedType[9]; + int TypeAmount[9]; + int c, d, u = 1; + + UsedType[0] = TypeBuffer[x][y]; + TypeAmount[0] = 1; + + for(d = 0; d < 4; ++d) + { + int X = x + DirX[d]; + int Y = y + DirY[d]; + + if(IsValidPos(X, Y)) + ANALYZE_TYPE(OldTypeBuffer[X][Y]); + } + + for(d = 4; d < 8; ++d) + { + int X = x + DirX[d]; + int Y = y + DirY[d]; + + if(IsValidPos(X, Y)) + ANALYZE_TYPE(TypeBuffer[X][Y]); + } + + int MostCommon = 0; + + for(c = 1; c < u; ++c) + if(TypeAmount[c] > TypeAmount[MostCommon] && UsedType[c] != OceanType) + MostCommon = c; + + return UsedType[MostCommon]; +} + +void worldmap::CalculateContinents() +{ + uint c; + + for(c = 1; c < Continent.size(); ++c) + delete Continent[c]; + + Continent.resize(1, 0); + memset(ContinentBuffer[0], 0, XSizeTimesYSize * sizeof(uchar)); + game::BusyAnimation(); + + for(int x = 0; x < XSize; ++x) + for(int y = 0; y < YSize; ++y) + if(AltitudeBuffer[x][y] > 0) + { + truth Attached = false; + + for(int d = 0; d < 8; ++d) + { + v2 Pos = v2(x, y) + game::GetMoveVector(d); + + if(IsValidPos(Pos)) + { + cint NearCont = ContinentBuffer[Pos.X][Pos.Y]; + + if(NearCont) + { + cint ThisCont = ContinentBuffer[x][y]; + + if(ThisCont) + { + if(ThisCont != NearCont) + if(Continent[ThisCont]->GetSize() + < Continent[NearCont]->GetSize()) + Continent[ThisCont]->AttachTo(Continent[NearCont]); + else + Continent[NearCont]->AttachTo(Continent[ThisCont]); + } + else + Continent[NearCont]->Add(v2(x, y)); + + Attached = true; + } + } + } + + if(!Attached) + { + if(Continent.size() == 255) + { + RemoveEmptyContinents(); + + if(Continent.size() == 255) + ABORT("Valpurus shall not carry more continents!"); + } + + continent* NewContinent = new continent(Continent.size()); + NewContinent->Add(v2(x, y)); + Continent.push_back(NewContinent); + } + } + + RemoveEmptyContinents(); + + for(c = 1; c < Continent.size(); ++c) + Continent[c]->GenerateInfo(); +} + +void worldmap::RemoveEmptyContinents() +{ + for(uint c = 1; c < Continent.size(); ++c) + if(!Continent[c]->GetSize()) + for(uint i = Continent.size() - 1; i >= c; i--) + if(Continent[i]->GetSize()) + { + Continent[i]->AttachTo(Continent[c]); + delete Continent[i]; + Continent.pop_back(); + break; + } + else + { + delete Continent[i]; + Continent.pop_back(); + } +} + +void worldmap::Draw(truth) const +{ + cint XMin = Max(game::GetCamera().X, 0); + cint YMin = Max(game::GetCamera().Y, 0); + cint XMax = Min(XSize, game::GetCamera().X + game::GetScreenXSize()); + cint YMax = Min(YSize, game::GetCamera().Y + game::GetScreenYSize()); + blitdata BlitData = { DOUBLE_BUFFER, + { 0, 0 }, + { 0, 0 }, + { TILE_SIZE, TILE_SIZE }, + { 0 }, + TRANSPARENT_COLOR, + ALLOW_ANIMATE|ALLOW_ALPHA }; + + if(!game::GetSeeWholeMapCheatMode()) + { + for(int x = XMin; x < XMax; ++x) + { + BlitData.Dest = game::CalculateScreenCoordinates(v2(x, YMin)); + wsquare** Square = &Map[x][YMin]; + + for(int y = YMin; y < YMax; + ++y, ++Square, BlitData.Dest.Y += TILE_SIZE) + if((*Square)->LastSeen) + (*Square)->Draw(BlitData); + } + } + else + { + for(int x = XMin; x < XMax; ++x) + { + BlitData.Dest = game::CalculateScreenCoordinates(v2(x, YMin)); + wsquare** Square = &Map[x][YMin]; + + for(int y = YMin; y < YMax; + ++y, ++Square, BlitData.Dest.Y += TILE_SIZE) + (*Square)->Draw(BlitData); + } + } +} + +void worldmap::CalculateLuminances() +{ + for(ulong c = 0; c < XSizeTimesYSize; ++c) + Map[0][c]->CalculateLuminance(); +} + +void worldmap::CalculateNeighbourBitmapPoses() +{ + for(ulong c = 0; c < XSizeTimesYSize; ++c) + Map[0][c]->GetGWTerrain()->CalculateNeighbourBitmapPoses(); +} + +wsquare* worldmap::GetNeighbourWSquare(v2 Pos, int I) const +{ + Pos += game::GetMoveVector(I); + + if(Pos.X >= 0 && Pos.Y >= 0 && Pos.X < XSize && Pos.Y < YSize) + return Map[Pos.X][Pos.Y]; + else + return 0; +} + +void worldmap::RevealEnvironment(v2 Pos, int Radius) +{ + rect Rect; + femath::CalculateEnvironmentRectangle(Rect, Border, Pos, Radius); + + for(int x = Rect.X1; x <= Rect.X2; ++x) + for(int y = Rect.Y1; y <= Rect.Y2; ++y) + Map[x][y]->SignalSeen(); +} + +outputfile& operator<<(outputfile& SaveFile, const worldmap* WorldMap) +{ + WorldMap->Save(SaveFile); + return SaveFile; +} + +inputfile& operator>>(inputfile& SaveFile, worldmap*& WorldMap) +{ + WorldMap = new worldmap; + WorldMap->Load(SaveFile); + return SaveFile; +} + +void worldmap::UpdateLOS() +{ + game::RemoveLOSUpdateRequest(); + int Radius = PLAYER->GetLOSRange(); + long RadiusSquare = Radius * Radius; + v2 Pos = PLAYER->GetPos(); + rect Rect; + femath::CalculateEnvironmentRectangle(Rect, Border, Pos, Radius); + + for(int x = Rect.X1; x <= Rect.X2; ++x) + for(int y = Rect.Y1; y <= Rect.Y2; ++y) + if(long(HypotSquare(Pos.X - x, Pos.Y - y)) <= RadiusSquare) + Map[x][y]->SignalSeen(); +} diff --git a/Main/Source/wskill.cpp b/Main/Source/wskill.cpp new file mode 100644 index 0000000..28ef35f --- /dev/null +++ b/Main/Source/wskill.cpp @@ -0,0 +1,195 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +#include "wskill.h" +#include "message.h" +#include "save.h" +#include "item.h" + +int CWeaponSkillLevelMap[] += { 0, 1500, 2000, 3000, 5000, 7500, 10000, 15000, + 20000, 30000, 50000, 75000, 100000, 150000, + 200000, 300000, 500000, 750000, 1000000, + 1500000, 2000000 }; + +ulong CWeaponSkillUnuseTickMap[] += { 500000, 400000, 300000, 250000, 200000, + 150000, 125000, 100000, 80000, 62500, + 50000, 40000, 30000, 25000, 20000, + 15000, 12500, 10000, 8000, 6250, 5000 }; + +int CWeaponSkillUnusePenaltyMap[] += { 100, 125, 150, 200, 250, 300, 400, 500, + 625, 800, 1000, 1250, 1500, 2000, 2500, + 3000, 4000, 5000, 6250, 8000, 10000 }; + +cchar* CWeaponSkillName[WEAPON_SKILL_CATEGORIES] += { "unarmed combat", + "kicking", + "biting", + "uncategorized", + "small swords", + "large swords", + "blunt weapons", + "axes", + "polearms", + "whips", + "shields" +}; + +int SWeaponSkillLevelMap[] += { 0, 500, 750, 1000, 1500, 2000, 3000, 5000, + 7500, 10000, 15000, 20000, 30000, 50000, + 75000, 100000, 150000, 200000, 300000, + 500000, 750000 }; + +ulong SWeaponSkillUnuseTickMap[] += { 250000, 200000, 150000, 125000, 100000, + 80000, 62500, 50000, 40000, 30000, + 25000, 20000, 15000, 12500, 10000, + 8000, 6250, 5000, 4000, 3000, 2500 }; + +int SWeaponSkillUnusePenaltyMap[] += { 250, 300, 400, 500, 625, 800, 1000, + 1250, 1500, 2000, 2500, 3000, 4000, + 5000, 6250, 8000, 10000, 12500, 15000, + 20000, 25000 }; + +int cweaponskill::GetLevelMap(int I) const +{ return CWeaponSkillLevelMap[I]; } +ulong cweaponskill::GetUnuseTickMap(int I) const +{ return CWeaponSkillUnuseTickMap[I]; } +int cweaponskill::GetUnusePenaltyMap(int I) const +{ return CWeaponSkillUnusePenaltyMap[I]; } +cchar* cweaponskill::GetName(int Category) const +{ return CWeaponSkillName[Category]; } + +sweaponskill::sweaponskill(citem* Item) +: ID(Item->GetID()), Weight(Item->GetWeight()), Config(Item->GetConfig()) { } +int sweaponskill::GetLevelMap(int I) const +{ return SWeaponSkillLevelMap[I]; } +ulong sweaponskill::GetUnuseTickMap(int I) const +{ return SWeaponSkillUnuseTickMap[I]; } +int sweaponskill::GetUnusePenaltyMap(int I) const +{ return SWeaponSkillUnusePenaltyMap[I]; } + +void weaponskill::Save(outputfile& SaveFile) const +{ + SaveFile << (int)Level << (int)Hits << (int)HitCounter; +} + +void weaponskill::Load(inputfile& SaveFile) +{ + SaveFile >> (int&)Level >> (int&)Hits >> (int&)HitCounter; +} + +truth weaponskill::AddHit(int AddHits) +{ + if(!AddHits) + return false; + + HitCounter = 0; + + if(Hits <= 5000000 - AddHits) + Hits += AddHits; + else + Hits = 5000000; + + int OldLevel = Level; + + while(Level < 20 && Hits >= GetLevelMap(Level + 1)) + ++Level; + + return Level != OldLevel; +} + +truth weaponskill::SubHit(int SubHits) +{ + if(!SubHits) + return false; + + if(Hits >= SubHits) + Hits -= SubHits; + else + Hits = 0; + + int OldLevel = Level; + + while(Level && Hits < GetLevelMap(Level)) + --Level; + + return Level != OldLevel; +} + +void cweaponskill::AddLevelUpMessage(int Category) const +{ + ADD_MESSAGE("You advance to skill level %d with %s!", + Level, CWeaponSkillName[Category]); +} + +void cweaponskill::AddLevelDownMessage(int Category) const +{ + ADD_MESSAGE("You have not practised enough with %s lately. " + "Your skill level is reduced to %d!", + CWeaponSkillName[Category], Level); +} + +void sweaponskill::AddLevelUpMessage(cchar* WeaponName) const +{ + ADD_MESSAGE("You advance to skill level %d with your %s!", + Level, WeaponName); +} + +void sweaponskill::AddLevelDownMessage(cchar* WeaponName) const +{ + ADD_MESSAGE("You have not practised enough with your %s lately. " + "Your skill level is reduced to %d!", WeaponName, Level); +} + +void sweaponskill::Save(outputfile& SaveFile) const +{ + weaponskill::Save(SaveFile); + SaveFile << ID << Weight << (int)Config; +} + +void sweaponskill::Load(inputfile& SaveFile) +{ + weaponskill::Load(SaveFile); + SaveFile >> ID >> Weight >> (int&)Config; +} + +truth weaponskill::Tick() +{ + if(Hits && HitCounter++ >= GetUnuseTickMap(Level)) + { + HitCounter -= GetUnuseTickMap(Level); + + if(SubHit(GetUnusePenaltyMap(Level))) + return true; + } + + return false; +} + +truth sweaponskill::IsSkillOf(citem* Item) const +{ + return (ID == Item->GetID() + && Weight == Item->GetWeight() + && Config == Item->GetConfig()); +} + +truth sweaponskill::IsSkillOfCloneMother(citem* Item, ulong CMID) const +{ + return (ID == CMID + && Weight == Item->GetWeight() + && Config == Item->GetConfig()); +} diff --git a/Main/Source/wsquare.cpp b/Main/Source/wsquare.cpp new file mode 100644 index 0000000..b796fda --- /dev/null +++ b/Main/Source/wsquare.cpp @@ -0,0 +1,190 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through wmapset.cpp */ + +wsquare::wsquare(worldmap* WorldMapUnder, v2 Pos) +: square(WorldMapUnder, Pos), GWTerrain(0), OWTerrain(0) { } + +wsquare::~wsquare() +{ + delete GWTerrain; + delete OWTerrain; +} + +void wsquare::Save(outputfile& SaveFile) const +{ + square::Save(SaveFile); + SaveFile << GWTerrain << OWTerrain; + SaveFile.Put(!!LastSeen); +} + +void wsquare::Load(inputfile& SaveFile) +{ + square::Load(SaveFile); + SaveFile >> GWTerrain >> OWTerrain; + LastSeen = SaveFile.Get(); + CalculateLuminance(); +} + +void wsquare::Draw(blitdata& BlitData) +{ + if(Flags & NEW_DRAW_REQUEST || AnimatedEntities) + { + BlitData.Luminance = ivanconfig::ApplyContrastTo(Luminance); + GWTerrain->Draw(BlitData); + + if(OWTerrain) + OWTerrain->Draw(BlitData); + + if(Character && Character->CanBeSeenByPlayer()) + { + BlitData.Luminance = ivanconfig::GetContrastLuminance(); + BlitData.CustomData = Character->GetSquareIndex(Pos) + |ALLOW_ANIMATE|ALLOW_ALPHA; + Character->Draw(BlitData); + } + + Flags &= ~STRONG_NEW_DRAW_REQUEST; + } +} + +void wsquare::ChangeWTerrain(gwterrain* NewGround, owterrain* NewOver) +{ + ChangeGWTerrain(NewGround); + ChangeOWTerrain(NewOver); +} + +void wsquare::ChangeGWTerrain(gwterrain* NewGround) +{ + if(GWTerrain->IsAnimated()) + DecStaticAnimatedEntities(); + + delete GWTerrain; + SetGWTerrain(NewGround); + Flags |= DESCRIPTION_CHANGE|NEW_DRAW_REQUEST; +} + +void wsquare::ChangeOWTerrain(owterrain* NewOver) +{ + if(OWTerrain && OWTerrain->IsAnimated()) + DecStaticAnimatedEntities(); + + delete OWTerrain; + SetOWTerrain(NewOver); + Flags |= DESCRIPTION_CHANGE|NEW_DRAW_REQUEST; +} + +void wsquare::SetWTerrain(gwterrain* NewGround, owterrain* NewOver) +{ + SetGWTerrain(NewGround); + SetOWTerrain(NewOver); +} + +void wsquare::SetGWTerrain(gwterrain* What) +{ + GWTerrain = What; + + if(What) + { + What->SetWSquareUnder(this); + + if(What->IsAnimated()) + IncStaticAnimatedEntities(); + } +} + +void wsquare::SetOWTerrain(owterrain* What) +{ + OWTerrain = What; + + if(What) + { + What->SetWSquareUnder(this); + + if(What->IsAnimated()) + IncStaticAnimatedEntities(); + } +} + +void wsquare::UpdateMemorizedDescription(truth Cheat) +{ + if(Flags & DESCRIPTION_CHANGE || Cheat) + { + MemorizedDescription.Empty(); + + if(OWTerrain) + { + OWTerrain->AddName(MemorizedDescription, INDEFINITE); + MemorizedDescription << " surrounded by "; + GWTerrain->AddName(MemorizedDescription, UNARTICLED); + } + else + GWTerrain->AddName(MemorizedDescription, UNARTICLED); + + if(Cheat) + { + festring Continent; + + if(GetWorldMap()->GetContinentUnder(Pos)) + Continent << ", continent " + << GetWorldMap()->GetContinentUnder(Pos)->GetName(); + + MemorizedDescription << " (pos " << Pos.X << ':' << Pos.Y + << Continent << ", height " + << GetWorldMap()->GetAltitude(Pos) << " m)"; + } + + Flags &= ~DESCRIPTION_CHANGE; + } +} + +gterrain* wsquare::GetGTerrain() const +{ + return GWTerrain; +} + +oterrain* wsquare::GetOTerrain() const +{ + return OWTerrain; +} + +truth wsquare::SignalSeen() +{ + UpdateMemorizedDescription(); + LastSeen = 1; + return true; +} + +void wsquare::CalculateLuminance() +{ + double T = log(1. + fabs(GetWorldMap()->GetAltitude(Pos)) / 500.); + int Element = Min((128 - int(37.5 * T)), 255); + Luminance = MakeRGB24(Element, Element, Element); +} + +int wsquare::GetWalkability() const +{ + return (OWTerrain + ? OWTerrain->GetWalkability() & GWTerrain->GetWalkability() + : GWTerrain->GetWalkability()); +} + +truth wsquare::CanBeSeenByPlayer(truth) const +{ + return LastSeen; +} + +truth wsquare::CanBeSeenFrom(v2 FromPos, long MaxDistance, truth) const +{ + return (Pos - FromPos).GetLengthSquare() <= MaxDistance; +} diff --git a/Main/Source/wterra.cpp b/Main/Source/wterra.cpp new file mode 100644 index 0000000..7976da6 --- /dev/null +++ b/Main/Source/wterra.cpp @@ -0,0 +1,166 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through wmapset.cpp */ + +gwterrainprototype::gwterrainprototype(gwterrainspawner Spawner, + cchar* ClassID) +: Spawner(Spawner), ClassID(ClassID) +{ Index = protocontainer::Add(this); } +owterrainprototype::owterrainprototype(owterrainspawner Spawner, + cchar* ClassID) +: Spawner(Spawner), ClassID(ClassID) +{ Index = protocontainer::Add(this); } + +int gwterrain::GetWalkability() const { return ANY_MOVE&~SWIM; } +int owterrain::GetWalkability() const { return ANY_MOVE; } +int owterrain::GetAttachedEntry() const { return STAIRS_UP; } + +void wterrain::AddName(festring& String, int Case) const +{ + if(!(Case & PLURAL)) + if(!(Case & ARTICLE_BIT)) + String << GetNameStem(); + else + if(!(Case & INDEFINE_BIT)) + String << "the " << GetNameStem(); + else + String << (UsesLongArticle() ? "an " : "a ") << GetNameStem(); + else + if(!(Case & ARTICLE_BIT)) + String << GetNameStem() << " terrains"; + else + if(!(Case & INDEFINE_BIT)) + String << "the " << GetNameStem() << " terrains"; + else + String << GetNameStem() << " terrains"; +} + +festring wterrain::GetName(int Case) const +{ + static festring Name; + Name.Empty(); + AddName(Name, Case); + return Name; +} + +void gwterrain::Draw(blitdata& BlitData) const +{ + cint AF = AnimationFrames; + cint F = !(BlitData.CustomData & ALLOW_ANIMATE) || AF == 1 + ? 0 : GET_TICK() & (AF - 1); + BlitData.Src = GetBitmapPos(F); + igraph::GetWTerrainGraphic()->LuminanceBlit(BlitData); + + for(int c = 0; c < 8 && Neighbour[c].second; ++c) + { + BlitData.Src = Neighbour[c].first; + igraph::GetWTerrainGraphic()->LuminanceMaskedBlit(BlitData); + } + + BlitData.Src.X = BlitData.Src.Y = 0; +} + +void owterrain::Draw(blitdata& BlitData) const +{ + cint AF = AnimationFrames; + cint F = !(BlitData.CustomData & ALLOW_ANIMATE) || AF == 1 + ? 0 : GET_TICK() & (AF - 1); + BlitData.Src = GetBitmapPos(F); + igraph::GetWTerrainGraphic()->LuminanceMaskedBlit(BlitData); + BlitData.Src.X = BlitData.Src.Y = 0; +} + +void wterrain::Load(inputfile&) +{ + WSquareUnder = (wsquare*)game::GetSquareInLoad(); +} + +void gwterrain::Save(outputfile& SaveFile) const +{ + SaveFile << (ushort)GetType(); +} + +void owterrain::Save(outputfile& SaveFile) const +{ + SaveFile << (ushort)GetType(); +} + +gwterrain* gwterrainprototype::SpawnAndLoad(inputfile& SaveFile) const +{ + gwterrain* Terrain = Spawner(); + Terrain->Load(SaveFile); + return Terrain; +} + +owterrain* owterrainprototype::SpawnAndLoad(inputfile& SaveFile) const +{ + owterrain* Terrain = Spawner(); + Terrain->Load(SaveFile); + return Terrain; +} + +truth DrawOrderer(const std::pair& Pair1, + const std::pair& Pair2) +{ + return Pair1.second < Pair2.second; +} + +void gwterrain::CalculateNeighbourBitmapPoses() +{ + int Index = 0; + v2 Pos = GetPos(); + worldmap* WorldMap = GetWorldMap(); + int Priority = GetPriority(); + + for(int d = 0; d < 8; ++d) + { + wsquare* NeighbourSquare = WorldMap->GetNeighbourWSquare(Pos, d); + + if(NeighbourSquare) + { + gwterrain* DoNeighbour = NeighbourSquare->GetGWTerrain(); + int NeighbourPriority = DoNeighbour->GetPriority(); + + if(NeighbourPriority > Priority) + { + Neighbour[Index].first = DoNeighbour->GetBitmapPos(0) + - (game::GetMoveVector(d) << 4); + Neighbour[Index].second = NeighbourPriority; + ++Index; + } + } + } + + std::sort(Neighbour, Neighbour + Index, DrawOrderer); + + if(Index < 8) + Neighbour[Index].second = 0; +} + +truth owterrain::Enter(truth DirectionUp) const +{ + if(DirectionUp) + { + if(!PLAYER->IsFlying()) + ADD_MESSAGE("You jump into the air. For some " + "reason you don't get too far above."); + else + ADD_MESSAGE("You fly around for some time."); + + return false; + } + + return game::TryTravel(GetAttachedDungeon(), + GetAttachedArea(), + GetAttachedEntry()); +} diff --git a/Main/Source/wterras.cpp b/Main/Source/wterras.cpp new file mode 100644 index 0000000..663a430 --- /dev/null +++ b/Main/Source/wterras.cpp @@ -0,0 +1,95 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* Compiled through wmapset.cpp */ + +cchar* ocean::GetNameStem() const { return "ocean"; } +v2 ocean::GetBitmapPos(int Frame) const +{ return v2(48 + ((Frame << 3)&~8), 48); } +cchar* ocean::SurviveMessage() const +{ return "you manage to reach the shore"; } +cchar* ocean::MonsterSurviveMessage() const +{ return "manages to reach the shore"; } +cchar* ocean::DeathMessage() const { return "you drown"; } +cchar* ocean::MonsterDeathVerb() const { return "drowns"; } +cchar* ocean::ScoreEntry() const { return "drowned"; } + +cchar* glacier::GetNameStem() const { return "glacier"; } +v2 glacier::GetBitmapPos(int) const { return v2(16, 16); } + +cchar* desert::GetNameStem() const { return "desert"; } +v2 desert::GetBitmapPos(int) const { return v2(64, 16); } + +cchar* snow::GetNameStem() const { return "tundra"; } +v2 snow::GetBitmapPos(int) const { return v2(112, 16); } + +cchar* jungle::GetNameStem() const { return "jungle"; } +v2 jungle::GetBitmapPos(int) const { return v2(208, 16); } + +cchar* leafyforest::GetNameStem() const { return "leafy forest"; } +v2 leafyforest::GetBitmapPos(int) const { return v2(304, 16); } + +cchar* evergreenforest::GetNameStem() const +{ return "evergreen forest"; } +v2 evergreenforest::GetBitmapPos(int) const { return v2(352, 16); } + +cchar* steppe::GetNameStem() const { return "steppe"; } +v2 steppe::GetBitmapPos(int) const { return v2(160, 16); } + +cchar* attnam::GetNameStem() const +{ return "migthy cathedral reaching the clouds"; } +v2 attnam::GetBitmapPos(int) const { return v2(0, 48); } +int attnam::GetAttachedDungeon() const { return ATTNAM; } + +cchar* elpuricave::GetNameStem() const +{ return "hideous cave entry radiating pure evil"; } +v2 elpuricave::GetBitmapPos(int) const { return v2(16, 48); } +int elpuricave::GetAttachedDungeon() const { return ELPURI_CAVE; } + +cchar* newattnam::GetNameStem() const { return "primitive village"; } +v2 newattnam::GetBitmapPos(int) const { return v2(16, 64); } +int newattnam::GetAttachedDungeon() const { return NEW_ATTNAM; } + +cchar* underwatertunnel::GetNameStem() const +{ return "entrance to an underwater tunnel"; } +v2 underwatertunnel::GetBitmapPos(int) const { return v2(32, 64); } +int underwatertunnel::GetAttachedDungeon() const +{ return UNDER_WATER_TUNNEL; } + +cchar* underwatertunnelexit::GetNameStem() const +{ return "exit from an underwater tunnel"; } +v2 underwatertunnelexit::GetBitmapPos(int) const { return v2(32, 64); } +int underwatertunnelexit::GetAttachedDungeon() const +{ return UNDER_WATER_TUNNEL; } + +const char* mondedr::GetNameStem() const { return "hidden village"; } +v2 mondedr::GetBitmapPos(int) const { return v2(16, 64); } +int mondedr::GetAttachedDungeon() const { return MONDEDR; } + +const char* dragontower::GetNameStem() const { return "ruined tower"; } +v2 dragontower::GetBitmapPos(int) const { return v2(50, 80); } +int dragontower::GetAttachedDungeon() const { return DRAGON_TOWER; } + +const char* darkforest::GetNameStem() const { return "dark forest"; } +v2 darkforest::GetBitmapPos(int) const { return v2(80, 80); } +int darkforest::GetAttachedDungeon() const { return DARK_FOREST; } + +const char* muntuo::GetNameStem() const { return "isolated, ruined temple"; } +v2 muntuo::GetBitmapPos(int) const { return v2(80, 96); } +int muntuo::GetAttachedDungeon() const { return MUNTUO; } + +cchar* xinrochtomb::GetNameStem() const +{ return "tomb of utter darkness where dwells the soul of Xinroch"; } +v2 xinrochtomb::GetBitmapPos(int) const { return v2(64, 80); } +int xinrochtomb::GetAttachedDungeon() const { return XINROCH_TOMB; } + +int ocean::GetWalkability() const { return ANY_MOVE&~WALK; } diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..9dbb3ad --- /dev/null +++ b/NEWS @@ -0,0 +1,216 @@ + +CLIVAN alpha +------------ + +What's new since LIVAN + +Places: +------- +- The Deep Forest - Go there for some new adventures +- Muntuo - Where dwells a peculiar character new to the game +- The Tomb of Xinroch - Spooky sounding, but sadly, reserved for future development + +Faces: +----------- +- Fruit Bat +- Fire Fox +- Mice +- Forest Men +- Levitating Hattifatteners +- Giant Eagles +- High Priestess of Scabies +- Goblin Prison Warden +- Prison guards +- A Mango grower +- Solicitus +- Insudo +- Priest of Solicitus +- Okapi +- Thin Pig +- Starved Ox +- Vampires +- Uldras +- Kabouters +- Thunderbirds +- Goblin Warlocks +- Orc Shamans +- Kobold Alchemists +- Rookie kamikaze Dwarfs +- Grenadier Dwarfs +- Ambulatory mushrooms +- Bare-hands doctors +- Noxious Orchids + +Things: +------- +- An underground resistance group in the deep forest +- A goblin jail +- An atheist god, who art above Valpurus +- The constitution of Independent Tweraif +- Klein bottles +- Cauldrons +- Maps - these reveal locations in the worldmap +- Mango tree seedlings +- Monographs of Atheism +- Eptyron, a named axe (people complained that there was no serious artifact axe) +- Pentagonal keys +- Mangoes +- The Holy Mango of Oren Ordent (guess who this is named after :p) +- Wards that prevent teleportation into certain rooms +- Taiahas +- Milestones - these are used instead of staircases to get around in the deep forest +- Filthy Tunic +- Scroll of Fireballs + +States: +------- +- Vampirism - Can drain energy with bite attacks, but stats quickly deteriorate +- Detecting - like teleportitis, but instead it comes up with the "detect material" message + +Other: +------ + +- Characters that can sweat now produce an amount of sweat proportional to endurance as: + Volume = long(.05 * sqrt(GetBodyVolume() * GetAttribute(ENDURANCE) / 10)); +- Added a new victory condition with a score weighting of 1x, which ties in the Forest + Arc with the liberation of Tweraif. +- Repaired confdef.h by changing #define ANTITODE_LIQUID (LIQUID_ID + 9) to #define ANTIDOTE_LIQUID (LIQUID_ID + 9) +- Function Slow() now gives temp state SLOWED instead of HASTED +- Added material softening algorithm +- "SoftenedMaterial" in material configs of material.dat to facilitate the alchemist +- Added an algorithm for AI to zap wands with, based on throwing algorithm. +- Added a way to change the player's sweat material through char.h +- Added milder acid liquid VINEGAR +- Added liquid material: Liquid Horror with EFFECT_PANIC +- Added Vladimir and Ivan image by blob to IVAN_LEVEL entering screen +- Added throwing algorithm and a pick-up-items-to-throw algorithm for the AI + + +Version 0.50 +------------ + +- fluids can now cover items and characters and interact with them +- items made of iron alloys can now rust +- added directional light and day and night which use it +- added some cosmetical weather effects +- New Attnam has now many new NPCs, for instance a sumo wrestler who + can be challenged +- polymorph control is now more interesting; you need to see a monster + once before you can polymorph into it, and more powerful ones require + more intelligence +- added wands of acid rain, mirroring and necromancy +- added scrolls of detect material, harden material and golem creation +- added several new monsters, eg. powerful named archangels for each god + and necromancers who raise skeletons and zombies to do their bidding +- one can now give pets tactical commands, change their equipment and + use them to carry extra stuff (these are accessed using 'C'hat and + 'I'ssue commands keys) +- the player can now panic if he gets hit too much, like the monsters + have done in previous versions +- the player can now become exhausted if he fights for too long and/or + uses the new r'u'n command too much +- spiders are now able to make webs +- you can now get stuck to slime +- badly hurt/trapped bodyparts now become unusable until they regain + some HP/become untrapped +- it is now possible to browse detailed death reasons of individual + monsters in the postgame massacre lists +- added many new informative graphical details, for instance recently + altered attributes are shown with a different color for some time +- gloomy cave is now longer and has more special levels and rooms +- all the endgame battles are more complex +- added leprosy, a nasty disease which causes your limbs to drop off + randomly + +Version 0.430 +------------- + +- certain monsters and their corpses are now larger than one square +- thirteen new monsters added, including electric hattifatteners and + reproducing rabbits +- monsters with high enough intelligence now use much more advanced + pathfinding methods +- a very unique side branch added to the underwater tunnel +- added an alternative keyboard layout to ease playing on some laptops +- score system simplified + +Version 0.420 +------------- + +- you can now find a level where you died in a former game (a bonefile) +- added one new monster, a few items and several materials +- the player's and monsters' representations on the game screen now depend + on what they wield +- added many new monster animations +- corrected many balancing problems, especially with magical mushrooms +- corrected several fatal bugs +- corrected some memory leaks +- the game's compile speed increased greatly +- lots of monster AI and graphics routine optimizations + +Version 0.410 +------------- + +- added smoke effects +- added eleven new monsters +- added several new animations +- user interface tweaks +- removed all assembler code +- the player's representation on the game screen now depends on his armor +- added search command and searching state which can detect traps +- a list of killed monsters is now displayed when the game ends +- breaking wands now all have unique effects +- dipping to corpses is again possible + +Version 0.401 +------------- + +- added lightning effects +- added several new artifact and regular weapons +- added floating eye +- it is now much easier to gather nutrition +- many abuses prohibited +- explosions are now stopped by walls +- corrected a fatal bug in the door breaking code +- corrected many non-fatal bugs +- decreased IVAN's RAM usage greatly +- doubled IVAN's compile speed + +Version 0.40 +------------ + +- bodyparts added +- animations added +- multi-colored lights added +- carnivorous plants, lion, buffalo, snake, orc and many other monsters added +- added unique monsters +- added modified versions of old and new monsters +- enlarged the quest by one new dungeon and one new village +- tripled the amount of items +- added many equipable items (gauntlets, boots, cloaks etc) + +Version 0.311 +------------- + +- a few fatal bugs fixed +- some non-fatal bugs fixed +- lots of optimization + +Version 0.310 +------------- + +Added stuff after version 0.301: + +- Linux and DOS ports +- fountain effects +- starting pet +- the first dungeon shop +- mammoth, unicorn, kamikaze dwarf and genie +- magic lamp, jewels, holy book, scroll of charging +- explosions +- doors can be locked and they can be broken by kicking +- graphical effects for wand beams +- dolphins have more living space +- danger levels and alignments added to the panel +- processor loads decreased +- ergonomic tweaks, e.g. with go, rest and look commands diff --git a/README b/README new file mode 100644 index 0000000..5419bbe --- /dev/null +++ b/README @@ -0,0 +1,225 @@ + + + + +/*****Remember to visit www.attnam.com and join the IVAN community!*****/ + + + + +CLIVAN updates can be found here: +http://sourceforge.net/projects/clivan/ + +It stands for Continuation of Lampshade's Iter Vehemens Ad Necem. It is a graphical +roguelike based on Iter Vehemens ad Necem by Timo Kiviluoto. Presently it is running +only in Windows (ugly, I know). + +Below is the readme for IVAN 0.5, which pretty much sums up the elementary stuff +to do with the game: + + + +Iter Vehems ad Necem v0.50 +--------------------------- + +For news and updates view our homepage at ivan.sourceforge.net. + +-------------------------- + +1. Description + +2. System requirements + +3. General gameplay + +4. FAQ + +----------------------------- + +1. Description + +Iter Vehemens ad Necem (IVAN) is a graphical roguelike game, which currently +runs in Windows, DOS and Linux. It features advanced bodypart and material +handling, multi-colored lighting and, above all, deep gameplay. + +----------------------------- + +2. System requirements + +We recommend that you use at least a: + + Pentium 266 MHz + 48 Megs of RAM + + Windows 9x/ME/XP/2000 (at least) + + or: + + DOS + Vesa 2.0 compatible video card + + or: + + Linux with + (SDL library version 1.2.0 or higher) + +----------------------------- + +3. General gameplay + +IVAN works pretty much in the same way as other roguelikes. The player +controls a character, which moves and attacks from the direction keys. +All other commands can be found by pressing ?-key in the game. + +Additional help can be obtained from the offical site at +http://ivan.sourceforge.net/. + +----------------------------- + +4. FAQ + +Q: What does "Iter Vehemens ad Necem" mean? + +A: It's latin and means a "Violent Road to Death". For most players, that's + a perfect description of the typical game. + +Q: Why can't I make multiple saves and why is my save deleted when I die? + +A: Like the creators of other roguelikes, we think this makes gaming much + more exciting, since you must take full responsibility of all your + actions. You have only one chance to live or die. Also, we agree that + "normal" saveloading is OK for games which remain the same in all gaming + sessions, since in these you are not meant to really die as replaying + everything in exactly the same way would be annoying. But the same does + not apply to games with a multitude of random areas and events like IVAN; + the whole fun is trying again and again in everchanging environment, + encountering stranger and more complex situations and becoming better + and better in tackling them. + +Q: Can't you reconsider your opinion about saveloading? + +A: No. There are the two things we swore never to do when starting the + project: discarding permadeath and making IVAN a 3D action game. Don't + bother us about this question anymore. + +Q: Not even as an "easy" game option? Pretty please? + +A: Shut up. + +Q: What do these strange markings like Dex, Agi mean in the right sidebar? + +A: + AStr = Arm Strength - Increases damage inflicted on enemies + LStr = Leg Strength - Increases carrying capacity and kicking damage + Dex = Dexterity - Increases accuracy and hit speed + Agi = Agility - Increase movement speed and ability to dodge attacks + End = Endurance - Increases maximum health points and healing rate + Per = Perception - Increases sight range and accuracy + Int = Intelligence - Increases your ability to read and use magic + Wis = Wisdom - Increases your ability to deal with gods + Cha = Charisma - Improves your ability to negotiate and make deals + + If there are two numbers visible after these the first is the attribute + after bonuses and penalties obtained from your equipment or some other + temporary reason. The second number shows the base attribute without the + said modifications. If there is no net-bonus or net-minus, only the base + attribute is shown. + + If the first number is green, then it has increased a short while ago and + if it is red it has decreased. + + Siz = Size or height, in cm - Increases your enemies' chance to hit you + HP = Health Points - The sum of your bodyparts' HPs + Gold = The amount of money you have + Day & Time = The amount of absolute game time you have spent in this game + Turn = The turns (commands which take time) you have used in this game + +Q: I had 20 HPs, but I still died. Is this a bug? + +A: No, this is not a bug. You will die (at least in human-form) if the HPs + of your groin, torso or head reach 0. + +Q: Why do I always miss spiders with my trusty iron mace? + +A: Mace is far too big for hitting small creatures. Do you really kill + spiders with such enormous objects in real life? + +Q: What do marks like L+, C-- mean in the pray menu? + +A: All gods have a property that tells whether they are supporters of Law + or Chaos or if they are Neutral. This is however too general description + so the plusses and minuses tell of slight differences between the gods. + Eg Valpurus has the letters L++ this means that Valpurus is extremely + lawful, when Sophos has the letters L-, which in turn means that Sophos + is lawful, but still leaning a bit towards neutrality and even chaos. + +Q: I just lost a leg. How do I get it back? + +A: Certain gods, potions and special characters may help you. + +Q: How is score calculated? + +A: First the kills of the player and his pets are merged to a single + list. Then for every monster type, the score is calculated with the + following formula + + Score = sqrt(a) * b * b + + sqrt(a) = Square root of the number of killed monsters of this type + b = Sum of the attributes of a typical monster of this type + + The individual scores of the monster types are then added together. + You also get a high bonus for winning, depending on your victory type + (the score is multiplied by 2, 3, 4 or 5). + +Q: Why does time pass so fast in the game? + +A: Who has said the a day is as long in IVAN's world as on earth? + +Q: How can I compile IVAN source code? + +A: See INSTALL. + +Q: I am a Linux user. Why can't I access the wizard (cheat) mode? + +A: The wizard mode functions aren't compiled by default. In Linux change + the environment variable CXXFLAGS to -DWIZARD and recompile. + +Q: I am a DOS user. When I try to run IVAN, I get the message "Load error: + no DPMI - Get csdpmi*b.zip". + + The DOS binary is compiled using DJGPP, so you need a DPMI server to run + it. One should have been provided along with IVAN, but if that's not the + case, download it from www.delorie.com. + +Q: I've found a bug. What should I do? + +A: Write a small description on how the bug occured and if possible even + how we could replicate it, and send this information by email to + ivan-users@lists.sourceforge.net. If you are a SourceForge user, you can + also report this bug at Ivan's SourceForge project page: + + http://sourceforge.net/projects/ivan + + You should mention your full name so we can credit you at the end of the + AUTHORS file, if you are the first to discover this bug. + +Q: I've got a great idea to make IVAN better! What should I do? + +A: Describe the idea to us by sending it to ivan-users@lists.sourceforge.net + or report it at Ivan's SourceForge project page (see above). You will be + credited if we implement the feature, it's non-trivial, and you're first + to suggest it. + +Q: I'm a programmer willing to help you. What should I do? + +A: Code and then send us (e-mail: ivan-devel@lists.sourceforge.net) + your diff. If we like your code we will integrate it to the next + release. You will of course be credited for your code. + + See .customs.emacs file for the official Ivan C++ Programming + Style. + +----------------------------- + +FREE SOFTWARE FOREVER! diff --git a/SDL.dll b/SDL.dll new file mode 100644 index 0000000..a5da7bb Binary files /dev/null and b/SDL.dll differ diff --git a/Script/CVS/Entries b/Script/CVS/Entries new file mode 100644 index 0000000..647b4ce --- /dev/null +++ b/Script/CVS/Entries @@ -0,0 +1,9 @@ +/Makefile.am/1.10/Tue Jun 22 13:36:58 2004// +/char.dat/1.259/Tue Aug 22 19:45:09 2006// +/define.dat/1.134/Tue Aug 22 19:45:09 2006// +/dungeon.dat/1.283/Sun Sep 3 18:02:56 2006// +/glterra.dat/1.30/Fri Dec 10 09:09:45 2004// +/item.dat/1.214/Tue Aug 22 19:45:09 2006// +/material.dat/1.150/Tue Aug 22 06:33:34 2006// +/olterra.dat/1.57/Mon Jul 25 10:50:03 2005// +D diff --git a/Script/CVS/Repository b/Script/CVS/Repository new file mode 100644 index 0000000..31c5ac5 --- /dev/null +++ b/Script/CVS/Repository @@ -0,0 +1 @@ +ivan/Script diff --git a/Script/CVS/Root b/Script/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/Script/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/Script/Makefile.am b/Script/Makefile.am new file mode 100644 index 0000000..59e2e46 --- /dev/null +++ b/Script/Makefile.am @@ -0,0 +1,3 @@ +scriptdir = $(datadir)/ivan/Script +script_DATA = char.dat define.dat dungeon.dat glterra.dat item.dat material.dat olterra.dat +EXTRA_DIST = char.dat define.dat dungeon.dat glterra.dat item.dat material.dat olterra.dat diff --git a/Script/Makefile.in b/Script/Makefile.in new file mode 100644 index 0000000..753cfbd --- /dev/null +++ b/Script/Makefile.in @@ -0,0 +1,215 @@ +# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMDEPBACKSLASH = @AMDEPBACKSLASH@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CC = @CC@ +CCAS = @CCAS@ +CCASFLAGS = @CCASFLAGS@ +CXX = @CXX@ +CYGPATH_W = @CYGPATH_W@ +DEPDIR = @DEPDIR@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +SDL_CFLAGS = @SDL_CFLAGS@ +SDL_CONFIG = @SDL_CONFIG@ +SDL_LIBS = @SDL_LIBS@ +STRIP = @STRIP@ +VERSION = @VERSION@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +scriptdir = $(datadir)/ivan/Script +script_DATA = char.dat define.dat dungeon.dat glterra.dat item.dat material.dat olterra.dat +EXTRA_DIST = char.dat define.dat dungeon.dat glterra.dat item.dat material.dat olterra.dat +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = +DATA = $(script_DATA) + +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu Script/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES) + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +install-scriptDATA: $(script_DATA) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(scriptdir) + @list='$(script_DATA)'; for p in $$list; do \ + if test -f $(srcdir)/$$p; then \ + echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(scriptdir)/$$p"; \ + $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(scriptdir)/$$p; \ + else if test -f $$p; then \ + echo " $(INSTALL_DATA) $$p $(DESTDIR)$(scriptdir)/$$p"; \ + $(INSTALL_DATA) $$p $(DESTDIR)$(scriptdir)/$$p; \ + fi; fi; \ + done + +uninstall-scriptDATA: + @$(NORMAL_UNINSTALL) + list='$(script_DATA)'; for p in $$list; do \ + rm -f $(DESTDIR)$(scriptdir)/$$p; \ + done +tags: TAGS +TAGS: + + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = Script + +distdir: $(DISTFILES) + here=`cd $(top_builddir) && pwd`; \ + top_distdir=`cd $(top_distdir) && pwd`; \ + distdir=`cd $(distdir) && pwd`; \ + cd $(top_srcdir) \ + && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu Script/Makefile + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$d/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: +install-exec: install-exec-am + +install-data-am: install-scriptDATA +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-scriptDATA +uninstall: uninstall-am +all-am: Makefile $(DATA) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(scriptdir) + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: uninstall-scriptDATA install-scriptDATA tags distdir info-am \ +info dvi-am dvi check check-am installcheck-am installcheck \ +install-exec-am install-exec install-data-am install-data install-am \ +install uninstall-am uninstall all-redirect all-am all installdirs \ +mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Script/char.dat b/Script/char.dat new file mode 100644 index 0000000..a0a81c7 --- /dev/null +++ b/Script/char.dat @@ -0,0 +1,8231 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* + * NOTICE!!! + * + * This file contains SPOILERS, which might ruin your IVAN experience + * totally. Also, editing anything can DESTROY GAME BALANCE or CAUSE + * OBSCURE BUGS if you don't know what you're doing. So from here on, + * proceed at your own risk! + */ + +/* Numerical character data loaded during game startup */ + +/* Default values: */ + +character +{ + /* Obligatory: DefaultArmStrength */ + /* Obligatory for humanoids: DefaultLegStrength */ + /* Obligatory for humanoids: DefaultDexterity */ + DefaultAgility = 0; + DefaultEndurance = 0; + DefaultPerception = 0; + DefaultIntelligence = 0; + DefaultWisdom = 0; + DefaultCharisma = 0; + DefaultMana = 0; + DefaultMoney = 0; + /* Obligatory: TotalSize */ + CanRead = false; + Sex = UNDEFINED; + CanBeGenerated = false; + CriticalModifier = 20; + StandVerb = "standing"; + ForceCustomStandVerb = false; + CanOpen = true; + Frequency = 10000; + EnergyResistance = 0; + FireResistance = 0; + PoisonResistance = 0; + ElectricityResistance = 0; + AcidResistance = 0; + IsUnique = false; + ConsumeFlags = CT_FRUIT|CT_MEAT|CT_LIQUID|CT_PROCESSED; + /* Obligatory: TotalVolume */ + IsNameable = true; + BaseEmitation = 0; + /* Obligatory: TorsoBitmapPos */ + UsesLongArticle = false; + Adjective = ""; + UsesLongAdjectiveArticle = false; + /* Obligatory: NameSingular */ + /* NameSingular + "s" by default: NamePlural */ + ArticleMode = 0; + IsAbstract = true; /* This is false by default and does not inherit! */ + IsPolymorphable = true; + /* At least one of the following three is obligatory: */ + BaseUnarmedStrength = 0; + BaseBiteStrength = 0; /* Defaults to BaseUnarmedStrength / 2 */ + BaseKickStrength = 0; /* Defaults to BaseUnarmedStrength * 2 */ + AttackStyle = USE_ARMS; + CanUseEquipment = false; + CanKick = false; + CanTalk = false; + ClassStates = 0; + CanBeWished = true; + CreateDivineConfigurations = false; + CreateGolemMaterialConfigurations = false; + AttributeBonus = 0; + RightSWeaponSkillHits = 0; + LeftSWeaponSkillHits = 0; + PanicLevel = 33; + CanBeCloned = true; + DangerModifier = 100; + DefaultName = ""; + HostileReplies == "@Dd grunts angrily."; + FriendlyReplies == "@Dd grunts happily."; + CanZap = false; + HasALeg = true; + DeathMessage = "@Dd is slain."; + IgnoreDanger = false; + HPRequirementForGeneration = 0; + DayRequirementForGeneration = 0; + IsExtraCoward = false; + SpillsBlood = true; + HasEyes = true; + HasHead = true; + CanThrow = false; + UsesNutrition = true; + AttackWisdomLimit = NO_LIMIT; + /* Obligatory: AttachedGod */ + BodyPartsDisappearWhenSevered = false; + CanBeConfused = true; + CanApply = false; + WieldedPosition = 0, 0; + NaturalSparkleFlags = 0; + BiteCapturesBodyPart = true; + IsPlant = false; + MoveType = WALK; + DestroysWalls = false; + IsRooted = false; + BloodMaterial = BLOOD; + VomitMaterial = VOMIT; + HasSecondaryMaterial = false; + IsImmuneToLeprosy = true; + PolymorphIntelligenceRequirement = DEPENDS_ON_ATTRIBUTES; + AutomaticallySeen = false; + CanHear = true; + DefaultCommandFlags = FOLLOW_PLAYER|DONT_CONSUME_ANYTHING_VALUABLE; + ConstantCommandFlags = 0; + WillCarryItems = false; + ForceVomitMessage = "You push your fingers down to your throat and vomit."; + DefaultSweatMaterial = SWEAT; + Sweats = true; + IsImmuneToItemTeleport = false; + AlwaysUseMaterialAttributes = false; + IsEnormous = false; + ScienceTalkAdjectiveAttribute = + { + 142, + "", "applied", "recent", "dark", "linear", + "molecular", "theoretical", "classical", "the future of", "queer", + "multilinear", "fundamental", "mythical", "nuclear", "loop quantum", + "higher", "terrible", "mad", "tertiary", "the art of", + "elementary", "practical", "artificial", "existential", "quantum", + + "modern", "ancient", "empirical", "statistical", "the axioms of", + "hypothetical", "organic", "dialectical", "black", "erotic", + "communal", "general", "special", "the theory of", "elliptical", + "heuristical", "urban", "spontaneous", "forbidden", "invisible", + "popular", "the current state of", "liberal", "nautical", "ceremonial", + + "good", "bad", "environmental", "the code of", "compressed", + "explosive", "educational", "recreational", "democratic", "public", + "scientific", "social", "motorized", "genetic", "revolutionary", + "replaceable", "atomic", "advanced", "amphibious", "neolithic", + "jurassic", "synthetic", "integrated", "chaotic", "strategic", + + "tropical", "corrupted", "diplomatic", "political", "experimental", + "happy", "the purpose of", "natural", "heroic", "hexagonal", + "wrong", "orthogonal", "paraller", "infinite", "the solvability of", + "mental", "orthodox", "the history of", "soft", "hard", + "illegitimate", "comical", "collective", "optical", "intellectual", + + "the creative uses of", "global", "the origin of", "royal", "clinical", + "evolutionary", "alien", "the implementation of", "psychic", "plasma", + "commercial", "wicked", "hypnotic", "polymorphic", "controlled", + "doctrine:", "frictionless", "the conservation of", "ideal", "moral", + "retroviral", "orbital", "nonlinear", "the secrets of", "self-aware", + + "sentient", "silksteel", "dead", "temporal", "transcendent", + "unified", "the threshold of", "finite", "postmodern", "selective", + "arctic", "the proofs of", "mortal", "teleological", "sick", + "the principle of", "kinetic"; + } + ScienceTalkSubstantiveAttribute = + { + 17, + "New Age", "military", "space", "neutronium", "punishment", + "last century", "mass", "horseback", "tachyon", "singularity", + "antimatter", "superstring", "nerd", "crystal", "free market", + "hybrid", "probability"; + } + ScienceTalkPrefix = + { + 51, + "", "", "", "", "", + "", "", "", "", "", + "macro", "nano", "neuro", "semi", "hemi", + "cryo", "eroto", "ethno", "hyper", "trans", + "bio", "beta", "gamma", "geo", "mega", + + "radio", "cosmo", "post", "photo", "tele", + "meta", "neo", "para", "micro", "necro", + "super", "auto", "laser", "ego", "eco", + "gyro", "sub", "multi", "cyber", "xeno", + "aero", "pyro", "helio", "caco", "pseudo", + "thermo"; + } + ScienceTalkName = + { + 265, + "sanitation", "relativity", "physics", "mathematics", "philosophy", + "numerology", "slavery", "mechanics", "magicks", "architecture", + "archaeology", "logic", "fiction", "questions", "theology", + "alchemy", "valpurism", "analysis", "gastronomy", "oceanography", + "astronomy", "astrology", "anthropology", "ethics", "linguistics", + + "psychology", "methods", "choreography", "music", "semiotics", + "culture", "forestry", "engineering", "journalism", "agriculture", + "ergonomics", "sagas", "pedagogy", "apiculture", "aquaculture", + "horticulture", "silviculture", "law", "dentistry", "surgery", + "rocketry", "fission", "pharmacy", "dogmatics", "cartography", + + "gerontology", "formalism", "feminism", "communism", "flight", + "cricket", "rhetorics", "navigation", "sculpture", "literature", + "shamanism", "painting", "utilitarism", "grammar", "arithmetic", + "algebra", "marxism", "capitalism", "behaviorism", "causality", + "folklore", "legends", "animism", "spiritualism", "idolatry", + + "constructions", "currency", "riding", "federalism", "monarchy", + "medicine", "tradition", "chivalry", "righteousness", "banking", + "gravity", "metallurgy", "magnetism", "nationalism", "industry", + "pollution", "espionage", "corporations", "particles", "transportation", + "refining", "combustion", "colonization", "steel", "materials", + + "hallucinations", "armours", "trance", "afterlife", "paradigms", + "customer support", "virginity", "zoology", "witchcraft", "wine", + "claustrophobia", "eddies", "vegetables", "mobility", "eudaimoia", + "ablutophobia", "palindromes", "jokes", "materialism", "surfaces", + "acarophobia", "energy", "momentum", "mushrooms", "brains", + + "selenophobia", "alloys", "equations", "bombs", "fashion", + "taurophobia", "naturism", "nirvana", "drugs", "everything", + "ranidaphobia", "horror", "sightseeing", "marriage", "mirages", + "phronemophobia", "cancer", "scrofula", "depression", "air power", + "phallophobia", "romance", "bondage", "interfaces", "software", + + "pentheraphobia", "nations", "levitation", "tubes", "structures", + "peladophobia", "travel", "meditation", "systems", "algorithms", + "lachanophobia", "taxonomy", "taxidermy", "viruses", "bacteria", + "epistaxiophobia", "camping", "resistance", "chauvinism", "fruits", + "ephebiphobia", "simulation", "computers", "conductors", "fibers", + + "coulrophobia", "defence", "broomsticks", "fusion", "power", + "arachnophobia", "balls", "diseases", "universes", "weapons", + "virtue", "immortality", "elevators", "marketing", "civilizations", + "dreams", "anatomy", "reproduction", "calligraphy", "chess", + "Go", "cooking", "altruism", "anomalities", "fishing", + + "crime", "nonsense", "monsters", "thought control", "euthanasia", + "genocide", "cremation", "waste disposal", "breeding", "eugenics", + "minds", "lottery", "sterilization", "utopias", "perpetual motion", + "dynamics", "entropy", "skepticism", "hoaxes", "creationism", + "cold fusion", "execution", "warfare", "miracles", "the truth", + + "herecy", "programming", "gaming", "spheres", "curves", + "shapes", "speciesism", "feudalism", "bananas", "poker", + "meteorology", "pornography", "waves", "anarchy", "torture", + "accidents", "activity", "theathre", "runes", "writing", + "masonry", "pottery", "religions", "burial", "working", + + "!Sid Meier's games", + "!the Art of Computer Programming", + "!the Theory of Everything", + "!the greatness of IVAN", + "!free software ideology", + "lobotomy", "circle squaring", "angle trisection", "cosmetics", "love", + "autopsies", "Zen", "polyandry", "polygyny", "bubbles"; + } + ScienceTalkPossibility = 0; + /* Obligatory if ScienceTalkPossibility != 0: ScienceTalkIntelligenceModifier */ + /* Obligatory if ScienceTalkPossibility != 0: ScienceTalkWisdomModifier */ + ScienceTalkCharismaModifier = 0; + /* Obligatory if ScienceTalkPossibility != 0: ScienceTalkIntelligenceRequirement */ + /* Obligatory if ScienceTalkPossibility != 0: ScienceTalkWisdomRequirement */ + ScienceTalkCharismaRequirement = 0; + IsExtraFragile = false; + AllowUnconsciousness = true; + CanChoke = true; + IsImmuneToStickiness = false; + RunDescriptionLineOne = ""; /* if empty, IVAN determines this itself */ + RunDescriptionLineTwo = ""; + VomittingIsUnhealthy = true; + AllowPlayerToChangeEquipment = true; + DefaultWillPower = 10; + TamingDifficulty = 0; + IsSadist = false; + IsMasochist = false; + IsCatacombCreature = false; + CreateUndeadConfigurations = false; + UndeadVersions = false; + UndeadAttributeModifier = 100; + UndeadVolumeModifier = 100; + UndeadCopyMaterials = true; + CanBeGeneratedOnlyInTheCatacombs = false; + IsAlcoholic = false; + IsImmuneToWhipOfThievery = false; + IsRangedAttacker = false; + WhatCategoryToThrow = NONE; + WhatWeaponConfigToThrow = NONE; + WhatThrowItemTypesToThrow = NONE; +} + +humanoid +{ + ClothColor = rgb16(111, 74, 37); + SkinColor = rgb16(230, 140, 100); + HairColor = rgb16(160, 80, 0); + EyeColor = rgb16(48, 8, 80); + BeltColor = rgb16(48, 48, 48); + BootColor = rgb16(16, 16, 16); + TorsoSpecialColor = 0; + ArmSpecialColor = 0; + LegSpecialColor = 0; + IsAbstract = true; + BaseUnarmedStrength = 150; + Sex = MALE; + CanUseEquipment = true; + CanKick = true; + CanTalk = true; + CanZap = true; /* if has at least one hand (hard coded) */ + FleshMaterial = HUMAN_FLESH; + DeathMessage = "@Dd dies screaming."; /* overridden if has no head */ + CanThrow = true; + CanApply = true; + IsImmuneToLeprosy = false; + WillCarryItems = true; + DisplacePriority = 0; + UndeadVersions = true; +} + +nonhumanoid +{ + IsAbstract = true; + DisplacePriority = -1; +} + +playerkind +{ + DefaultArmStrength = 10; + DefaultLegStrength = 10; + DefaultDexterity = 10; + DefaultAgility = 10; + DefaultEndurance = 10; + DefaultPerception = 10; + DefaultIntelligence = 10; + DefaultWisdom = 10; + DefaultCharisma = 10; + DefaultMana = 10; + DefaultMoney = 45; + TorsoBitmapPos = 32, 416; + LegBitmapPos = 0, 416; + TotalVolume = 80000; + TotalSize = 175; + CanRead = true; + NameSingular = "human"; + AttachedGod = SEGES; + DisplacePriority = 2; + Inventory == amulet(AMULET_OF_LIFE_SAVING) { Times = BONUS_LIVES; } + UndeadVersions = false; + IsRangedAttacker = false; /* AI only */ + WhatCategoryToThrow = NONE; + WhatWeaponConfigToThrow = NONE; + WhatThrowItemTypesToThrow = NONE; +} + +petrus +{ + DefaultArmStrength = 40; + DefaultLegStrength = 40; + DefaultDexterity = 40; + DefaultAgility = 40; + DefaultEndurance = 40; + DefaultPerception = 40; + DefaultIntelligence = 40; + DefaultWisdom = 40; + DefaultCharisma = 40; + DefaultMana = 40; + TamingDifficulty = NO_TAMING; + IsNameable = false; + IsUnique = true; + HairColor = rgb16(160, 160, 160); + ClothColor = rgb16(48, 48, 48); + EyeColor = rgb16(100, 60, 30); + HeadBitmapPos = 96, 144; + TorsoBitmapPos = 32, 80; + ArmBitmapPos = 64, 16; + LegBitmapPos = 0, 0; + TotalVolume = 80000; + TotalSize = 225; + CanRead = true; + NameSingular = "high priest"; + PostFix = "of the Great Frog"; + IsPolymorphable = false; + Amulet = amulet(AMULET_OF_LIFE_SAVING); + BodyArmor = SAPPHIRE bodyarmor(PLATE_MAIL) { Enchantment = 4; } + Cloak = ANGEL_HAIR cloak(CLOAK_OF_ELECTRICITY_RESISTANCE) { Enchantment = 4; } + Belt = ILLITHIUM belt { Enchantment = 4; } + RightWielded = justifier; + RightGauntlet = DRAGON_HIDE gauntlet(GAUNTLET_OF_STRENGTH) { Enchantment = 4; } + RightRing = ring(RING_OF_FIRE_RESISTANCE); + LeftRing = ring(RING_OF_POISON_RESISTANCE); + RightBoot = PHOENIX_FEATHER boot(BOOT_OF_AGILITY) { Enchantment = 4; } + KnownCWeaponSkills = { 4, UNARMED, KICK, BITE, LARGE_SWORDS; } + CWeaponSkillHits = { 4, 2000, 2000, 2000, 2000; } + RightSWeaponSkillHits = 1000; + PanicLevel = 0; + CanBeCloned = false; + DefaultName = "Petrus"; + DeathMessage = "The high priest disappears in a bright light and only his left nut is left behind."; + /* Replies overridden */ + AttachedGod = VALPURUS; + BodyPartsDisappearWhenSevered = true; + CanBeConfused = false; + EnergyResistance = 50; + IsImmuneToLeprosy = true; + IsImmuneToItemTeleport = true; + AllowUnconsciousness = false; + DisplacePriority = 10; + UndeadVersions = false; + IsImmuneToWhipOfThievery = true; +} + +denim +{ + DefaultArmStrength = 35; + DefaultLegStrength = 35; + DefaultDexterity = 20; + DefaultAgility = 20; + DefaultEndurance = 35; + DefaultPerception = 30; + DefaultIntelligence = 25; + DefaultWisdom = 25; + DefaultCharisma = 30; + DefaultMana = 30; + TamingDifficulty = NO_TAMING; + IsNameable = false; + IsUnique = true; + HeadBitmapPos = 112, 160; + TorsoBitmapPos = 32, 128; + ArmBitmapPos = 64, 80; + LegBitmapPos = 0, 64; + TotalVolume = 80000; + TotalSize = 225; + CanRead = true; + Inventory = { 2, SUN_CRYSTAL stone, solstone; } + NameSingular = "vault keeper"; + PostFix = "of Loricatus"; + IsPolymorphable = false; + Amulet = amulet(AMULET_OF_LIFE_SAVING); + BodyArmor = OCTIRON bodyarmor(PLATE_MAIL) { Enchantment = 1; } + Cloak = SPIDER_SILK cloak(CLOAK_OF_ELECTRICITY_RESISTANCE) { Enchantment = 2; } + Belt = DIAMOND belt { Enchantment = 4; } + RightWielded = vormav; + RightGauntlet = ILLITHIUM gauntlet(GAUNTLET_OF_STRENGTH) { Enchantment = 4; } + RightBoot = ADAMANT boot(BOOT_OF_STRENGTH) { Enchantment = 4; } + KnownCWeaponSkills = { 4, UNARMED, KICK, BITE, AXES; } + CWeaponSkillHits = { 4, 2000, 2000, 2000, 2000; } + RightSWeaponSkillHits = 1000; + PanicLevel = 0; + CanBeCloned = false; + DefaultName = "Denim"; + HostileReplies == "\"I can't allow you to do this! I'll have to go back to banana growing if you take my most precious artifacts.\""; + FriendlyReplies == "\"Don't take any artifacts from here. I am investing them.\""; + DeathMessage = "The vault keeper disappears in a bright light."; + /* Replies overridden */ + AttachedGod = LORICATUS; + BodyPartsDisappearWhenSevered = true; + CanBeConfused = false; + EnergyResistance = 50; + IsImmuneToLeprosy = true; + IsImmuneToItemTeleport = true; + AllowUnconsciousness = false; + DisplacePriority = 10; + UndeadVersions = false; + IsImmuneToWhipOfThievery = true; +} + +raven +{ + DefaultArmStrength = 35; + DefaultLegStrength = 35; + DefaultDexterity = 50; + DefaultAgility = 50; + DefaultEndurance = 30; + DefaultPerception = 30; + DefaultIntelligence = 25; + DefaultWisdom = 25; + DefaultCharisma = 30; + DefaultMana = 30; + TamingDifficulty = NO_TAMING; + IsNameable = false; + IsUnique = true; + ClothColor = rgb16(50, 50, 50); + HeadBitmapPos = 112, 464; + TorsoBitmapPos = 48, 224; + ArmBitmapPos = 80, 64; + LegBitmapPos = 16, 160; + EyeColor = rgb16(200, 200, 0); + TotalVolume = 80000; + TotalSize = 225; + CanRead = true; + NameSingular = "assassin leader"; + PostFix = "of Cleptia"; + IsPolymorphable = false; + Amulet = amulet(AMULET_OF_LIFE_SAVING); + BodyArmor = OCTIRON bodyarmor(CHAIN_MAIL) { Enchantment = 8; } + Cloak = SPIDER_SILK cloak(CLOAK_OF_ELECTRICITY_RESISTANCE) { Enchantment = 4; } + Belt = DRAGON_HIDE belt { Enchantment = 4; } + RightWielded = BLACK_DIAMOND daggerofvenom { Enchantment = 6; } + LeftWielded = DAMASCUS_STEEL DAMASCUS_STEEL maingauche { Enchantment = 6; } + RightGauntlet = DRAGON_HIDE gauntlet(GAUNTLET_OF_DEXTERITY) { Enchantment = 4; } + RightRing = ring(RING_OF_FIRE_RESISTANCE); + LeftRing = ring(RING_OF_POISON_RESISTANCE); + RightBoot = ARCANITE boot(BOOT_OF_AGILITY) { Enchantment = 4; } + KnownCWeaponSkills = { 4, UNARMED, KICK, BITE, SMALL_SWORDS; } + CWeaponSkillHits = { 4, 2000, 2000, 2000, 2000; } + RightSWeaponSkillHits = 1000; + LeftSWeaponSkillHits = 1000; + PanicLevel = 0; + CanBeCloned = false; + DefaultName = "Raven"; + DeathMessage = "Raven is engulfed in a black shadow and disappears."; + /* Replies overridden */ + AttachedGod = CLEPTIA; + BodyPartsDisappearWhenSevered = true; + CanBeConfused = false; + EnergyResistance = 30; + IsImmuneToLeprosy = true; + IsImmuneToItemTeleport = true; + AllowUnconsciousness = false; + DisplacePriority = 10; + UndeadVersions = false; + IsImmuneToWhipOfThievery = true; + ClassStates = INFRA_VISION|ESP|TELEPORT|TELEPORT_CONTROL; + Inventory == solstone; +} + +vulcan + { + DefaultArmStrength = 30; + DefaultLegStrength = 22; + DefaultDexterity = 30; + DefaultAgility = 26; + DefaultEndurance = 30; + DefaultPerception = 30; + DefaultIntelligence = 25; + DefaultWisdom = 20; + DefaultCharisma = 5; + DefaultMana = 30; + TamingDifficulty = NO_TAMING; + IsNameable = false; + IsUnique = true; + HeadBitmapPos = 112, 464; + TorsoBitmapPos = 48, 224; + ArmBitmapPos = 80, 64; + LegBitmapPos = 16, 160; + EyeColor = rgb16(255, 0, 0); + TotalVolume = 70000; + TotalSize = 225; + CanRead = true; + IsPolymorphable = false; + NameSingular = "assassin"; + ClothColor = rgb16(160, 0, 0); + BodyArmor = OMMEL_HAIR bodyarmor(PLATE_MAIL) { Enchantment = 2; } + Cloak = OMMEL_HAIR cloak(CLOAK_OF_ELECTRICITY_RESISTANCE) { Enchantment = 1; } + Belt = OMMEL_HAIR belt { Enchantment = 4; } + RightWielded = MITHRIL MITHRIL daggerofvenom { Enchantment = 2; } + LeftWielded = whipofthievery { Enchantment = 1; } + RightGauntlet = OMMEL_HAIR gauntlet(GAUNTLET_OF_DEXTERITY) { Enchantment = 1; } + RightRing = ringofthieves; + RightBoot = OMMEL_HAIR boot(BOOT_OF_AGILITY) { Enchantment = 1; } + KnownCWeaponSkills = { 5, UNARMED, KICK, BITE, SMALL_SWORDS, WHIPS; } + CWeaponSkillHits = { 5, 1000, 1000, 1000, 1000, 1000; } + RightSWeaponSkillHits = 1000; + LeftSWeaponSkillHits = 1000; + PanicLevel = 0; + CanBeCloned = false; + DefaultName = "Vulcan-Loki"; + /* Replies overridden */ + AttachedGod = CLEPTIA; + BodyPartsDisappearWhenSevered = false; + CanBeConfused = false; + EnergyResistance = 15; + IsImmuneToLeprosy = true; + Inventory == ring(RING_OF_POLYMORPH_CONTROL); + IsImmuneToItemTeleport = true; + AllowUnconsciousness = false; + DisplacePriority = 10; + UndeadVersions = false; + IsImmuneToWhipOfThievery = true; + Inventory = {2, GOLD VALDEMAR potion, solstone; } + ClassStates = INFRA_VISION|TELEPORT|TELEPORT_CONTROL; + } + +rogue +{ + DefaultArmStrength = 10; + DefaultLegStrength = 10; + DefaultDexterity = 18; + DefaultAgility = 15; + DefaultEndurance = 15; + DefaultPerception = 20; + DefaultIntelligence = 10; + HeadBitmapPos = 112, 464; + TorsoBitmapPos = 48, 224; + ArmBitmapPos = 80, 64; + LegBitmapPos = 16, 160; + ClothColor = rgb16(100, 50, 50); + Cloak = cloak; + RightWielded = meleeweapon(DAGGER); + KnownCWeaponSkills == SMALL_SWORDS; + KnownCWeaponSkills = { 2, SMALL_SWORDS, SHIELDS; } + CWeaponSkillHits = { 2, 500, 500; } + RightSWeaponSkillHits = 10; + PanicLevel = 25; + ClothColor = rgb16(80, 80, 80); + CanRead = true; + NameSingular = "rogue"; + CanBeGenerated = false; + ClassStates = INFRA_VISION; + HostileReplies == "\"They say my rich parents spoiled me but they just fed me the wealth. How's that bad for your character?\""; + FriendlyReplies == "\"Heh, heh, mind if I take anything valuable?.\""; + IgnoreDanger = true; + AttachedGod = CLEPTIA; + IsSadist = true; + ClassStates = INFRA_VISION; +} + +assassin +{ + DefaultArmStrength = 12; + DefaultLegStrength = 12; + DefaultDexterity = 20; + DefaultAgility = 17; + DefaultEndurance = 17; + DefaultPerception = 20; + DefaultIntelligence = 10; + HeadBitmapPos = 112, 464; + TorsoBitmapPos = 48, 224; + ArmBitmapPos = 80, 64; + LegBitmapPos = 16, 160; + ClothColor = rgb16(100, 50, 50); + BodyArmor = HARDENED_LEATHER bodyarmor(PLATE_MAIL); + Cloak = HARDENED_LEATHER HARDENED_LEATHER cloak; + RightWielded = meleeweapon(DAGGER); + LeftWielded = maingauche; + KnownCWeaponSkills == SMALL_SWORDS; + KnownCWeaponSkills = { 2, SMALL_SWORDS, SHIELDS; } + CWeaponSkillHits = { 2, 500, 500; } + RightSWeaponSkillHits = 15; + LeftSWeaponSkillHits = 15; + PanicLevel = 10; + ClothColor = rgb16(80, 80, 160); + CanRead = true; + NameSingular = "assassin"; + CanBeGenerated = true; + ClassStates = INFRA_VISION; + HostileReplies == "\"Trying to reason me with diplomacy won't work on me.\""; + FriendlyReplies == "\"Eh, gets the job done..\""; + IgnoreDanger = true; + AttachedGod = CLEPTIA; + IsSadist = true; + ClassStates = INFRA_VISION; + Frequency = 1000; +} + +farmer +{ + DefaultArmStrength = 20; + DefaultLegStrength = 20; + DefaultDexterity = 20; + DefaultAgility = 10; + DefaultEndurance = 20; + DefaultPerception = 18; + DefaultIntelligence = 10; + DefaultWisdom = 10; + DefaultCharisma = 10; + DefaultMana = 5; + DefaultMoney = 20; + TorsoSpecialColor = rgb16(0, 96, 0); + TotalVolume = 50000; + TotalSize = 170; + NameSingular = "farmer"; + TorsoBitmapPos = 32, 16; + LegBitmapPos = 0, 0; + Belt = LEATHER belt; + RightWielded = meleeweapon(AXE); + KnownCWeaponSkills == AXES; + CWeaponSkillHits == 50; + RightSWeaponSkillHits = 20; + HostileReplies == "\"Did you think I use this axe only to chop wood?\""; + FriendlyReplies = + { + 4, + "\"Crops are so lousy around here. Perhaps because the summer lasts two weeks.\"", + "@Dd seems suspicious. \"You look like one from Luppliva! Go away!\"", + "@Dd sighs: \"Again polar bears ate my cattle...\"", + "\"The prices are infamous here. Ivan should smack that capitalist shopkeeper hard!\""; + } + AttachedGod = SEGES; + ScienceTalkPossibility = 33; + ScienceTalkIntelligenceModifier = 5; + ScienceTalkWisdomModifier = 10; + ScienceTalkIntelligenceRequirement = 10; + ScienceTalkWisdomRequirement = 10; + ScienceTalkName = + { + 10, + "agriculture", "apiculture", "aquaculture", "horticulture", "silviculture", + "herbalism", "plants", "breeding", "snow", "forestry"; + } + DisplacePriority = -2; + + Config MONDEDR; + { + TorsoSpecialColor = rgb16(0, 0, 96); + Cloak = CLOTH cloak; + Inventory == lantern; + FriendlyReplies = + { + 3, + "\"I wish our crops are as good as Attnam's. It's always dark here!\"", + "@Dd sighs: \"I tried to talk peace with the Attamese passing my herd but all they did was slaughter them...\"", + "\"That dirty bastard goblin keeps hogging all our crop space for not paying my non-existant rental fee!\""; + } +} +} + +wisefarmer +{ + DefaultArmStrength = 40; + DefaultLegStrength = 40; + DefaultDexterity = 30; + DefaultAgility = 20; + DefaultEndurance = 30; + DefaultPerception = 10; + DefaultIntelligence = 20; + DefaultWisdom = 100; + DefaultCharisma = 10; + DefaultMana = 5; + TorsoSpecialColor = rgb16(0, 96, 0); + TotalVolume = 50000; + TotalSize = 170; + NameSingular = "The Wise Farmer"; + TorsoBitmapPos = 32, 16; + LegBitmapPos = 0, 0; + Belt = LEATHER belt { Enchantment = 10; } + RightWielded = meleeweapon(AXE) { Enchantment = 10; } + KnownCWeaponSkills == AXES; + CWeaponSkillHits == 50; + RightSWeaponSkillHits = 20; + HostileReplies == "\"Wanderer, your future says you will face wretched agony-- please allow me to spare you the pain.\""; + FriendlyReplies = + { + 5, + "\"I would rather grow a share of crops in hell than stay alive.\"", + "@Dd gives you a blank stare. \"To succeed in your quest, you must look at life like it is a double-edged blade.\"", + "\"Having both invincibility and immortality... a thought too painful for me.\"", + "\"If only I knew what's keeping me alive - I would have been dead centuries ago.\""; + "\"I'm led to consider a different path: Heaven for climate, Hell for company.\""; + } + AttachedGod = SEGES; + ScienceTalkPossibility = 33; + ScienceTalkIntelligenceModifier = 5; + ScienceTalkWisdomModifier = 10; + ScienceTalkIntelligenceRequirement = 10; + ScienceTalkWisdomRequirement = 10; + ScienceTalkName = + { + 10, + "agriculture", "apiculture", "aquaculture", "horticulture", "silviculture", + "herbalism", "plants", "breeding", "snow", "forestry"; + } + DisplacePriority = -2; +} + +bum +{ + DefaultArmStrength = 18; + DefaultLegStrength = 18; + DefaultDexterity = 10; + DefaultAgility = 10; + DefaultEndurance = 25; + DefaultPerception = 10; + DefaultIntelligence = 6; + DefaultWisdom = 6; + DefaultCharisma = 2; + DefaultMana = 5; + SkinColor = rgb16(255, 212, 192); + HairColor = rgb16(35, 35, 35); + TorsoMainColor = rgb16(30, 30, 30); + ArmBitmapPos = 140, 654; + TotalVolume = 60000; + TotalSize = 190; + NameSingular = "bum"; + HeadBitmapPos = 160, 645; + TorsoBitmapPos = 162, 659; + LegBitmapPos = 142, 679; + CWeaponSkillHits == 50; + RightSWeaponSkillHits = 20; + HostileReplies == "@Dd yells gibberish while in bum rage."; + IsAlcoholic = true; + FriendlyReplies = + { + 4, + "\"One time Petrus decided to add a law that prohibits alcohol, but too many townspersons could not handle the law. Petrus decided to add a contradicting law to say that alcohol is a'okay.\"", + "@Dd tells you extremely poor jokes."; + "@Dd rants: \"I'm sick and tired of those social security bastards I tell ya! I sold my furniture the welfare gave me for booze, and when they gave me more they decided to screw them straight into the floor.\"", + "\"Attnam used to sell ethanol-based windscreen cleaners but when I began to drink it they switched to methanol-based cleaners.\""; + } +} + +guard +{ + DefaultArmStrength = 25; + DefaultLegStrength = 25; + DefaultDexterity = 15; + DefaultAgility = 15; + DefaultEndurance = 20; + DefaultPerception = 25; + DefaultIntelligence = 10; + DefaultWisdom = 10; + DefaultCharisma = 15; + DefaultMana = 10; + HairColor = rgb16(140, 60, 60); /* horns */ + HeadBitmapPos = 96, 112; + TorsoBitmapPos = 32, 128; + ArmBitmapPos = 64, 80; + LegBitmapPos = 0, 64; + TotalVolume = 70000; + TotalSize = 180; + NameSingular = "guard"; + KnownCWeaponSkills = { 2, LARGE_SWORDS, SHIELDS; } + IsAbstract = true; + HostileReplies == "\"A fair trial? Hah! Prepare to be executed!\""; + AttachedGod = LEGIFER; + IsSadist = true; + IsAlcoholic = true; + + Config ROOKIE; + { + AttributeBonus = -20; + Helmet = BRONZE helmet(FULL_HELMET); + BodyArmor = BRONZE bodyarmor(PLATE_MAIL); + RightWielded = BRONZE BRONZE meleeweapon(LONG_SWORD); + LeftWielded = BRONZE shield; + Adjective = "rookie"; + CWeaponSkillHits = { 2, 20, 20; } + PanicLevel = 50; + ClothColor = rgb16(130, 80, 30); + FriendlyReplies = + { + 4, + "@Dd says sadly: \"Back then I used to love bananas. One day the master guard slipped on a peel I'd dropped. Guess where I got transferred?\"", + "\"Lions killed a couple of banana growers again. Hope they're now satisfied and don't attack us.\"", + "\"This place's as hot as the elemental plane of fire! I want back home.\"", + "\"New Attnam, shit, I'm still only in New Attnam. Every time I think I'm gonna wake up back in the dungeon.\""; + } + AutomaticallySeen = true; + DisplacePriority = -5; + } + + Config VETERAN; + { + Helmet = IRON helmet(FULL_HELMET); + BodyArmor = IRON bodyarmor(PLATE_MAIL); + RightWielded = IRON IRON meleeweapon(LONG_SWORD); + LeftWielded = IRON shield; + Adjective = "veteran"; + CWeaponSkillHits = { 2, 100, 100; } + RightSWeaponSkillHits = 10; + LeftSWeaponSkillHits = 10; + PanicLevel = 25; + ClothColor = rgb16(80, 80, 80); + FriendlyReplies = + { + 4, + "@Dd says gravely: \"You don't have a life. Get it in the army.\"", + "\"Don't even think of breaking rules.\"", + "\"The high priest is my idol. I would want a sword as big as his!\"", + "@Dd sighs. \"Fighting bears bores me. Why can't an amazon army attack us for a change?\""; + } + } + + Config DWARVEN_GUARD; + { + Helmet = IRON helmet(FULL_HELMET); + BodyArmor = IRON bodyarmor(PLATE_MAIL); + RightWielded = IRON meleeweapon(MACE); + LeftWielded = IRON shield; + Adjective = "dwarven"; + CWeaponSkillHits = { 2, 100, 100; } + RightSWeaponSkillHits = 10; + LeftSWeaponSkillHits = 10; + PanicLevel = 25; + ClothColor = rgb16(80, 80, 80); + FriendlyReplies = + { + 2, + "\"Don't you dare take anything from the vault\"", + "\"Don't you dare take anything from the vault\"", + } + } + + Config MONDEDR_GUARD; + { + DefaultArmStrength = 20; + DefaultLegStrength = 20; + DefaultDexterity = 25; + DefaultAgility = 30; + DefaultEndurance = 15; + DefaultPerception = 25; + DefaultIntelligence = 15; + HeadBitmapPos = 112, 464; + TorsoBitmapPos = 48, 224; + ArmBitmapPos = 80, 64; + LegBitmapPos = 16, 160; + ClothColor = rgb16(50, 50, 50); + BodyArmor = HARDENED_LEATHER bodyarmor(PLATE_MAIL); + Cloak = HARDENED_LEATHER cloak; + RightWielded = IRON IRON meleeweapon(DAGGER); + LeftWielded = STEEL maingauche; + Adjective = "mondedr"; + KnownCWeaponSkills = { 2, SMALL_SWORDS, SHIELDS; } + CWeaponSkillHits = { 2, 500, 500; } + RightSWeaponSkillHits = 10; + LeftSWeaponSkillHits = 10; + PanicLevel = 25; + AttachedGod = CLEPTIA; + ClothColor = rgb16(80, 80, 80); + FriendlyReplies = + { + 3, + "\"Even us guards are allowed to steal from our enemies.\"", + "\"They say Mondedr is not invisible, but hard to find.\"", + "\"I heard that a long time ago Petrus tried to search for Mondedr but Raven added legs to it so Petrus could give up searching.\"", + } + } + + Config EUNUCH; + { + Helmet = STEEL helmet(FULL_HELMET); + BodyArmor = STEEL bodyarmor(PLATE_MAIL); + RightWielded = STEEL STEEL meleeweapon(LONG_SWORD); + LeftWielded = STEEL shield; + UsesLongAdjectiveArticle = true; + Adjective = "eunuch"; + CWeaponSkillHits = { 2, 200, 200; } + RightSWeaponSkillHits = 20; + LeftSWeaponSkillHits = 20; + PanicLevel = 20; + ClothColor = rgb16(110, 110, 110); + FriendlyReplies = + { + 4, + "@Dd states in a very serious tone: \"My job is vital for the safety of the nation. No one knows what Petrus would do if something happened to his most dear lovers, or one of them escaped.\"", + "\"Don't you dare to touch any of the ladies!\"", + "\"And everyone said I didn't have the balls to achieve a high status in the guard! Hah!\"", + "@Dd seems very proud. \"I'm not just a guard, but also the lead singer in the Cathedral's choir. My mezzo-soprano voice is famous Attnam-wide.\""; + } + } + + Config PATROL; + { + Helmet = helmet(HELM_OF_PERCEPTION) { Enchantment = 2; } + BodyArmor = IRON bodyarmor(PLATE_MAIL); + RightWielded = IRON IRON meleeweapon(LONG_SWORD); + LeftWielded = IRON shield; + RightRing = ring(RING_OF_INFRA_VISION); + Adjective = "patrol"; + TamingDifficulty = NO_TAMING; + CWeaponSkillHits = { 2, 200, 200; } + RightSWeaponSkillHits = 100; + LeftSWeaponSkillHits = 100; + PanicLevel = 20; + ClothColor = rgb16(220, 220, 220); + FriendlyReplies = + { + 5, + "@Dd shudders in the freezing air. \"Boy this rusty plate mail is cold in winter!\"", + "@Dd sighs: \"Why didn't I choose an office career... An inquisitor, for instance.\"", + "@Dd seems very suspicious. \"Do you have the papers?\"", + "\"Decent people have nothing to hide so they don't mind me investigating their homes every now and then.\"", + "\"Again I'm all white! The master guard doesn't like me bringing snow in the barracks...\""; + } + Inventory == lantern; + } + + Config SHOP; + { + AttributeBonus = 30; + Helmet = STEEL helmet(FULL_HELMET) { Enchantment = 1; } + BodyArmor = STEEL bodyarmor(PLATE_MAIL) { Enchantment = 1; } + RightWielded = STEEL meleeweapon(HALBERD) { Enchantment = 2; } + LeftWielded = STEEL shield { Enchantment = 2; } + RightRing = ring(RING_OF_INFRA_VISION) { Chance = 50; } + Adjective = "shop"; + CWeaponSkillHits = { 2, 500, 500; } + RightSWeaponSkillHits = 500; + LeftSWeaponSkillHits = 500; + PanicLevel = 15; + ClothColor = rgb16(100, 100, 100); + FriendlyReplies = + { + 5, + "@Dd says gravely: \"You don't have a life. Get it as a shop guard.\"", + "@Dd seems very suspicious. \"Don't even think of stealing anything.\"", + "\"Yes, this is a dangerous place to work, but our boss pays us well.\"", + "\"Attnam's guards can barely wield a sword. But we are trained by the laws of the dungeon, so don't do any rash moves here.\"", + "\"When I was here, I wanted to be there, when I was there all I could think of was getting back into the dungeon.\""; + } + } + + Config FOREST_SHOP; + { + AttributeBonus = 30; + Helmet = STEEL helmet(FULL_HELMET) { Enchantment = 1; } + BodyArmor = STEEL bodyarmor(PLATE_MAIL) { Enchantment = 1; } + RightWielded = STEEL meleeweapon(HALBERD) { Enchantment = 2; } + LeftWielded = STEEL shield { Enchantment = 2; } + RightRing = ring(RING_OF_INFRA_VISION) { Chance = 50; } + Adjective = "shop"; + CWeaponSkillHits = { 2, 500, 500; } + RightSWeaponSkillHits = 500; + LeftSWeaponSkillHits = 500; + PanicLevel = 15; + ClothColor = rgb16(100, 100, 100); + FriendlyReplies = + { + 5, + "@Dd says gravely: \"You don't have a life. Get it as a shop guard.\"", + "@Dd seems very suspicious. \"Don't even think of stealing anything.\"", + "\"Yes, this is a dangerous place to work, but our boss pays us well.\"", + "\"The troops of the UTFA can barely wield a sword. But we are trained by the laws of the forest, so don't do any rash moves here.\"", + "\"Grah! All I can think about right now is getting back into the dungeon.\""; + } + } + + Config ELITE; + { + TamingDifficulty = NO_TAMING; + AttributeBonus = 40; + Helmet = MITHRIL helmet(FULL_HELMET) { Enchantment = 2; } + BodyArmor = MITHRIL bodyarmor(PLATE_MAIL) { Enchantment = 2; } + RightWielded = MITHRIL MITHRIL meleeweapon(LONG_SWORD) { Enchantment = 2; } + LeftWielded = MITHRIL shield { Enchantment = 2; } + RightGauntlet = MITHRIL gauntlet { Enchantment = 2; } + RightBoot = MITHRIL boot { Enchantment = 2; } + RightRing = ring(RING_OF_INFRA_VISION); + LeftRing = ring(RING_OF_INVISIBILITY); + UsesLongAdjectiveArticle = true; + Adjective = "elite"; + CWeaponSkillHits = { 2, 500, 500; } + RightSWeaponSkillHits = 50; + LeftSWeaponSkillHits = 50; + PanicLevel = 10; + TotalVolume = 80000; + ClothColor = rgb16(220, 220, 220); + NaturalSparkleFlags = CLOTH_COLOR; + FriendlyReplies = + { + 5, + "@Dd sounds annoyed. \"Why do I have to work with these amateurs? They can't beat a rookie dark knight properly!\"", + "\"Don't believe Haedlac if he boasts with that enner beast. In reality he fled immediately and the monster stepped on a land mine while chasing him.\"", + "@Dd shouts excited: \"Attnam victoor!\"", + "\"We are the Imperial Guard. Bow.\"", + "\"If you even touch the shadow of our magnificent high priest, I'll squash you.\""; + } + } + + Config MASTER; + { + AttributeBonus = 60; + TotalVolume = 120000; + Helmet = helmet(HELM_OF_PERCEPTION) { Enchantment = 3; } + Amulet = amulet(AMULET_OF_ESP); + BodyArmor = ILLITHIUM bodyarmor(PLATE_MAIL) { Enchantment = 3; } + Cloak = PHOENIX_FEATHER cloak(CLOAK_OF_FIRE_RESISTANCE); + RightWielded = VALPURIUM VALPURIUM meleeweapon(KNIGHT_SWORD) { Enchantment = 3; } + LeftWielded = VALPURIUM shield { Enchantment = 3; } + RightRing = ring(RING_OF_TELEPORT_CONTROL); + LeftRing = ring(RING_OF_INVISIBILITY); + RightGauntlet = ANGEL_HAIR gauntlet(GAUNTLET_OF_STRENGTH) { Enchantment = 3; } + RightBoot = SAPPHIRE boot(BOOT_OF_STRENGTH) { Enchantment = 3; } + Adjective = "master"; + CWeaponSkillHits = { 2, 2000, 2000; } + RightSWeaponSkillHits = 200; + LeftSWeaponSkillHits = 200; + PanicLevel = 5; + TamingDifficulty = NO_TAMING; + IsUnique = true; + DefaultName = "Sir Haedlac Galladon VII"; + IsNameable = false; + CanBeCloned = false; + IsPolymorphable = false; + ClothColor = rgb16(144, 144, 200); + CanBeConfused = false; + NaturalSparkleFlags = CLOTH_COLOR; + Inventory == key(HEXAGONAL_LOCK); + FriendlyReplies = + { + 5, + "\"And this medal I got after killing my first enner beast using ingenious tactical maneuvers...\"", + "\"My grand-grand-father, Sir Haedlac Galladon IV, once told me that when the young Petrus lead the Revolution, his beard was only one feet long. Not that I would believe such urban legends.\"", + "\"In addition to all my other responsibilities, I am the head of the Attnamese Bureau of Investigation. By the way, you love mangos and hate bananas.\"", + "\"See the amulet? Don't think badly of the Government.\"", + "\"The house of Galladon has served Petrus ever since he took over.\""; + } + IsImmuneToItemTeleport = true; + AllowUnconsciousness = false; + DisplacePriority = 4; + UndeadVersions = false; + } + + Config ENQUIOX; + { + AttributeBonus = 45; + TorsoBitmapPos = 48, 16; + ArmBitmapPos = 80, 0; + LegBitmapPos = 0, 208; + HairColor = rgb16(144, 72, 0); + TotalVolume = 120000; + Helmet = SAPPHIRE helmet(FULL_HELMET) { Enchantment = 3; } + Amulet = amulet(AMULET_OF_ESP); + BodyArmor = SAPPHIRE bodyarmor(PLATE_MAIL) { Enchantment = 3; } + Cloak = FABRIC cloak(CLOAK_OF_FIRE_RESISTANCE) { Enchantment = 6; } + RightWielded = smite; + RightRing = ring(RING_OF_TELEPORT_CONTROL); + LeftRing = ring(RING_OF_INFRA_VISION); + RightGauntlet = NYMPH_HAIR gauntlet(GAUNTLET_OF_STRENGTH) { Enchantment = 3; } + RightBoot = MITHRIL boot(BOOT_OF_STRENGTH) { Enchantment = 3; } + Adjective = "master"; + KnownCWeaponSkills = { 2, LARGE_SWORDS, SHIELDS; } + CWeaponSkillHits = { 2, 2000, 2000; } + RightSWeaponSkillHits = 200; + LeftSWeaponSkillHits = 200; + PanicLevel = 5; + TamingDifficulty = NO_TAMING; + IsUnique = true; + DefaultName = "Enquiox"; + IsNameable = false; + CanBeCloned = false; + IsPolymorphable = false; + ClothColor = rgb16(144, 144, 200); + CanBeConfused = false; + NaturalSparkleFlags = CLOTH_COLOR; + IsImmuneToItemTeleport = true; + FriendlyReplies = + { + 2, + "\"Don't you dare take anything from the vault\"", + "\"Don't you dare take anything from the vault\"", + } + AllowUnconsciousness = false; + DisplacePriority = 4; + UndeadVersions = false; + Inventory == solstone; + } + + Config SENTINEL; + { + TamingDifficulty = NO_TAMING; + AttributeBonus = 50; + Helmet = ILLITHIUM helmet(FULL_HELMET) { Enchantment = 2; } + BodyArmor = ILLITHIUM bodyarmor(PLATE_MAIL) { Enchantment = 2; } + RightWielded = ILLITHIUM ILLITHIUM meleeweapon(LONG_SWORD) { Enchantment = 2; } + LeftWielded = ILLITHIUM shield { Enchantment = 2; } + RightGauntlet = ILLITHIUM gauntlet { Enchantment = 2; } + RightBoot = ILLITHIUM boot { Enchantment = 2; } + RightRing = ring(RING_OF_INFRA_VISION); + LeftRing = ring(RING_OF_INVISIBILITY); + Adjective = "divine"; + NameSingular = "sentinel"; + CWeaponSkillHits = { 2, 500, 500; } + RightSWeaponSkillHits = 50; + LeftSWeaponSkillHits = 50; + PanicLevel = 5; + TotalVolume = 90000; + ClothColor = rgb16(200, 200, 240); + NaturalSparkleFlags = CLOTH_COLOR; + FriendlyReplies = + { + 6, + "@Dd boasts. \"I am here to watch over Solicitus, that he be safe from harm, and the world be safe from him!\"", + "@Dd warns: \"Pay no attention to the counsels of Solicitus! They will only lead you to disaster.\"", + "@Dd sighs: \"You know, watching over a toppled-god all these years has been tedious. How I long for adventure!\"", + "@Dd explains: \"Solicitus is not a political prisoner, he's a danger to the world! Legifer has deemed it just.\"", + "@Dd explains: \"Insudo keeps watch over Eptyron, it must not be allowed to fall into the hands of mortals.\"", + "\"He's not a god, he's a very naughty boy!\""; + } + } + UndeadVersions = false; + AllowUnconsciousness = false; +} + +shopkeeper +{ + DefaultArmStrength = 20; + DefaultLegStrength = 30; + DefaultDexterity = 10; + DefaultAgility = 10; + DefaultEndurance = 20; + DefaultPerception = 30; + DefaultIntelligence = 25; + DefaultWisdom = 15; + DefaultCharisma = 30; + DefaultMana = 10; + DefaultMoney = 3000; + TorsoSpecialColor = rgb16(0, 96, 0); + HeadBitmapPos = 96, 64; + TorsoBitmapPos = 32, 32; + ArmBitmapPos = 64, 16; + LegBitmapPos = 0, 32; + TotalVolume = 150000; + TotalSize = 160; + NameSingular = "shopkeeper"; + CanRead = true; + HostileReplies == "\"Criminal! Mellis bless my efforts of removing you!\""; + IsAbstract = true; + IsUnique = true; + CanBeWished = false; + IsNameable = false; + CanBeCloned = false; + IsPolymorphable = false; + Inventory == holybook(MELLIS); + TamingDifficulty = NO_TAMING; + AttachedGod = MELLIS; + CanBeConfused = false; + IsSadist = true; + + Config NEW_ATTNAM; + { + AttributeBonus = -25; + Helmet = LEATHER helmet; + BodyArmor = LEATHER bodyarmor(PLATE_MAIL); + Belt = LEATHER belt(BELT_OF_CARRYING); + RightBoot = LEATHER boot; + HeadBitmapPos = 96, 64; + TorsoBitmapPos = 48, 192; + ArmBitmapPos = 64, 0; + LegBitmapPos = 0, 16; + DefaultName = "Zolku"; + FriendlyReplies = + { + 4, + "\"Welcome to the cheapest banana shop in the world!\"", + "@Dd sighs: \"It's depressing that the locals have so little purchasing power... But Mellis bless the tourists!\"", + "\"No, I don't sell spoiled food. Decos's alchemists have pumped so much magic potions into the soil that nothing here will go bad before you buy it.\"", + "\"I wish I was as brilliant an economic genius as the viceroy. Who would have thought levitating ostriches were so low-cost and efficient form of food delivery?\""; + } + AutomaticallySeen = true; + } + + Config ATTNAM; + { + Helmet = MITHRIL helmet { Enchantment = 1; } + Cloak = NYMPH_HAIR cloak { Enchantment = 1; } + BodyArmor = MITHRIL bodyarmor(CHAIN_MAIL) { Enchantment = 1; } + Belt = NYMPH_HAIR belt(BELT_OF_CARRYING) { Enchantment = 1; } + RightWielded = MITHRIL pickaxe { Enchantment = 2; } + RightGauntlet = NYMPH_HAIR gauntlet { Enchantment = 1; } + RightBoot = NYMPH_HAIR boot { Enchantment = 1; } + KnownCWeaponSkills == AXES; + CWeaponSkillHits == 200; + RightSWeaponSkillHits = 100; + DefaultName = "Hulbo"; + FriendlyReplies = + { + 4, + "@Dd sighs: \"If only I hadn't chosen a city in the middle of nowhere...\"", + "@Dd sighs: \"Mutant mushrooms ate the last caravan, and the one before it ran into an enner beast. It must be all Elpuri's doings!\"", + "\"You truly can't find better prices in this city! Indeed, you can't find ANY prices, since my store is a monopoly.\"", + "\"Don't try anything. The high priest is a friend of mine.\""; + } + } + + Config ELPURI_CAVE; + { + AttributeBonus = 25; + Helmet = MITHRIL helmet { Enchantment = 2; } + Cloak = NYMPH_HAIR cloak { Enchantment = 2; } + BodyArmor = MITHRIL bodyarmor(CHAIN_MAIL) { Enchantment = 2; } + Belt = NYMPH_HAIR belt(BELT_OF_CARRYING) { Enchantment = 2; } + RightWielded = MITHRIL pickaxe { Enchantment = 3; } + RightGauntlet = NYMPH_HAIR gauntlet { Enchantment = 2; } + RightBoot = NYMPH_HAIR boot { Enchantment = 2; } + KnownCWeaponSkills == AXES; + CWeaponSkillHits == 1000; + RightSWeaponSkillHits = 500; + DefaultName = "Merka"; + FriendlyReplies = + { + 3, + "@Dd sighs: \"I wonder why I have so few customers these days...\"", + "\"The topmost reason why I work here is that the monsters devour tax collectors.\"", + "\"The monsters don't attack me, because of our mutually profitable contract.\""; + } + } + + Config KHARAZ_ARAD_SHOP; + { + AttributeBonus = 25; + Helmet = MITHRIL helmet { Enchantment = 2; } + Cloak = NYMPH_HAIR cloak { Enchantment = 2; } + BodyArmor = MITHRIL bodyarmor(CHAIN_MAIL) { Enchantment = 2; } + Belt = NYMPH_HAIR belt(BELT_OF_CARRYING) { Enchantment = 2; } + RightWielded = MITHRIL pickaxe { Enchantment = 3; } + RightGauntlet = NYMPH_HAIR gauntlet { Enchantment = 2; } + RightBoot = NYMPH_HAIR boot { Enchantment = 2; } + KnownCWeaponSkills == AXES; + CWeaponSkillHits == 1000; + RightSWeaponSkillHits = 500; + DefaultName = "Crow"; + FriendlyReplies = + { + 2, + "@Dd sighs: \"I wonder why I have so few customers these days...\"", + "\"Did you know I came from Mondedr? Before I left Raven gave me somes wares before I left.\"", + } + } + Config MONDEDR; + { + + SkinColor = rgb16(0, 96, 0); + EyeColor = rgb16(200, 200, 0); + ClothColor = rgb16(48, 32, 16); + Helmet = MITHRIL helmet { Enchantment = 1; } + Cloak = NYMPH_HAIR cloak { Enchantment = 1; } + BodyArmor = MITHRIL bodyarmor(CHAIN_MAIL) { Enchantment = 1; } + Belt = NYMPH_HAIR belt(BELT_OF_CARRYING) { Enchantment = 1; } + RightWielded = MITHRIL pickaxe { Enchantment = 2; } + RightGauntlet = NYMPH_HAIR gauntlet { Enchantment = 1; } + RightBoot = NYMPH_HAIR boot { Enchantment = 1; } + KnownCWeaponSkills == AXES; + CWeaponSkillHits == 200; + RightSWeaponSkillHits = 100; + DefaultName = "Gobs"; + FriendlyReplies = + { + 2, + "\"Don't kill me! I am just an innocent bystander... Just don't ask where I got them hee-hee-hee.\"", + "\"I heard the previous owner of this shop, Crow, moved to Kharaz-Arad.\""; + } + } + + Config DARK_FOREST; + { + AttributeBonus = 30; + Helmet = MITHRIL helmet { Enchantment = 2; } + Cloak = NYMPH_HAIR cloak { Enchantment = 2; } + BodyArmor = MITHRIL bodyarmor(CHAIN_MAIL) { Enchantment = 2; } + Belt = NYMPH_HAIR belt(BELT_OF_CARRYING) { Enchantment = 2; } + RightWielded = MITHRIL pickaxe { Enchantment = 3; } + RightGauntlet = NYMPH_HAIR gauntlet { Enchantment = 2; } + RightBoot = NYMPH_HAIR boot { Enchantment = 2; } + KnownCWeaponSkills == AXES; + CWeaponSkillHits == 1000; + RightSWeaponSkillHits = 500; + DefaultName = "Kazu"; + FriendlyReplies = + { + 3, + "@Dd sighs: \"I wonder why I have so few customers these days...\"", + "\"The topmost reason why I work here is that the monsters devour tax collectors.\"", + "\"The monsters don't attack me, because of our mutually profitable contract.\""; + } + } +} + +priest +{ + CanRead = true; + NameSingular = "priest"; + CanBeCloned = false; + TamingDifficulty = NO_TAMING; + MoveType = WALK|SWIM; + IsAbstract = true; + Inventory == potion { Times = 2; SecondaryMaterial = ANTIDOTE_LIQUID; } + + Config VALPURUS; + { + DefaultArmStrength = 15; + DefaultLegStrength = 20; + DefaultDexterity = 10; + DefaultAgility = 10; + DefaultEndurance = 15; + DefaultPerception = 24; + DefaultIntelligence = 15; + DefaultWisdom = 25; + DefaultCharisma = 20; + DefaultMana = 20; + CapColor = rgb16(180, 0, 80); + HeadBitmapPos = 96, 128; + TorsoBitmapPos = 32, 16; + ArmBitmapPos = 64, 16; + LegBitmapPos = 0, 32; + TotalVolume = 100000; + TotalSize = 180; + AttachedGod = VALPURUS; + PostFix = "of Valpurus"; + Helmet = MITHRIL helmet { Enchantment = 2; } + BodyArmor = MITHRIL bodyarmor(CHAIN_MAIL) { Enchantment = 2; } + Cloak = NYMPH_HAIR cloak { Enchantment = 2; } + Belt = NYMPH_HAIR belt { Enchantment = 2; } + RightWielded = ARCANITE meleeweapon(MACE) { Enchantment = 3; } + RightGauntlet = NYMPH_HAIR gauntlet { Enchantment = 2; } + RightBoot = NYMPH_HAIR boot { Enchantment = 2; } + KnownCWeaponSkills == BLUNT_WEAPONS; + CWeaponSkillHits == 50; + RightSWeaponSkillHits = 20; + FriendlyReplies == "@Dd talks to you: \"Valpurus the Great Frog is the highest of all gods. The Wise know that the world is really a square pancake which He carries on His back. This is why this Cathedral and the whole city of Attnam is dedicated to His worship.\" \"In thy prayers thou must understand He is a busy god who knows His importance. He will not help newbies. Thou shouldst only pray Him when He hath called thee a Champion!\""; + } + + Config SILVA; + { + DefaultArmStrength = 10; + DefaultLegStrength = 10; + DefaultDexterity = 15; + DefaultAgility = 10; + DefaultEndurance = 10; + DefaultPerception = 24; + DefaultIntelligence = 20; + DefaultWisdom = 35; + DefaultCharisma = 30; + DefaultMana = 25; + TotalVolume = 60000; + TotalSize = 170; + HeadBitmapPos = 112, 0; + TorsoBitmapPos = 32, 208; + ArmBitmapPos = 64, 208; + LegBitmapPos = 0, 144; + SkinColor = rgb16(160, 100, 64); + HairColor = rgb16(80, 48, 32); + AttachedGod = SILVA; + NameSingular = "priestess"; + PostFix = "of Silva"; + Sex = FEMALE; + Cloak = NYMPH_HAIR cloak; + RightWielded = EBONY_WOOD EBONY_WOOD meleeweapon(QUARTER_STAFF) { Enchantment = 1; } + KnownCWeaponSkills == BLUNT_WEAPONS; + CWeaponSkillHits == 50; + RightSWeaponSkillHits = 20; + FriendlyReplies = + { + 4, + "\"Silva is the goddess of nature, who protects all living things. Her earthly manifestation, located in the elven nation of Lunethia, is a giant holy tree which reaches the clouds.\"", + "\"In the old temple we had a marvellous altar carved from a branch of Silva Herself, but Decos made firewood out of it.\"", + "\"You cannot contact any god unless you know the right rituals. Alas, I'm unable to teach you any, since after the invasion the Attnamese forbade us to officially worship any god save Valpurus. I can therefore only help you as a healer.\"", + "\"With my holy powers, I'm capable of reattaching severed limbs and removing various diseases and poisons. For a price, of course. Should I not bring enough income to the colony, the occupiers would make me a banana grower, too.\""; + } + AutomaticallySeen = true; + } + + Config CLEPTIA; + { + DefaultArmStrength = 15; + DefaultLegStrength = 20; + DefaultDexterity = 15; + DefaultAgility = 15; + DefaultEndurance = 15; + DefaultPerception = 24; + DefaultIntelligence = 15; + DefaultWisdom = 25; + DefaultCharisma = 20; + DefaultMana = 20; + HeadBitmapPos = 112, 271; + TorsoBitmapPos = 48, 224; + ArmBitmapPos = 80, 64; + LegBitmapPos = 16, 160; + ClothColor = rgb16(50, 50, 50); + TotalVolume = 100000; + TotalSize = 180; + AttachedGod = CLEPTIA; + PostFix = "of Cleptia"; + Helmet = HARDENED_LEATHER helmet { Enchantment = 2; } + BodyArmor = HARDENED_LEATHER bodyarmor(CHAIN_MAIL) { Enchantment = 2; } + Cloak = OMMEL_HAIR cloak { Enchantment = 2; } + Belt = OMMEL_HAIR belt { Enchantment = 2; } + RightWielded = OCTIRON meleeweapon(MACE) { Enchantment = 3; } + RightGauntlet = OMMEL_HAIR gauntlet { Enchantment = 2; } + RightBoot = OMMEL_HAIR boot { Enchantment = 2; } + KnownCWeaponSkills == BLUNT_WEAPONS; + CWeaponSkillHits == 50; + RightSWeaponSkillHits = 20; + FriendlyReplies == "@Dd talks to you: \"They say the avatar of Cleptia is a guy named Sparrow Hat.\""; + } + + Config SOLICITU; + { + DefaultArmStrength = 15; + DefaultLegStrength = 20; + DefaultDexterity = 10; + DefaultAgility = 10; + DefaultEndurance = 15; + DefaultPerception = 24; + DefaultIntelligence = 15; + DefaultWisdom = 25; + DefaultCharisma = 20; + DefaultMana = 20; + CapColor = rgb16(180, 0, 80); + HeadBitmapPos = 96, 128; + TorsoBitmapPos = 32, 16; + ArmBitmapPos = 64, 16; + LegBitmapPos = 0, 32; + TotalVolume = 100000; + TotalSize = 180; + AttachedGod = LORICATUS; + PostFix = "of Solicitus"; + Helmet = TIN helmet { Enchantment = 2; } + BodyArmor = COPPER bodyarmor(CHAIN_MAIL) { Enchantment = 2; } + Cloak = LEATHER cloak { Enchantment = 1; } + Belt = LEATHER belt { Enchantment = 1; } + RightWielded = CITRINE meleeweapon(AXE) { Enchantment = 2; } + RightGauntlet = LEATHER gauntlet { Enchantment = 1; } + RightBoot = LEATHER boot { Enchantment = 1; } + KnownCWeaponSkills == AXES; + CWeaponSkillHits == 50; + RightSWeaponSkillHits = 20; + FriendlyReplies = + { + 5, + "@Dd explains: \"Solicitus, the toppled-god of Stress and Hopeless Situations was once an illustrious member of the blessed Pantheon. Alas, he wrote his Celestial Monograph on athiesm and the other gods cast him out.\"", + "\"If you devote yourself to my god Solicitus, he might just give you a copy of his Monograph! Neat huh? You should probably draw some liquid fear from the fountain first though.\"", + "\"I don't bother with fancy clothes, seeing as Insudo uses me for target practice...\"", + "\"Becoming Solicitus' Champion is not as complicated as it seems. You just have to ask him. I wouldn't exactly jump at the chance though, in fact I'd rather swallow a dead dark frog.\"", + "\"I agree, being a devout priest of a toppled-god who is himself an atheist, is a dubious vocation. But hey, at least I'm unique in this world!\""; + } + } +} + +morbe /* high priestess of scabies */ +{ + CanRead = true; + TamingDifficulty = NO_TAMING; + MoveType = WALK|SWIM; + Inventory == potion { Times = 2; SecondaryMaterial = ANTIDOTE_LIQUID; } + DefaultArmStrength = 20; + DefaultLegStrength = 20; + DefaultDexterity = 40; + DefaultAgility = 40; + DefaultEndurance = 10; + DefaultPerception = 30; + DefaultIntelligence = 30; + DefaultWisdom = 45; + DefaultCharisma = 40; + DefaultMana = 40; + TotalVolume = 45000; + TotalSize = 180; + HeadBitmapPos = 112, 256; + TorsoBitmapPos = 32, 160; + ArmBitmapPos = 64, 208; + LegBitmapPos = 16, 160; + SkinColor = rgb16(160, 200, 160); + HairColor = rgb16(160, 48, 48); + ClothColor = rgb16(160, 60, 160); + EyeColor = rgb16(180, 0, 160); + BaseEmitation = rgb16(125, 160, 125); + AttachedGod = SCABIES; + NameSingular = "high priestess"; + PostFix = "of Scabies"; + Sex = FEMALE; + Cloak = HESSIAN_CLOTH cloak; + RightWielded = RUBY daggerofvenom { Enchantment = 4; } + KnownCWeaponSkills == SMALL_SWORDS; + CWeaponSkillHits == 500; + RightSWeaponSkillHits = 200; + /* Replies overridden */ + AutomaticallySeen = true; + IsNameable = false; + IsUnique = true; + IsPolymorphable = false; + Amulet = amulet(AMULET_OF_VANITY); + BodyArmor = RUBY bodyarmor(PLATE_MAIL) { Enchantment = 4; } + Cloak = DAEMON_FLESH cloak(CLOAK_OF_ELECTRICITY_RESISTANCE) { Enchantment = 4; } + Belt = ELPURI_FLESH belt { Enchantment = 4; } + RightGauntlet = DRAGON_HIDE gauntlet(GAUNTLET_OF_STRENGTH) { Enchantment = 4; } + RightRing = ring(RING_OF_FIRE_RESISTANCE); + LeftRing = ring(RING_OF_POISON_RESISTANCE); + RightBoot = SPIDER_SILK boot(BOOT_OF_AGILITY) { Enchantment = 4; } + PanicLevel = 0; + CanBeCloned = false; + DefaultName = "Morbe"; + DeathMessage = "The high priestess vanishes from the bowels of Valpurus."; + BodyPartsDisappearWhenSevered = true; + CanBeConfused = false; + EnergyResistance = 50; + IsImmuneToLeprosy = true; + IsImmuneToItemTeleport = true; + AllowUnconsciousness = false; + DisplacePriority = 10; + UndeadVersions = false; + IsImmuneToWhipOfThievery = true; + IsExtraFragile = true; +} + +oree +{ + DefaultArmStrength = 40; + DefaultLegStrength = 40; + DefaultDexterity = 80; + DefaultAgility = 80; + DefaultEndurance = 25; + DefaultPerception = 30; + DefaultIntelligence = 30; + DefaultWisdom = 20; + DefaultCharisma = 3; + DefaultMana = 30; + TamingDifficulty = NO_TAMING; + IsNameable = false; + IsUnique = true; + HeadBitmapPos = 96, 32; + TorsoBitmapPos = 48, 160; + ArmBitmapPos = 80, 160; + LegBitmapPos = 16, 144; + ClothColor = rgb16(30, 20, 0); + SkinColor = rgb16(180, 0, 0); + EyeColor = rgb16(100, 0, 0); + TotalVolume = 120000; + TotalSize = 225; + CanRead = true; + NameSingular = "blood daemon king"; + IsPolymorphable = false; + AttackStyle = USE_ARMS|USE_LEGS|USE_HEAD; + BaseUnarmedStrength = 1250; + BaseKickStrength = 2500; + BaseBiteStrength = 6000; + Alias == "Oree"; + ClassStates = INVISIBLE|INFRA_VISION|ESP; + Helmet = DIAMOND helmet { Enchantment = 3; } + Cloak = PHOENIX_FEATHER cloak { Enchantment = 3; } + Belt = ARCANITE belt { Enchantment = 3; } + BodyArmor = goldeneagleshirt; + RightGauntlet = NYMPH_HAIR gauntlet(GAUNTLET_OF_DEXTERITY) { Enchantment = 3; } + RightBoot = OMMEL_HAIR boot(BOOT_OF_AGILITY) { Enchantment = 3; } + KnownCWeaponSkills = { 3, UNARMED, KICK, BITE; } + CWeaponSkillHits = { 3, 2000, 2000, 2000; } + PanicLevel = 0; + CanBeCloned = false; + Inventory == can { MainMaterial = IRON { Volume = 10; } SecondaryMaterial = PEPSI { Volume = 330; } } + DefaultName = "Oree"; + HostileReplies == "@Dd laughs: \"No time for small talk. Time to drink blood!\""; + FriendlyReplies == "@Dd grumbles angrily: \"I really hate it when people cheat IVAN. It spoils them and takes away the thrill and mystery of it.\""; + FleshMaterial = DAEMON_FLESH; + DeathMessage = "@Dd vomits blood for one last time and then dies."; + AttachedGod = MORTIFER; + CanBeConfused = false; + WieldedPosition = 0, -1; + BiteCapturesBodyPart = false; + VomitMaterial = ACIDOUS_BLOOD; + BloodMaterial = ACIDOUS_BLOOD; + DefaultSweatMaterial = ACIDOUS_BLOOD; + IsImmuneToItemTeleport = true; + AllowUnconsciousness = false; + DisplacePriority = 8; + VomittingIsUnhealthy = false; + IsSadist = true; + UndeadVersions = false; +} + +darkknight +{ + DefaultArmStrength = 30; + DefaultLegStrength = 30; + DefaultDexterity = 20; + DefaultAgility = 20; + DefaultEndurance = 22; + DefaultPerception = 25; + DefaultIntelligence = 20; + DefaultWisdom = 10; + DefaultCharisma = 10; + DefaultMana = 10; + HeadBitmapPos = 96, 240; + TorsoBitmapPos = 32, 16; + ArmBitmapPos = 80, 176; + LegBitmapPos = 0, 48; + ClothColor = rgb16(0, 0, 100); + EyeColor = rgb16(255, 255, 0); + TotalVolume = 80000; + TotalSize = 200; + CanRead = true; + NameSingular = "dark knight"; + CanBeGenerated = true; + IsAbstract = true; + KnownCWeaponSkills = { 2, LARGE_SWORDS, SHIELDS; } + ClassStates = INFRA_VISION; + HostileReplies == "@Dd yells: \"For Mortifer I shall slay you!\""; + FriendlyReplies == "\"Don't bother me unless you need help to die.\""; + AttachedGod = CRUENTUS; + IsExtraFragile = true; + IsSadist = true; + + Config ROOKIE; + { + AttributeBonus = -20; + Helmet = BRONZE helmet(FULL_HELMET); + BodyArmor = BRONZE bodyarmor(PLATE_MAIL); + RightWielded = BRONZE BRONZE meleeweapon(LONG_SWORD); + LeftWielded = BRONZE shield; + Adjective = "rookie"; + CWeaponSkillHits = { 2, 200, 200; } + RightSWeaponSkillHits = 100; + LeftSWeaponSkillHits = 100; + PanicLevel = 15; + ClothColor = rgb16(70, 70, 70); + CapColor = rgb16(48, 48, 48); + GauntletColor = rgb16(50, 50, 50); + } + + Config VETERAN; + { + Helmet = IRON helmet(FULL_HELMET) { Enchantment = 1; } + BodyArmor = IRON bodyarmor(PLATE_MAIL) { Enchantment = 1; } + RightWielded = IRON IRON meleeweapon(LONG_SWORD) { Enchantment = 1; } + LeftWielded = IRON shield { Enchantment = 1; } + RightGauntlet = NYMPH_HAIR gauntlet { Enchantment = 1; } + RightBoot = IRON boot { Enchantment = 1; } + Adjective = "veteran"; + CWeaponSkillHits = { 2, 500, 500; } + RightSWeaponSkillHits = 200; + LeftSWeaponSkillHits = 200; + PanicLevel = 10; + ClothColor = rgb16(40, 40, 40); + CapColor = rgb16(48, 48, 48); + GauntletColor = rgb16(50, 50, 50); + } + + Config ELITE; + { + AttributeBonus = 20; + Helmet = STEEL helmet(FULL_HELMET) { Enchantment = 2; } + BodyArmor = STEEL bodyarmor(PLATE_MAIL) { Enchantment = 2; } + RightWielded = STEEL STEEL meleeweapon(TWO_HANDED_SWORD) { Enchantment = 2; } + RightGauntlet = OMMEL_HAIR gauntlet { Enchantment = 2; } + RightBoot = STEEL boot { Enchantment = 2; } + UsesLongAdjectiveArticle = true; + Adjective = "elite"; + KnownCWeaponSkills == LARGE_SWORDS; + CWeaponSkillHits == 1000; + RightSWeaponSkillHits = 500; + PanicLevel = 5; + TotalVolume = 90000; + ClothColor = rgb16(20, 20, 120); + CapColor = rgb16(48, 48, 48); + GauntletColor = rgb16(50, 50, 50); + } + + Config GRAND_MASTER; + { + AttributeBonus = 40; + TotalVolume = 100000; + Helmet = RUBY helmet(FULL_HELMET) { Enchantment = 3; } + BodyArmor = MITHRIL bodyarmor(PLATE_MAIL) { Enchantment = 3; } + Cloak = DRAGON_HIDE cloak { Enchantment = 3; } + Belt = SAPPHIRE belt { Enchantment = 3; } + RightWielded = SAPPHIRE RUBY meleeweapon(TWO_HANDED_SCIMITAR) { Enchantment = 4; } + LeftRing = ring(RING_OF_TELEPORT_CONTROL); + RightGauntlet = SPIDER_SILK gauntlet { Enchantment = 3; } + RightBoot = RUBY boot { Enchantment = 3; } + Adjective = "grand master"; + KnownCWeaponSkills == LARGE_SWORDS; + CWeaponSkillHits == 5000; + RightSWeaponSkillHits = 2000; + PanicLevel = 0; + TamingDifficulty = 40; + IsUnique = true; + CanBeWished = true; + DefaultName = "Golgor Dhan"; + DangerModifier = 1500; + IsNameable = false; + CanBeCloned = false; + IsPolymorphable = false; + Inventory = { 3, scrollofenchantarmor { Times = 2; }, scrollofenchantweapon { Times = 2; }, solstone; } + HeadBitmapPos = 96, 112; + ClothColor = rgb16(100, 0, 0); + CapColor = rgb16(48, 48, 48); + HairColor = rgb16(32, 32, 32); + GauntletColor = rgb16(50, 50, 50); + CanBeConfused = false; + FireResistance = 30; + ElectricityResistance = 30; + EnergyResistance = 30; + IsImmuneToItemTeleport = true; + IsExtraFragile = false; + AllowUnconsciousness = false; + DisplacePriority = 8; + UndeadVersions = false; + IsImmuneToWhipOfThievery = true; + } +} + +ennerbeast +{ + DefaultArmStrength = 10; + DefaultLegStrength = 10; + DefaultDexterity = 10; + DefaultAgility = 10; + DefaultEndurance = 20; + DefaultPerception = 12; + DefaultIntelligence = 1; + DefaultWisdom = 1; + DefaultCharisma = 1; + DefaultMana = 0; + CanOpen = false; + CanApply = false; + TotalVolume = 65000; + TotalSize = 150; + Adjective = "enner"; + NameSingular = "beast"; + UsesLongAdjectiveArticle = true; + IsPolymorphable = false; + AttackStyle = USE_HEAD; + BaseBiteStrength = 5000; + CanUseEquipment = false; + CanBeWished = false; + LegBitmapPos = 16, 48; + TorsoBitmapPos = 32, 48; + ArmBitmapPos = 64, 48; + HeadBitmapPos = 96, 320; + HairColor = rgb16(64, 20, 0); + ClothColor = rgb16(0, 0, 100); + BootColor = rgb16(90, 50, 10); + LegMainColor = rgb16(64, 20, 0); + PanicLevel = 75; + FleshMaterial = ENNER_BEAST_FLESH; + DeathMessage = "@Dd dies and the world is finally freed from this terrible monster."; + Inventory == horn(FEAR); + IsUnique = true; /* currently */ + HostileReplies == "Aighee!"; + FriendlyReplies = + { + 4, + "\"Fishing is fun! Do you fish?\"", + "\"And then I got that perch weighting fifty stones...\"", + "\"I only want to talk but everyone is rude and dies when I try.\"", + "\"Can you help me with this download problem?\""; + } + AttachedGod = SCABIES; + BiteCapturesBodyPart = false; +} + +frog +{ + HostileReplies == "@Dd croaks angrily."; + FriendlyReplies = + { + 4, + "@Dd frolics in religious ecstasy.", + "@Dd croaks with pious joy.", + "@Dd jumps up and down in enlightened trance.", + "@Dd goes \"Ribbit! Ribbit!\" full of transcendent delight."; + } + NameSingular = "frog"; + IsAbstract = true; + AttackStyle = USE_HEAD; + BaseBiteStrength = 750; + KnownCWeaponSkills == BITE; + FleshMaterial = FROG_FLESH; + PanicLevel = 50; + MoveType = WALK|SWIM; + CanRead = true; + CanChoke = false; + StandVerb = "jumping about"; + + Config DARK; + { + DefaultArmStrength = 5; + DefaultAgility = 40; + DefaultEndurance = 10; + DefaultPerception = 24; + DefaultIntelligence = 15; + DefaultWisdom = 20; + DefaultCharisma = 5; + DefaultMana = 20; + BloodMaterial = DARK_FROG_BLOOD; + TotalVolume = 5000; + TorsoBitmapPos = 80, 0; + TotalSize = 25; + SkinColor = rgb16(60, 60, 60); + Adjective = "dark"; + CanBeGenerated = true; + ClassStates = INFRA_VISION; + CWeaponSkillHits == 100; + AttachedGod = SCABIES; + ScienceTalkPossibility = 25; + ScienceTalkIntelligenceModifier = 10; + ScienceTalkWisdomModifier = 5; + ScienceTalkIntelligenceRequirement = 10; + ScienceTalkWisdomRequirement = 5; + FriendlyReplies == "@Dd croaks happily."; + IsCatacombCreature = true; + } + + Config GREATER_DARK; + { + DefaultArmStrength = 15; + DefaultAgility = 35; + DefaultEndurance = 15; + DefaultPerception = 30; + DefaultIntelligence = 20; + DefaultWisdom = 25; + DefaultCharisma = 4; + DefaultMana = 25; + BaseBiteStrength = 1000; + BloodMaterial = DARK_FROG_BLOOD; + TotalVolume = 50000; + TorsoBitmapPos = 96, 0; + TotalSize = 100; + SkinColor = rgb16(60, 60, 60); + Adjective = "greater dark"; + CanBeGenerated = true; + ClassStates = INFRA_VISION; + CWeaponSkillHits == 200; + AttachedGod = SCABIES; + ScienceTalkPossibility = 50; + ScienceTalkIntelligenceModifier = 25; + ScienceTalkWisdomModifier = 10; + ScienceTalkIntelligenceRequirement = 15; + ScienceTalkWisdomRequirement = 10; + FriendlyReplies == "@Dd croaks happily."; + IsCatacombCreature = true; + } + + Config GIANT_DARK; + { + DefaultArmStrength = 45; + DefaultAgility = 30; + DefaultEndurance = 20; + DefaultPerception = 36; + DefaultIntelligence = 25; + DefaultWisdom = 30; + DefaultCharisma = 3; + DefaultMana = 30; + BaseBiteStrength = 1250; + BloodMaterial = DARK_FROG_BLOOD; + TotalVolume = 250000; + TorsoBitmapPos = 64, 0; + TotalSize = 200; + SkinColor = rgb16(60, 60, 60); + Adjective = "giant dark"; + CanBeGenerated = true; + ClassStates = INFRA_VISION|TELEPORT_CONTROL; + CWeaponSkillHits == 500; + AttachedGod = SCABIES; + IsPolymorphable = false; + IsEnormous = true; + ScienceTalkPossibility = 75; + ScienceTalkIntelligenceModifier = 50; + ScienceTalkWisdomModifier = 25; + ScienceTalkIntelligenceRequirement = 20; + ScienceTalkWisdomRequirement = 15; + FriendlyReplies == "@Dd croaks happily."; + IsCatacombCreature = true; + } + + Config LIGHT; + { + DefaultArmStrength = 5; + DefaultAgility = 40; + DefaultEndurance = 10; + DefaultPerception = 24; + DefaultIntelligence = 5; + DefaultWisdom = 30; + DefaultCharisma = 15; + DefaultMana = 20; + TotalVolume = 5000; + TorsoBitmapPos = 80, 0; + TotalSize = 25; + SkinColor = rgb16(32, 88, 32); + Adjective = "light"; + CWeaponSkillHits == 100; + AttachedGod = VALPURUS; + ScienceTalkPossibility = 25; + ScienceTalkIntelligenceModifier = 2; + ScienceTalkWisdomModifier = 5; + ScienceTalkIntelligenceRequirement = 5; + ScienceTalkWisdomRequirement = 15; + } + + Config GREATER_LIGHT; + { + DefaultArmStrength = 15; + DefaultAgility = 30; + DefaultEndurance = 15; + DefaultPerception = 36; + DefaultIntelligence = 10; + DefaultWisdom = 35; + DefaultCharisma = 20; + DefaultMana = 25; + BaseBiteStrength = 1000; + TotalVolume = 50000; + TorsoBitmapPos = 96, 0; + TotalSize = 100; + SkinColor = rgb16(32, 88, 32); + Adjective = "greater light"; + CWeaponSkillHits == 200; + AttachedGod = VALPURUS; + ScienceTalkPossibility = 50; + ScienceTalkIntelligenceModifier = 5; + ScienceTalkWisdomModifier = 10; + ScienceTalkIntelligenceRequirement = 10; + ScienceTalkWisdomRequirement = 20; + } + + Config GIANT_LIGHT; + { + DefaultArmStrength = 45; + DefaultAgility = 30; + DefaultEndurance = 20; + DefaultPerception = 36; + DefaultIntelligence = 15; + DefaultWisdom = 40; + DefaultCharisma = 25; + DefaultMana = 30; + BaseBiteStrength = 1250; + TotalVolume = 250000; + TorsoBitmapPos = 64, 0; + TotalSize = 200; + SkinColor = rgb16(32, 88, 32); + Adjective = "giant light"; + CWeaponSkillHits == 500; + AttachedGod = VALPURUS; + IsPolymorphable = false; + ClassStates = TELEPORT_CONTROL; + IsEnormous = true; + ScienceTalkPossibility = 75; + ScienceTalkIntelligenceModifier = 10; + ScienceTalkWisdomModifier = 25; + ScienceTalkIntelligenceRequirement = 15; + ScienceTalkWisdomRequirement = 25; + } +} + +billswill +{ + DefaultArmStrength = 1; + DefaultAgility = 50; + DefaultEndurance = 5; + DefaultPerception = 27; + DefaultIntelligence = 2; + DefaultWisdom = 2; + DefaultCharisma = 5; + DefaultMana = 10; + StandVerb = "floating"; + Frequency = 250; + DangerModifier = 500; + TotalVolume = 500000; + TorsoBitmapPos = 48, 0; + TotalSize = 100; + Adjective = "pure"; + NameSingular = "mass"; + NamePlural = "masses"; + PostFix = "of Bill's will"; + BaseBiteStrength = 3000; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 100; + BaseEmitation = rgb24(110, 110, 130); + HostileReplies == "\"You will install the One OS. Resistance is futile. Prepare to be assimilited.\""; + FriendlyReplies == "\"Praise the Corporation!\""; + Inventory == amulet(AMULET_OF_ESP) { Chance = 10; } + PanicLevel = 0; + HasALeg = false; + FleshMaterial = MAGICAL_AIR; + DeathMessage = "@Dd vanishes from existence."; + SpillsBlood = false; + Sweats = false; + HasHead = false; + UsesNutrition = false; + AttachedGod = MELLIS; + BodyPartsDisappearWhenSevered = true; + CanTalk = true; + AttackStyle = USE_HEAD; + BiteCapturesBodyPart = false; + ClassStates = HASTE|ESP|GAS_IMMUNITY|LEVITATION; + MoveType = ETHEREAL; + CanChoke = false; + IsImmuneToStickiness = true; +} + +skeleton +{ + DefaultArmStrength = 10; + DefaultLegStrength = 10; + DefaultDexterity = 10; + DefaultAgility = 10; + DefaultEndurance = 10; + DefaultPerception = 9; + DefaultIntelligence = 4; + DefaultWisdom = 4; + DefaultCharisma = 3; + DefaultMana = 5; + SkinColor = rgb16(160, 160, 160); + EyeColor = rgb16(0, 0, 0); + ClothColor = rgb16(111, 74, 37); + HeadBitmapPos = 96, 96; + TorsoBitmapPos = 32, 96; + ArmBitmapPos = 64, 96; + LegBitmapPos = 0, 80; + TotalVolume = 10000; + TotalSize = 150; + NameSingular = "skeleton"; + CanBeGenerated = true; + Helmet = helmet; + RightWielded = meleeweapon(AXE) { Enchantment = -2; } + KnownCWeaponSkills == AXES; + CWeaponSkillHits == 20; + RightSWeaponSkillHits = 10; + PanicLevel = 0; + HostileReplies == "@Dd grunts: \"Bones. Need more bones.\""; + FriendlyReplies == "@Dd sings: \"Leg bone is connected to the hib bone, hib bone is connected to the rib bone...\""; + FleshMaterial = BONE; + DeathMessage = "@Dd is transformed into a crumpled heap of bones."; + SpillsBlood = false; + Sweats = false; + UsesNutrition = false; + AttachedGod = MORTIFER; + ClassStates = GAS_IMMUNITY; + WieldedPosition = 0, -1; + IsExtraFragile = true; + CanChoke = false; + IsCatacombCreature = true; + UndeadVersions = false; + CreateUndeadConfigurations = true; + UndeadAttributeModifier = 75; + UndeadVolumeModifier = 25; + UndeadCopyMaterials = false; + Frequency = 500; + + Config WARRIOR; + { + AttributeBonus = 100; + RightWielded = meleeweapon(MACE) { Enchantment = -1; } + KnownCWeaponSkills == BLUNT_WEAPONS; + CWeaponSkillHits == 100; + RightSWeaponSkillHits = 50; + NameSingular = "skeleton warrior"; + TotalVolume = 20000; + EyeColor = rgb16(255, 0, 0); + ClothColor = rgb16(32, 32, 32); + CanRead = true; + CreateUndeadConfigurations = false; + } + + Config WAR_LORD; + { + AttributeBonus = 200; + Helmet = METEORIC_STEEL helmet(FULL_HELMET); + Cloak = PHOENIX_FEATHER cloak; + BodyArmor = ILLITHIUM bodyarmor(BROKEN|CHAIN_MAIL); + RightWielded = RUBY RUBY flamingsword(LONG_SWORD) { Enchantment = 0; } + LeftWielded = ARCANITE shield; + RightGauntlet = ANGEL_HAIR gauntlet; + RightRing = ring(RING_OF_INFRA_VISION); + RightBoot = MITHRIL boot; + KnownCWeaponSkills = { 2, LARGE_SWORDS, SHIELDS; } + CWeaponSkillHits = { 2, 500, 500; } + RightSWeaponSkillHits = 200; + LeftSWeaponSkillHits = 200; + NameSingular = "skeleton warlord"; + IsUnique = true; + CanBeWished = true; + DefaultName = "Xinroch"; + DangerModifier = 1500; + IsNameable = false; + CanBeCloned = false; + IsPolymorphable = false; + TotalVolume = 40000; + EyeColor = rgb16(255, 255, 0); + ClothColor = rgb16(100, 0, 0); + Inventory = { 2, wand(WAND_OF_RESURRECTION), solstone; } + CanBeConfused = false; + CanRead = true; + FireResistance = 100; + ElectricityResistance = 100; + EnergyResistance = 100; + ClassStates = GAS_IMMUNITY|TELEPORT_CONTROL; + IsImmuneToItemTeleport = true; + IsExtraFragile = false; + TamingDifficulty = NO_TAMING; + CreateUndeadConfigurations = false; + } +} + +goblin +{ + DefaultArmStrength = 10; + DefaultLegStrength = 10; + DefaultDexterity = 10; + DefaultAgility = 15; + DefaultEndurance = 12; + DefaultPerception = 15; + DefaultIntelligence = 5; + DefaultWisdom = 5; + DefaultCharisma = 5; + DefaultMana = 5; + SkinColor = rgb16(0, 96, 0); + EyeColor = rgb16(200, 200, 0); + ClothColor = rgb16(48, 32, 16); + HeadBitmapPos = 96, 48; + TorsoBitmapPos = 32, 112; + ArmBitmapPos = 64, 144; + LegBitmapPos = 16, 64; + TotalVolume = 25000; + TotalSize = 100; + NameSingular = "goblin"; + CanBeGenerated = true; + Sex = UNDEFINED; + RightWielded = COPPER COPPER meleeweapon(SHORT_SWORD); + KnownCWeaponSkills == SMALL_SWORDS; + CWeaponSkillHits == 10; + RightSWeaponSkillHits = 5; + PanicLevel = 66; + HostileReplies == "@Dd yells goblin war cries at you."; + FriendlyReplies == "@Dd laughs: \"Humie friend. Many mommo we kill. Many spider we eat.\""; + FleshMaterial = GOBLINOID_FLESH; + AttachedGod = CRUENTUS; + WieldedPosition = 0, -2; + + Config BERSERKER; + { + AttributeBonus = 25; + RightWielded = BRONZE BRONZE meleeweapon(SHORT_SWORD); + CWeaponSkillHits == 20; + RightSWeaponSkillHits = 10; + NameSingular = "goblin berserker"; + ClothColor = rgb16(0, 96, 0); + } + + Config BUTCHER; + { + AttributeBonus = 50; + Helmet = LEATHER helmet; + BodyArmor = LEATHER bodyarmor(PLATE_MAIL); + RightWielded = IRON IRON meleeweapon(SHORT_SWORD); + CWeaponSkillHits == 50; + RightSWeaponSkillHits = 20; + NameSingular = "goblin butcher"; + ClothColor = rgb16(200, 0, 0); + Inventory == potion { SecondaryMaterial = TROLL_BLOOD; Chance = 10; } + } + + Config PRINCE; + { + AttributeBonus = 75; + Helmet = BRONZE helmet(FULL_HELMET); + BodyArmor = HARDENED_LEATHER bodyarmor(PLATE_MAIL); + RightWielded = STEEL STEEL meleeweapon(SHORT_SWORD) { Enchantment = 1; } + LeftWielded = STEEL STEEL meleeweapon(SHORT_SWORD) { Enchantment = 1; } + CWeaponSkillHits == 100; + RightSWeaponSkillHits = 50; + LeftSWeaponSkillHits = 50; + NameSingular = "goblin prince"; + HeadBitmapPos = 112, 288; + TorsoBitmapPos = 48, 208; + HairColor = rgb16(220, 220, 0); + ClothColor = rgb16(200, 0, 0); + Inventory = { 2, potion { SecondaryMaterial = ANTIDOTE_LIQUID; Chance = 20; }, Random { Category = RING; Chance = 20; } } + } + + Config KING; + { + AttributeBonus = 100; + Helmet = STEEL helmet(FULL_HELMET) { Enchantment = 1; } + BodyArmor = MITHRIL bodyarmor(CHAIN_MAIL) { Enchantment = 1; } + Cloak = OMMEL_HAIR cloak { Enchantment = 1; } + RightWielded = MITHRIL MITHRIL meleeweapon(SHORT_SWORD) { Enchantment = 2; } + LeftWielded = MITHRIL MITHRIL meleeweapon(SHORT_SWORD) { Enchantment = 2; } + RightRing = ring(RING_OF_TELEPORTATION); + RightGauntlet = OMMEL_HAIR gauntlet { Enchantment = 1; } + RightBoot = STEEL boot { Enchantment = 1; } + TotalVolume = 50000; + CWeaponSkillHits == 200; + RightSWeaponSkillHits = 100; + LeftSWeaponSkillHits = 100; + NameSingular = "goblin king"; + IsUnique = true; + CanBeWished = true; + DefaultName = "Guugzamesh"; + DangerModifier = 2000; + IsNameable = false; + CanBeCloned = false; + IsPolymorphable = false; + HeadBitmapPos = 96, 288; + TorsoBitmapPos = 48, 208; + HairColor = rgb16(220, 220, 0); + ClothColor = rgb16(200, 0, 0); + Inventory = { 6, stone { Chance = 50; }, stone { Chance = 50; }, stone { Chance = 50; }, stone { Chance = 50; }, stone { Chance = 50; }, solstone; } + CanBeConfused = false; + NaturalSparkleFlags = HAIR_COLOR; + FireResistance = 30; + ElectricityResistance = 30; + EnergyResistance = 30; + TamingDifficulty = 20; + UndeadVersions = false; + } + + Config JAILER; + { + AttributeBonus = 50; + Helmet = LEATHER helmet; + BodyArmor = LEATHER bodyarmor(PLATE_MAIL); + NameSingular = "goblin jailer"; + ClothColor = rgb16(124, 110, 60); + CanBeGenerated = false; + UndeadVersions = false; + RightWielded = LEATHER whip;/* { Enchantment = 1; }*/ + KnownCWeaponSkills == WHIPS; + CWeaponSkillHits == 50; + RightSWeaponSkillHits = 20; + } + + Config PRISON_WARDEN; + { + AttributeBonus = 80; + Helmet = STEEL helmet(FULL_HELMET) { Enchantment = 1; } + BodyArmor = TIN bodyarmor(CHAIN_MAIL) { Enchantment = 1; } + Cloak = OMMEL_HAIR cloak { Enchantment = 1; } + RightWielded = STEEL STEEL meleeweapon(SHORT_SWORD) { Enchantment = 2; } + LeftWielded = STEEL STEEL meleeweapon(SHORT_SWORD) { Enchantment = 2; } + RightRing = ring(RING_OF_ELECTRICITY_RESISTANCE); + RightGauntlet = OMMEL_HAIR gauntlet { Enchantment = 1; } + RightBoot = STEEL boot { Enchantment = 1; } + TotalVolume = 50000; + CWeaponSkillHits == 200; + RightSWeaponSkillHits = 100; + LeftSWeaponSkillHits = 100; + NameSingular = "goblin prison warden"; + IsUnique = true; + CanBeWished = false; + DefaultName = "Cbolroci"; + DangerModifier = 1000; + IsNameable = false; + CanBeCloned = false; + IsPolymorphable = false; + HeadBitmapPos = 112, 288; + TorsoBitmapPos = 48, 208; + HairColor = rgb16(132, 0, 0); + ClothColor = rgb16(44, 88, 88); + Inventory = { 6, stone { Chance = 50; }, stone { Chance = 50; }, stone { Chance = 50; }, stone { Chance = 50; }, stone { Chance = 50; }, solstone; } + CanBeConfused = false; + /*NaturalSparkleFlags = HAIR_COLOR;*/ + FireResistance = 30; + ElectricityResistance = 30; + EnergyResistance = 30; + TamingDifficulty = 20; + UndeadVersions = false; + CanBeGenerated = false; + } +} + +mommo +{ + DefaultEndurance = 8; + DefaultMana = 0; + CanOpen = false; + StandVerb = "bubbling"; + ForceCustomStandVerb = true; + HostileReplies == "@Dd vibrates in fury."; + FriendlyReplies == "@Dd vibrates oddly."; + NameSingular = "mommo slime"; + IsAbstract = true; + AttackStyle = USE_HEAD; + BaseBiteStrength = 800; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 20; + PanicLevel = 0; + HasALeg = false; + DeathMessage = "@Dd turns into lifeless goo."; + HasEyes = false; + HasHead = false; + AttachedGod = SCABIES; + ClassStates = GAS_IMMUNITY; + BiteCapturesBodyPart = false; + IgnoreDanger = true; + ForceVomitMessage = "You spurt some slime from your body."; + CanChoke = false; + RunDescriptionLineOne = "Flowing"; + RunDescriptionLineTwo = "ahead fast"; + VomittingIsUnhealthy = false; + UsesNutrition = false; + IsImmuneToStickiness = true; + + Config CONICAL; + { + DefaultArmStrength = 2; + DefaultAgility = 4; + DefaultPerception = 9; + DefaultIntelligence = 3; + DefaultWisdom = 3; + DefaultCharisma = 4; + TotalVolume = 150000; + TorsoBitmapPos = 176, 0; + TotalSize = 100; + Adjective = "conical"; + HPRequirementForGeneration = 120; + DayRequirementForGeneration = 12; + Frequency = 1000; + BloodMaterial = YELLOW_SLIME; + FleshMaterial = YELLOW_SLIME; + VomitMaterial = YELLOW_SLIME; + } + + Config FLAT; + { + DefaultArmStrength = 4; + DefaultAgility = 2; + DefaultPerception = 9; + DefaultIntelligence = 2; + DefaultWisdom = 2; + DefaultCharisma = 3; + TotalVolume = 300000; + TorsoBitmapPos = 192, 0; + TotalSize = 75; + Adjective = "flat"; + HPRequirementForGeneration = 80; + DayRequirementForGeneration = 8; + Frequency = 1500; + CanHear = false; + BloodMaterial = BROWN_SLIME; + FleshMaterial = BROWN_SLIME; + VomitMaterial = BROWN_SLIME; + } +} + +golem +{ + DefaultArmStrength = 0; /* depends on material */ + DefaultLegStrength = 0; /* depends on material */ + DefaultDexterity = 0; /* depends on material */ + DefaultAgility = 0; /* depends on material */ + DefaultEndurance = 0; /* has no effect */ + DefaultPerception = 12; + DefaultIntelligence = 4; + DefaultWisdom = 4; + DefaultCharisma = 5; + DefaultMana = 5; + TotalVolume = 100000; + TorsoBitmapPos = 256, 0; + TotalSize = 250; + NameSingular = "golem"; + CanBeGenerated = true; + Sex = UNDEFINED; + SkinColor = rgb16(160, 32, 16); /* for flesh golems */ + EyeColor = rgb16(0, 0, 0); + HeadBitmapPos = 96, 256; + TorsoBitmapPos = 32, 256; + ArmBitmapPos = 64, 256; + LegBitmapPos = 0, 256; + CanUseEquipment = false; + CreateGolemMaterialConfigurations = true; + IsAbstract = true; + BaseUnarmedStrength = 500; + KnownCWeaponSkills == UNARMED; + CWeaponSkillHits == 50; + PanicLevel = 0; + HostileReplies == "Yes, master. Golem kill human. Golem then return."; /* No quotes! (golem engraves) */ + FriendlyReplies == "Yes, master?"; + /* FleshMaterial overridden */ + DeathMessage = "The Holy Words of @dd fly away. The monster's magic fades and it vanishes in seconds."; + UsesNutrition = false; + AttachedGod = NONE; + ClassStates = GAS_IMMUNITY; + Frequency = 1500; + CanTalk = false; + CanRead = true; + BodyPartsDisappearWhenSevered = true; + DangerModifier = 75; + AlwaysUseMaterialAttributes = true; + IsEnormous = true; + CanChoke = false; + UndeadVersions = false; + + Config VALPURIUM; + { + Frequency = 1000; + DangerModifier = 500; + Adjective = "valpurium"; + AttachedGod = VALPURUS; + } + + Config SPIDER_SILK; + { + DefaultName = "experiment ZQ-29"; + DangerModifier = 1500; + IsUnique = true; + CanBeWished = true; + Inventory = { 7, wand(WAND_OF_INVISIBILITY), wand(WAND_OF_TELEPORTATION), wand(WAND_OF_CLONING), scrollofchangematerial, holybook(SOPHOS), SPIDER_SILK bodyarmor(PLATE_MAIL), solstone; } + ClassStates = GAS_IMMUNITY|HASTE|INFRA_VISION|INVISIBLE|TELEPORT_CONTROL; + IsNameable = false; + CanBeCloned = false; + IsPolymorphable = false; + Adjective = "spider silk"; + AttachedGod = SOPHOS; + CanBeConfused = false; + Frequency = 10000; + IsImmuneToItemTeleport = true; + TamingDifficulty = 35; + } + + Config ACIDOUS_BLOOD; + { + CanBeGenerated = false; + Adjective = "acidous blood"; + AttachedGod = CRUENTUS; + } +} + +canine +{ + IsAbstract = true; + ConsumeFlags = CT_FRUIT|CT_MEAT|CT_LIQUID|CT_PROCESSED|CT_BONE; + AttackStyle = USE_HEAD; + KnownCWeaponSkills == BITE; +} + +wolf +{ + DefaultArmStrength = 10; + DefaultAgility = 25; + DefaultEndurance = 10; + DefaultPerception = 24; + DefaultIntelligence = 7; + DefaultWisdom = 5; + DefaultCharisma = 10; + DefaultMana = 0; + TotalVolume = 40000; + TorsoBitmapPos = 224, 0; + HostileReplies == "@Dd growls madly."; + FriendlyReplies == "@Dd growls happily."; + TotalSize = 100; + /* SkinColor overridden */ + NameSingular = "wolf"; + NamePlural = "wolves"; + BaseBiteStrength = 600; + CanBeGenerated = true; + CWeaponSkillHits == 200; + ClassStates = INFRA_VISION; + FleshMaterial = WOLF_FLESH; + AttachedGod = SILVA; +} + +dog +{ + DefaultArmStrength = 5; + DefaultAgility = 15; + DefaultEndurance = 8; + DefaultPerception = 18; + DefaultIntelligence = 5; + DefaultWisdom = 5; + DefaultCharisma = 15; + DefaultMana = 0; + TotalVolume = 20000; + TorsoBitmapPos = 240, 16; + HostileReplies == "@Dd barks in fury."; + /* FriendlyReplies overridden */ + TotalSize = 70; + SkinColor = rgb16(111, 74, 37); + NameSingular = "puppy"; + NamePlural = "puppies"; + BaseBiteStrength = 350; + CanBeGenerated = true; + CWeaponSkillHits == 20; + FleshMaterial = DOG_FLESH; + AttachedGod = SILVA; + Alias == "dog"; + DeathMessage = "@Dd is killed."; + ScienceTalkAdjectiveAttribute = + { + 16, + "arf", "r-ruff", "bark", "woof", "rowf", + "yip", "yap", "yelp", "[fart]", "snarl", + "bow-wow", "AHROUFF", "WOOF", "BARK", "grrrrrrr", + "aaaaoooooooooooo"; + } + ScienceTalkSubstantiveAttribute = + { + 16, + "arf", "r-ruff", "bark", "woof", "rowf", + "yip", "yap", "yelp", "[fart]", "snarl", + "bow-wow", "AHROUFF", "WOOF", "BARK", "grrrrrrr", + "aaaaoooooooooooo"; + } + ScienceTalkPrefix == ""; + ScienceTalkName = + { + 16, + "arf", "r-ruff", "bark", "woof", "rowf", + "yip", "yap", "yelp", "[fart]", "snarl", + "bow-wow", "AHROUFF", "WOOF", "BARK", "grrrrrrr", + "aaaaoooooooooooo"; + } + ScienceTalkPossibility = 90; + ScienceTalkIntelligenceModifier = 1; + ScienceTalkWisdomModifier = 2; + ScienceTalkIntelligenceRequirement = 3; + ScienceTalkWisdomRequirement = 4; +} + +spider +{ + DefaultMana = 0; + BloodMaterial = SPIDER_BLOOD; + CanOpen = false; + SkinColor = rgb16(64, 64, 100); + NameSingular = "spider"; + AttackStyle = USE_HEAD; + CanBeGenerated = true; + ClassStates = INFRA_VISION; + KnownCWeaponSkills == BITE; + FleshMaterial = SPIDER_FLESH; + AttachedGod = SCABIES; + BiteCapturesBodyPart = false; + AutomaticallySeen = true; + IsImmuneToStickiness = true; + DangerModifier = 25; + IsAbstract = true; + TorsoMainColor = rgb16(30, 30, 30); + IsCatacombCreature = true; + + Config LARGE; + { + DefaultArmStrength = 2; + DefaultAgility = 5; + DefaultEndurance = 4; + DefaultPerception = 9; + DefaultIntelligence = 3; + DefaultWisdom = 3; + DefaultCharisma = 4; + Adjective = "large"; + TorsoBitmapPos = 304, 16; + BaseBiteStrength = 400; + CWeaponSkillHits == 50; + TotalVolume = 500; + TotalSize = 10; + HostileReplies == "@Dd isn't interested in negotiation with you."; + FriendlyReplies == "@Dd ignores your small talk."; + } + + Config GIANT; + { + DefaultArmStrength = 4; + DefaultAgility = 10; + DefaultEndurance = 12; + DefaultPerception = 15; + DefaultIntelligence = 4; + DefaultWisdom = 4; + DefaultCharisma = 5; + Adjective = "giant"; + TorsoBitmapPos = 256, 0; + BaseBiteStrength = 400; + CWeaponSkillHits == 50; + TotalVolume = 5000; + TotalSize = 50; + HostileReplies == "@Dd stridulates wildly, producing a small creaking noise."; + FriendlyReplies == "@Dd stridulates softly, producing a barely audible but quite confortable noise."; + } + + Config ARANEA; + { + DefaultArmStrength = 20; + DefaultAgility = 26; + DefaultEndurance = 18; + DefaultPerception = 26; + DefaultIntelligence = 4; + DefaultWisdom = 4; + DefaultCharisma = 5; + Adjective = "aranea"; + TorsoBitmapPos = 256, 0; + BaseBiteStrength = 400; + CWeaponSkillHits == 50; + TotalVolume = 5000; + TotalSize = 50; + Frequency = 0; + HostileReplies == "@Dd stridulates wildly, producing a small creaking noise."; + FriendlyReplies == "@Dd stridulates softly, producing a barely audible but quite confortable noise."; + } +} + +jackal +{ + DefaultArmStrength = 3; + DefaultAgility = 12; + DefaultEndurance = 6; + DefaultPerception = 18; + DefaultIntelligence = 5; + DefaultWisdom = 5; + DefaultCharisma = 7; + DefaultMana = 0; + TotalVolume = 15000; + TorsoBitmapPos = 304, 0; + HostileReplies == "@Dd howls in fury."; + FriendlyReplies == "@Dd howls happily."; + TotalSize = 80; + SkinColor = rgb16(255, 255, 255); + NameSingular = "jackal"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 300; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 50; + FleshMaterial = JACKAL_FLESH; + AttachedGod = SILVA; +} + +ass +{ + DefaultArmStrength = 20; + DefaultAgility = 4; + DefaultEndurance = 15; + DefaultPerception = 15; + DefaultIntelligence = 5; + DefaultWisdom = 5; + DefaultCharisma = 5; + DefaultMana = 0; + TotalVolume = 40000; + TorsoBitmapPos = 288, 0; + HostileReplies == "@Dd brays angrily."; + FriendlyReplies == "@Dd brays cheerfully."; + TotalSize = 150; + SkinColor = rgb16(80, 80, 80); + Adjective = "mutant"; + NameSingular = "ass"; + NamePlural = "asses"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 150; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 20; + FleshMaterial = MUTANT_ASS_FLESH; + DeathMessage = "@Dd neighs one last time and dies."; + BaseEmitation = rgb24(140, 100, 100); + Alias == "donkey"; + AttachedGod = SCABIES; + AutomaticallySeen = true; + WillCarryItems = true; + BloodMaterial = GLOWING_BLOOD; +} + +communist /* Ivan */ +{ + DefaultArmStrength = 40; + DefaultLegStrength = 40; + DefaultDexterity = 20; + DefaultAgility = 20; + DefaultEndurance = 30; + DefaultPerception = 18; + DefaultIntelligence = 7; + DefaultWisdom = 6; + DefaultCharisma = 10; + DefaultMana = 5; + CriticalModifier = 4; + IsNameable = false; + ArmSpecialColor = rgb16(160, 0, 0); + BeltColor = rgb16(32, 32, 32); + ClothColor = rgb16(64, 56, 24); + HeadBitmapPos = 96, 176; + TorsoBitmapPos = 32, 144; + ArmBitmapPos = 64, 112; + LegBitmapPos = 0, 0; + TotalVolume = 120000; + TotalSize = 230; + CanRead = true; + NameSingular = "communist"; + Alias == "Ivan"; + ClassStates = INFRA_VISION; + Helmet = helmet(GOROVITS_FAMILY_GAS_MASK); + Cloak = OMMEL_HAIR cloak(CLOAK_OF_ELECTRICITY_RESISTANCE) { Enchantment = 2; } + BodyArmor = STEEL bodyarmor(PLATE_MAIL) { Enchantment = 2; } + Belt = TROLL_HIDE belt { Enchantment = 2; } + RightWielded = gorovitsweapon(GOROVITS_HAMMER); + LeftWielded = gorovitsweapon(GOROVITS_SICKLE); + RightRing = ring(RING_OF_FIRE_RESISTANCE); + LeftRing = ring(RING_OF_POISON_RESISTANCE); + KnownCWeaponSkills = { 5, UNARMED, SMALL_SWORDS, BLUNT_WEAPONS, AXES, POLE_ARMS; } + CWeaponSkillHits = { 5, 100, 200, 200, 100, 100; } + RightSWeaponSkillHits = 100; + LeftSWeaponSkillHits = 100; + RightGauntlet = PHOENIX_FEATHER gauntlet { Enchantment = 2; } + RightBoot = STEEL boot(BOOT_OF_AGILITY) { Enchantment = 2; } + PanicLevel = 33; + Inventory = { 3, lantern, fiftymillionroubles, potion { SecondaryMaterial = VODKA; Times = 5; } } + IsUnique = true; + CanBeWished = true; + DefaultName = "Ivan"; + DeathMessage = "@Dd falls groaning bravely: \"Party revenges @nu\"!"; + HostileReplies == "\"You capitalist! Lenin want @nu kill capitalists!\""; + FriendlyReplies = + { + 11, + "\"Da, @nu like killing.\"", + "\"@Nu ruski specialist.\"", + "\"@Nu work. Else @nu nerve stapled.\"", + "\"Party mean big weapons. @Nu like big weapons. @Nu kill for Party.\"", + "\"CCCP roxxx.\"", + "\"@Nu like throw Ladas. You want compete?\"", + "\"Why AK not invented?\"", + "\"@Nu buy kyber eyes. @Nu see in dark.\"", + "\"Uncle Lenin live in Russia. Lenin strong guy. @Nu like.\"", + "\"Vodka strong, meat rotten.\"", + "\"Vladimir @nu best buddy. @Nu meet Vladimir first in magic test area near Voktsovadil.\""; + } + AttachedGod = LORICATUS; + ConstantCommandFlags = FLEE_FROM_ENEMIES; + IsEnormous = true; + ScienceTalkName = + { + 21, + "materialism", "marxism", "leninism", "stalinism", "socialism", + "castroism", "maoism", "trotskyism", "anticapitalism", "agriculture", + "communism", "atheism", "hoxhaism", "class struggles", "socialist art", + "communitarianism", "bolshevism", "sickle smithing", "ballistics", "weaponry", + "vodka distillation"; + } + ScienceTalkPossibility = 25; + ScienceTalkIntelligenceModifier = 10; + ScienceTalkWisdomModifier = 25; + ScienceTalkIntelligenceRequirement = 3; + ScienceTalkWisdomRequirement = 3; + IsAlcoholic = true; +} + +hunter +{ + DefaultArmStrength = 25; + DefaultLegStrength = 15; + DefaultDexterity = 15; + DefaultAgility = 20; + DefaultEndurance = 15; + DefaultPerception = 24; + DefaultIntelligence = 10; + DefaultWisdom = 10; + DefaultCharisma = 15; + DefaultMana = 5; + ClothColor = rgb16(128, 80, 48); + BeltColor = rgb16(144, 96, 60); + BootColor = rgb16(90, 50, 10); + HeadBitmapPos = 96, 192; + TorsoBitmapPos = 32, 192; + ArmBitmapPos = 64, 128; + LegBitmapPos = 0, 96; + TotalVolume = 80000; + TotalSize = 180; + NameSingular = "hunter"; + Helmet = HARDENED_LEATHER helmet; + Cloak = LEATHER cloak; + BodyArmor = TROLL_HIDE bodyarmor(PLATE_MAIL); + RightWielded = IRON meleeweapon(SPEAR); + RightGauntlet = LEATHER gauntlet; + LeftGauntlet = 0; + RightBoot = HARDENED_LEATHER boot; + KnownCWeaponSkills == POLE_ARMS; + CWeaponSkillHits == 200; + RightSWeaponSkillHits = 200; + PanicLevel = 20; + Inventory == beartrap; + HostileReplies == "\"Your head will look fine above my fireplace!\""; + FriendlyReplies = + { + 5, + "\"A man is not a man unless he has lost his left arm in a battle against a polar bear.\"", + "\"Bears, ogres, slaves, farmers... Ah, there's so much to hunt here!\"", + "\"I am the Great White Hunter. Get out of My way!\"", + "\"I saw a communist visiting the city a few days past. I'm now organising a party to seek and hunt him down.\"", + "\"It is good Petrus had his predecessor assassinated. Can you believe it, the dude tried to take from us our natural right to carry a spear in the street!\""; + } + AttachedGod = LORICATUS; + ScienceTalkAdjectiveAttribute = + { + 16, + "!hunting", "!laying traps for", "!tracking", "!stuffing", "!techniques of slaying", + "!the locations of the vital organs of", "!the joy of defeating", "!capturing", "!ambushing", "!sharpshooting", + "!preemptively striking against", "!ridding the world of all", "!skinning", "!lynching", "!eliminating the threat of", + "!chasing"; + } + ScienceTalkSubstantiveAttribute = + { + 17, + "black", "dangerous", "baby", "young", "wild", + "fierce", "endangered", "mad", "rare", "giant", + "evil", "malicious", "injured", "weak", "tasty", + "sick", "fat"; + } + ScienceTalkPrefix == ""; + ScienceTalkName = + { + 74, + "slaves", "farmers", "communists", "prisoners", "witches", + "goblins", "orcs", "kobolds", "gibberlings", "stray cats", + "neighbour's pets", "reindeer", "deer", "elk", "moose", + "bears", "polar bears", "mammoths", "ducks", "mules", + "cows", "levitating ostriches", "ankhegs", "rabbits", "fish", + + "wolves", "hamsters", "vegetables", "moles", "lemmings", + "hedgehogs", "foxes", "vixens", "beavers", "geese", + "wives", "women", "lice", "oxen", "wildebeest", + "grouse", "roe", "sheep", "rhinoceros", "heathen", + "chamois", "mushrooms", "hippopotami", "dwarves", "elves", + + "land octopi", "outlaws", "bunnies", "jackals", "hippies", + "bats", "scientists", "dragons", "goats", "falcons", + "ravens", "trolls", "werewolves", "zombies", "Siberian tigers", + "gnus", "llamas", "nerds", "people you don't like", "squirrels", + "flying squirrels", "lynxes", "chickens", "democrats"; + } + ScienceTalkPossibility = 85; + ScienceTalkIntelligenceModifier = 2; + ScienceTalkWisdomModifier = 1; + ScienceTalkIntelligenceRequirement = 6; + ScienceTalkWisdomRequirement = 3; + IsSadist = true; +} + +bear +{ + DefaultArmStrength = 50; + DefaultAgility = 15; + DefaultEndurance = 20; + DefaultPerception = 15; + DefaultIntelligence = 8; + DefaultWisdom = 10; + DefaultCharisma = 15; + DefaultMana = 0; + TorsoBitmapPos = 336, 0; + HostileReplies == "@Dd growls madly."; + FriendlyReplies == "@Dd growls happily."; + NameSingular = "bear"; + AttackStyle = USE_HEAD|USE_ARMS; + BaseUnarmedStrength = 600; + KnownCWeaponSkills = { 2, UNARMED, BITE; } + CWeaponSkillHits == 100; + FleshMaterial = BEAR_FLESH; + DeathMessage = "@Dd groans terribly and falls dead to the ground."; + AttachedGod = SILVA; + IsAbstract = true; + +Config PANDA_BEAR; + { + Adjective = "panda"; + SkinColor = rgb16(80, 80, 80); + CanBeGenerated = true; + TotalVolume = 125000; + TotalSize = 125; + AttributeBonus = -35; + TorsoBitmapPos = 193, 16; + DeathMessage = "@Dd groans terribly and falls dead to the ground. You feel real bad for the endangering of these species."; + Frequency = 5000; + } + + Config BLACK_BEAR; + { + Adjective = "black"; + SkinColor = rgb16(80, 80, 80); + CanBeGenerated = true; + TotalVolume = 150000; + TotalSize = 150; + AttributeBonus = -30; + } + + Config GRIZZLY_BEAR; + { + Adjective = "grizzly"; + SkinColor = rgb16(140, 80, 40); + CanBeGenerated = true; + TotalVolume = 200000; + TotalSize = 200; + AttributeBonus = -15; + } + + Config CAVE_BEAR; + { + Adjective = "cave"; + SkinColor = rgb16(170, 130, 90); + CanBeGenerated = true; + TotalVolume = 250000; + TotalSize = 250; + IsEnormous = true; + } + + Config POLAR_BEAR; + { + Adjective = "polar"; + SkinColor = rgb16(240, 240, 240); + TotalVolume = 300000; + TotalSize = 300; + AttributeBonus = 15; + IsEnormous = true; + } +} + +dolphin +{ + DefaultArmStrength = 10; + DefaultAgility = 30; + DefaultEndurance = 10; + DefaultPerception = 30; + DefaultIntelligence = 100; + DefaultWisdom = 100; + DefaultCharisma = 30; + DefaultMana = 0; + StandVerb = "rolling"; /* only shown if not swimming */ + CanOpen = false; + MoveType = SWIM; + TotalVolume = 150000; + TorsoBitmapPos = 320, 0; + HostileReplies == "@Dd peeps diabolically to you."; + FriendlyReplies == "@Dd peeps passionately to you."; + TotalSize = 300; + SkinColor = rgb16(144, 144, 144); + EyeColor = rgb16(100, 100, 255); + Adjective = "female"; + NameSingular = "dolphin"; + PostFix = "in season"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 200; + HasALeg = false; + FleshMaterial = DOLPHIN_FLESH; + AttachedGod = SOPHOS; + ForceVomitMessage = "You push your fin down to your throat and vomit."; + ScienceTalkPossibility = 100; + ScienceTalkIntelligenceModifier = 1000; + ScienceTalkWisdomModifier = 1000; + ScienceTalkIntelligenceRequirement = 50; + ScienceTalkWisdomRequirement = 50; +} + +slave +{ + DefaultArmStrength = 25; + DefaultLegStrength = 25; + DefaultDexterity = 15; + DefaultAgility = 15; + DefaultEndurance = 20; + DefaultPerception = 15; + DefaultIntelligence = 15; + DefaultWisdom = 20; + DefaultCharisma = 10; + DefaultMana = 5; + SkinColor = rgb16(128, 80, 48); + HairColor = rgb16(80, 48, 32); + ClothColor = rgb16(56, 48, 20); + TotalVolume = 60000; + TotalSize = 160; + NameSingular = "slave"; + KnownCWeaponSkills = { 2, UNARMED, BLUNT_WEAPONS; } + CWeaponSkillHits = { 2, 100, 100; } + DefaultMoney = 0; + FriendlyReplies = /* used only if the slave has been bought */ + { + 5, + "\"Whatever the master wants.\"", + "\"Work work work all day long. No, that was not a complaint! Don't punish!\"", + "\"I love all my masters. At least when the whip is being washed.\"", + "\"I would like to be like Ivan. Ivan is a good worker.\"", + "\"I am putting myself to the fullest possible use, which is all I think that any conscious entity can ever hope to do.\""; + } + AttachedGod = MELLIS; + Inventory == 0; + IsMasochist = true; + + Config DARK_FOREST; + { + AttributeBonus = -50; + } +} + +petrusswife +{ + DefaultArmStrength = 7; + DefaultLegStrength = 7; + DefaultDexterity = 7; + DefaultAgility = 7; + DefaultEndurance = 7; + DefaultPerception = 21; + DefaultIntelligence = 8; + DefaultWisdom = 10; + DefaultCharisma = 80; + DefaultMana = 20; + Sex = FEMALE; + ClothColor = rgb16(150, 0, 0); + BeltColor = rgb16(180, 180, 0); + TorsoBitmapPos = 32, 160; + ArmBitmapPos = 64, 160; + LegBitmapPos = 0, 112; + TotalVolume = 40000; + TotalSize = 170; + CanRead = true; + NameSingular = "Petrus's wife"; + NamePlural = "Petrus's wives"; + ArticleMode = NO_ARTICLE; + IsAbstract = true; + PanicLevel = 95; + BaseUnarmedStrength = 200; + IsExtraCoward = true; + WieldedPosition = 0, -1; + HostileReplies == "\"Murderer! Just wait until Petrus finds you!\""; + FriendlyReplies = + { + 5, + "\"I'm so sick jealous to those dolphins...\"", + "\"I'm Petrus's favorite, not she!\"", + "\"Why must Petrus stay in this forest? There isn't even a proper hairdresser here!\"", + "\"Being one of six wives is a dream job. Pay is good and you only have to work about one night a week!\""; + "\"That head on the wall looks really scary sometimes. Just like we were being watched.\""; + } + AttachedGod = DULCIS; + WillCarryItems = false; + IsExtraFragile = true; + Belt = chastitybelt(OCTAGONAL_LOCK); + UndeadVersions = false; + + Config 1; + { + HairColor = rgb16(24, 24, 24); + HeadBitmapPos = 112, 0; + PostFix = "number 1"; + } + + Config 2; + { + HairColor = rgb16(24, 24, 24); + HeadBitmapPos = 112, 16; + PostFix = "number 2"; + } + + Config 3; + { + HairColor = rgb16(48, 40, 8); + HeadBitmapPos = 112, 0; + PostFix = "number 3"; + } + + Config 4; + { + DefaultIntelligence = 4; /* she's blond */ + DefaultWisdom = 8; + DefaultCharisma = 85; + HairColor = rgb16(200, 96, 0); + HeadBitmapPos = 112, 32; + PostFix = "number 4"; + } + + Config 5; + { + HairColor = rgb16(60, 48, 24); + HeadBitmapPos = 112, 48; + PostFix = "number 5"; + } + + Config 6; + { + HairColor = rgb16(200, 0, 0); + HeadBitmapPos = 112, 64; + PostFix = "number 6"; + } +} + +housewife +{ + DefaultArmStrength = 10; + DefaultLegStrength = 10; + DefaultDexterity = 15; + DefaultAgility = 15; + DefaultEndurance = 15; + DefaultPerception = 24; + DefaultIntelligence = 10; + DefaultWisdom = 20; + DefaultCharisma = 20; + DefaultMana = 10; + Sex = FEMALE; + TorsoMainColor = rgb16(200, 200, 200); + ArmMainColor = rgb16(100, 100, 100); + LegMainColor = rgb16(180, 80, 0); + /* HairColor is random */ + /* HeadBitmapPos is random */ + TorsoBitmapPos = 32, 160; + ArmBitmapPos = 64, 160; + LegBitmapPos = 0, 128; + TotalVolume = 70000; + TotalSize = 160; + CanRead = true; + NameSingular = "housewife"; + NamePlural = "housewives"; + KnownCWeaponSkills = { 2, UNARMED, UNCATEGORIZED; } + CWeaponSkillHits = { 2, 50, 50; } + BaseUnarmedStrength = 200; + IsExtraCoward = true; + HostileReplies == "\"Face my rolling pin! Graah!\""; + AttachedGod = SEGES; + WieldedPosition = 0, -1; + FriendlyReplies = + { + 5, + "\"Can you help me find my husband? He is hiding somewhere. He's that farmer who's just been mugged with a frying pan.\"", + "\"Yesterday a bear rushed through my kitchen wall and ruined all my pies. Animals are truly annoying. Why can't we just burn the whole forest down?\"", + "\"Wolves ate my seventh daughter last week. Damn. It'll take eight years to produce an equally good replacement worker.\"", + "\"Petrus's wives are so arrogant towards us working class ones. Grr...\"", + "\"If you men only knew!\""; + } + + Config MONDEDR; + { + LegMainColor = rgb16(0, 80, 180); + Cloak = CLOTH cloak; + Inventory == lantern; + FriendlyReplies = + { + 2, + "\"If Mondedr wasn't on Petrus's wanted list it wouldn't be so dark all the time.\"", + "\"I heard the shopkeeper in town is a dirty one. I'd rather have the previous owner of the shop.\""; + } + } + + Config DARK_FOREST; + { + LegMainColor = rgb16(120, 120, 120); + Cloak = CLOTH cloak; + FriendlyReplies = + { + 2, + "\"Gah! Put me back in the jail cell! I don't want to end up with those ghastly UTFA floosies.\"", + "\"Just hurry and get me to Attnam already. Has that jeweller's shop opened up there yet?\""; + } + } +} + +femaleslave +{ + DefaultArmStrength = 10; + DefaultLegStrength = 10; + DefaultDexterity = 10; + DefaultAgility = 10; + DefaultEndurance = 10; + DefaultPerception = 18; + DefaultIntelligence = 15; + DefaultWisdom = 25; + DefaultCharisma = 25; + DefaultMana = 5; + Sex = FEMALE; + SkinColor = rgb16(160, 100, 64); + HairColor = rgb16(80, 48, 32); + HeadBitmapPos = 112, 80; + TorsoBitmapPos = 32, 208; + ArmBitmapPos = 64, 208; + LegBitmapPos = 0, 144; + TotalVolume = 40000; + TotalSize = 170; + Adjective = "female"; + RightWielded = palmbranch; + BaseUnarmedStrength = 200; + HostileReplies == "\"Yikes!\""; + IsAbstract = true; + IsExtraCoward = true; + CanRead = true; + + Config ATTNAM; + { + NameSingular = "slave"; + AttachedGod = MELLIS; + FriendlyReplies = + { + 5, + "\"Praise our lord Petrus!\"", + "\"Does that toy frog really need us serving it?\"", + "\"I'm not a slave. I'm a high-ranking palm branch officer with a good career history.\"", + "\"I can tell you, serving Petrus is much more fun than pleasing that Decos bastard.\"", + "\"You seem oddly familiar. Oh! Isn't that the monkey smell of my home village!\""; + } + } + + Config NEW_ATTNAM; + { + DefaultAgility = 15; + DefaultEndurance = 15; + DefaultIntelligence = 20; + DefaultWisdom = 30; + DefaultCharisma = 20; + NameSingular = "servant"; + AttachedGod = SEGES; + FriendlyReplies = + { + 7, /* 4 first are used before revolution */ + "\"Thank Silva I don't have to gather bananas!\"", + "\"My sister got a promotion last year. I've heard she is now a very successful slave in the Cathedral of Attnam. I am so jealous!\"", + "\"Palm leaves are good for health. Mr. Decos loves their smell.\"", + "\"When I was a highly educated doctor in independent Tweraif, I didn't have to wear stupid bikinis all day long.\"", + "\"Our hero!\" @Dd shouts as @pp hugs you. \"Aargh! Go away, you're all sweaty and bloody!\"", + "\"Don't tell me you need a palm leaf expert, too.\"", + "\"Now it's my sister's turn to be jealous!\""; + } + AutomaticallySeen = true; + } +} + +librarian +{ + DefaultArmStrength = 10; + DefaultLegStrength = 10; + DefaultDexterity = 5; + DefaultAgility = 5; + DefaultEndurance = 10; + DefaultPerception = 12; + DefaultIntelligence = 30; + DefaultWisdom = 15; + DefaultCharisma = 10; + DefaultMana = 20; + HairColor = rgb16(200, 200, 200); + ClothColor = rgb16(48, 48, 48); + ArmMainColor = rgb16(180, 180, 180); + HeadBitmapPos = 96, 224; + TorsoBitmapPos = 32, 80; + ArmBitmapPos = 64, 16; + LegBitmapPos = 0, 0; + TotalVolume = 80000; + TotalSize = 170; + CanRead = true; + NameSingular = "librarian"; + PanicLevel = 50; + CanBeCloned = false; + DefaultMoney = 2000; + /* Replies depend on story state */ + IsUnique = true; + IsNameable = false; + DefaultName = "Haathbar"; + TamingDifficulty = NO_TAMING; + IsExtraCoward = true; + AttachedGod = SOPHOS; + PolymorphIntelligenceRequirement = 20; + ScienceTalkPossibility = 25; + ScienceTalkIntelligenceModifier = 100; + ScienceTalkWisdomModifier = 25; + ScienceTalkIntelligenceRequirement = 20; + ScienceTalkWisdomRequirement = 10; +} +/*this should be done sometime */ +/* +jeweller +{ + Sex = FEMALE; + + DefaultArmStrength = 10; + DefaultLegStrength = 10; + DefaultDexterity = 5; + DefaultAgility = 5; + DefaultEndurance = 10; + DefaultPerception = 12; + DefaultIntelligence = 30; + DefaultWisdom = 15; + DefaultCharisma = 10; + DefaultMana = 20; + HairColor = rgb16(200, 200, 200); + ClothColor = rgb16(48, 48, 48); + ArmMainColor = rgb16(180, 180, 180); + HeadBitmapPos = 96, 224; + TorsoBitmapPos = 32, 80; + ArmBitmapPos = 64, 16; + LegBitmapPos = 0, 0; + TotalVolume = 80000; + TotalSize = 170; + CanRead = true; + NameSingular = "librarian"; + PanicLevel = 50; + CanBeCloned = false; + DefaultMoney = 2000;*/ + /* Replies depend on story state */ + /*IsUnique = true; + IsNameable = false; + DefaultName = "Libra"; + TamingDifficulty = NO_TAMING; + IsExtraCoward = true; + AttachedGod = MELLIS; + PolymorphIntelligenceRequirement = 20; + ScienceTalkPossibility = 25; + ScienceTalkIntelligenceModifier = 100; + ScienceTalkWisdomModifier = 25; + ScienceTalkIntelligenceRequirement = 20; + ScienceTalkWisdomRequirement = 10; +}*/ + +zombie +{ + DefaultArmStrength = 12; + DefaultLegStrength = 12; + DefaultDexterity = 3; + DefaultAgility = 4; + DefaultEndurance = 10; + DefaultPerception = 12; + DefaultIntelligence = 3; + DefaultWisdom = 3; + DefaultCharisma = 3; + DefaultMana = 0; + SkinColor = rgb16(0, 120, 120); + EyeColor = rgb16(200, 200, 0); + ClothColor = rgb16(56, 16, 96); + HeadBitmapPos = 112, 112; + TorsoBitmapPos = 32, 224; + ArmBitmapPos = 64, 224; + LegBitmapPos = 0, 160; + TotalVolume = 80000; + TotalSize = 160; + NameSingular = "zombie"; + CanBeGenerated = true; + Sex = UNDEFINED; + PanicLevel = 0; + BaseUnarmedStrength = 200; + /* Replies overridden */ + DeathMessage = "@Dd is slain (again)."; + AttachedGod = MORTIFER; + ClassStates = GAS_IMMUNITY; + WieldedPosition = -1, -2; + IsExtraFragile = true; + IsCatacombCreature = true; + CreateUndeadConfigurations = true; + UndeadVersions = false; + UndeadAttributeModifier = 75; + Frequency = 500; + + Config ZOMBIE_OF_KHAZ_ZADM; + { + DefaultName = "Khaz-zadm"; + TamingDifficulty = NO_TAMING; + IsNameable = false; + IsUnique = true; + DefaultArmStrength = 15; + DefaultLegStrength = 15; + DefaultDexterity = 10; + DefaultAgility = 20; + DefaultEndurance = 15; + DefaultPerception = 18; + DefaultIntelligence = 7; + DefaultWisdom = 5; + DefaultCharisma = 5; + RightWielded = VALPURIUM meleeweapon(HAMMER); + ClassStates = LEPROSY; + Sex = MALE; + IsImmuneToLeprosy = true; + CanBeGenerated = false; + CreateUndeadConfigurations = false; + } +} + +imp +{ + DefaultPerception = 15; + DefaultIntelligence = 10; + DefaultWisdom = 10; + DefaultCharisma = 5; + DefaultMana = 10; + EyeColor = rgb16(200, 200, 0); + ClothColor = rgb16(111, 74, 37); + HairColor = rgb16(100, 0, 0); + HeadBitmapPos = 96, 16; + TorsoBitmapPos = 48, 112; + ArmBitmapPos = 64, 32; + LegBitmapPos = 16, 80; + TotalVolume = 40000; + TotalSize = 100; + NameSingular = "imp"; + UsesLongArticle = true; + CanBeGenerated = true; + Sex = UNDEFINED; + CanUseEquipment = false; + KnownCWeaponSkills == UNARMED; + CWeaponSkillHits == 50; + PanicLevel = 75; + BaseUnarmedStrength = 300; + FleshMaterial = SULFUR; + UsesNutrition = false; + AttachedGod = MORTIFER; + ClassStates = GAS_IMMUNITY; + FireResistance = 1000; + WillCarryItems = false; + CanChoke = false; + UndeadVersions = false; +} + +bat +{ + DefaultArmStrength = 2; + DefaultAgility = 40; + DefaultEndurance = 8; + DefaultPerception = 24; + DefaultIntelligence = 7; + DefaultWisdom = 5; + DefaultCharisma = 5; + DefaultMana = 0; + StandVerb = "flying"; + CanOpen = false; + TotalVolume = 1000; + TorsoBitmapPos = 464, 16; + HostileReplies == "@Dd squeaks evilly."; + FriendlyReplies == "@Dd squeaks happily."; + TotalSize = 20; + SkinColor = rgb16(64, 64, 64); + NameSingular = "bat"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 200; + CanBeGenerated = true; + ClassStates = INFRA_VISION; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 20; + PanicLevel = 75; + FleshMaterial = BAT_FLESH; + AttachedGod = INFUSCOR; + BiteCapturesBodyPart = false; + MoveType = FLY; + IsCatacombCreature = true; +} + +fruitbat +{ + DefaultArmStrength = 2; + DefaultAgility = 40; + DefaultEndurance = 8; + DefaultPerception = 24; + DefaultIntelligence = 7; + DefaultWisdom = 5; + DefaultCharisma = 10; + DefaultMana = 0; + StandVerb = "flying"; + CanOpen = false; + TotalVolume = 1000; + TorsoBitmapPos = 464, 16; + HostileReplies == "@Dd squeaks rapaciously."; + FriendlyReplies == "@Dd squeaks happily."; + TotalSize = 20; + SkinColor = rgb16(190, 130, 64); + TorsoMainColor = rgb16(180, 180, 180); + NameSingular = "fruit bat"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 100; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 20; + PanicLevel = 75; + FleshMaterial = BAT_FLESH; + AttachedGod = CLEPTIA; + ClassStates = INFRA_VISION; /* So that invisible player doesn't screw the AI */ + BiteCapturesBodyPart = false; + MoveType = FLY; +} + +mistress +{ + DefaultArmStrength = 20; + DefaultLegStrength = 20; + DefaultDexterity = 40; + DefaultAgility = 40; + DefaultEndurance = 20; + DefaultPerception = 30; + DefaultIntelligence = 20; + DefaultWisdom = 20; + DefaultCharisma = 40; + DefaultMana = 10; + Sex = FEMALE; + SkinColor = rgb16(255, 212, 192); + HairColor = rgb16(200, 200, 200); + ClothColor = rgb16(35, 35, 35); + HeadBitmapPos = 112, 240; + TorsoBitmapPos = 48, 96; + ArmBitmapPos = 80, 80; + LegBitmapPos = 16, 32; + TotalVolume = 50000; + TotalSize = 180; + CanRead = true; + NameSingular = "mistress"; + NamePlural = "mistresses"; + CanBeGenerated = true; + BodyArmor = LEATHER bodyarmor(PLATE_MAIL); + RightGauntlet = LEATHER gauntlet; + RightBoot = LEATHER boot; + RightWielded = NYMPH_HAIR whip { Enchantment = 1; } + LeftWielded = NYMPH_HAIR whip { Enchantment = 1; } + KnownCWeaponSkills == WHIPS; + CWeaponSkillHits == 200; + RightSWeaponSkillHits = 200; + LeftSWeaponSkillHits = 200; + ClassStates = INFRA_VISION; + PanicLevel = 20; + Inventory = { 2, banana { Chance = 10; }, holybanana { Chance = 1; } } + HostileReplies == "\"Come closer, little boy, I'll teach you something...\""; + AttachedGod = NEFAS; + WieldedPosition = 0, -1; + FriendlyReplies = + { + 3, + "\"What? You want more whip?\"", + "\"Ever participated in the weekly orgy at Nefas's High Temple? I guess not. You wouldn't have survived.\"", + "\"If you think I'm rough, you should avoid Sherarax, my queen. No man has ever lived through a night with her.\""; + } + ScienceTalkAdjectiveAttribute = + { + 52, + "applied", "theoretical", "classical", "the future of", "queer", + "practical", "the secrets of", "ancient", "empirical", "statistical", + "the axioms of", "forbidden", "popular", "obscene", "ceremonial", + "environmental", "the code of", "modern", "hard", "unorthodox", + "the principle of", "oral", "commercial", "slippery", "scientific", + + "the theory of", "educational", "recreational", "phallic", "gothic", + "motorized", "tropical", "the purpose of", "happy", "political", + "the history of", "the creative uses of", "chaotic", "erotic", "arctic", + "experimental", "lewd", "clinical", "electrical", "nymphomaniacal", + "intellectual", "the origin of", "hot", "frictionless", "lesbian", + + "the art of", "sick"; + } + ScienceTalkSubstantiveAttribute = + { + 4, + "New Age", "last century", "horseback", "pagan"; + } + ScienceTalkPrefix = + { + 19, + "", "", "", "", "", + "", "", "", "", "", + "ethno", "mega", "neo", "necro", "aero", + "pyro", "caco", "sado", "hetero"; + } + ScienceTalkName = + { + 52, + "impalement", "bondage", "slavery", "torture", "disembowelment", + "whipmaking", "orgies", "heresy", "brazen bulls", "necrophilia", + "masochism", "dentistry", "swedish", "feminism", "licking", + "mushrooms", "rock", "nefasim", "birth-control", "fetishes", + "testicle crushing", "pleasures", "anatomy", "pain", "sin", + + "bananas", "bestiality", "agony", "Kama Sutra", "spanking", + "piercings", "adultery", "frottage", "thumbscrews", "egoism", + "beer", "castration", "watersports", "scaphism", "seduction", + "iron maidens", "fisting", "drugs", "lingerie", "tablillas", + "poker", "reproduction", "sake", "idolatry", "horror", + + "sensory deprivation tanks", "hallucinations"; + } + ScienceTalkPossibility = 95; + ScienceTalkIntelligenceModifier = 2; + ScienceTalkWisdomModifier = 1; + ScienceTalkIntelligenceRequirement = 6; + ScienceTalkWisdomRequirement = 3; + IsSadist = true; + IsMasochist = true; + + Config TORTURING_CHIEF; + { + HairColor = rgb16(200, 200, 0); + AttributeBonus = 15; + BodyArmor = NYMPH_HAIR bodyarmor(PLATE_MAIL) { Enchantment = 1; } + RightGauntlet = NYMPH_HAIR gauntlet { Enchantment = 1; } + RightBoot = NYMPH_HAIR boot { Enchantment = 1; } + RightWielded = OMMEL_HAIR whip { Enchantment = 2; } + LeftWielded = OMMEL_HAIR whip { Enchantment = 2; } + CWeaponSkillHits == 500; + RightSWeaponSkillHits = 500; + LeftSWeaponSkillHits = 500; + NameSingular = "mistress torturing chief"; + PanicLevel = 15; + } + + Config WHIP_CHAMPION; + { + HairColor = rgb16(200, 0, 0); + AttributeBonus = 30; + BodyArmor = OMMEL_HAIR bodyarmor(PLATE_MAIL) { Enchantment = 2; } + RightGauntlet = OMMEL_HAIR gauntlet { Enchantment = 2; } + RightBoot = OMMEL_HAIR boot { Enchantment = 2; } + RightWielded = PHOENIX_FEATHER whip(RUNED_WHIP) { Enchantment = 3; } + LeftWielded = PHOENIX_FEATHER whip(RUNED_WHIP) { Enchantment = 3; } + CWeaponSkillHits == 1000; + RightSWeaponSkillHits = 1000; + LeftSWeaponSkillHits = 1000; + NameSingular = "mistress whip champion"; + PanicLevel = 10; + } + + Config WAR_LADY; + { + HairColor = rgb16(80, 64, 32); + AttributeBonus = 45; + BodyArmor = PHOENIX_FEATHER bodyarmor(PLATE_MAIL) { Enchantment = 3; } + Cloak = PHOENIX_FEATHER cloak { Enchantment = 3; } + RightGauntlet = PHOENIX_FEATHER gauntlet { Enchantment = 3; } + RightBoot = PHOENIX_FEATHER boot { Enchantment = 3; } + RightWielded = ANGEL_HAIR RUBY whip(RUNED_WHIP) { Enchantment = 4; } + LeftWielded = ANGEL_HAIR RUBY whip(RUNED_WHIP) { Enchantment = 4; } + CWeaponSkillHits == 2000; + RightSWeaponSkillHits = 2000; + LeftSWeaponSkillHits = 2000; + NameSingular = "mistress warlady"; + NamePlural = "mistress warladies"; + PanicLevel = 5; + Inventory == Random { Category = RING; Chance = 10; } + } + + Config QUEEN; + { + HairColor = rgb16(35, 35, 35); + AttributeBonus = 60; + BodyArmor = ANGEL_HAIR bodyarmor(PLATE_MAIL) { Enchantment = 4; } + Amulet = amulet(AMULET_OF_LIFE_SAVING); + Cloak = SPIDER_SILK cloak { Enchantment = 4; } + Belt = DRAGON_HIDE belt { Enchantment = 4; } + RightGauntlet = PHOENIX_FEATHER gauntlet(GAUNTLET_OF_DEXTERITY) { Enchantment = 4; } + RightRing = ring(RING_OF_TELEPORT_CONTROL); + LeftRing = ring(RING_OF_TELEPORTATION); + RightBoot = DRAGON_HIDE boot(BOOT_OF_KICKING) { Enchantment = 4; } + RightWielded = SPIDER_SILK whipofthievery { Enchantment = 5; } + LeftWielded = SPIDER_SILK chameleonwhip { Enchantment = 5; } + KnownCWeaponSkills = { 2, WHIPS, KICK; } + CWeaponSkillHits = { 2, 5000, 1000; } + RightSWeaponSkillHits = 5000; + LeftSWeaponSkillHits = 5000; + NameSingular = "mistress queen"; + PanicLevel = 0; + ClassStates = INVISIBLE|HASTE|INFRA_VISION|ESP; + TamingDifficulty = 50; + IsUnique = true; + CanBeWished = true; + DefaultName = "Sherarax"; + DangerModifier = 1500; + IsNameable = false; + CanBeCloned = false; + IsPolymorphable = false; + Inventory = { 5, scrolloftaming, scrolloftaming, wand(WAND_OF_HASTE), wand(WAND_OF_HASTE), solstone; } + CanBeConfused = false; + FireResistance = 40; + ElectricityResistance = 40; + EnergyResistance = 40; + IsImmuneToItemTeleport = true; + AllowUnconsciousness = false; + UndeadVersions = false; + IsImmuneToWhipOfThievery = true; + } +} + +werewolfhuman +{ + DefaultArmStrength = 20; + DefaultLegStrength = 20; + DefaultDexterity = 20; + DefaultAgility = 20; + DefaultEndurance = 15; + DefaultPerception = 18; + DefaultIntelligence = 20; + DefaultWisdom = 20; + DefaultCharisma = 20; + DefaultMana = 20; + TotalVolume = 70000; + EyeColor = rgb16(160, 0, 0); + ClothColor = rgb16(114, 84, 52); + HeadBitmapPos = 96, 0; + TorsoBitmapPos = 48, 0; + ArmBitmapPos = 64, 0; + LegBitmapPos = 0, 176; + HostileReplies == "@Dd screams in fury."; + FriendlyReplies == "@Dd screams in a friendly manner."; + TotalSize = 170; + NameSingular = "werewolf"; + NamePlural = "werewolves"; + AttackStyle = USE_ARMS; + CanBeGenerated = true; + CanRead = true; + ClassStates = LYCANTHROPY; + KnownCWeaponSkills == UNARMED; + CWeaponSkillHits == 200; + BaseUnarmedStrength = 300; + FleshMaterial = WERE_WOLF_FLESH; + AttachedGod = INFUSCOR; + UndeadVersions = false; +} + +werewolfwolf +{ + DefaultArmStrength = 25; + DefaultLegStrength = 25; + DefaultDexterity = 35; + DefaultAgility = 35; + DefaultEndurance = 25; + DefaultPerception = 24; + DefaultIntelligence = 10; + DefaultWisdom = 10; + DefaultCharisma = 10; + DefaultMana = 0; + TotalVolume = 70000; + EyeColor = rgb16(200, 200, 0); + ClothColor = rgb16(114, 84, 52); + SkinColor = rgb16(100, 100, 100); + HeadBitmapPos = 112, 144; + TorsoBitmapPos = 48, 0; + ArmBitmapPos = 64, 240; + LegBitmapPos = 0, 192; + HostileReplies == "@Dd growls in fury."; + FriendlyReplies == "@Dd growls cheerfully."; + TotalSize = 200; + NameSingular = "werewolf"; + NamePlural = "werewolves"; + AttackStyle = USE_HEAD; + Sex = UNDEFINED; + BaseBiteStrength = 1200; + CanUseEquipment = false; + CanBeWished = false; + ClassStates = INFRA_VISION; + PanicLevel = 0; + FleshMaterial = WERE_WOLF_FLESH; + AttachedGod = INFUSCOR; + WillCarryItems = true; + UndeadVersions = false; +} + +kobold +{ + DefaultArmStrength = 6; + DefaultLegStrength = 6; + DefaultDexterity = 7; + DefaultAgility = 9; + DefaultEndurance = 9; + DefaultPerception = 12; + DefaultIntelligence = 4; + DefaultWisdom = 4; + DefaultCharisma = 4; + DefaultMana = 0; + SkinColor = rgb16(60, 120, 120); + EyeColor = rgb16(180, 180, 0); + HairColor = rgb16(35, 35, 35); + ClothColor = rgb16(48, 48, 48); + LegMainColor = rgb16(111, 74, 37); + HeadBitmapPos = 112, 208; + TorsoBitmapPos = 48, 176; + ArmBitmapPos = 64, 176; + LegBitmapPos = 16, 112; + TotalVolume = 30000; + TotalSize = 90; + NameSingular = "kobold"; + CanBeGenerated = true; + Sex = UNDEFINED; + RightWielded = BALSA_WOOD BALSA_WOOD meleeweapon(SPEAR); + KnownCWeaponSkills == POLE_ARMS; + CWeaponSkillHits == 5; + RightSWeaponSkillHits = 0; + PanicLevel = 75; + FleshMaterial = KOBOLD_FLESH; + DeathMessage = "@Dd dies yelling like a tortured hyena."; + AttachedGod = CRUENTUS; + WieldedPosition = 0, -1; + IsExtraFragile = true; + + /* Lines below were used for testing Eptyron >:] */ + /* + BodyArmor = TROLL_HIDE bodyarmor(CHAIN_MAIL) { Enchantment = 0; } + RightGauntlet = TROLL_HIDE gauntlet(GAUNTLET_OF_DEXTERITY) { Enchantment = 0; } + RightBoot = TROLL_HIDE boot(BOOT_OF_AGILITY) { Enchantment = 0; } + Helmet = ADAMANT helmet(FULL_HELMET) { Enchantment = 0; } + */ + + Config CHIEFTAIN; + { + AttributeBonus = 30; + RightWielded = COPPER meleeweapon(SPEAR); + CWeaponSkillHits == 10; + RightSWeaponSkillHits = 5; + NameSingular = "kobold chieftain"; + PanicLevel = 66; + ClothColor = rgb16(100, 100, 48); + LegMainColor = rgb16(111, 74, 37); + } + + Config LORD; + { + AttributeBonus = 60; + RightWielded = IRON meleeweapon(SPEAR); + CWeaponSkillHits == 20; + RightSWeaponSkillHits = 10; + NameSingular = "kobold lord"; + PanicLevel = 50; + ClothColor = rgb16(160, 0, 0); + LegMainColor = rgb16(111, 74, 37); + Inventory == Random { Category = RING; Chance = 5; } + } + + Config PATRIARCH; + { + AttributeBonus = 120; + Helmet = METEORIC_STEEL helmet; + BodyArmor = METEORIC_STEEL bodyarmor(CHAIN_MAIL); + RightWielded = MITHRIL meleeweapon(SPEAR) { Enchantment = 1; } + Belt = OMMEL_HAIR belt; + Cloak = NYMPH_HAIR cloak; + RightGauntlet = NYMPH_HAIR gauntlet; + RightBoot = TROLL_HIDE boot; + CWeaponSkillHits == 100; + RightSWeaponSkillHits = 50; + NameSingular = "kobold patriarch"; + IsUnique = true; + CanBeWished = true; + DefaultName = "Rondol"; + DangerModifier = 2500; + IsNameable = false; + CanBeCloned = false; + TotalVolume = 70000; + PanicLevel = 33; + TotalSize = 120; + ClothColor = rgb16(144, 0, 144); + LegMainColor = rgb16(111, 74, 37); + Inventory = { 2, wand(WAND_OF_POLYMORPH), solstone; } + FireResistance = 20; + ElectricityResistance = 20; + EnergyResistance = 20; + IsExtraFragile = false; + TamingDifficulty = 15; + IsSadist = true; + UndeadVersions = false; + } +} + +kabouter +{ + DefaultArmStrength = 8; + DefaultLegStrength = 8; + DefaultDexterity = 12; + DefaultAgility = 16; + DefaultEndurance = 14; + DefaultPerception = 20; + DefaultIntelligence = 10; + DefaultWisdom = 20; + DefaultCharisma = 40; + DefaultMana = 0; + SkinColor = rgb16(206, 140, 96); + EyeColor = rgb16(0, 123, 223); + HairColor = rgb16(180, 180, 180); + ClothColor = rgb16(140, 0, 0); + CapColor = rgb16(160, 0, 0); + TorsoSpecialColor = rgb16(255, 236, 0); + LegMainColor = rgb16(140, 0, 0); + BootColor = rgb16(64, 0, 0); + HeadBitmapPos = 96, 704; + TorsoBitmapPos = 32, 704; + ArmBitmapPos = 64, 704; + LegBitmapPos = 0, 704; + TotalVolume = 20000; + TotalSize = 90; + NameSingular = "kabouter"; + HostileReplies == "@Dd grimaces furiously."; + FriendlyReplies == "@Dd smiles cheerfully."; + CanBeGenerated = true; + Sex = MALE; + RightWielded = SILVER BIRCH_WOOD meleeweapon(SICKLE); + KnownCWeaponSkills == SMALL_SWORDS; + CWeaponSkillHits == 30; + RightSWeaponSkillHits = 20; + PanicLevel = 50; + FleshMaterial = KABOUTER_FLESH; + DeathMessage = "@Dd dies with a yelp."; + AttachedGod = ATAVUS; + WieldedPosition = 2, 1; + Inventory = { 1, wand(WAND_OF_SLOW);} + FireResistance = 10; + ElectricityResistance = 10; + EnergyResistance = 10; + UndeadVersions = false; + RightBoot = BLACK_LEATHER boot { Enchantment = 1; } + Helmet = BLACK_LEATHER belt { Enchantment = 1; } + ClassStates = GAS_IMMUNITY|INFRA_VISION; +} + +uldra +{ + DefaultArmStrength = 12; + DefaultLegStrength = 10; + DefaultDexterity = 16; + DefaultAgility = 20; + DefaultEndurance = 18; + DefaultPerception = 30; + DefaultIntelligence = 20; + DefaultWisdom = 25; + DefaultCharisma = 40; + DefaultMana = 0; + SkinColor = rgb16(150, 90, 56); + EyeColor = rgb16(192, 192, 192); + HairColor = rgb16(210, 210, 210); + ClothColor = rgb16(124, 124, 124); + CapColor = rgb16(124, 124, 124); + TorsoSpecialColor = rgb16(255, 236, 0); /*belt*/ + BootColor = rgb16(64, 0, 0); + HeadBitmapPos = 112, 704; + TorsoBitmapPos = 48, 704; + ArmBitmapPos = 80, 704; + LegBitmapPos = 16, 704; + TotalVolume = 24000; + TotalSize = 100; + NameSingular = "uldra"; + HostileReplies == "@Dd grins maniaically."; + FriendlyReplies == "@Dd grins."; + CanBeGenerated = true; + Sex = MALE; + RightWielded = MITHRIL OAK_WOOD meleeweapon(SICKLE) { Enchantment = 2; } + KnownCWeaponSkills == SMALL_SWORDS; + CWeaponSkillHits == 30; + RightSWeaponSkillHits = 20; + PanicLevel = 33; + FleshMaterial = ULDRA_FLESH; + DeathMessage = "@Dd dies with a yelp."; + AttachedGod = ATAVUS; + WieldedPosition = 2, 1; + /*Inventory = { 1, wand(WAND_OF_SLOW);}*/ + FireResistance = 20; + ElectricityResistance = 20; + EnergyResistance = 20; + UndeadVersions = false; + CanBeCloned = false; + RightBoot = BLACK_LEATHER boot { Enchantment = 1; } + Helmet = BLACK_LEATHER belt { Enchantment = 1; } + ClassStates = ESP|GAS_IMMUNITY|TELEPORT_CONTROL; + HPRequirementForGeneration = 150; + DayRequirementForGeneration = 5; + DangerModifier = 250; +} + +gibberling +{ + DefaultArmStrength = 7; + DefaultLegStrength = 7; + DefaultDexterity = 5; + DefaultAgility = 15; + DefaultEndurance = 10; + DefaultPerception = 15; + DefaultIntelligence = 4; + DefaultWisdom = 4; + DefaultCharisma = 5; + DefaultMana = 0; + SkinColor = rgb16(100, 100, 200); + HairColor = rgb16(50, 20, 80); + ClothColor = rgb16(111, 74, 37); + EyeColor = rgb16(50, 0, 0); + HeadBitmapPos = 112, 96; + TorsoBitmapPos = 48, 144; + ArmBitmapPos = 80, 144; + LegBitmapPos = 16, 128; + TotalVolume = 30000; + TotalSize = 90; + SkinColor = rgb16(100, 100, 200); + NameSingular = "gibberling"; + CanBeGenerated = true; + Sex = UNDEFINED; + CanUseEquipment = false; + CanTalk = false; + KnownCWeaponSkills == UNARMED; + CWeaponSkillHits == 50; + PanicLevel = 85; + BaseUnarmedStrength = 300; + FleshMaterial = GIBBERLING_FLESH; + AttachedGod = SILVA; + HostileReplies == "@Dd gibbers diabolically."; + FriendlyReplies == "@Dd gibbers joyfully."; + WillCarryItems = false; + IsExtraFragile = true; +} + +largecat +{ + DefaultArmStrength = 4; + DefaultAgility = 35; + DefaultEndurance = 10; + DefaultPerception = 21; + DefaultIntelligence = 10; + DefaultWisdom = 10; + DefaultCharisma = 20; + DefaultMana = 0; + TotalVolume = 15000; + TorsoBitmapPos = 496, 0; + HostileReplies == "@Dd mews."; + FriendlyReplies == "@Dd purrs."; + TotalSize = 60; + SkinColor = rgb16(50, 50, 50); + Adjective = "large"; + NameSingular = "cat"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 600; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 50; + ClassStates = INFRA_VISION; + FleshMaterial = CAT_FLESH; + AttachedGod = SILVA; + DangerModifier = 50; +} + +largerat +{ + DefaultArmStrength = 3; + DefaultAgility = 20; + DefaultEndurance = 10; + DefaultPerception = 12; + DefaultIntelligence = 5; + DefaultWisdom = 5; + DefaultCharisma = 4; + DefaultMana = 0; + CanOpen = false; + TotalVolume = 4000; + TorsoBitmapPos = 512, 0; + HostileReplies == "The furious @du squeaks."; + FriendlyReplies == "@Dd squeaks in a friendly manner."; + TotalSize = 30; + SkinColor = rgb16(180, 100, 40); + Adjective = "large"; + NameSingular = "rat"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 350; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 20; + ClassStates = INFRA_VISION; + FleshMaterial = RAT_FLESH; + AttachedGod = SCABIES; + MoveType = WALK|SWIM; + IsCatacombCreature = true; +} + +angel +{ + DefaultArmStrength = 25; + DefaultLegStrength = 25; + DefaultDexterity = 25; + DefaultAgility = 25; + DefaultEndurance = 25; + DefaultPerception = 35; + DefaultIntelligence = 25; + DefaultWisdom = 35; + DefaultCharisma = 50; + DefaultMana = 35; + TamingDifficulty = 30; + Sex = FEMALE; + TotalVolume = 60000; + TorsoBitmapPos = 432, 0; + TotalSize = 200; + CanRead = true; + NameSingular = "angel"; + UsesLongArticle = true; + ClassStates = ESP|GAS_IMMUNITY|TELEPORT_CONTROL; + SkinColor = rgb16(200, 200, 200); + HairColor = rgb16(180, 180, 0); + EyeColor = rgb16(48, 48, 255); + HeadBitmapPos = 112, 256; + TorsoBitmapPos = 48, 256; + ArmBitmapPos = 80, 256; + /* LegBitmapPos is not used */ + CreateDivineConfigurations = true; + IsAbstract = true; + /* Equipment initialization overridden */ + PanicLevel = 0; + BaseUnarmedStrength = 200; + HostileReplies == "\"With the power of @Gd, I shall slay thee, sinner!\""; + FriendlyReplies == "\"@Gd be with you, mortal.\""; + DeathMessage = "@Dd leaves this mortal plane behind."; + StandVerb = "flying"; + AttachedGod = NONE; + BodyPartsDisappearWhenSevered = true; + CanBeConfused = false; + WieldedPosition = 0, -2; + MoveType = FLY; + UsesNutrition = false; + IsPolymorphable = false; + ScienceTalkPossibility = 25; + ScienceTalkIntelligenceModifier = 25; + ScienceTalkWisdomModifier = 100; + ScienceTalkIntelligenceRequirement = 10; + ScienceTalkWisdomRequirement = 20; + CanChoke = false; + UndeadVersions = false; + + Config VALPURUS; + { + RightWielded = VALPURIUM VALPURIUM meleeweapon(LONG_SWORD); + LeftWielded = VALPURIUM shield; + KnownCWeaponSkills = { 2, LARGE_SWORDS, SHIELDS; } + CWeaponSkillHits = { 2, 500, 500; } + CWeaponSkillHits == 400; + RightSWeaponSkillHits = 150; + } + + Config LEGIFER; + { + BodyArmor = ILLITHIUM bodyarmor(CHAIN_MAIL); + RightWielded = ILLITHIUM ILLITHIUM meleeweapon(KATANA); + LeftWielded = ILLITHIUM shield; + KnownCWeaponSkills = { 2, LARGE_SWORDS, SHIELDS; } + CWeaponSkillHits = { 2, 500, 500; } + CWeaponSkillHits == 400; + RightSWeaponSkillHits = 150; + } + + Config ATAVUS; + { + BodyArmor = ARCANITE bodyarmor(PLATE_MAIL); + RightWielded = ARCANITE meleeweapon(HALBERD) { Enchantment = 1; } + KnownCWeaponSkills == POLE_ARMS; + CWeaponSkillHits == 350; + RightSWeaponSkillHits = 100; + } + + Config DULCIS; + { + BodyArmor = ANGEL_HAIR bodyarmor(PLATE_MAIL); + Cloak = ANGEL_HAIR cloak; + RightGauntlet = ANGEL_HAIR gauntlet; + RightWielded = MARBLE meleeweapon(HAMMER) { Enchantment = 2; } + LeftWielded = MARBLE meleeweapon(HAMMER) { Enchantment = 2; } + KnownCWeaponSkills == BLUNT_WEAPONS; + CWeaponSkillHits == 350; + RightSWeaponSkillHits = 100; + LeftSWeaponSkillHits = 100; + } + + Config SEGES; + { + BodyArmor = ANGEL_HAIR bodyarmor(PLATE_MAIL); + RightWielded = UNICORN_HORN UNICORN_HORN meleeweapon(DAGGER) { Enchantment = 4; } + KnownCWeaponSkills == SMALL_SWORDS; + CWeaponSkillHits == 300; + RightSWeaponSkillHits = 100; + } + + Config SOPHOS; + { + RightWielded = OCTIRON meleeweapon(HAMMER) { Enchantment = 2; } + LeftWielded = OCTIRON meleeweapon(HAMMER) { Enchantment = 2; } + KnownCWeaponSkills == BLUNT_WEAPONS; + CWeaponSkillHits == 350; + RightSWeaponSkillHits = 100; + LeftSWeaponSkillHits = 100; + } + + Config SILVA; + { + RightWielded = EBONY_WOOD EBONY_WOOD meleeweapon(QUARTER_STAFF) { Enchantment = 2; } + KnownCWeaponSkills == BLUNT_WEAPONS; + CWeaponSkillHits == 300; + RightSWeaponSkillHits = 100; + } + + Config LORICATUS; + { + RightWielded = MITHRIL meleeweapon(WAR_HAMMER) { Enchantment = 1; } + LeftWielded = MITHRIL meleeweapon(WAR_HAMMER) { Enchantment = 1; } + KnownCWeaponSkills == BLUNT_WEAPONS; + CWeaponSkillHits == 300; + RightSWeaponSkillHits = 100; + LeftSWeaponSkillHits = 100; + } + + Config MELLIS; + { + IsSadist = true; + BodyArmor = MITHRIL bodyarmor(CHAIN_MAIL); + RightWielded = MITHRIL pickaxe { Enchantment = 1; } + KnownCWeaponSkills == AXES; + CWeaponSkillHits == 300; + RightSWeaponSkillHits = 100; + } + + Config CLEPTIA; + { + IsSadist = true; + RightWielded = whipofthievery { Enchantment = 1; } + KnownCWeaponSkills == WHIPS; + CWeaponSkillHits == 400; + RightSWeaponSkillHits = 100; + LeftSWeaponSkillHits = 100; + Cloak = ANGEL_HAIR cloak; + RightGauntlet = ANGEL_HAIR gauntlet; + + } + + Config NEFAS; + { + IsSadist = true; + IsMasochist = true; + RightWielded = ANGEL_HAIR whip; + LeftWielded = ANGEL_HAIR whip; + KnownCWeaponSkills == WHIPS; + CWeaponSkillHits == 400; + RightSWeaponSkillHits = 100; + LeftSWeaponSkillHits = 100; + Belt = HARDENED_LEATHER belt; + Cloak = HARDENED_LEATHER cloak; + RightGauntlet = HARDENED_LEATHER gauntlet; + HeadBitmapPos = 112, 240; + } + + Config SCABIES; + { + IsSadist = true; + RightWielded = ARCANITE daggerofvenom { Enchantment = 3; } + LeftWielded = ARCANITE daggerofvenom { Enchantment = 3; } + KnownCWeaponSkills == SMALL_SWORDS; + CWeaponSkillHits == 400; + RightSWeaponSkillHits = 100; + LeftSWeaponSkillHits = 100; + Cloak = ANGEL_HAIR cloak { Enchantment = 1; } + } + + Config INFUSCOR; + { + IsSadist = true; + RightWielded = OCTIRON OCTIRON meleeweapon(QUARTER_STAFF); + KnownCWeaponSkills == BLUNT_WEAPONS; + CWeaponSkillHits == 400; + RightSWeaponSkillHits = 100; + Cloak = OMMEL_HAIR cloak { Enchantment = 1; } + } + + Config CRUENTUS; + { + IsSadist = true; + RightWielded = MITHRIL MITHRIL meleeweapon(TWO_HANDED_SCIMITAR) { Enchantment = 1; } + KnownCWeaponSkills == LARGE_SWORDS; + CWeaponSkillHits == 400; + RightSWeaponSkillHits = 100; + } + + Config MORTIFER; + { + RightWielded = OMMEL_TOOTH meleeweapon(SCYTHE) { Enchantment = 1; } + Belt = BONE belt { Enchantment = 8; } + EyeColor = rgb16(255, 48, 48); + IsSadist = true; + KnownCWeaponSkills == POLE_ARMS; + CWeaponSkillHits == 500; + RightSWeaponSkillHits = 200; + } +} + +kamikazedwarf +{ + DefaultArmStrength = 20; + DefaultLegStrength = 20; + DefaultDexterity = 20; + DefaultAgility = 20; + DefaultEndurance = 15; + DefaultPerception = 18; + DefaultIntelligence = 10; + DefaultWisdom = 5; + DefaultCharisma = 10; + DefaultMana = 10; + HeadBitmapPos = 112, 160; + TorsoBitmapPos = 48, 16; + ArmBitmapPos = 80, 0; + LegBitmapPos = 0, 208; + HairColor = rgb16(144, 72, 0); + BeltColor = rgb16(72, 56, 16); + TotalVolume = 60000; + TotalSize = 130; + CanRead = true; + Adjective = "kamikaze"; + NameSingular = "dwarf"; + NamePlural = "dwarves"; + CanBeGenerated = true; + CreateDivineConfigurations = true; + IsAbstract = true; + /* Equipment initialization overridden */ + PanicLevel = 1; + Inventory == backpack; + FleshMaterial = DWARF_FLESH; + DeathMessage = "@Dd dies smiling."; + IgnoreDanger = true; + HPRequirementForGeneration = 60; + DayRequirementForGeneration = 6; + Frequency = 300; + HostileReplies == "\"Heaven awaits me in the house of @Gd after I bomb you, heretic!\""; + AttachedGod = NONE; + UndeadVersions = false; + WieldedPosition = -1, -2; + FriendlyReplies = + { + 4, + "\"Would you like me to teach you the best suicidal bombing tactics?\"", + "@Dd shouts: \"Death to disbelievers!\"", + "@Dd praises @Gd with numerous hymns. @Pp is obviously a very devoted follower.", + "\"One day, Holy War will break out and I shall sacrifice my life with joy.\""; + } +} + +axethrowerdwarf /*this guy has been superseded by the rookie kamikaze dwarf */ +{ + DefaultArmStrength = 20; + DefaultLegStrength = 20; + DefaultDexterity = 20; + DefaultAgility = 10; + DefaultEndurance = 12; + DefaultPerception = 18; + DefaultIntelligence = 10; + DefaultWisdom = 5; + DefaultCharisma = 10; + DefaultMana = 10; + HeadBitmapPos = 112, 160; + TorsoBitmapPos = 48, 16; + ArmBitmapPos = 80, 0; + LegBitmapPos = 0, 208; + HairColor = rgb16(144, 72, 0); + BeltColor = rgb16(72, 56, 16); + TotalVolume = 50000; + TotalSize = 100; + CanRead = true; + NameSingular = "axethrower dwarf"; + NamePlural = "axethrower dwarves"; + CanBeGenerated = false; + CreateDivineConfigurations = true; + IsAbstract = true; + /* Equipment initialization is overridden */ + PanicLevel = 1; + Inventory == IRON meleeweapon(AXE) {Times = 6;} + FleshMaterial = DWARF_FLESH; + DeathMessage = "@Dd dies in agony."; + IgnoreDanger = true; + /*HPRequirementForGeneration = 60;*/ + /*DayRequirementForGeneration = 6;*/ + Frequency = 0; + HostileReplies == "\"In the name of @Gd I will slash you to pieces!\""; + AttachedGod = NONE; + UndeadVersions = false; + WieldedPosition = -1, -2; + FriendlyReplies = + { + 4, + "\"Would you like me to teach you the best way to slice tomatoes?\"", + "@Dd shouts: \"Death to disbelievers!\"", + "@Dd praises @Gd with numerous hymns. @Pp is obviously a very devoted follower.", + "\"One day, Holy War will break out, hopefully I will earn my backpack by then!\""; + } + IsRangedAttacker = true; + KnownCWeaponSkills == AXES; + CWeaponSkillHits == 100; + RightSWeaponSkillHits = 50; + UsesLongArticle = true; + WhatCategoryToThrow = WEAPON; + WhatWeaponConfigToThrow = AXE; + WhatThrowItemTypesToThrow = THROW_AXE; +} + +mammoth +{ + DefaultArmStrength = 80; + DefaultAgility = 20; + DefaultEndurance = 15; + DefaultPerception = 18; + DefaultIntelligence = 5; + DefaultWisdom = 7; + DefaultCharisma = 10; + DefaultMana = 0; + TotalVolume = 2000000; + TorsoBitmapPos = 528, 0; + HostileReplies == "@Dd roars furiously."; + FriendlyReplies == "@Dd roars kindly."; + TotalSize = 500; + SkinColor = rgb16(100, 130, 160); + Adjective = "baby"; + NameSingular = "mammoth"; + AttackStyle = USE_LEGS|USE_HEAD; + BaseKickStrength = 1000; + BaseBiteStrength = 500; + CanBeGenerated = true; + CanKick = true; + KnownCWeaponSkills = { 2, KICK, BITE; } + CWeaponSkillHits = { 2, 50, 50; } + PanicLevel = 10; + FleshMaterial = MAMMOTH_FLESH; + AttachedGod = SILVA; + Frequency = 2500; + IsEnormous = true; +} + +unicorn +{ + DefaultArmStrength = 15; + DefaultAgility = 40; + DefaultEndurance = 10; + DefaultPerception = 18; + DefaultIntelligence = 25; + DefaultWisdom = 20; + DefaultCharisma = 30; + DefaultMana = 25; + HostileReplies == "The furious @du neighs."; + FriendlyReplies == "The happy @du neighs."; + TotalVolume = 100000; + TorsoBitmapPos = 544, 0; + TotalSize = 200; + NameSingular = "unicorn"; + AttackStyle = USE_LEGS|USE_HEAD; + BaseKickStrength = 700; + BaseBiteStrength = 350; + CanBeGenerated = true; + CanKick = true; + IsAbstract = true; + TorsoMainColor = rgb16(200, 200, 200); /* the horn */ + KnownCWeaponSkills = { 2, KICK, BITE; } + CWeaponSkillHits = { 2, 50, 50; } + Inventory = { 2, stone { Chance = 50; }, stone { Chance = 50; } } + Frequency = 5000; + + Config GOOD; + { + SkinColor = rgb16(200, 200, 200); + Adjective = "white"; + FleshMaterial = WHITE_UNICORN_FLESH; + AttachedGod = DULCIS; + } + + Config NEUTRAL; + { + SkinColor = rgb16(144, 144, 144); + Adjective = "gray"; + FleshMaterial = GRAY_UNICORN_FLESH; + AttachedGod = TERRA; + } + + Config EVIL; + { + SkinColor = rgb16(80, 80, 80); + Adjective = "black"; + FleshMaterial = BLACK_UNICORN_FLESH; + AttachedGod = MORTIFER; + IsSadist = true; + } +} + +genie +{ + DefaultArmStrength = 20; + DefaultLegStrength = 20; /* not used */ + DefaultDexterity = 25; + DefaultAgility = 50; + DefaultEndurance = 15; + DefaultPerception = 18; + DefaultIntelligence = 20; + DefaultWisdom = 25; + DefaultCharisma = 20; + DefaultMana = 50; + StandVerb = "floating"; + TotalVolume = 200000; + TotalSize = 250; + CanRead = true; + NameSingular = "genie"; + ClothColor = rgb16(100, 100, 160); + HeadBitmapPos = 96, 272; + TorsoBitmapPos = 32, 272; + ArmBitmapPos = 64, 272; + /* LegBitmapPos is not used */ + KnownCWeaponSkills == LARGE_SWORDS; + CWeaponSkillHits == 500; + RightSWeaponSkillHits = 200; + PanicLevel = 0; + BaseEmitation = rgb24(110, 110, 130); + HostileReplies == "\"Fall, puny primy!\""; + FriendlyReplies == "\"You know, it's not fun to stay in an oil lamp for three centuries...\""; + Belt = SPIDER_SILK belt; + FleshMaterial = MAGICAL_AIR; + DeathMessage = "@Dd vanishes from existence."; + SpillsBlood = false; + Sweats = false; + AttachedGod = ATAVUS; + BodyPartsDisappearWhenSevered = true; + ClassStates = GAS_IMMUNITY|LEVITATION; + WieldedPosition = -1, -3; + CanChoke = false; + IsImmuneToStickiness = true; + UndeadVersions = false; +} + +lion +{ + DefaultArmStrength = 20; + DefaultAgility = 25; + DefaultEndurance = 15; + DefaultPerception = 24; + DefaultIntelligence = 7; + DefaultWisdom = 15; + DefaultCharisma = 20; + DefaultMana = 0; + TotalVolume = 100000; + TorsoBitmapPos = 576, 0; + HostileReplies == "@Dd growls furiously."; + FriendlyReplies == "@Dd growls happily."; + TotalSize = 200; + SkinColor = rgb16(200, 200, 112); + NameSingular = "lion"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 800; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 200; + FleshMaterial = LION_FLESH; + DeathMessage = "@Dd growls and is slain."; + AttachedGod = SILVA; + AutomaticallySeen = true; +} + +carnivorousplant +{ + DefaultArmStrength = 4; + DefaultAgility = 2; + DefaultEndurance = 7; + DefaultPerception = 6; + DefaultIntelligence = 3; + DefaultWisdom = 2; + DefaultCharisma = 3; + DefaultMana = 0; + CanOpen = false; + TotalVolume = 20000; + TorsoBitmapPos = 0, 16; + HostileReplies == "@Dd is silent."; + FriendlyReplies == "@Dd is silent."; + TotalSize = 100; + Adjective = "carnivorous"; + NameSingular = "plant"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 300; + SkinColor = rgb16(111, 64, 37); + TorsoMainColor = rgb16(0, 160, 0); /* the leaves */ + /* TorsoSpecialColor (the flower) is random */ + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 20; + PanicLevel = 0; + ClassStates = INFRA_VISION; + HasALeg = false; + FleshMaterial = PLANT_FIBER; + DeathMessage = "@Dd is destroyed."; + SpillsBlood = false; + Sweats = false; + StandVerb = "rooted"; + AttachedGod = SILVA; + IsPlant = true; + IsRooted = true; + AllowUnconsciousness = false; + CanChoke = false; + + Config GREATER; + { + AttributeBonus = 175; + TorsoBitmapPos = 96, 16; + Adjective = "greater carnivorous"; + BaseBiteStrength = 500; + CWeaponSkillHits == 50; + TotalVolume = 30000; + TotalSize = 175; + } + + Config GIANT; + { + AttributeBonus = 250; + TorsoBitmapPos = 80, 16; + Adjective = "giant carnivorous"; + BaseBiteStrength = 700; + CWeaponSkillHits == 100; + TotalVolume = 40000; + TotalSize = 250; + IsEnormous = true; + } + + Config SHAMBLING; + { + AttributeBonus = 250; + Adjective = "shambling carnivorous"; + TorsoBitmapPos = 80, 16; + BaseBiteStrength = 1000; + CWeaponSkillHits == 100; + TotalVolume = 40000; + TotalSize = 250; + IsEnormous = true; + IsRooted = false; + StandVerb = "skittering"; + } + + Config LILY; + { + AttributeBonus = 250; + Adjective = "carnivorous"; + TorsoBitmapPos = 80, 16; + NameSingular = "lily pad"; + BaseBiteStrength = 1000; + CWeaponSkillHits == 100; + TotalVolume = 40000; + TotalSize = 250; + IsEnormous = true; + IsRooted = false; + MoveType = SWIM; + StandVerb = "sticking"; /*only used if not swimming*/ + } +} + +buffalo +{ + DefaultArmStrength = 40; + DefaultAgility = 25; + DefaultEndurance = 20; + DefaultPerception = 24; + DefaultIntelligence = 5; + DefaultWisdom = 5; + DefaultCharisma = 5; + DefaultMana = 0; + TotalVolume = 200000; + TorsoBitmapPos = 16,16; + HostileReplies == "The angry @du snarls."; + FriendlyReplies == "The friendly @du snarls."; + TotalSize = 250; + SkinColor = rgb16(90, 85, 80); + TorsoMainColor = rgb16(120, 120, 120); /* the horns */ + NameSingular = "buffalo"; + AttackStyle = USE_LEGS|USE_HEAD; + BaseKickStrength = 500; + BaseBiteStrength = 250; + CanKick = true; + CanBeGenerated = true; + KnownCWeaponSkills = { 2, KICK, BITE; } + CWeaponSkillHits = { 2, 50, 50; } + PanicLevel = 10; + FleshMaterial = BUFFALO_FLESH; + DeathMessage = "@Dd snarls one last time."; + AttachedGod = SILVA; + Frequency = 2500; + IsEnormous = true; +} + +snake +{ + DefaultArmStrength = 7; + DefaultAgility = 5; + DefaultEndurance = 10; + DefaultPerception = 9; + DefaultIntelligence = 5; + DefaultWisdom = 5; + DefaultCharisma = 5; + DefaultMana = 0; + TotalVolume = 10000; + TorsoBitmapPos = 384, 0; + HostileReplies == "@Dd hisses in fury."; + FriendlyReplies == "@Dd hisses in a friendly manner."; + TotalSize = 250; + SkinColor = rgb16(130, 0, 0); + NameSingular = "snake"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 500; + StandVerb = "lying"; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 100; + ClassStates = INFRA_VISION; + HasALeg = false; + FleshMaterial = SNAKE_FLESH; + DangerModifier = 75; + CanOpen = false; + AttachedGod = MELLIS; + AutomaticallySeen = true; + RunDescriptionLineOne = "Crawling"; + RunDescriptionLineTwo = "very fast"; +} + +orc +{ + DefaultArmStrength = 15; + DefaultLegStrength = 15; + DefaultDexterity = 10; + DefaultAgility = 20; + DefaultEndurance = 15; + DefaultPerception = 18; + DefaultIntelligence = 7; + DefaultWisdom = 6; + DefaultCharisma = 4; + DefaultMana = 0; + SkinColor = rgb16(48, 48, 48); + EyeColor = rgb16(200, 200, 0); + ClothColor = rgb16(180, 120, 80); + BeltColor = rgb16(72, 60, 24); + LegMainColor = rgb16(96, 80, 48); + HeadBitmapPos = 112, 192; + TorsoBitmapPos = 48, 0; + ArmBitmapPos = 80, 32; + LegBitmapPos = 0, 240; + TotalVolume = 90000; + TotalSize = 180; + UsesLongArticle = true; + NameSingular = "orc"; + CanBeGenerated = true; + Sex = UNDEFINED; + BodyArmor = TROLL_HIDE bodyarmor(PLATE_MAIL); + RightWielded = BRONZE meleeweapon(AXE); + LeftWielded = BRONZE meleeweapon(AXE); + KnownCWeaponSkills == AXES; + CWeaponSkillHits == 50; + RightSWeaponSkillHits = 20; + LeftSWeaponSkillHits = 20; + ClassStates = GAS_IMMUNITY|INFRA_VISION; + PanicLevel = 50; + HostileReplies == "\"Nice scalp! Me want it!\""; + FriendlyReplies == "\"Has you seen any elf or dwarf? Me hungry.\""; + FleshMaterial = ORC_FLESH; + AttachedGod = CRUENTUS; + WieldedPosition = -1, -1; + DangerModifier = 75; + ScienceTalkPossibility = 25; + ScienceTalkIntelligenceModifier = 1; + ScienceTalkWisdomModifier = 1; + ScienceTalkIntelligenceRequirement = 3; + ScienceTalkWisdomRequirement = 3; + ScienceTalkName = + { + 10, + "killing", "murder", "rape", "torture", "burns", + "cannibalism", "slaughtering", "malice", "axes", "blood"; + } + IsSadist = true; + + Config SLAUGHTERER; + { + TorsoBitmapPos = 48, 48; + AttributeBonus = 20; + Helmet = BRONZE helmet; + BodyArmor = BRONZE bodyarmor(CHAIN_MAIL); + RightWielded = IRON meleeweapon(AXE); + LeftWielded = IRON meleeweapon(AXE); + CWeaponSkillHits == 100; + RightSWeaponSkillHits = 50; + LeftSWeaponSkillHits = 50; + NameSingular = "orc slaughterer"; + PanicLevel = 40; + Inventory == potion { SecondaryMaterial = TROLL_BLOOD; Chance = 10; } + } + + Config SQUAD_LEADER; + { + HairColor = rgb16(120, 120, 120); + HeadBitmapPos = 112, 176; + TorsoBitmapPos = 48, 48; + AttributeBonus = 40; + Helmet = IRON helmet(FULL_HELMET); + BodyArmor = IRON bodyarmor(CHAIN_MAIL); + RightWielded = IRON meleeweapon(HALBERD); + LeftWielded = 0; + KnownCWeaponSkills == POLE_ARMS; + CWeaponSkillHits == 200; + RightSWeaponSkillHits = 100; + NameSingular = "orc squad leader"; + PanicLevel = 30; + } + + Config OFFICER; + { + HairColor = rgb16(0, 200, 0); + ClothColor = rgb16(96, 96, 96); + LegMainColor = rgb16(72, 72, 72); + LegSpecialColor = rgb16(112, 80, 48); + HeadBitmapPos = 112, 176; + TorsoBitmapPos = 48, 32; + ArmBitmapPos = 80, 16; + LegBitmapPos = 0, 224; + AttributeBonus = 60; + Helmet = STEEL helmet(FULL_HELMET); + BodyArmor = STEEL bodyarmor(CHAIN_MAIL); + RightWielded = STEEL meleeweapon(HALBERD) { Enchantment = 1; } + LeftWielded = 0; + KnownCWeaponSkills == POLE_ARMS; + CWeaponSkillHits == 500; + RightSWeaponSkillHits = 200; + NameSingular = "orc officer"; + PanicLevel = 20; + } + + Config GENERAL; + { + HairColor = rgb16(200, 0, 0); + ClothColor = rgb16(72, 72, 72); + LegMainColor = rgb16(48, 48, 48); + LegSpecialColor = rgb16(96, 64, 32); + HeadBitmapPos = 112, 176; + TorsoBitmapPos = 48, 32; + ArmBitmapPos = 80, 16; + LegBitmapPos = 0, 224; + AttributeBonus = 80; + Helmet = MITHRIL helmet(FULL_HELMET) { Enchantment = 1; } + BodyArmor = MITHRIL bodyarmor(CHAIN_MAIL) { Enchantment = 1; } + RightWielded = MITHRIL meleeweapon(HALBERD) { Enchantment = 2; } + LeftWielded = 0; + KnownCWeaponSkills == POLE_ARMS; + CWeaponSkillHits == 1000; + RightSWeaponSkillHits = 500; + NameSingular = "orc general"; + PanicLevel = 10; + Inventory = { 2, potion { SecondaryMaterial = HEALING_LIQUID; Chance = 10; }, Random { Category = RING; Chance = 10; } } + TotalVolume = 120000; + } + + Config MARSHAL; + { + HairColor = rgb16(200, 0, 200); + ClothColor = rgb16(48, 48, 48); + LegMainColor = rgb16(32, 32, 32); + LegSpecialColor = rgb16(80, 48, 16); + HeadBitmapPos = 112, 176; + TorsoBitmapPos = 48, 32; + ArmBitmapPos = 80, 16; + LegBitmapPos = 0, 224; + AttributeBonus = 100; + Helmet = MITHRIL helmet(FULL_HELMET) { Enchantment = 2; } + Cloak = ANGEL_HAIR cloak { Enchantment = 2; } + BodyArmor = DRAGON_HIDE bodyarmor(PLATE_MAIL) { Enchantment = 2; } + RightWielded = RUBY meleeweapon(HALBERD) { Enchantment = 3; } + LeftRing = ring(RING_OF_TELEPORT_CONTROL); + LeftWielded = 0; + Belt = RUBY belt { Enchantment = 2; } + RightGauntlet = DRAGON_HIDE gauntlet(GAUNTLET_OF_STRENGTH) { Enchantment = 2; } + RightBoot = SPIDER_SILK boot { Enchantment = 2; } + LeftWielded = 0; + KnownCWeaponSkills == POLE_ARMS; + CWeaponSkillHits == 2000; + RightSWeaponSkillHits = 1000; + NameSingular = "orc marshal"; + PanicLevel = 0; + IsUnique = true; + CanBeWished = true; + DefaultName = "Ur-Khan"; + DangerModifier = 1500; + IsNameable = false; + CanBeCloned = false; + IsPolymorphable = false; + TotalVolume = 150000; + Inventory = { 5, potion { SecondaryMaterial = OMMEL_URINE; }, potion { SecondaryMaterial = OMMEL_URINE; }, potion { SecondaryMaterial = OMMEL_URINE; }, potion { SecondaryMaterial = OMMEL_URINE; }, solstone;} + CanBeConfused = false; + FireResistance = 30; + ElectricityResistance = 30; + EnergyResistance = 30; + IsImmuneToItemTeleport = true; + IsEnormous = true; + AllowUnconsciousness = false; + TamingDifficulty = 35; + UndeadVersions = false; + IsImmuneToWhipOfThievery = true; + } +} + +cossack +{ + DefaultArmStrength = 20; + DefaultLegStrength = 20; + DefaultDexterity = 15; + DefaultAgility = 15; + DefaultEndurance = 15; + DefaultPerception = 21; + DefaultIntelligence = 10; + DefaultWisdom = 7; + DefaultCharisma = 7; + DefaultMana = 0; + BeltColor = rgb16(48, 48, 48); + ClothColor = rgb16(64, 56, 24); + HeadBitmapPos = 112, 224; + TorsoBitmapPos = 48, 80; + ArmBitmapPos = 64, 16; + LegBitmapPos = 0, 96; + TotalVolume = 70000; + TotalSize = 190; + NameSingular = "cossack"; + Helmet = HARDENED_LEATHER helmet; + Cloak = HARDENED_LEATHER cloak; + BodyArmor = IRON bodyarmor(CHAIN_MAIL); + Belt = HARDENED_LEATHER belt; + RightWielded = IRON IRON meleeweapon(LONG_SWORD); + KnownCWeaponSkills == LARGE_SWORDS; + CWeaponSkillHits == 100; + RightSWeaponSkillHits = 100; + RightGauntlet = HARDENED_LEATHER gauntlet; + RightBoot = HARDENED_LEATHER boot; + PanicLevel = 10; + DeathMessage = "@Dd falls shouting: \"Hope there's vodka in hell!"; + HostileReplies == "@Dd shouts wildly: \"For Tataria!\""; + AttachedGod = SILVA; + FriendlyReplies = + { + 4, + "\"Graah! Eating raw flesh makes one feel so masculine and powerful! (and sick)\"", + "\"It surely is cold on this island. Remembers me of my six years in Siberia after breaking into the local pub's booze cellar...\"", + "\"What, why have I no horse? Er, I lost it in poker.\"", + "\"Women are odd. No matter how many times I take them to hunt wild beasts of the Steppe or show them my collection of old vodka bottles, none of them still likes me.\""; + } +} + +bananagrower +{ + DefaultArmStrength = 10; + DefaultLegStrength = 15; + DefaultDexterity = 10; + DefaultAgility = 20; + DefaultEndurance = 15; + DefaultPerception = 15; + DefaultIntelligence = 20; + DefaultWisdom = 30; + DefaultCharisma = 10; + DefaultMana = 5; + TotalVolume = 80000; + TotalSize = 170; + NameSingular = "banana grower"; + RightGauntlet = LEATHER gauntlet; + SkinColor = rgb16(128, 80, 48); + HairColor = rgb16(80, 48, 32); + ClothColor = rgb16(56, 48, 20); + HeadBitmapPos = 96, 0; + TorsoBitmapPos = 32, 0; + ArmBitmapPos = 64, 64; + LegBitmapPos = 0, 288; + Inventory == banana { Times = 10; } + TamingDifficulty = NO_TAMING; /* AI will go insane if he leaves New Attnam */ + HostileReplies == "\"Banana POWER!\""; + AttachedGod = SILVA; + CanRead = true; + FriendlyReplies = + { + 9, /* 6 first are used before the revolution */ + "@Dd curses: \"I hate bananas. I wish I still was @pd.\"", + "\"I was @pd before Attnam invaded our peaceful land.\"", + "@Dd glances thoughtfully to the sky: \"Our climate is truly optimal for bananas. It rains all the time here. Damn.\"", + "\"1 + 1 = 3. I still don't believe it.\"", + "@Dd sighs: \"Piranhas ate my mother-in-law a few days ago. And I thought the nature brings us no good!\"", + "@Dd seems irritated. \"Go away! I know you can leave and I can't, and I'd prefer to forget that fact.\""; + "@Dd seems very joyful. \"I can finally be @pd again!\"", + "\"I knew you would come to save us!\"", + "\"Kill Petrus for us, too!\""; + } + AutomaticallySeen = true; + ScienceTalkPossibility = 10; + ScienceTalkIntelligenceModifier = 10; + ScienceTalkWisdomModifier = 25; + ScienceTalkIntelligenceRequirement = 5; + ScienceTalkWisdomRequirement = 10; + IsMasochist = true; +} + +mangogrower +{ + DefaultArmStrength = 10; + DefaultLegStrength = 15; + DefaultDexterity = 10; + DefaultAgility = 20; + DefaultEndurance = 15; + DefaultPerception = 15; + DefaultIntelligence = 20; + DefaultWisdom = 30; + DefaultCharisma = 10; + DefaultMana = 5; + TotalVolume = 80000; + TotalSize = 170; + NameSingular = "mango grower"; + RightGauntlet = HARDENED_LEATHER gauntlet; + SkinColor = rgb16(128, 80, 48); + HairColor = rgb16(80, 48, 32); + ClothColor = rgb16(56, 48, 20); + HeadBitmapPos = 96, 0; + TorsoBitmapPos = 32, 0; + ArmBitmapPos = 64, 64; + LegBitmapPos = 0, 288; + Inventory == mango { Times = 10; } + TamingDifficulty = NO_TAMING; + HostileReplies == "\"Mango POWER!\""; + AttachedGod = SILVA; + CanRead = true; + FriendlyReplies = + { + 6, + "@Dd curses: \"I hate bananas. I will go and grow Mangos forthwith.\"", + "\"I was ex-prime-minister before Attnam invaded our peaceful land and I was jailed.\"", + "\"1 + 1 = ... I don't remember. It been so long in this rotten jail!\"", + "@Dd seems very joyful. \"Maybe I can be prime-minister again?\"", + "\"I knew you would come to save us!\"", + "\"Kill Petrus for us, too!\""; + } + AutomaticallySeen = true; + ScienceTalkPossibility = 10; + ScienceTalkIntelligenceModifier = 10; + ScienceTalkWisdomModifier = 25; + ScienceTalkIntelligenceRequirement = 5; + ScienceTalkWisdomRequirement = 10; + IsMasochist = true; +} + +imperialist +{ + DefaultArmStrength = 20; + DefaultLegStrength = 25; + DefaultDexterity = 15; + DefaultAgility = 20; + DefaultEndurance = 15; + DefaultPerception = 24; + DefaultIntelligence = 35; + DefaultWisdom = 10; + DefaultCharisma = 25; + DefaultMana = 10; + TotalVolume = 160000; + TotalSize = 160; + NameSingular = "imperialist"; + Cloak = NYMPH_HAIR cloak { Enchantment = 1; } + BodyArmor = MITHRIL bodyarmor(CHAIN_MAIL) { Enchantment = 1; } + Belt = GOLD belt(BELT_OF_CARRYING) { Enchantment = 2; } + RightWielded = OMMEL_HAIR RUBY whip(RUNED_WHIP) { Enchantment = 3; } + LeftWielded = OMMEL_HAIR RUBY whip(RUNED_WHIP) { Enchantment = 3; } + LeftRing = ring(RING_OF_INFRA_VISION); + RightGauntlet = NYMPH_HAIR gauntlet { Enchantment = 1; } + RightBoot = MITHRIL boot(BOOT_OF_KICKING) { Enchantment = 3; } + KnownCWeaponSkills = { 2, WHIPS, KICK; } + CWeaponSkillHits = { 2, 500, 500; } + RightSWeaponSkillHits = 500; + LeftSWeaponSkillHits = 500; + PanicLevel = 10; + TorsoBitmapPos = 48, 240; + HeadBitmapPos = 96, 208; + ArmBitmapPos = 80, 112; + LegBitmapPos = 0, 0; + ClothColor = rgb16(50, 50, 50); + UsesLongArticle = true; + Inventory = { 6, stone, stone, stone, stone, stone, holybook(MELLIS); } + CanRead = true; + HostileReplies == "\"Die you communist pig!\""; + IsUnique = true; + IsNameable = false; + CanBeCloned = false; + DefaultName = "Richel Decos"; + AttackStyle = USE_ARMS|USE_LEGS; + CriticalModifier = 4; + TamingDifficulty = NO_TAMING; + StandVerb = "smiling in a twisted way"; + AttachedGod = MELLIS; + CanBeConfused = false; + IsPolymorphable = false; + FriendlyReplies = + { + 7, /* the last isn't used if the player is a sumo champion */ + "\"And they said levitating ostriches had no future! Hah!", + "\"Poor people shouldn't complain - after all it's their own fault.\"", + "\"Why can't those darn tax collectors ever believe an honest but poor enterpreneur who says he can't pay? Hmmm. I wonder if my room has something to do with it?\"", + "\"I couldn't possibly thank this city enough for making Decos Bananas Co. the world's greatest fruit producer. So I don't do it all.\"", + "\"Low work and transportation costs have allowed me to thrive in a way envied by all rivals.\"", + "\"I won't stop until my bananas lie on the breakfast tables of every single dwarf, elf and man above Valpurus!\"", + "\"What? You're still here? What the hell I pay you for? Wait, I don't pay you anything. Never mind.\""; + } + AutomaticallySeen = true; + ScienceTalkAdjectiveAttribute = + { + 81, + "applied", "recent", "theoretical", "classical", "the future of", + "fundamental", "legendary", "higher", "elementary", "practical", + "artificial", "the wonders of", "modern", "empirical", "statistical", + "the axioms of", "the theory of", "urban", "forbidden", "nautical", + "popular", "the current state of", "liberal", "ceremonial", "heroic", + + "mythical", "scientific", "advanced", "revolutionary", "motorized", + "neolithic", "jurassic", "synthetic", "polymorphic", "strategic", + "tropical", "diplomatic", "political", "experimental", "glorious", + "the evolution of", "global", "natural", "genetic", "intellectual", + "hard", "compressed", "educational", "recreational", "teleological", + + "the creative uses of", "selective", "the origin of", "royal", "alien", + "evolutionary", "the art of", "psychic", "unified", "postmodern", + "hypnotic", "doctrine:", "frictionless", "the secrets of", "mental", + "transcendent", "the purpose of", "the history of", "good", "arctic", + "the implementation of", "paraller", "orthodox", "soft", "chaotic", + + "happy", "the blessings of", "ancient", "flexible", "laissez-faire", + "the magnificence of"; + } + ScienceTalkSubstantiveAttribute = + { + 7, + "New Age", "space", "last century", "mass", "free market", + "hybrid", "probability"; + } + ScienceTalkPrefix = + { + 18, + "", "", "", "", "", + "macro", "ethno", "hyper", "trans", "geo", + "mega", "cosmo", "meta", "super", "ego", + "pseudo", "eroto", "neo"; + } + + ScienceTalkName = + { + 37, + "commercials", "lockouts", "banking", "profits", "egoism", + "bazaars", "corporations", "sackings", "advertising", "capitalism", + "monopolies", "!M.U.L.E.", "tax cuts", "bargaining", "brands", + "wholesaling", "cartels", "economics", "commerse", "lobbying", + + "the industrial revolution", "game theory", "!the Wealth of Nations", + "economic growth", "right-wing politics", "customer manipulation", + "public relations", "guerilla marketing", "industrial espionage", + "the anti-communist movement", "investor relations", "price warfare", + "the crusade against labour unions", "ESP marketing", "insurance firms", + "cigarettes", "!the Prince"; + } + ScienceTalkPossibility = 80; + ScienceTalkIntelligenceModifier = 25; + ScienceTalkWisdomModifier = 0; + ScienceTalkCharismaModifier = 50; + ScienceTalkIntelligenceRequirement = 10; + ScienceTalkWisdomRequirement = 0; + ScienceTalkCharismaRequirement = 15; + IsSadist = true; + UndeadVersions = false; +} + +smith +{ + DefaultArmStrength = 35; + DefaultLegStrength = 20; + DefaultDexterity = 30; + DefaultAgility = 10; + DefaultEndurance = 30; + DefaultPerception = 15; + DefaultIntelligence = 15; + DefaultWisdom = 15; + DefaultCharisma = 15; + DefaultMana = 10; + TotalVolume = 80000; + TotalSize = 190; + NameSingular = "smith"; + Helmet = STEEL helmet { Enchantment = 1; } + Cloak = OMMEL_HAIR cloak(CLOAK_OF_FIRE_RESISTANCE) { Enchantment = 1; } + BodyArmor = STEEL bodyarmor(PLATE_MAIL) { Enchantment = 1; } + RightWielded = MITHRIL meleeweapon(HAMMER) { Enchantment = 2; } + RightGauntlet = LEATHER gauntlet(GAUNTLET_OF_DEXTERITY) { Enchantment = 4; } + RightRing = ring(RING_OF_FIRE_RESISTANCE); + LeftRing = ring(RING_OF_FIRE_RESISTANCE); + RightBoot = STEEL boot { Enchantment = 1; } + KnownCWeaponSkills == BLUNT_WEAPONS; + CWeaponSkillHits == 500; + RightSWeaponSkillHits = 200; + LeftSWeaponSkillHits = 200; + TorsoBitmapPos = 48, 192; + HeadBitmapPos = 112, 160; + ArmBitmapPos = 64, 64; + LegBitmapPos = 0, 96; + HairColor = rgb16(200, 200, 200); + ClothColor = rgb16(100, 100, 100); + CanRead = true; + /* Replies overridden */ + IsUnique = true; + IsNameable = false; + CanBeCloned = false; + DefaultName = "Ikiros"; + TamingDifficulty = NO_TAMING; + AttachedGod = LORICATUS; + +Config KHARAZ_ARAD; + { + DefaultName = "Khorocko"; + HeadBitmapPos = 112, 160; + TorsoBitmapPos = 48, 16; + ArmBitmapPos = 80, 0; + LegBitmapPos = 0, 208; + HairColor = rgb16(144, 72, 0); + BeltColor = rgb16(72, 56, 16); + } +} + +ostrich +{ + DefaultArmStrength = 15; + DefaultAgility = 30; + DefaultEndurance = 10; + DefaultPerception = 15; + DefaultIntelligence = 5; + DefaultWisdom = 5; + DefaultCharisma = 7; + DefaultMana = 0; + TotalVolume = 120000; + TorsoBitmapPos = 64, 16; + HostileReplies == "@Dd cackles diabolically."; + FriendlyReplies == "@Dd cackles cheerfully. @Pp likes @sp life."; + TotalSize = 230; + SkinColor = rgb16(160, 140, 140); + TorsoMainColor = rgb16(48, 48, 48); /* the body feathers */ + Adjective = "levitating"; + NameSingular = "ostrich"; + NamePlural = "ostriches"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 200; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 20; + PanicLevel = 95; + FleshMaterial = OSTRICH_FLESH; + DeathMessage = "@Dd is squashed to a bloody mass of feathers."; + StandVerb = "floating"; + TamingDifficulty = NO_TAMING; /* AI will go insane if it leaves New Attnam */ + AttachedGod = SILVA; + ClassStates = LEVITATION; + AutomaticallySeen = true; +} + +elder +{ + DefaultArmStrength = 7; + DefaultLegStrength = 10; + DefaultDexterity = 7; + DefaultAgility = 15; + DefaultEndurance = 7; + DefaultPerception = 21; + DefaultIntelligence = 25; + DefaultWisdom = 40; + DefaultCharisma = 20; + DefaultMana = 20; + TotalVolume = 70000; + TotalSize = 160; + NameSingular = "village elder"; + SkinColor = rgb16(128, 80, 48); + HairColor = rgb16(32, 32, 32); + ClothColor = rgb16(56, 48, 20); + HeadBitmapPos = 96, 224; + TorsoBitmapPos = 32, 0; + ArmBitmapPos = 64, 0; + LegBitmapPos = 0, 288; + IsUnique = true; + DefaultName = "Kaethos"; + TamingDifficulty = NO_TAMING; + AttachedGod = SOPHOS; + CanRead = true; + Inventory == holybook(SILVA); + HostileReplies == "\"I knew those hippos couldn't raise anything decent!\""; + FriendlyReplies = + { + 10, + "\"So you're leaving? The stars tell me you will fight glorious battles, meet interesting people, find out suprising things and eventually die a violent death. Good luck.\"", + "\"I remember still clearly when we first found you in the jungle. You were five and had seemingly been raised by hippos since birth.\"", + "\"Even though you have lived here, you are somehow different from the Tweraifians, I can sense it. Is it your aggressive and wild character or your stupidity that separates us? Or is it just that your skin is white and our black?\"", + "\"Not long ago Tweraif was a civilization advanced beyound your comprehension. But we were pacifists and could not repulse Attnam's grunt army.\"", + "@Dd seems very melancholic. \"I was the head chancellor of the University of Tweraif before the invasion...\"", + "\"For a decade I served this Decos lad as a banana grower like everyone else, but then an elephant stepped on my toe and my leg was amputated. I couldn't climb trees anymore so they made me a tourist guide.\"", + "\"According to old knowledge ostriches were once kings of the sky. Their divine perception, seven feet wings and almost supernatural speed were envied by men and gods alike. Then one of them discovered levitation and soon they all became lazy and eventually dropped their wings.\"", + "\"Previously we could use donkeys to carry the bananas. Then one day Attnamese tested a fierce magical bomb nearby. All the asses mutated horribly and started attacking us. The donkeys, I mean, not the colonists.\"", + "\"The government of Attnam is led by the high priest of Valpurus, the Great Frog who carries the world. When I was young, Petrus assumed this position by killing the former high priest.\""; + "\"Some time ago Attnamese military alchemists managed to crossbreed the carnivorous plant and the pineapple tree. They named the result as genetrix vesana and discovered it was a powerful hunter. The colonists tried to transport it to Attnam through the underwater tunnel but never arrived in the destination.\"", + /*"\"Oh, you're going to Attnam through the tunnel? I don't envy you. There's a dreadful monster dwelling in the its forbidden depths: Lobh-Se, the misbegotten daughter of Scabies, who exists only to devour any man or beast she senses. Through the millenia she has gained every imaginable disease and bitten by every existing poisonous creature; now she is practically invulnerable to all damage.\"", + "\"Beware and avoid Lobh-Se at all costs! Fortunately, this is rather easy, as she only leaves her lair in the heart of the night and even then does not venture far, since nutrition is plenty there and she returns promptly when satiated.\"";*/ + } + AutomaticallySeen = true; + ScienceTalkPossibility = 75; + ScienceTalkIntelligenceModifier = 50; + ScienceTalkWisdomModifier = 100; + ScienceTalkIntelligenceRequirement = 10; + ScienceTalkWisdomRequirement = 15; + IsExtraFragile = true; +} + +encourager +{ + DefaultArmStrength = 15; + DefaultLegStrength = 10; + DefaultDexterity = 10; + DefaultAgility = 10; + DefaultEndurance = 15; + DefaultPerception = 21; + DefaultIntelligence = 7; + DefaultWisdom = 7; + DefaultCharisma = 7; + DefaultMana = 0; + CapColor = rgb16(64, 64, 64); + EyeColor = rgb16(140, 100, 70); + BootColor = rgb16(90, 60, 20); + HeadBitmapPos = 96, 240; + TorsoBitmapPos = 32, 0; + ArmBitmapPos = 64, 0; + LegBitmapPos = 16, 208; + TotalVolume = 90000; + TotalSize = 170; + NameSingular = "banana grower encourager"; + Belt = LEATHER belt; + RightWielded = HARDENED_LEATHER whip; + RightBoot = HARDENED_LEATHER boot; + KnownCWeaponSkills = { 2, KICK, WHIPS; } + CWeaponSkillHits = { 2, 100, 100; } + RightSWeaponSkillHits = 100; + AttackStyle = USE_ARMS|USE_LEGS; + TamingDifficulty = NO_TAMING; + HostileReplies == "\"Ah! Again me kick ye!\""; + AttachedGod = MELLIS; + FriendlyReplies = + { + 4; + "@Dd shouts harshly: \"Work! Work! Bring food to yer betters!\"", + "@Dd shouts almost sadly: \"Gonna leave, man? Pity. Me kinda liked kicking ya.\"", + "@Dd screams at your face: \"Me talented shouter. That made everyone in Attnam jealous. Me was sent here and told to never come back.\"", + "@Dd shouts smiling: \"Me has team management diploma from Decos business school! Me very good at encouraging workers.\""; + } + AutomaticallySeen = true; + IsSadist = true; +} + +chameleon +{ + DefaultArmStrength = 4; + DefaultAgility = 20; + DefaultEndurance = 12; + DefaultPerception = 12; + DefaultIntelligence = 5; + DefaultWisdom = 5; + DefaultCharisma = 8; + DefaultMana = 0; + CanOpen = false; + TotalVolume = 4500; + TorsoBitmapPos = 16, 0; + HostileReplies == "The skin of @du is blood red for a moment."; + FriendlyReplies == "The skin of @du is white for a moment."; + TotalSize = 30; + /* SkinColor overridden */ + NameSingular = "chameleon"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 300; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 20; + FleshMaterial = CHAMELEON_FLESH; + IgnoreDanger = true; + HPRequirementForGeneration = 40; + DayRequirementForGeneration = 4; + Frequency = 1000; + IsExtraCoward = true; + AttachedGod = SCABIES; +} + +floatingeye +{ + DefaultArmStrength = 1; + DefaultAgility = 3; + DefaultEndurance = 6; + DefaultPerception = 50; + DefaultIntelligence = 2; + DefaultWisdom = 2; + DefaultCharisma = 8; + DefaultMana = 0; + CanOpen = false; + TotalVolume = 30000; + HostileReplies == "@du stares at you."; + FriendlyReplies == "@du stares at you."; + TotalSize = 40; + Adjective = "floating"; + NameSingular = "eye"; + /* AttackStyle is not used */ + BaseBiteStrength = 1; + CanBeGenerated = true; + FleshMaterial = FLOATING_EYE_FLESH; + IgnoreDanger = true; + HPRequirementForGeneration = 40; + DayRequirementForGeneration = 4; + Frequency = 1500; + TorsoBitmapPos = 32, 0; + SkinColor = rgb16(180,180,180); + ClothColor = rgb16(0, 0, 160); /* eye color */ + StandVerb = "levitating"; + HasALeg = false; + CanRead = true; + HasHead = false; + UsesNutrition = false; + AttackWisdomLimit = 8; + AttachedGod = LEGIFER; + ClassStates = INFRA_VISION|SEARCHING|GAS_IMMUNITY; + AttackStyle = USE_HEAD; + BaseBiteStrength = 0; + MoveType = FLY; + CanHear = false; + AllowUnconsciousness = false; + CanChoke = false; +} + +eddy +{ + DefaultArmStrength = 1; + DefaultAgility = 10; + DefaultEndurance = 3; + DefaultPerception = 12; + DefaultIntelligence = 1; + DefaultWisdom = 1; + DefaultCharisma = 1; + DefaultMana = 10; + TotalVolume = 150000; /* most of it is in some other dimension */ + TorsoBitmapPos = 64, 32; + HostileReplies == "The @du twirls angrily."; + FriendlyReplies == "The @du twirls faster for a moment."; + TotalSize = 100; + SkinColor = rgb16(200, 200, 112); + NameSingular = "eddy"; + NamePlural = "eddies"; + PostFix = "in space-time continuum"; + CanBeGenerated = true; + HasEyes = false; + HasHead = false; + HasALeg = false; + CanBeGenerated = true; + IgnoreDanger = true; + HPRequirementForGeneration = 50; + DayRequirementForGeneration = 5; + Frequency = 1000; + PanicLevel = 0; + StandVerb = "twirling"; + ForceCustomStandVerb = true; + UsesNutrition = false; + SpillsBlood = false; + Sweats = false; + FleshMaterial = MAGICAL_AIR; + DeathMessage = "@Dd disappears."; + AttachedGod = SOPHOS; + BodyPartsDisappearWhenSevered = true; + ClassStates = GAS_IMMUNITY; + AttackStyle = USE_HEAD; + BaseBiteStrength = 0; + TamingDifficulty = NO_TAMING; + CanHear = false; + CanChoke = false; + IsImmuneToStickiness = true; + MoveType = ETHEREAL|FLY; +} + +mushroom +{ + DefaultArmStrength = 4; + DefaultAgility = 2; + DefaultEndurance = 10; + DefaultPerception = 5; + DefaultIntelligence = 2; + DefaultWisdom = 2; + DefaultCharisma = 8; + DefaultMana = 4; + TotalVolume = 6250; + TorsoBitmapPos = 208, 16; + TotalSize = 40; + Adjective = "giant"; + NameSingular = "mushroom"; + HasEyes = false; + HasHead = false; + HasALeg = false; + UsesNutrition = false; + SpillsBlood = false; + Sweats = false; + FleshMaterial = MUSHROOM_FLESH; + SkinColor = rgb16(180, 180, 180); + CanBeGenerated = true; + AttachedGod = NEFAS; + DeathMessage = "@Dd is squashed."; + BaseUnarmedStrength = 200; + PanicLevel = 0; + CanOpen = false; + StandVerb = "rooted"; + IsRooted = true; + AllowUnconsciousness = false; + CanChoke = false; + HostileReplies == "@Dd expresses @sp utter contempt for your pitiful and loathsome existence by wobbling erratically."; + FriendlyReplies == "@Dd wobbles knowingly."; + + Config AMBULATORY; + { + AttributeBonus = 150; + Adjective = "giant ambulatory"; + TotalVolume = 8000; + TotalSize = 50; + IsRooted = false; + StandVerb = "crawling"; + DangerModifier = 75; + DayRequirementForGeneration = 4; + } +} + +magicmushroom +{ + DefaultArmStrength = 6; + DefaultAgility = 3; + DefaultEndurance = 20; + DefaultPerception = 7; + DefaultIntelligence = 3; + DefaultWisdom = 3; + DefaultCharisma = 10; + DefaultMana = 7; + TotalVolume = 10000; + TorsoBitmapPos = 128, 32; + TotalSize = 60; + Adjective = "magical"; + NameSingular = "mushroom"; + HasEyes = false; + HasHead = false; + HasALeg = false; + UsesNutrition = false; + FleshMaterial = MAGIC_MUSHROOM_FLESH; + SkinColor = rgb16(200, 170, 170); + CanBeGenerated = true; + AttachedGod = SCABIES; + BaseUnarmedStrength = 300; + KnownCWeaponSkills == UNARMED; + CWeaponSkillHits == 20; + Frequency = 4500; + DangerModifier = 75; + StandVerb = "wobbling"; + CanChoke = false; +} + +darkmage +{ + HeadBitmapPos = 112, 304; + TorsoBitmapPos = 48, 224; + ArmBitmapPos = 80, 64; + LegBitmapPos = 16, 160; + ClothColor = rgb16(50, 50, 50); + CanRead = true; + CanBeGenerated = true; + ClassStates = INFRA_VISION; + HostileReplies == "@Dd yells: \"Prepare to meet your maker!\""; + FriendlyReplies == "\"Death awaits our foes.\""; + IgnoreDanger = true; + AttachedGod = INFUSCOR; + IsAbstract = true; + KnownCWeaponSkills == BLUNT_WEAPONS; + IsExtraFragile = true; + IsSadist = true; + + Config APPRENTICE; + { + DefaultArmStrength = 15; + DefaultLegStrength = 15; + DefaultDexterity = 15; + DefaultAgility = 21; + DefaultEndurance = 15; + DefaultPerception = 21; + DefaultIntelligence = 15; + DefaultWisdom = 10; + DefaultCharisma = 10; + DefaultMana = 20; + UsesLongAdjectiveArticle = true; + Adjective = "apprentice"; + NameSingular = "dark mage"; + TotalVolume = 60000; + TotalSize = 180; + ClothColor = rgb16(80, 80, 80); + BeltColor = rgb16(80, 80, 80); + Cloak = LEATHER cloak { Enchantment = 2; } + RightWielded = OAK_WOOD OAK_WOOD meleeweapon(QUARTER_STAFF) { Enchantment = 3; } + HPRequirementForGeneration = 150; + DayRequirementForGeneration = 15; + Frequency = 1500; + PanicLevel = 75; + Inventory == lantern; + CWeaponSkillHits == 50; + RightSWeaponSkillHits = 20; + LeftSWeaponSkillHits = 20; + PolymorphIntelligenceRequirement = 5; + Inventory = { 2, Random { MaxPrice = 500; Category = WAND|SCROLL; Chance = 10; }, Random { MinPrice = 500; Category = WAND|SCROLL; Chance = 5; } } + ScienceTalkPossibility = 25; + ScienceTalkIntelligenceModifier = 10; + ScienceTalkWisdomModifier = 5; + ScienceTalkIntelligenceRequirement = 10; + ScienceTalkWisdomRequirement = 5; + } + + Config BATTLE_MAGE; + { + DefaultArmStrength = 13; + DefaultLegStrength = 13; + DefaultDexterity = 20; + DefaultAgility = 18; + DefaultEndurance = 15; + DefaultPerception = 18; + DefaultIntelligence = 30; + DefaultWisdom = 15; + DefaultCharisma = 15; + DefaultMana = 30; + Adjective = "dark"; + NameSingular = "battlemage"; + TotalVolume = 70000; + TotalSize = 170; + HairColor = rgb16(180, 40, 40); + CapColor = rgb16(180, 40, 40); + Cloak = NYMPH_HAIR cloak { Enchantment = 3; } + RightWielded = TEAK_WOOD TEAK_WOOD meleeweapon(QUARTER_STAFF) { Enchantment = 4; } + HPRequirementForGeneration = 250; + DayRequirementForGeneration = 25; + Frequency = 1000; + PanicLevel = 25; + CWeaponSkillHits == 100; + RightSWeaponSkillHits = 50; + LeftSWeaponSkillHits = 50; + PolymorphIntelligenceRequirement = 20; + Inventory = { 2, Random { MaxPrice = 500; Category = WAND|SCROLL; Chance = 25; }, Random { MinPrice = 500; Category = WAND|SCROLL; Chance = 10; } } + ScienceTalkPossibility = 50; + ScienceTalkIntelligenceModifier = 25; + ScienceTalkWisdomModifier = 10; + ScienceTalkIntelligenceRequirement = 20; + ScienceTalkWisdomRequirement = 10; + } + + Config ELDER; + { + DefaultArmStrength = 11; + DefaultLegStrength = 11; + DefaultDexterity = 25; + DefaultAgility = 15; + DefaultEndurance = 15; + DefaultPerception = 15; + DefaultIntelligence = 45; + DefaultWisdom = 20; + DefaultCharisma = 20; + DefaultMana = 40; + UsesLongAdjectiveArticle = true; + Adjective = "elder"; + NameSingular = "dark mage"; + ClassStates = INFRA_VISION|ESP; + TotalVolume = 80000; + TotalSize = 160; + HairColor = rgb16(140, 140, 140); + CapColor = rgb16(80, 80, 160); + Cloak = OMMEL_HAIR cloak { Enchantment = 4; } + RightWielded = ARCANITE ARCANITE meleeweapon(QUARTER_STAFF) { Enchantment = 5; } + HPRequirementForGeneration = 350; + DayRequirementForGeneration = 35; + Frequency = 500; + PanicLevel = 10; + CWeaponSkillHits == 200; + RightSWeaponSkillHits = 100; + LeftSWeaponSkillHits = 100; + PolymorphIntelligenceRequirement = 35; + Inventory = { 2, Random { MaxPrice = 500; Category = WAND|SCROLL; Chance = 50; }, Random { MinPrice = 500; Category = WAND|SCROLL; Chance = 50; } } + ScienceTalkPossibility = 100; + ScienceTalkIntelligenceModifier = 100; + ScienceTalkWisdomModifier = 50; + ScienceTalkIntelligenceRequirement = 35; + ScienceTalkWisdomRequirement = 25; + } + + Config ARCH_MAGE; + { + DefaultArmStrength = 9; + DefaultLegStrength = 9; + DefaultDexterity = 30; + DefaultAgility = 12; + DefaultEndurance = 15; + DefaultPerception = 12; + DefaultIntelligence = 60; + DefaultWisdom = 25; + DefaultCharisma = 25; + DefaultMana = 50; + Adjective = "dark"; + NameSingular = "archmage"; + DefaultName = "Ischaldirh"; + ClassStates = INVISIBLE|INFRA_VISION|ESP|TELEPORT_CONTROL; + TotalVolume = 100000; + TotalSize = 150; + IsUnique = true; + PanicLevel = 0; + TamingDifficulty = 50; + IsNameable = false; + CanBeCloned = false; + IsPolymorphable = false; + HairColor = rgb16(200, 200, 200); + EyeColor = rgb16(0, 0, 0); + ClothColor = rgb16(30, 30, 30); + CapColor = rgb16(50, 50, 50); + BodyArmor = ARCANITE bodyarmor(CHAIN_MAIL) { Enchantment = 5; } + Cloak = PHOENIX_FEATHER cloak { Enchantment = 5; } + Belt = ANGEL_HAIR belt { Enchantment = 5; } + RightBoot = DRAGON_HIDE boot(BOOT_OF_AGILITY) { Enchantment = 5; } + RightWielded = OCTIRON OCTIRON meleeweapon(QUARTER_STAFF) { Enchantment = 6; } + Inventory = { 10, wand(WAND_OF_RESURRECTION), wand(WAND_OF_STRIKING), wand(WAND_OF_CLONING), wand(WAND_OF_LIGHTNING), wand(WAND_OF_FIRE_BALLS), scrollofcharging, scrollofwishing, holybook(INFUSCOR), key(OCTAGONAL_LOCK), solstone; } + HPRequirementForGeneration = 450; + DayRequirementForGeneration = 45; + PanicLevel = 0; + CanBeConfused = false; + CWeaponSkillHits == 500; + RightSWeaponSkillHits = 200; + LeftSWeaponSkillHits = 200; + FireResistance = 40; + ElectricityResistance = 40; + EnergyResistance = 40; + PolymorphIntelligenceRequirement = 50; + IsImmuneToItemTeleport = true; + IsExtraFragile = false; + AllowUnconsciousness = false; + UndeadVersions = false; + } +} + +ghost +{ + DefaultArmStrength = 5; + DefaultAgility = 8; + DefaultEndurance = 8; + DefaultPerception = 24; + DefaultIntelligence = 15; + DefaultWisdom = 10; + DefaultCharisma = 10; + DefaultMana = 15; + StandVerb = "hovering"; + Frequency = 5000; + TotalVolume = 300000; + TorsoBitmapPos = 112, 16; + TotalSize = 100; + NameSingular = "ghost"; + ClassStates = INFRA_VISION|ESP|GAS_IMMUNITY|LEVITATION; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 100; + BaseEmitation = rgb24(110, 110, 110); + HostileReplies == "\"Boo!\""; + FriendlyReplies == "\"A very good Boo to you, my friend.\""; + PanicLevel = 0; + HasALeg = false; + FleshMaterial = GHOST; + DeathMessage = "@Dd is sucked into hell."; + SpillsBlood = false; + Sweats = false; + UsesNutrition = false; + AttachedGod = MORTIFER; + BodyPartsDisappearWhenSevered = true; + BaseBiteStrength = 1000; + AttackStyle = USE_HEAD; + BiteCapturesBodyPart = false; + MoveType = ETHEREAL; + CanChoke = false; + IsImmuneToStickiness = true; + CreateUndeadConfigurations = true; + UndeadAttributeModifier = 75; + UndeadCopyMaterials = false; + CanBeGenerated = true; + CanBeGeneratedOnlyInTheCatacombs = true; + IsCatacombCreature = true; +} + +twoheadedmoose +{ + DefaultArmStrength = 25; + DefaultAgility = 20; + DefaultEndurance = 15; + DefaultPerception = 18; + DefaultIntelligence = 5; + DefaultWisdom = 5; + DefaultCharisma = 3; + DefaultMana = 0; + TotalVolume = 200000; + TorsoBitmapPos = 176, 16; + HostileReplies == "Both heads of the moose snarl angrily at you."; + FriendlyReplies == "Both heads of the moose nod at you."; + TotalSize = 250; + SkinColor = rgb16(90, 85, 80); + NameSingular = "moose"; + NamePlural = "moose"; + Adjective = "two-headed"; + AttackStyle = USE_HEAD; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 100; + FleshMaterial = MOOSE_FLESH; + DeathMessage = "@Dd dies both heads snarling bitterly."; + AttachedGod = SCABIES; + BaseBiteStrength = 300; + CanKick = true; + DangerModifier = 50; + IsEnormous = true; +} + +magpie +{ + DefaultArmStrength = 2; + DefaultAgility = 40; + DefaultEndurance = 8; + DefaultPerception = 20; + DefaultIntelligence = 6; + DefaultWisdom = 5; + DefaultCharisma = 6; + DefaultMana = 0; + StandVerb = "flying"; + CanOpen = false; + TotalVolume = 500; + TorsoBitmapPos = 560, 0; + HostileReplies == "@Dd peeps evilly."; + FriendlyReplies == "@Dd chirps happily."; + TotalSize = 20; + SkinColor = rgb16(64, 64, 64); + TorsoMainColor = rgb16(180, 180, 180); + Adjective = "giant"; + NameSingular = "magpie"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 400; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 50; + PanicLevel = 75; + FleshMaterial = MAGPIE_FLESH; + AttachedGod = CLEPTIA; + ClassStates = INFRA_VISION; /* So that invisible player doesn't screw the AI */ + BiteCapturesBodyPart = false; + MoveType = FLY; +} + +skunk +{ + DefaultArmStrength = 6; + DefaultAgility = 10; + DefaultEndurance = 9; + DefaultPerception = 18; + DefaultIntelligence = 5; + DefaultWisdom = 5; + DefaultCharisma = 4; + DefaultMana = 0; + TotalVolume = 10000; + TorsoBitmapPos = 128, 0; + HostileReplies == "@Dd grawls."; + FriendlyReplies == "@Dd discusses philosophy with you."; + TotalSize = 50; + SkinColor = rgb16(80, 80, 80); + Adjective = "big"; + NameSingular = "skunk"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 350; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 100; + FleshMaterial = SKUNK_FLESH; + AttachedGod = SILVA; + PanicLevel = 85; + TorsoMainColor = rgb16(220, 220, 220); + PoisonResistance = 10; + DangerModifier = 33; + ClassStates = GAS_IMMUNITY; +} + +invisiblestalker +{ + DefaultArmStrength = 5; + DefaultAgility = 25; + DefaultEndurance = 15; + DefaultPerception = 24; + DefaultIntelligence = 8; + DefaultWisdom = 6; + DefaultCharisma = 5; + DefaultMana = 0; + TotalVolume = 40000; + TorsoBitmapPos = 224, 16; + FriendlyReplies == "\"Gods are away on business.\""; + HostileReplies == "\"Misery is the river of the world.\""; + TotalSize = 170; + NameSingular = "stalker"; + NamePlural = "stalkers"; + Adjective = "invisible"; + UsesLongAdjectiveArticle = true; + AttackStyle = USE_ARMS; + CanBeGenerated = true; + FleshMaterial = MAGICAL_AIR; + BaseUnarmedStrength = 750; + DeathMessage = "@Dd hisses and vanishes."; + AttachedGod = CLEPTIA; + CanKick = true; + DangerModifier = 250; + KnownCWeaponSkills == UNARMED; + CWeaponSkillHits == 200; + ClassStates = INVISIBLE; + BodyPartsDisappearWhenSevered = true; + IsImmuneToStickiness = true; +} + +largecreature +{ + IsAbstract = true; + CanBeWished = false; + CanBeGenerated = false; + IsEnormous = true; +} + +elpuri +{ + DefaultArmStrength = 80; + DefaultAgility = 25; + DefaultEndurance = 20; + DefaultPerception = 42; + DefaultIntelligence = 45; + DefaultWisdom = 35; + DefaultCharisma = 2; + DefaultMana = 35; + TamingDifficulty = NO_TAMING; + IsNameable = false; + IsUnique = true; + TotalVolume = 1000000; + TorsoBitmapPos = 160, 64; + TotalSize = 250; + NameSingular = "Master Dark Frog"; + IsPolymorphable = false; + CanBeGenerated = false; + Alias == "Elpuri"; + CWeaponSkillHits == 1000; + PanicLevel = 0; + CanBeCloned = false; + DefaultName = "Elpuri"; + HostileReplies == "@Dd roars horribly: \"DiE hUmAn!!\""; + BloodMaterial = DARK_FROG_BLOOD; + SkinColor = rgb16(60, 60, 60); + ClassStates = INFRA_VISION|TELEPORT_CONTROL; + BaseBiteStrength = 1500; + FleshMaterial = ELPURI_FLESH; + DeathMessage = "@Dd groans horribly and drops @sp head."; + AttachedGod = SCABIES; + CanTalk = true; + CanBeConfused = false; + AttackStyle = USE_HEAD; + KnownCWeaponSkills == BITE; + MoveType = WALK|SWIM; + DestroysWalls = true; + AllowUnconsciousness = false; +} + +genetrixvesana +{ + DefaultArmStrength = 16; + DefaultAgility = 8; + DefaultEndurance = 30; + DefaultPerception = 24; + DefaultIntelligence = 12; + DefaultWisdom = 8; + DefaultCharisma = 20; + DefaultMana = 0; + TotalVolume = 60000; + TorsoBitmapPos = 128, 64; + TotalSize = 250; + Adjective = "mother carnivorous"; + NameSingular = "plant"; + DefaultName = "genetrix vesana"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 600; + SkinColor = rgb16(111, 64, 37); + TorsoMainColor = rgb16(0, 160, 0); /* the leaves */ + TorsoSpecialColor = rgb16(160, 0, 0); + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 100; + PanicLevel = 0; + TamingDifficulty = NO_TAMING; + IsNameable = false; + IsUnique = true; + DeathMessage = "@Dd is brutally destroyed."; + BaseEmitation = rgb24(140, 100, 100); + FleshMaterial = MUTANT_PLANT_FIBER; + AttachedGod = SCABIES; + IsPlant = true; + CanOpen = false; + HostileReplies == "@Dd is silent."; + FriendlyReplies == "@Dd is silent."; + ClassStates = INFRA_VISION; + HasALeg = false; + SpillsBlood = false; + Sweats = false; + StandVerb = "rooted"; + IsRooted = true; + AllowUnconsciousness = false; + Sex = FEMALE; +} + +genefourxvesana +{ + DefaultArmStrength = 32; + DefaultAgility = 16; + DefaultEndurance = 20; + DefaultPerception = 32; + DefaultIntelligence = 24; + DefaultWisdom = 16; + DefaultCharisma = 3; + DefaultMana = 0; + TotalVolume = 60000; + TorsoBitmapPos = 128, 64; + TotalSize = 350; + Adjective = "grandmother carnivorous"; + NameSingular = "mutant plant"; + DefaultName = "genefourx vesana"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 1600; + SkinColor = rgb16(111, 64, 37); + TorsoMainColor = rgb16(0, 160, 0); /* the leaves */ + TorsoSpecialColor = rgb16(220, 0, 0); + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 100; + PanicLevel = 0; + TamingDifficulty = NO_TAMING; + IsNameable = false; + IsUnique = true; + DeathMessage = "@Dd is brutally destroyed."; + BaseEmitation = rgb24(140, 100, 100); + FleshMaterial = MUTANT_PLANT_FIBER; + AttachedGod = SCABIES; + IsPlant = true; + CanOpen = false; + HostileReplies == "@Dd produces a gutteral roaring noise."; + FriendlyReplies == "@Dd produces a gutteral purring noise."; + ClassStates = INFRA_VISION; + HasALeg = false; + SpillsBlood = false; + Sweats = false; + StandVerb = "rooted"; + IsRooted = true; + AllowUnconsciousness = false; + Sex = FEMALE; +} + +menatrixfusanga +{ + DefaultArmStrength = 62; + DefaultAgility = 20; + DefaultEndurance = 80; + DefaultPerception = 32; + DefaultIntelligence = 40; + DefaultWisdom = 8; + DefaultDexterity = 20; + DefaultCharisma = 40; + DefaultMana = 0; + TotalVolume = 60000; + TorsoBitmapPos = 0, 237; + TotalSize = 250; + Adjective = "massive magical"; + NameSingular = "mother mushroom"; + DefaultName = "menatrix fusanga"; + SkinColor = rgb16(111, 74, 60); + TorsoMainColor = rgb16(111, 74, 60); /* the cap */ + TorsoSpecialColor = rgb16(111, 74, 60); + PanicLevel = 0; + TamingDifficulty = NO_TAMING; + IsNameable = false; + IsUnique = true; + DeathMessage = "@Dd is brutally destroyed."; + BaseEmitation = rgb24(140, 100, 100); + FleshMaterial = MAGIC_MUSHROOM_FLESH; + AttachedGod = SCABIES; + CanOpen = false; + ClassStates = INFRA_VISION|TELEPORT_CONTROL|GAS_IMMUNITY; + HasEyes = false; + HasHead = false; + HasALeg = false; + IsPolymorphable = false; + SpillsBlood = false; + Sweats = false; + StandVerb = "wobbling"; + IsRooted = true; + AllowUnconsciousness = false; + BaseUnarmedStrength = 600; + KnownCWeaponSkills == UNARMED; + CWeaponSkillHits == 30; + HostileReplies == "@Dd expresses @sp utter contempt for your pitiful and loathsome existence by wobbling erratically."; + FriendlyReplies == "@Dd wobbles disappointedly due to the fact that you are cheating."; +} + +solicitus +{ + DefaultArmStrength = 100; + DefaultAgility = 2; + DefaultEndurance = 100; + DefaultPerception = 100; + DefaultIntelligence = 100; + DefaultWisdom = 100; + DefaultCharisma = 50; + DefaultMana = 100; + TotalVolume = 100000; + TorsoBitmapPos = 288, 64; + TotalSize = 500; + Adjective = "toppled"; + NameSingular = "god"; + PostFix = "of stress and hopeless situations"; + DefaultName = "Solicitus"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 1000; + SkinColor = rgb16(230, 140, 100); /* face color */ + TorsoMainColor = rgb16(20, 20, 160); /* his shirt */ + TorsoSpecialColor = rgb16(180, 180, 180); /* his eyes */ + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 100; + PanicLevel = 25; + IsNameable = false; + IsUnique = true; + TamingDifficulty = NO_TAMING; + DeathMessage = "@Dd is raised to purgatory in a fiery explosion by the grace of Legifer."; + BaseEmitation = rgb24(125, 125, 168); /*rgb24(0, 150, 200);*/ + BloodMaterial = SICK_BLOOD; + FleshMaterial = ANGEL_FLESH; + AttachedGod = SCABIES; + CanOpen = true; + /* Replies overridden */ + ClassStates = INFRA_VISION; + HasALeg = false; + SpillsBlood = false; + Sweats = true; + StandVerb = "seated"; + IsRooted = true; + AllowUnconsciousness = false; + BiteCapturesBodyPart = false; + AutomaticallySeen = true; + IsImmuneToStickiness = true; + IsPolymorphable = false; + CanBeGenerated = false; + CanBeCloned = false; + CanBeConfused = false; + DestroysWalls = true; + Sex = MALE; + IsEnormous = false; +} + +hedgehog +{ + DefaultArmStrength = 2; + DefaultAgility = 15; + DefaultEndurance = 10; + DefaultPerception = 12; + DefaultIntelligence = 5; + DefaultWisdom = 5; + DefaultCharisma = 6; + DefaultMana = 0; + TotalVolume = 5000; + TorsoBitmapPos = 112, 0; + HostileReplies == "@Dd ignores you."; + FriendlyReplies == "@Dd ignores you."; + TotalSize = 30; + SkinColor = rgb16(180, 160, 130); + Adjective = "large"; + NameSingular = "hedgehog"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 300; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 20; + FleshMaterial = HEDGEHOG_FLESH; + AttachedGod = SILVA; + AutomaticallySeen = true; + CanOpen = false; + + Config SONIC; + { + + DefaultArmStrength = 20; + DefaultAgility = 65; + DefaultEndurance = 30; + DefaultPerception = 25; + DefaultIntelligence = 10; + DefaultWisdom = 10; + DefaultCharisma = 50; + Adjective = 0; + DefaultName = "Sonic"; + Alias == "Sonic"; + IsUnique = true; + SkinColor = rgb16(50, 50, 180); + FriendlyReplies = + { + 11, + "\"Way past cool!\"", + "\"I'm waaaiiitiiiinnng. \"", + "\"Up, over, and gone!.\""; + "\"Gotta juice and loose.\""; + "\"Let's go slo-mo!\""; + "\"'scuse me while I chunk my breakfeast...\""; + "\"Asta la vista!.\""; + "\"Gotta speed jeeves.\""; + "\"Let's do it to it.\""; + "\"It's juice and jam time.\""; + "\"Bummer majores!\""; + } + + } +} + +anvitas +{ + DefaultArmStrength = 70; + DefaultAgility = 50; + DefaultEndurance = 30; + DefaultPerception = 33; + DefaultIntelligence = 8; + DefaultWisdom = 7; + DefaultCharisma = 16; + DefaultMana = 0; + TorsoBitmapPos = 192, 96; + HostileReplies == "@Dd roars terribly."; + FriendlyReplies == "@Dd roars cheerfully."; + SkinColor = rgb16(140, 90, 100); + TorsoSpecialColor = rgb16(165, 4, 61); + NameSingular = "hedgehog"; + Adjective = "gigantic mutant"; + AttackStyle = USE_HEAD; + BaseUnarmedStrength = 1000; + BaseBiteStrength = 500; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 250; + FleshMaterial = MUTANT_HEDGEHOG_FLESH; + AttachedGod = SCABIES; + TotalSize = 300; + MoveType = WALK; + DestroysWalls = true; + DefaultName = "Anvitas"; + Alias == "Anvitas"; + TotalVolume = 600000; + IsUnique = true; + CanApply = true; + IsPolymorphable = false; + CanBeCloned = false; + BloodMaterial = GLOWING_BLOOD; +} + +bunny +{ + DefaultEndurance = 16; + DefaultPerception = 18; + DefaultIntelligence = 10; + DefaultWisdom = 6; + DefaultCharisma = 10; + DefaultMana = 0; + TorsoBitmapPos = 160, 0; + HostileReplies == "@Dd squeals furiously."; + FriendlyReplies == "@Dd squeals happily."; + SkinColor = rgb16(90, 70, 60); + NameSingular = "bunny"; + NamePlural = "bunnies"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 650; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 20; + FleshMaterial = MUTANT_BUNNY_FLESH; + AttachedGod = SILVA; + IsAbstract = true; + CanOpen = false; + BloodMaterial = GLOWING_BLOOD; + + Config BABY_MALE; + { + Adjective = "young male carnivorous mutant"; + TotalVolume = 20000; + AttributeBonus = -50; + TorsoBitmapPos = 144, 0; + DefaultArmStrength = 8; + DefaultAgility = 10; + TotalSize = 100; + } + + Config BABY_FEMALE; + { + Adjective = "young female carnivorous mutant"; + TotalVolume = 17500; + AttributeBonus = -50; + TorsoBitmapPos = 144, 0; + DefaultArmStrength = 4; + DefaultAgility = 15; + TotalSize = 90; + } + + Config ADULT_MALE; + { + Adjective = "adult male carnivorous mutant"; + TotalVolume = 40000; + TorsoBitmapPos = 160, 0; + DefaultArmStrength = 16; + DefaultAgility = 20; + TotalSize = 50; + } + + Config ADULT_FEMALE; + { + Adjective = "adult female carnivorous mutant"; + TotalVolume = 35000; + TorsoBitmapPos = 160, 0; + DefaultArmStrength = 8; + DefaultAgility = 30; + TotalSize = 45; + } +} + +vladimir +{ + DefaultArmStrength = 60; + DefaultAgility = 30; + DefaultEndurance = 25; + DefaultPerception = 30; + DefaultIntelligence = 8; + DefaultWisdom = 7; + DefaultCharisma = 15; + DefaultMana = 0; + TorsoBitmapPos = 288, 32; + HostileReplies == "@Dd roars terribly."; + FriendlyReplies == "@Dd roars cheerfully."; + SkinColor = rgb16(110, 90, 80); + NameSingular = "bunny"; + Adjective = "gigantic carnivorous mutant"; + AttackStyle = USE_HEAD|USE_ARMS; + BaseUnarmedStrength = 1000; + BaseBiteStrength = 500; + KnownCWeaponSkills = { 2, UNARMED, BITE; } + CWeaponSkillHits == 200; + FleshMaterial = MUTANT_BUNNY_FLESH; + AttachedGod = SCABIES; + TotalSize = 400; + MoveType = WALK; + DestroysWalls = true; + DefaultName = "Vladimir"; + Alias == "Vladimir"; + TotalVolume = 800000; + IsUnique = true; + CanApply = true; + IsPolymorphable = false; + CanBeCloned = false; + ConstantCommandFlags = FLEE_FROM_ENEMIES; + BloodMaterial = GLOWING_BLOOD; +} + +haastseagle +{ + DefaultArmStrength = 60; + DefaultAgility = 30; + DefaultEndurance = 25; + DefaultPerception = 30; + DefaultIntelligence = 10; + DefaultWisdom = 7; + DefaultCharisma = 5; + DefaultMana = 0; + TorsoBitmapPos = 128, 128; + HostileReplies == "@Dd cries bloodcurdlingly."; + FriendlyReplies == "@Dd cries cheerfully."; + SkinColor = rgb16(128, 102, 70); + TorsoMainColor = rgb16(255, 255, 0); + TorsoSpecialColor = rgb16(148, 148, 120); + NameSingular = "haast's eagle"; + Adjective = "enormous"; + UsesLongAdjectiveArticle = true; + AttackStyle = USE_HEAD; + BaseBiteStrength = 800; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 200; + FleshMaterial = EAGLE_FLESH; + AttachedGod = SILVA; + TotalSize = 200; + MoveType = FLY; + TotalVolume = 24000; + IsPolymorphable = false; + CanBeCloned = false; + StandVerb = "flying"; + CanOpen = false; + CanBeGenerated = true; + /*ClassStates = INFRA_VISION;*/ + BiteCapturesBodyPart = true; + MoveType = FLY; + IsCatacombCreature = false; +} + +hattifattener +{ + DefaultArmStrength = 3; + DefaultAgility = 10; + DefaultEndurance = 10; + DefaultPerception = 9; + DefaultIntelligence = 3; + DefaultWisdom = 3; + DefaultCharisma = 6; + DefaultMana = 0; + TotalVolume = 10000; + TorsoBitmapPos = 208, 0; + HostileReplies == "@Dd emits a thousand sparkles of furious electricity."; + FriendlyReplies == "@Dd looks at you with @sp round, colourless eyes."; + TotalSize = 80; + SkinColor = rgb16(255, 255, 255); + TorsoSpecialColor = rgb16(48, 48, 48); + NameSingular = "hattifattener"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 1; + CanBeGenerated = true; + FleshMaterial = HATTIFATTENER_FLESH; + AttachedGod = TERRA; + IgnoreDanger = true; + HPRequirementForGeneration = 60; + DayRequirementForGeneration = 6; + ElectricityResistance = 100; + Frequency = 1000; + PanicLevel = 0; + BodyPartsDisappearWhenSevered = true; + DeathMessage = "@Dd explodes in a burst of electricity!"; + BaseEmitation = rgb24(130, 130, 130); + SpillsBlood = false; + Sweats = false; + Alias == "Hattivatti"; + AllowUnconsciousness = false; + CanChoke = false; + + Config FLOATIE; + { + Adjective = "levitating"; + MoveType = FLY; + StandVerb = "floating"; + } +} + +necromancer +{ + HeadBitmapPos = 112, 128; + TorsoBitmapPos = 48, 352; + ArmBitmapPos = 80, 96; + LegBitmapPos = 16, 160; + ClothColor = rgb16(50, 50, 50); + EyeColor = rgb16(200, 0, 0); + BeltColor = rgb16(100, 100, 100); + ArmSpecialColor = rgb16(100, 100, 140); + CanRead = true; + CanBeGenerated = true; + ClassStates = INFRA_VISION; + HostileReplies == "@Dd yells: \"Death! Death!\""; + FriendlyReplies == "\"I see dead people.\""; + IgnoreDanger = true; + AttachedGod = INFUSCOR; + IsAbstract = true; + KnownCWeaponSkills == BLUNT_WEAPONS; + NameSingular = "necromancer"; + IsExtraFragile = true; + IsSadist = true; + IsCatacombCreature = true; + + Config APPRENTICE_NECROMANCER; + { + DefaultArmStrength = 15; + DefaultLegStrength = 15; + DefaultDexterity = 15; + DefaultAgility = 8; + DefaultEndurance = 15; + DefaultPerception = 21; + DefaultIntelligence = 15; + DefaultWisdom = 10; + DefaultCharisma = 10; + DefaultMana = 20; + UsesLongAdjectiveArticle = true; + Adjective = "apprentice"; + TotalVolume = 60000; + TotalSize = 180; + Cloak = LEATHER cloak { Enchantment = 2; } + RightWielded = OAK_WOOD OAK_WOOD meleeweapon(QUARTER_STAFF) { Enchantment = 3; } + HPRequirementForGeneration = 50; + DayRequirementForGeneration = 5; + Frequency = 1500; + PanicLevel = 5; + CWeaponSkillHits == 50; + RightSWeaponSkillHits = 20; + LeftSWeaponSkillHits = 20; + CapColor = rgb16(150, 150, 100); + GauntletColor = rgb16(150, 150, 100); + PolymorphIntelligenceRequirement = 5; + Inventory = { 3, skull, Random { MaxPrice = 500; Category = WAND|SCROLL; Chance = 5; }, Random { MinPrice = 500; Category = WAND|SCROLL; Chance = 1; } } + ScienceTalkPossibility = 10; + ScienceTalkIntelligenceModifier = 5; + ScienceTalkWisdomModifier = 2; + ScienceTalkIntelligenceRequirement = 10; + ScienceTalkWisdomRequirement = 5; + } + + Config MASTER_NECROMANCER; + { + DefaultArmStrength = 13; + DefaultLegStrength = 13; + DefaultDexterity = 20; + DefaultAgility = 6; + DefaultEndurance = 15; + DefaultPerception = 18; + DefaultIntelligence = 30; + DefaultWisdom = 15; + DefaultCharisma = 15; + DefaultMana = 30; + Adjective = "master"; + TotalVolume = 70000; + TotalSize = 170; + Cloak = NYMPH_HAIR cloak { Enchantment = 3; } + RightWielded = TEAK_WOOD TEAK_WOOD meleeweapon(QUARTER_STAFF) { Enchantment = 4; } + HPRequirementForGeneration = 125; + DayRequirementForGeneration = 12; + Frequency = 1000; + PanicLevel = 0; + CWeaponSkillHits == 100; + RightSWeaponSkillHits = 50; + LeftSWeaponSkillHits = 50; + CapColor = rgb16(100, 100, 200); + GauntletColor = rgb16(100, 100, 200); + PolymorphIntelligenceRequirement = 20; + Inventory = { 5, BLUE_CRYSTAL skull { Chance = 20; }, PURPLE_CRYSTAL skull { Chance = 20; }, GREEN_CRYSTAL skull { Chance = 20; }, Random { MaxPrice = 500; Category = WAND|SCROLL; Chance = 10; }, Random { MinPrice = 500; Category = WAND|SCROLL; Chance = 3; } } + ScienceTalkPossibility = 50; + ScienceTalkIntelligenceModifier = 50; + ScienceTalkWisdomModifier = 25; + ScienceTalkIntelligenceRequirement = 20; + ScienceTalkWisdomRequirement = 10; + } +} + +sumowrestler +{ + DefaultArmStrength = 25; + DefaultLegStrength = 35; + DefaultDexterity = 15; + DefaultAgility = 6; + DefaultEndurance = 12; + DefaultPerception = 15; + DefaultIntelligence = 10; + DefaultWisdom = 15; + DefaultCharisma = 6; + DefaultMana = 0; + TotalVolume = 200000; + TotalSize = 160; + NameSingular = "sumo wrestler"; + DefaultName = "Huang Ming Pong"; + SkinColor = rgb16(128, 80, 48); + HairColor = rgb16(80, 48, 32); + ClothColor = rgb16(56, 48, 20); + HeadBitmapPos = 96, 336; + TorsoBitmapPos = 32, 352; + ArmBitmapPos = 64, 336; + LegBitmapPos = 16, 176; + TamingDifficulty = NO_TAMING; + HostileReplies == "\"SSSUUUMMMMMMOOOOORRRRGGGGGHHHHH!!!! (Mr. Decos said sumo wrestlers always yell like this)\""; + AttachedGod = SILVA; + KnownCWeaponSkills = { 2, UNARMED, KICK; } + CWeaponSkillHits = { 2, 1000, 1000; } + AttackStyle = USE_ARMS|USE_LEGS; + IsUnique = true; + CanBeWished = true; + IsPolymorphable = false; + PanicLevel = 10; + BaseUnarmedStrength = 100; + FriendlyReplies = + { + 9, /* 6 first are used before the revolution */ + "@Dd smiles broadly: \"'Tis the perfect occupation, this. I eat like kings! Or at least like kings who only eat bananas.\"", + "\"The ancient rules of this traditional martial art were designed by Mr. Decos himself, so don't complain about them.\"", + "\"My real name is Alfred, but the viceroy said it wasn't exotic enough for this job.\"", + "@Dd is in a buoyant mood. \"I love the new rule! Before the invasion, my wife always complained about my weight. Now she doesn't, since she's a slave in Attnam.\"", + "@Dd seems to be recalling sorrowful memories. \"When Tweraif was still sovereign, I tried to run a restaurant for a while. It ended in a bankrupt since I ate all the food myself.\"", + "@Dd winks at you: \"A belt of levitation is really useful for large people.\"", + "@Dd looks almost panicked. \"Hey, why aren't people bringing me bananas anymore! I'm hungry! Help! Don't let me starve!\"", + "\"If you attack Attnam, too, please don't liberate my wife. She'll force me to commence a merciless diet for sure!\"", + "\"I think I'm going to move abroad and start teaching sumo wrestling to foreigners. I'm sure it'll be the number one fashion sport of big-boned people in no time!\""; + } + AutomaticallySeen = true; + IsEnormous = true; +} + +tourist +{ + DefaultMana = 0; + CanRead = true; + IsAbstract = true; + AttachedGod = MELLIS; + SkinColor = rgb16(200, 110, 70); + IsExtraCoward = true; + AutomaticallySeen = true; + DefaultCommandFlags = FOLLOW_PLAYER|FLEE_FROM_ENEMIES; + ConstantCommandFlags = DONT_CONSUME_ANYTHING_VALUABLE; + WillCarryItems = false; + IsExtraFragile = true; + + Config HUSBAND; + { + DefaultArmStrength = 10; + DefaultLegStrength = 15; + DefaultDexterity = 12; + DefaultAgility = 8; + DefaultEndurance = 10; + DefaultPerception = 15; + DefaultIntelligence = 10; + DefaultWisdom = 7; + DefaultCharisma = 8; + TotalVolume = 90000; + TotalSize = 175; + NameSingular = "male tourist"; + HeadBitmapPos = 96, 64; + TorsoBitmapPos = 32, 368; + ArmBitmapPos = 80, 48; + LegBitmapPos = 16, 224; + DefaultMoney = 5000; + BootColor = rgb16(200, 0, 0); + ClothColor = rgb16(200, 16, 200); + LegMainColor = rgb16(50, 100, 132); + CapColor = rgb16(200, 200, 0); + HostileReplies == "\"Violence is barbaric. Couldn't we settle this little dispute of ours in a court of law?\""; + FriendlyReplies = + { + 6, + "\"Don't you barbarians really eat anything but fruits? I need some greasy sausages badly!\"", + "\"We're from Bazaria, the great trader nation, where every coin has equal rights. Richel Decos was our fellow countryman before he bought this town from the Attnamese.\"", + "\"Back home I plan commercials acted during theatre play breaks.\"", + "\"It is good for my son to see how we would live if science and trade hadn't brought us the prosperity and wellfare it has.\"", + "\"I couldn't believe my eyes when the travel agent showed me the extraordinary low prices of Decos Paradise Tours Inc. I believed they were a joke until he revealed the ingenious travel method: customers levitate over the ocean after consuming magical mushrooms! Needless to say, our holiday plans were immediately decided.\"", + "\"I presume you backward aborigines still boil people alive and eat them. Any chance of me seeing a young, gorgeous maiden facing this horrible destiny? Just for cultural education, you know.\""; + } + } + + Config WIFE; + { + DefaultArmStrength = 8; + DefaultLegStrength = 12; + DefaultDexterity = 8; + DefaultAgility = 12; + DefaultEndurance = 8; + DefaultPerception = 21; + DefaultIntelligence = 7; + DefaultWisdom = 10; + DefaultCharisma = 10; + TotalVolume = 70000; + TotalSize = 170; + Sex = FEMALE; + NameSingular = "female tourist"; + HeadBitmapPos = 112, 48; + TorsoBitmapPos = 48, 368; + ArmBitmapPos = 64, 160; + LegBitmapPos = 16, 192; + BootColor = rgb16(20, 20, 20); + HairColor = rgb16(60, 48, 24); + ClothColor = rgb16(50, 200, 50); + LegMainColor = rgb16(60, 60, 60); + LegSpecialColor = rgb16(60,50,60); + AttackStyle = USE_ARMS|USE_LEGS; + HostileReplies == "\"Help! A horribly hideous cannibal aborigine wants to capture me, hit me in the head with his club, drag me into a smelly sinister cave and mate with me!\""; + FriendlyReplies = + { + 5, + "\"I heard natives like you paint every inch of their body, dance complex ritual dances around a big bonfire and utter forbidden voodoo spells. When will you begin?\"", + "\"Why are all the villagers so sweaty? Can't they try to wash themselves often enough at least when civilized people are around?\"", + "\"I so love bananas! This is a heaven!\"", + "\"My friend's brother's ex-wife's uncle's old schoolmate inherited some money, bought shares of Decos Bananas Co. and became a millionaire. I think of it as an example of how anyone can become rich and happy if he tries hard enough.\"", + "\"Richel Decos is such a wonderful person! I wish I was married to someone as well-off as him!\""; + } + } + + Config CHILD; + { + DefaultArmStrength = 7; + DefaultLegStrength = 7; + DefaultDexterity = 15; + DefaultAgility = 15; + DefaultEndurance = 10; + DefaultPerception = 18; + DefaultIntelligence = 12; + DefaultWisdom = 5; + DefaultCharisma = 6; + TotalVolume = 50000; + TotalSize = 130; + NameSingular = "child tourist"; + HeadBitmapPos = 112, 352; + TorsoBitmapPos = 32, 384; + ArmBitmapPos = 80, 352; + LegBitmapPos = 16, 16; + BootColor = rgb16(200, 200, 200); + ClothColor = rgb16(200, 200, 200); + LegMainColor = rgb16(50, 100, 132); + CapColor = rgb16(40, 40, 40); + AttackStyle = USE_ARMS|USE_LEGS|USE_HEAD; + HostileReplies == "\"Daddy!!! Hit this man!!! He teases me!!!\""; + FriendlyReplies = + { + 4, + "\"A real native!!! Can I touch him, mommy? Can I? Can I?\"", + "\"I wanna see you sacrifice someone to tree gods!!!\"", + "\"Mister, can you catch me a demon spider? I wanna bring one home as a pet!!!\"", + "\"Did you see that Pong person? He's fatter than daddy!!! I can't believe it!!!\""; + } + } +} + +blinkdog +{ + DefaultArmStrength = 8; + DefaultAgility = 25; + DefaultEndurance = 8; + DefaultPerception = 21; + DefaultIntelligence = 10; + DefaultWisdom = 5; + DefaultCharisma = 10; + DefaultMana = 5; + TotalVolume = 30000; + TotalSize = 100; + SkinColor = rgb16(225, 225, 32); + NameSingular = "blink dog"; + BaseBiteStrength = 450; + CWeaponSkillHits == 100; + AttachedGod = SOPHOS; + DangerModifier = 50; + FleshMaterial = BLINK_DOG_FLESH; +} + +veterankamikazedwarf +{ + AttributeBonus = 50; + DefaultIntelligence = 3; + DefaultWisdom = 2; + Adjective = "veteran kamikaze"; + Inventory == backpack { SecondaryMaterial = GUN_POWDER { Volume = 30000; } } + HPRequirementForGeneration = 200; + DayRequirementForGeneration = 20; + Frequency = 100; + IsAbstract = true; + CreateDivineConfigurations = true; + HPRequirementForGeneration = 80; + DayRequirementForGeneration = 8; + UndeadVersions = false; + FriendlyReplies = + { + 6, + "\"Everyone believes I failed intentionally. Never! My loyalty to @Gd is spotless!\"", + "\"The commander gave me too few explosives! It wasn't my fault!\"", + "@Dd shouts: \"Death to disbelievers!\"", + "@Dd praises @Gd with numerous hymns. @Pp is obviously a very devoted follower.", + "@Dd seems very sorrowful. \"You can't believe how much I've been despised since that fateful mission...\"", + "@Dd almost sheds tears. \"I've lost all my friends and self-confidence due to the failure.\""; + } +} + +rookiekamikazedwarf +{ + AttributeBonus = -20; + DefaultIntelligence = 10; + DefaultWisdom = 5; + Adjective = "rookie kamikaze"; + Inventory == backpack { SecondaryMaterial = GUN_POWDER { Volume = 10000; } } + Frequency = 200; + IsAbstract = true; + DeathMessage = "@Dd dies screaming in agony."; + CreateDivineConfigurations = true; + HPRequirementForGeneration = 58; + DayRequirementForGeneration = 5; + UndeadVersions = false; + FriendlyReplies = + { + 6, + "\"I love @Gd, but don't you think there are better ways to show devotion and loyalty?\"", + "\"Look, the commander gave me these explosives! Do you think it's enough to do the job?\"", + "@Dd yells: \"Er... Death to disbelievers!\"", + "You ask @Dd if @Pp know any good songs. @Pp confesses: \"I don't know any hymns to @Gd! Please don't tell anyone.\"", + "@Dd bargains with you. \"You won't think any less of me if I just throw some weapons now and then, will you? It's just that I am considering joining the grenadiers.\"", + "@Dd seems stoic. \"My devotion to @Gd must be perfect. I must go on to a glorious end.\""; + } + IsRangedAttacker = true; + WhatThrowItemTypesToThrow = THROW_AXE; + KnownCWeaponSkills == AXES; + CWeaponSkillHits == 100; + RightSWeaponSkillHits = 50; +} + +grenadierdwarf +{ + AttributeBonus = 60; + DefaultIntelligence = 3; + DefaultWisdom = 2; + Adjective = "grenadier"; + Inventory == backpack { SecondaryMaterial = GUN_POWDER { Volume = 30000; } } + Frequency = 100; + IsAbstract = true; + CreateDivineConfigurations = true; + HPRequirementForGeneration = 100; + DayRequirementForGeneration = 10; + UndeadVersions = false; + FriendlyReplies = + { + 4, + "\"As you see, I have been promoted to the glorious ranks of the grenadiers. @Gd be praised!\"", + "\"My grenade throwing abilities were too important to be lost to the wanton cause of martyrdom!\"", + "@Dd shouts: \"Death to disbelievers!\"", + "@Dd praises @Gd with numerous hymns. @Pp is obviously a very devoted follower."; + } + IsRangedAttacker = true; + WhatThrowItemTypesToThrow = THROW_GAS_GRENADE; +} + +archangel +{ + DefaultArmStrength = 35; + DefaultLegStrength = 35; + DefaultDexterity = 35; + DefaultAgility = 35; + DefaultEndurance = 35; + DefaultPerception = 45; + DefaultIntelligence = 35; + DefaultWisdom = 45; + DefaultCharisma = 60; + DefaultMana = 45; + IsAbstract = true; + ClassStates = TELEPORT|HASTE|INFRA_VISION|ESP|TELEPORT_CONTROL|POLYMORPH_CONTROL|GAS_IMMUNITY|LIFE_SAVED; + TamingDifficulty = NO_TAMING; + IsUnique = true; + IsNameable = false; + CanBeCloned = false; + IsPolymorphable = false; + CanBeConfused = false; + FireResistance = 40; + ElectricityResistance = 40; + EnergyResistance = 40; + NameSingular = "archangel"; + BodyArmor = ANGEL_HAIR bodyarmor(PLATE_MAIL) { Enchantment = 4; } + Cloak = ANGEL_HAIR cloak { Enchantment = 4; } + Belt = ANGEL_HAIR belt { Enchantment = 4; } + RightGauntlet = ANGEL_HAIR gauntlet(GAUNTLET_OF_DEXTERITY) { Enchantment = 4; } + IsImmuneToItemTeleport = true; + ScienceTalkPossibility = 100; + ScienceTalkIntelligenceModifier = 50; + ScienceTalkWisdomModifier = 250; + ScienceTalkIntelligenceRequirement = 20; + ScienceTalkWisdomRequirement = 40; + AllowUnconsciousness = false; + CanChoke = false; + DisplacePriority = 10; + AllowPlayerToChangeEquipment = false; + + Config VALPURUS; + { + DefaultName = "Inlux"; + } + + Config LEGIFER; + { + DefaultName = "Iustitia"; + } + + Config ATAVUS; + { + DefaultName = "Beneficus"; + } + + Config DULCIS; + { + DefaultName = "Amatrix"; + } + + Config SEGES; + { + DefaultName = "Salubris"; + } + + Config SOPHOS; + { + DefaultName = "Magus"; + } + + Config SILVA; + { + DefaultName = "Nux"; + } + + Config LORICATUS; + { + DefaultName = "Ignigena"; + } + + Config MELLIS; + { + DefaultName = "Leguleius"; + IsSadist = true; + } + + Config CLEPTIA; + { + DefaultName = "Latro"; + IsSadist = true; + } + + Config NEFAS; + { + DefaultName = "Rapax"; + IsSadist = true; + IsMasochist = true; + } + + Config SCABIES; + { + DefaultName = "Pestilentia"; + IsSadist = true; + } + + Config INFUSCOR; + { + DefaultName = "Sinistra"; + IsSadist = true; + } + + Config CRUENTUS; + { + DefaultName = "Gladius"; + IsSadist = true; + } + + Config MORTIFER; + { + DefaultName = "Erado"; + IsSadist = true; + } +} + +insudo +{ + DefaultArmStrength = 35; + DefaultLegStrength = 35; + DefaultDexterity = 35; + DefaultAgility = 35; + DefaultEndurance = 35; + DefaultPerception = 45; + DefaultIntelligence = 35; + DefaultWisdom = 45; + DefaultCharisma = 60; + DefaultMana = 45; + IsAbstract = false; + ClassStates = HASTE|INFRA_VISION|ESP|TELEPORT_CONTROL|POLYMORPH_CONTROL|GAS_IMMUNITY|LIFE_SAVED; + TamingDifficulty = NO_TAMING; + IsUnique = true; + IsNameable = false; + CanBeCloned = false; + IsPolymorphable = false; + CanBeConfused = false; + FireResistance = 40; + ElectricityResistance = 40; + EnergyResistance = 40; + NameSingular = "archangel"; + BodyArmor = ANGEL_HAIR bodyarmor(PLATE_MAIL) { Enchantment = 4; } + Cloak = ANGEL_HAIR cloak { Enchantment = 4; } + Belt = ANGEL_HAIR belt { Enchantment = 4; } + RightGauntlet = ANGEL_HAIR gauntlet(GAUNTLET_OF_DEXTERITY) { Enchantment = 4; } + IsImmuneToItemTeleport = true; + ScienceTalkPossibility = 100; + ScienceTalkIntelligenceModifier = 50; + ScienceTalkWisdomModifier = 250; + ScienceTalkIntelligenceRequirement = 20; + ScienceTalkWisdomRequirement = 40; + AllowUnconsciousness = false; + CanChoke = false; + DisplacePriority = 10; + AllowPlayerToChangeEquipment = false; + DefaultName = "Insudo"; + Sex = MALE; + CreateDivineConfigurations = false; + HostileReplies == "\"With the power of Eptyron, I shall slay thee, sinner!\""; + FriendlyReplies == "\"Solicitus be with you, mortal.\""; + DeathMessage = "@Dd leaves this mortal plane behind."; + StandVerb = "flying"; + AttachedGod = NONE; + PostFix = "of Solicitus"; +} + +tailor +{ + DefaultArmStrength = 15; + DefaultLegStrength = 15; + DefaultDexterity = 35; + DefaultAgility = 15; + DefaultEndurance = 20; + DefaultPerception = 27; + DefaultIntelligence = 15; + DefaultWisdom = 15; + DefaultCharisma = 15; + DefaultMana = 10; + TotalVolume = 70000; + TotalSize = 180; + NameSingular = "tailor"; + Cloak = LEATHER cloak { Enchantment = 1; } + BodyArmor = HARDENED_LEATHER bodyarmor(PLATE_MAIL) { Enchantment = 1; } + RightWielded = MITHRIL meleeweapon(DAGGER) { Enchantment = 2; } + RightGauntlet = NYMPH_HAIR gauntlet(GAUNTLET_OF_DEXTERITY) { Enchantment = 4; } + RightBoot = OMMEL_HAIR boot { Enchantment = 1; } + KnownCWeaponSkills == BLUNT_WEAPONS; + CWeaponSkillHits == 500; + RightSWeaponSkillHits = 200; + LeftSWeaponSkillHits = 200; + TorsoBitmapPos = 48, 48; + HeadBitmapPos = 96, 0; + ArmBitmapPos = 64, 16; + LegBitmapPos = 0, 0; + ClothColor = rgb16(200, 200, 200); + LegMainColor = rgb16(111, 64, 37); + CanRead = true; + /* Replies overridden */ + IsUnique = true; + IsNameable = false; + CanBeCloned = false; + DefaultName = "Mirvo"; + TamingDifficulty = NO_TAMING; + AttachedGod = SOPHOS; +} + +mysticfrog +{ + DefaultArmStrength = 10; + DefaultAgility = 25; + DefaultEndurance = 12; + DefaultPerception = 18; + DefaultMana = 40; + BaseBiteStrength = 1000; + TotalVolume = 30000; + TorsoBitmapPos = 96, 0; + TotalSize = 50; + CWeaponSkillHits == 100; + IsPolymorphable = false; + IsAbstract = true; + ScienceTalkPossibility = 100; + CanChoke = false; + + Config DARK; + { + DefaultIntelligence = 35; + DefaultWisdom = 25; + DefaultCharisma = 6; + BloodMaterial = DARK_FROG_BLOOD; + SkinColor = rgb16(60, 60, 60); + Adjective = "mystic dark"; + CanBeGenerated = true; + ClassStates = INFRA_VISION|TELEPORT_CONTROL|INVISIBLE|ESP; + AttachedGod = INFUSCOR; + IgnoreDanger = true; + HPRequirementForGeneration = 400; + DayRequirementForGeneration = 40; + Frequency = 250; + PolymorphIntelligenceRequirement = 25; + ScienceTalkIntelligenceModifier = 100; + ScienceTalkWisdomModifier = 50; + ScienceTalkIntelligenceRequirement = 25; + ScienceTalkWisdomRequirement = 15; + FriendlyReplies == "@Dd croaks happily."; + IsCatacombCreature = true; + } + + Config LIGHT; + { + DefaultIntelligence = 25; + DefaultWisdom = 35; + DefaultCharisma = 30; + SkinColor = rgb16(32, 88, 32); + Adjective = "mystic light"; + ClassStates = TELEPORT_CONTROL|INVISIBLE|ESP; + AttachedGod = VALPURUS; + PolymorphIntelligenceRequirement = 15; + ScienceTalkIntelligenceModifier = 25; + ScienceTalkWisdomModifier = 50; + ScienceTalkIntelligenceRequirement = 20; + ScienceTalkWisdomRequirement = 30; + } +} + +lobhse +{ + DefaultArmStrength = 40; + DefaultAgility = 20; + DefaultEndurance = 40; + DefaultPerception = 21; + DefaultIntelligence = 5; + DefaultWisdom = 5; + DefaultCharisma = 6; + DefaultMana = 0; + BloodMaterial = SICK_BLOOD; + CanOpen = false; + HostileReplies == "@Dd says nothing."; + FriendlyReplies == "@Dd says nothing."; + TotalSize = 10; + SkinColor = rgb16(64, 64, 100); + BeltColor = rgb16(50, 150, 50); /* the mouth */ + TorsoMainColor = rgb16(30, 30, 30); + NameSingular = "behemoth spider"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 400; + ClassStates = INFRA_VISION; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 50; + FleshMaterial = SICK_SPIDER_FLESH; + AttachedGod = SCABIES; + BiteCapturesBodyPart = false; + AutomaticallySeen = true; + IsImmuneToStickiness = true; + DefaultName = "Lobh-Se"; + TorsoBitmapPos = 192, 64; + TotalVolume = 500000; + TamingDifficulty = NO_TAMING; + IsNameable = false; + IsUnique = true; + TotalSize = 200; + IsPolymorphable = false; + CanBeGenerated = false; + PanicLevel = 0; + CanBeCloned = false; + DeathMessage = "@Dd groans horribly and drops @pp head."; + CanBeConfused = false; + AttackStyle = USE_HEAD; + KnownCWeaponSkills == BITE; + AllowUnconsciousness = false; + DestroysWalls = true; +} + +siren +{ + DefaultArmStrength = 3; + DefaultLegStrength = 3; + DefaultDexterity = 15; + DefaultAgility = 20; + DefaultEndurance = 12; + DefaultPerception = 20; + DefaultIntelligence = 10; + DefaultWisdom = 15; + DefaultCharisma = 50; + DefaultMana = 5; + Sex = FEMALE; + SkinColor = rgb16(160, 100, 64); + HairColor = rgb16(80, 48, 32); + HeadBitmapPos = 112, 368; + TorsoBitmapPos = 48, 288; + ArmBitmapPos = 80, 240; + LegBitmapPos = 16, 240; + TotalVolume = 40000; + TotalSize = 170; + BaseUnarmedStrength = 200; + HostileReplies == "\"LAallaaalalaaa!\""; + CanRead = true; + NameSingular = "siren"; + AttachedGod = DULCIS; + FriendlyReplies == "\"I'm singing in the rain... Oh wait...\""; + + /* if somebody really wants to J_Kahvi has provided + detailed discussion of nipple and pubic hair + color in Doc/Data/Dialog.txt starting from + line 2552 to 2640 */ + + Config LIGHT_ASIAN_SIREN; + { + SkinColor = rgb16(254, 247, 208); + HairColor = rgb16(80, 48, 32); + TorsoMainColor = rgb16(70, 40, 25); + NameSingular = "test1"; + } + + Config DARK_ASIAN_SIREN; + { + SkinColor = rgb16(254, 247, 183); + HairColor = rgb16(64, 48, 32); + TorsoMainColor = rgb16(50, 40, 28); + NameSingular = "test2"; + } + + Config CAUCASIAN_SIREN; + { + SkinColor = rgb16(255, 212, 192); + HairColor = rgb16(35, 35, 35); + TorsoMainColor = rgb16(30, 30, 30); + } + + Config DARK_SIREN; + { + SkinColor = rgb16(128, 80, 48); + HairColor = rgb16(80, 48, 32); + TorsoMainColor = rgb16(60, 30, 25); + } + + Config GREEN_SIREN; + { + SkinColor = rgb16(180, 255, 150); + HairColor = rgb16(200, 48, 32); + TorsoMainColor = rgb16(180, 40, 25); + } +} + +mindworm +{ + DefaultMana = 5; + CanOpen = false; + ClassStates = ESP; + FleshMaterial = MIND_WORM_FLESH; + AttachedGod = SCABIES; + IsAbstract = true; + TorsoMainColor = rgb16(200, 30, 30); + + Config HATCHLING; + { + TotalVolume = 30; + TorsoBitmapPos = 208, 32; + TotalSize = 7; + NameSingular = "mind worm hatchling"; + } + + Config BOIL; + { + TotalVolume = 30; + TorsoBitmapPos = 208, 32; + TotalSize = 7; + NameSingular = "mind worm boil"; + } +} + +punisher +{ + DefaultArmStrength = 15; + DefaultLegStrength = 10; + DefaultDexterity = 10; + DefaultAgility = 10; + DefaultEndurance = 15; + DefaultPerception = 21; + DefaultIntelligence = 15; + DefaultWisdom = 7; + DefaultCharisma = 10; + DefaultMana = 0; + HeadBitmapPos = 96, 352; + TorsoBitmapPos = 48, 48; + ArmBitmapPos = 64, 256; + LegBitmapPos = 0, 48; + CapColor = rgb16(200, 200, 10); + TorsoMainColor = rgb16(30, 30, 30); + LegMainColor = rgb16(30,30,30); + SkinColor = rgb16(255, 222, 202); + TotalVolume = 95000; + TotalSize = 175; + NameSingular = "punisher"; + Belt = BLACK_LEATHER belt; + RightWielded = BLACK_LEATHER whip; + RightBoot = BLACK_LEATHER boot; + KnownCWeaponSkills = { 2, KICK, WHIPS; } + CWeaponSkillHits = { 2, 200, 200; } + RightSWeaponSkillHits = 200; + AttackStyle = USE_ARMS|USE_LEGS; + TamingDifficulty = NO_TAMING; + HostileReplies == "\"You are now about to enter a world of pain!\""; + AttachedGod = CRUENTUS; + FriendlyReplies == "\"My cousin a banana encourager in New Attnam\""; + IsSadist = true; +} + +mysteryman +{ + DefaultArmStrength = 15; + DefaultLegStrength = 20; + DefaultDexterity = 15; + DefaultAgility = 20; + DefaultEndurance = 10; + DefaultPerception = 25; + DefaultIntelligence = 30; + DefaultWisdom = 25; + DefaultCharisma = 10; + DefaultMana = 0; + HeadBitmapPos = 96, 64; + TorsoBitmapPos = 48, 48; + ArmBitmapPos = 64, 16; + LegBitmapPos = 0, 48; + SkinColor = rgb16(255, 222, 202); + TotalVolume = 95000; + TotalSize = 175; + CapColor = rgb16(30, 30, 30); + TorsoMainColor = rgb16(30, 30, 30); + LegMainColor = rgb16(30,30,30); + NameSingular = "mystery man"; + Helmet = BLACK_LEATHER helmet; + BodyArmor = BLACK_LEATHER bodyarmor(PLATE_MAIL); + Belt = BLACK_LEATHER belt; + RightBoot = BLACK_LEATHER boot; + RightGauntlet = BLACK_LEATHER gauntlet; + KnownCWeaponSkills = { 2, KICK, UNARMED; } + CWeaponSkillHits = { 2, 1000, 1000; } + RightSWeaponSkillHits = 1000; + LeftSWeaponSkillHits = 1000; + AttackStyle = USE_ARMS|USE_LEGS; + TamingDifficulty = NO_TAMING; + HostileReplies == "\"This is why the government doesn't mess with me, fool!\""; + AttachedGod = CRUENTUS; + IsSadist = true; +} + +reaper +{ + DefaultArmStrength = 20; + DefaultLegStrength = 20; /* not used */ + DefaultDexterity = 25; + DefaultAgility = 50; + DefaultEndurance = 15; + DefaultPerception = 18; + DefaultIntelligence = 20; + DefaultWisdom = 25; + DefaultCharisma = 10; + DefaultMana = 50; + StandVerb = "floating"; + TotalVolume = 200000; + TotalSize = 250; + CanRead = true; + NameSingular = "reaper"; + ClothColor = rgb16(100, 100, 160); + HeadBitmapPos = 96, 95; + TorsoBitmapPos = 32, 272; + ArmBitmapPos = 64, 272; + /* LegBitmapPos is not used */ + KnownCWeaponSkills == POLE_ARMS; + CWeaponSkillHits == 500; + RightSWeaponSkillHits = 200; + PanicLevel = 0; + BaseEmitation = rgb24(110, 110, 130); + HostileReplies == "\"I'm an invitation Mortifer personally sent!\""; + FriendlyReplies == "\"You know, I've been on both sides. Mortifer is a party god but Valpurus is too boring to hang around. \""; + FleshMaterial = BONE; + DeathMessage = "@Dd vanishes from existence."; + SpillsBlood = false; + Sweats = false; + AttachedGod = MORTIFER; + BodyPartsDisappearWhenSevered = true; + ClassStates = GAS_IMMUNITY|LEVITATION; + WieldedPosition = -1, -3; + CanChoke = false; + IsImmuneToStickiness = true; + UndeadVersions = false; + RightWielded = meleeweapon(SCYTHE); + IsCatacombCreature = true; + Frequency = 1000; + SkinColor = rgb16(160, 160, 160); + EyeColor = rgb16(0, 0, 0); + TorsoMainColor = rgb16(0, 0, 0); + LegMainColor = rgb16(160,160,160); + ArmMainColor = rgb16(0, 0, 0); + CanBeGenerated = true; +} + +bluedragon +{ + DefaultArmStrength = 30; + DefaultAgility = 40; + DefaultEndurance = 50; + DefaultPerception = 42; + DefaultIntelligence = 30; + DefaultWisdom = 30; + DefaultCharisma = 20; + DefaultMana = 0; + TotalVolume = 50000; + TorsoBitmapPos = 354, 0; + HostileReplies == "@Dd glares at you."; + FriendlyReplies == "@Dd happily wiggles its tail."; + TotalSize = 40; + SkinColor = rgb16(0, 20, 180); + EyeColor = rgb16(255, 255, 255); + NameSingular = "blue dragon"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 600; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 250; + FleshMaterial = JACKAL_FLESH; + Frequency = 1000; + CanRead = true; + ElectricityResistance = 100; +} + +reddragon +{ + DefaultArmStrength = 30; + DefaultAgility = 40; + DefaultEndurance = 50; + DefaultPerception = 42; + DefaultIntelligence = 30; + DefaultWisdom = 30; + DefaultCharisma = 20; + DefaultMana = 0; + TotalVolume = 50000; + TorsoBitmapPos = 354, 0; + HostileReplies == "@Dd glares at you."; + FriendlyReplies == "@Dd happily wiggles its tail."; + TotalSize = 40; + SkinColor = rgb16(180, 20, 0); + EyeColor = rgb16(255, 255, 255); + NameSingular = "red dragon"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 600; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 250; + FleshMaterial = JACKAL_FLESH; + Frequency = 1000; + CanRead = true; + FireResistance = 100; +} + +forestman +{ + CanRead = true; + IsAbstract = true; + AttachedGod = SILVA; + SkinColor = rgb16(200, 110, 70); + DefaultArmStrength = 12; + DefaultLegStrength = 12; + DefaultDexterity = 10; + DefaultAgility = 10; + DefaultEndurance = 12; + DefaultPerception = 20; + DefaultIntelligence = 12; + DefaultWisdom = 12; + DefaultCharisma = 10; + DefaultMana = 10; + TotalVolume = 60000; + TotalSize = 160; + NameSingular = "forest-man"; + HeadBitmapPos = 96, 0; + TorsoBitmapPos = 32, 80; + ArmBitmapPos = 64, 80; + LegBitmapPos = 0, 64; + TamingDifficulty = NO_TAMING; + UndeadVersions = false; + + Config ROVER; + { + AttributeBonus = 0; + Helmet = TIN helmet; + BodyArmor = HARDENED_LEATHER bodyarmor(PLATE_MAIL); + RightWielded = BRONZE meleeweapon(DAGGER) { Enchantment = 2; } + LeftWielded = BIRCH_WOOD shield; + Belt = HARDENED_LEATHER belt; + RightBoot = HARDENED_LEATHER boot; + RightGauntlet = HARDENED_LEATHER gauntlet; + Cloak = HARDENED_LEATHER cloak { Enchantment = 1; } + Adjective = "roving"; + KnownCWeaponSkills = { 2, SMALL_SWORDS, SHIELDS; } + CWeaponSkillHits = { 2, 20, 20; } + PanicLevel = 50; + AutomaticallySeen = true; + ClothColor = rgb16(88, 50, 30); + HairColor = rgb16(100, 100, 100); + DeathMessage = "@Dd dies yelling \"FREEDOM!\""; + HostileReplies == "\"Get lost!\""; + FriendlyReplies = + { + 4, + "\"Hullo there friend!\""; + "@Dd salutes you solemnly \"Solidarity Brother!\""; + "@Dd grasps your shoulder \"Have you seen Molly? Is he well?\""; + "@Dd chortles \"You know you're on the steppe when your dog runs away and you can still see him running for at least three days.\""; + } + } + + Config BAND_LEADER; + { + AttributeBonus = 10; + Helmet = SILVER helmet; + BodyArmor = NYMPH_HAIR bodyarmor(PLATE_MAIL) { Enchantment = 2; } + RightWielded = SILVER meleeweapon(SPEAR) { SecondaryMaterial = OAK_WOOD; Enchantment = 2; } + Belt = NYMPH_HAIR belt { Enchantment = 2; } + RightBoot = HARDENED_LEATHER boot; + RightGauntlet = HARDENED_LEATHER gauntlet; + Cloak = FABRIC cloak { Enchantment = 8; } + Adjective = "roving"; + PostFix = "band-leader"; + KnownCWeaponSkills = { 2, POLE_ARMS, SHIELDS; } + CWeaponSkillHits = { 2, 40, 40; } + PanicLevel = 20; + AutomaticallySeen = true; + ClothColor = rgb16(70, 88, 42); + HairColor = rgb16(220, 220, 220); + DeathMessage = "@Dd dies yelling \"FREEDOM FOR TWERAIF!\""; + HostileReplies == "\"Piss off!\""; + FriendlyReplies = + { + 4, + "\"Hullo there traveller. We're UTFA.\""; + "@Dd steps forward \"I am the leader of our roving band of Forest Men (UTFA Soldiery).\""; + "@Dd embraces you \"Have you come to redeem us?\""; + "@Dd explains gravely \"Deep inside the forest is a prison where our kindred are being held. Visit Regii and the officials in the underground and help us free them.\""; + } + } +} + +okapi +{ + DefaultArmStrength = 25; + DefaultAgility = 15; + DefaultEndurance = 20; + DefaultPerception = 18; + DefaultIntelligence = 5; + DefaultWisdom = 8; + DefaultCharisma = 10; + DefaultMana = 0; + TotalVolume = 40000; + TorsoBitmapPos = 304, 112; + HostileReplies == "@Dd whinneys vengefully."; + FriendlyReplies == "@Dd whinneys cheerfully."; + TotalSize = 150; + SkinColor = rgb16(108, 30, 18); + TorsoMainColor = rgb16(238, 234, 210); + TorsoSpecialColor = rgb16(108, 30, 18); + NameSingular = "okapi"; + NamePlural = "okapies"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 180; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 20; + FleshMaterial = OKAPI_FLESH; + DeathMessage = "@Dd whinneys one last time and dies."; + AttachedGod = SCABIES; + AutomaticallySeen = true; + IsSadist = true; + IsMasochist = true; + CanZap = true; + UsesLongArticle = true; +} + +vampire +{ + DefaultArmStrength = 20; + DefaultLegStrength = 20; + DefaultDexterity = 35; + DefaultAgility = 35; + DefaultEndurance = 20; + DefaultPerception = 24; + DefaultIntelligence = 10; + DefaultWisdom = 10; + DefaultCharisma = 10; + DefaultMana = 0; + TotalVolume = 60000; + EyeColor = rgb16(180, 0, 0); + HairColor = rgb16(5, 5, 5); + ClothColor = rgb16(75, 95, 75); + SkinColor = rgb16(208, 164, 0); + BeltColor = rgb16(15, 15, 15); + BootColor = rgb16(5, 5, 5); + TorsoSpecialColor = rgb16(187, 7, 7); + ArmSpecialColor = rgb16(187, 7, 7); + HeadBitmapPos = 96, 720; + TorsoBitmapPos = 32, 720; + ArmBitmapPos = 64, 720; + LegBitmapPos = 0, 720; + HostileReplies == "@Dd roars angrily."; + FriendlyReplies == "@Dd capers cheerfully."; + TotalSize = 160; + NameSingular = "vampire"; + AttackStyle = USE_HEAD; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 80; + Sex = MALE; + BaseBiteStrength = 800; + CanBeWished = false; + ClassStates = HASTE|INFRA_VISION|VAMPIRISM; + PanicLevel = 0; + FleshMaterial = VAMPIRE_FLESH; + AttachedGod = MORTIFER; + WillCarryItems = true; + UndeadVersions = false; + CanUseEquipment = false; +} + +mouse +{ + DefaultArmStrength = 2; + DefaultAgility = 20; + DefaultEndurance = 10; + DefaultPerception = 10; + DefaultIntelligence = 12; + DefaultWisdom = 5; + DefaultCharisma = 4; + DefaultMana = 0; + CanOpen = false; + TotalVolume = 200; + TorsoBitmapPos = 272, 96; + HostileReplies == "The @du squeaks haughtily."; + FriendlyReplies == "@Dd squeaks contentedly."; + TotalSize = 30; + NameSingular = "mouse"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 350; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 20; + ClassStates = INFRA_VISION; + FleshMaterial = MOUSE_FLESH; + AttachedGod = SILVA; + MoveType = WALK|SWIM; + IsCatacombCreature = true; + IsAbstract = true; + + Config FIELD_MOUSE; + { + Adjective = "field"; + SkinColor = rgb16(180, 100, 40); + TotalVolume = 250; + TotalSize = 35; + AttributeBonus = 0; + } + + Config LABORATORY_MOUSE; + { + Adjective = "laboratory"; + SkinColor = rgb16(250, 250, 250); + TotalVolume = 300; + TotalSize = 30; + AttributeBonus = 15; + DefaultIntelligence = 50; + DefaultWisdom = 50; + CanBeGenerated = false; + FriendlyReplies == "The @du squeaks: \"Do you know, we lab-mice are really a race of hyper-intelligent pan-dimensional beings?\""; + ScienceTalkPossibility = 33; + ScienceTalkIntelligenceModifier = 10; + ScienceTalkWisdomModifier = 5; + ScienceTalkIntelligenceRequirement = 45; + ScienceTalkWisdomRequirement = 45; + ScienceTalkName = + { + 10, + "Life", "the Universe", "Everything", "5D chat shows", "computer architectonics", + "cheese", "experiments", "Ultimate Questions", "pan-dimensionality", "On-Turning"; + } + } +} + +pig +{ + DefaultArmStrength = 25; + DefaultAgility = 8; + DefaultEndurance = 15; + DefaultPerception = 15; + DefaultIntelligence = 15; + DefaultWisdom = 5; + DefaultCharisma = 12; + DefaultMana = 0; + TotalVolume = 40000; + TorsoBitmapPos = 304, 96; + HostileReplies == "@Dd grunts angrily."; + FriendlyReplies == "@Dd grunts cheerfully."; + TotalSize = 150; + NameSingular = "pig"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 400; + CanBeGenerated = false; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 20; + FleshMaterial = PORK; + DeathMessage = "@Dd dies squealing forlornly."; + AttachedGod = SILVA; + AutomaticallySeen = true; + IsAbstract = true; + + Config THIN_PIG; + { + Adjective = "thin"; + SkinColor = rgb16(255, 196, 255); + TotalVolume = 30000; + TotalSize = 100; + AttributeBonus = -15; + } +} + +ox +{ + DefaultArmStrength = 40; + DefaultAgility = 25; + DefaultEndurance = 20; + DefaultPerception = 24; + DefaultIntelligence = 5; + DefaultWisdom = 5; + DefaultCharisma = 5; + DefaultMana = 0; + TotalVolume = 200000; + TorsoBitmapPos = 288, 96; + HostileReplies == "@Dd bawls furiously."; + FriendlyReplies == "@Dd moos happily."; + TotalSize = 240; + NameSingular = "ox"; + NamePlural = "oxen"; + AttackStyle = USE_LEGS|USE_HEAD; + CanBeGenerated = false; + FleshMaterial = BEEF; + DeathMessage = "@Dd moos mournfully and dies."; + AttachedGod = SILVA; + WillCarryItems = true; + CanKick = true; + KnownCWeaponSkills = { 2, KICK, BITE; } + CWeaponSkillHits = { 2, 50, 50; } + PanicLevel = 10; + BaseKickStrength = 500; + BaseBiteStrength = 250; + IsAbstract = true; + + Config STARVED_OX; + { + Adjective = "starved"; + SkinColor = rgb16(155, 70, 42); + TorsoMainColor = rgb16(215, 215, 215); /* the horns */ + TotalVolume = 100000; + TotalSize = 150; + AttributeBonus = -20; + } +} + +firefox +{ + DefaultArmStrength = 8; + DefaultAgility = 20; + DefaultEndurance = 8; + DefaultPerception = 20; + DefaultIntelligence = 6; + DefaultWisdom = 5; + DefaultCharisma = 12; + DefaultMana = 0; + TotalVolume = 25000; + TorsoBitmapPos = 256, 96; + HostileReplies == "@Dd yaps in fury."; + FriendlyReplies == "@Dd yaps gleefully."; + TotalSize = 80; + SkinColor = rgb16(180, 100, 40); + TorsoMainColor = rgb16(239, 239, 220); /* the tail */ + Adjective = "fire"; + NameSingular = "fox"; + NamePlural = "foxes"; + Alias == "foxy"; + BaseBiteStrength = 425; + CanBeGenerated = true; + CWeaponSkillHits == 100; + ClassStates = INFRA_VISION; + FleshMaterial = FOX_FLESH; + AttachedGod = SILVA; + DeathMessage = "@Dd is killed."; + BaseEmitation = rgb24(150, 120, 90); + Frequency = 500; +} + +thunderbird +{ + DefaultArmStrength = 2; + DefaultAgility = 40; + DefaultEndurance = 8; + DefaultPerception = 20; + DefaultIntelligence = 6; + DefaultWisdom = 5; + DefaultCharisma = 6; + DefaultMana = 0; + StandVerb = "flying"; + CanOpen = false; + TotalVolume = 500; + TorsoBitmapPos = 560, 0; + HostileReplies == "@Dd peeps thunderously."; + FriendlyReplies == "@Dd chirps happily."; + TotalSize = 20; + SkinColor = rgb16(142, 100, 50); + TorsoMainColor = rgb16(220, 180, 0); + Adjective = "thunder"; + NameSingular = "bird"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 400; + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 50; + PanicLevel = 75; + FleshMaterial = THUNDER_BIRD_FLESH; + AttachedGod = SILVA; + BiteCapturesBodyPart = false; + MoveType = FLY; + BaseEmitation = rgb24(130, 130, 130); + DangerModifier = 500; + Frequency = 500; + HPRequirementForGeneration = 70; + DayRequirementForGeneration = 3; +} + +noxiousorchid +{ + DefaultArmStrength = 4; + DefaultAgility = 2; + DefaultEndurance = 7; + DefaultPerception = 6; + DefaultIntelligence = 3; + DefaultWisdom = 2; + DefaultCharisma = 3; + DefaultMana = 0; + CanOpen = false; + TotalVolume = 20000; + TorsoBitmapPos = 256, 112; + HostileReplies == "@Dd gurgles balefully."; + FriendlyReplies == "@Dd gurgles pleasantly."; + TotalSize = 100; + Adjective = "noxious"; + NameSingular = "orchid"; + AttackStyle = USE_HEAD; + BaseBiteStrength = 300; + SkinColor = rgb16(0, 160, 0); /*the stalk */ + TorsoMainColor = rgb16(218, 255, 0); /* the fluid */ + /* TorsoSpecialColor (the flower) is random */ + CanBeGenerated = true; + KnownCWeaponSkills == BITE; + CWeaponSkillHits == 20; + PanicLevel = 0; + ClassStates = INFRA_VISION; + HasALeg = false; + FleshMaterial = MUTANT_PLANT_FIBER; + DeathMessage = "@Dd is destroyed."; + SpillsBlood = false; + Sweats = false; + StandVerb = "rooted"; + AttachedGod = SILVA; + IsPlant = true; + IsRooted = true; + AllowUnconsciousness = false; + CanChoke = false; + HasEyes = false; + HasHead = false; + UsesNutrition = false; + + Config GREATER; + { + AttributeBonus = 175; + TorsoBitmapPos = 272, 112; + Adjective = "greater noxious"; + BaseBiteStrength = 500; + CWeaponSkillHits == 50; + TotalVolume = 30000; + TotalSize = 175; + } + + Config GIANT; + { + AttributeBonus = 250; + TorsoBitmapPos = 288, 112; + Adjective = "giant noxious"; + BaseBiteStrength = 700; + CWeaponSkillHits == 100; + TotalVolume = 40000; + TotalSize = 250; + IsEnormous = true; + } +} + +shaman +{ + DefaultArmStrength = 10; + DefaultLegStrength = 12; + DefaultDexterity = 10; + DefaultAgility = 20; + DefaultEndurance = 15; + DefaultPerception = 18; + DefaultIntelligence = 15; + DefaultWisdom = 10; + DefaultCharisma = 4; + DefaultMana = 20; + SkinColor = rgb16(48, 48, 48); + EyeColor = rgb16(200, 200, 0); + ClothColor = rgb16(180, 120, 80); + BeltColor = rgb16(72, 60, 24); + LegMainColor = rgb16(96, 80, 48); + HeadBitmapPos = 112, 192; + TorsoBitmapPos = 48, 0; + ArmBitmapPos = 80, 32; + LegBitmapPos = 0, 240; + TotalVolume = 70000; + TotalSize = 180; + Adjective = "orc"; + NameSingular = "shaman"; + CanBeGenerated = true; + Sex = UNDEFINED; + CanRead = true; + CanBeGenerated = true; + HostileReplies == "@Dd growls: \"Me make you feel pain!\""; + FriendlyReplies == "\"What do you want me to kill?\""; + IgnoreDanger = true; + AttachedGod = INFUSCOR; + /*IsAbstract = true;*/ + KnownCWeaponSkills == POLE_ARMS; + Cloak = LEATHER cloak { Enchantment = -1; } + BodyArmor = TROLL_HIDE bodyarmor(PLATE_MAIL) { Enchantment = -2; } + RightWielded = BRONZE BRONZE meleeweapon(QUARTER_STAFF); + CWeaponSkillHits == 50; + RightSWeaponSkillHits = 20; + LeftSWeaponSkillHits = 20; + ClassStates = GAS_IMMUNITY|INFRA_VISION; + PanicLevel = 50; + FleshMaterial = ORC_FLESH; + WieldedPosition = -1, -1; + DangerModifier = 200; + UsesLongAdjectiveArticle = true; + HPRequirementForGeneration = 50; + DayRequirementForGeneration = 3; + Frequency = 1500; + PolymorphIntelligenceRequirement = 5; + ScienceTalkPossibility = 10; + ScienceTalkIntelligenceModifier = 1; + ScienceTalkWisdomModifier = 1; + ScienceTalkIntelligenceRequirement = 10; + ScienceTalkWisdomRequirement = 5; + IsRangedAttacker = true; + WhatCategoryToThrow = POTION; + WhatWeaponConfigToThrow = DAGGER; + WhatThrowItemTypesToThrow = THROW_POTION; + IsAlcoholic = true; + ScienceTalkName = + { + 10, + "killing", "murder", "rape", "torture", "burns", + "cannibalism", "slaughtering", "malice", "axes", "blood"; + } + Inventory = {4, GLASS POISON_LIQUID potion, GLASS VINEGAR potion, GLASS LIQUID_HORROR potion, horn(CONFUSION); } +} + +warlock +{ + DefaultArmStrength = 10; + DefaultLegStrength = 10; + DefaultDexterity = 10; + DefaultAgility = 15; + DefaultEndurance = 12; + DefaultPerception = 20; + DefaultIntelligence = 15; + DefaultWisdom = 10; + DefaultCharisma = 5; + DefaultMana = 25; + SkinColor = rgb16(0, 96, 0); + EyeColor = rgb16(180, 100, 0); + ClothColor = rgb16(48, 32, 16); + HeadBitmapPos = 96, 528; + TorsoBitmapPos = 32, 112; + ArmBitmapPos = 64, 144; + LegBitmapPos = 16, 64; + TotalVolume = 25000; + TotalSize = 100; + NameSingular = "warlock"; + CanBeGenerated = true; + Sex = UNDEFINED; + Adjective = "goblin"; + CanRead = true; + ClassStates = INFRA_VISION; + IgnoreDanger = true; + AttachedGod = INFUSCOR; + /*IsAbstract = true;*/ + KnownCWeaponSkills == POLE_ARMS; + Cloak = LEATHER cloak { Enchantment = 2; } + Helmet = LEATHER helmet; + BodyArmor = HARDENED_LEATHER bodyarmor(PLATE_MAIL) { Enchantment = 1; } + RightWielded = BONE BONE meleeweapon(QUARTER_STAFF) { Enchantment = 1; } + CWeaponSkillHits == 20; + RightSWeaponSkillHits = 10; + LeftSWeaponSkillHits = 10; + DangerModifier = 150; + HPRequirementForGeneration = 50; + DayRequirementForGeneration = 3; + Frequency = 1500; + PolymorphIntelligenceRequirement = -5; + PanicLevel = 66; + HostileReplies == "@Dd yells goblin spells at you."; + FriendlyReplies == "@Dd laughs: \"Humie friend. Many mommo we teleport. Many spider we polymorph.\""; + FleshMaterial = GOBLINOID_FLESH; + WieldedPosition = 0, -2; + Inventory = { 3, wand(WAND_OF_TELEPORTATION), wand(WAND_OF_DOOR_CREATION), wand(WAND_OF_ACID_RAIN); } + IsAlcoholic = true; +} + +alchemist +{ + DefaultArmStrength = 10; + DefaultLegStrength = 10; + DefaultDexterity = 12; + DefaultAgility = 18; + DefaultEndurance = 10; + DefaultPerception = 18; + DefaultIntelligence = 15; + DefaultWisdom = 10; + DefaultCharisma = 4; + DefaultMana = 20; + SkinColor = rgb16(60, 120, 120); + EyeColor = rgb16(180, 180, 0); + HairColor = rgb16(35, 35, 35); + ClothColor = rgb16(48, 48, 48); + LegMainColor = rgb16(111, 74, 37); + HeadBitmapPos = 112, 208; + TorsoBitmapPos = 48, 176; + ArmBitmapPos = 64, 176; + LegBitmapPos = 16, 112; + TotalVolume = 30000; + TotalSize = 90; + Adjective = "kobold"; + NameSingular = "alchemist"; + CanBeGenerated = true; + Sex = UNDEFINED; + RightWielded = BALSA_WOOD BALSA_WOOD meleeweapon(QUARTER_STAFF) { Enchantment = 1; } + KnownCWeaponSkills == POLE_ARMS; + CWeaponSkillHits == 20; + RightSWeaponSkillHits = 10; + PanicLevel = 50; + FleshMaterial = KOBOLD_FLESH; + DeathMessage = "@Dd dies yelling like a tortured hyena."; + WieldedPosition = 0, -1; + IsExtraFragile = true; + CanRead = true; + /*ClassStates = INFRA_VISION;*/ + AttachedGod = INFUSCOR; + Cloak = LEATHER cloak; + Helmet = LEATHER helmet; + BodyArmor = LEATHER bodyarmor(PLATE_MAIL) { Enchantment = -1; } + DangerModifier = 100; + HPRequirementForGeneration = 50; + DayRequirementForGeneration = 2; + Frequency = 1000; + PolymorphIntelligenceRequirement = -10; + IsAlcoholic = true; +} + +doctor +{ + DefaultArmStrength = 7; + DefaultLegStrength = 10; + DefaultDexterity = 7; + DefaultAgility = 15; + DefaultEndurance = 7; + DefaultPerception = 21; + DefaultIntelligence = 25; + DefaultWisdom = 40; + DefaultCharisma = 20; + DefaultMana = 20; + TotalVolume = 70000; + TotalSize = 160; + NameSingular = "bare-hands doctor"; + SkinColor = rgb16(128, 80, 48); + HairColor = rgb16(32, 32, 32); + ClothColor = rgb16(56, 48, 20); + HeadBitmapPos = 96, 224; + TorsoBitmapPos = 32, 0; + ArmBitmapPos = 64, 0; + LegBitmapPos = 0, 288; + /*IsUnique = true;*/ + TamingDifficulty = 15; + AttachedGod = SEGES; + CanRead = true; + HostileReplies == "\"Bacillus! I surgically detach your every limb!\""; + DeathMessage = "@Dd is dead before he hits the ground."; + FriendlyReplies = + { + 4, + "\"I can re-attach limb, chop limb, cure disease or perform acupuncture, yes yes. But first we make payment to Party. \"", + "\"Who need god when scalpel does same job? \"", + "\"I once was employed by great-frog priest to make eunuch for Attnam cathedral. You want be eunuch?\"", + "\"First, I was butcher, then Party offer me doctor job, yes yes. \""; + } + AutomaticallySeen = true; + ScienceTalkPossibility = 75; + ScienceTalkIntelligenceModifier = 50; + ScienceTalkWisdomModifier = 100; + ScienceTalkIntelligenceRequirement = 25; + ScienceTalkWisdomRequirement = 30; + IsExtraFragile = true; + IsSadist = true; + IsMasochist = true; + CanBeCloned = false; + Inventory == potion { Times = 2; SecondaryMaterial = WATER; } + CanBeGenerated = true; + PanicLevel = 0; + Cloak = LEATHER cloak; + Frequency = 2000; +} + +regii +{ + DefaultArmStrength = 20; + DefaultLegStrength = 25; + DefaultDexterity = 15; + DefaultAgility = 20; + DefaultEndurance = 15; + DefaultPerception = 24; + DefaultIntelligence = 35; + DefaultWisdom = 10; + DefaultCharisma = 25; + DefaultMana = 10; + TotalVolume = 160000; + TotalSize = 160; + NameSingular = "UTFA resistance leader"; + Cloak = NYMPH_HAIR cloak { Enchantment = 1; } + BodyArmor = IRON bodyarmor(CHAIN_MAIL) { Enchantment = 1; } + Belt = BLACK_LEATHER belt(BELT_OF_CARRYING) { Enchantment = 2; } + RightWielded = STEEL meleeweapon(HALBERD) { Enchantment = 3; } + LeftRing = ring(RING_OF_INFRA_VISION); + RightGauntlet = NYMPH_HAIR gauntlet { Enchantment = 1; } + RightBoot = HARDENED_LEATHER boot(BOOT_OF_KICKING) { Enchantment = 1; } + KnownCWeaponSkills = { 2, POLE_ARMS, KICK; } + CWeaponSkillHits = { 2, 500, 500; } + RightSWeaponSkillHits = 500; + LeftSWeaponSkillHits = 500; + PanicLevel = 10; + TorsoBitmapPos = 48, 80; + HeadBitmapPos = 96, 224; + ArmBitmapPos = 64, 0; + LegBitmapPos = 0, 64; + ClothColor = rgb16(50, 50, 50); + HairColor = rgb16(244, 84, 4); + UsesLongArticle = true; + Inventory = { 6, stone, stone, stone, stone, stone, holybook(MELLIS); } + CanRead = true; + HostileReplies == "\"Bugger off!\""; + IsUnique = true; + IsNameable = false; + CanBeCloned = false; + DefaultName = "Regii"; + AttackStyle = USE_ARMS|USE_LEGS; + CriticalModifier = 4; + TamingDifficulty = NO_TAMING; + AttachedGod = MELLIS; + CanBeConfused = false; + IsPolymorphable = false; + FriendlyReplies = + { + 1, + "\"How goes your preparation for the mission brother?\""; + } + AutomaticallySeen = true; + ScienceTalkPossibility = 0; + UndeadVersions = false; +} + +UTFAOfficial +{ + DefaultArmStrength = 12; + DefaultLegStrength = 18; + DefaultDexterity = 14; + DefaultAgility = 14; + DefaultEndurance = 12; + DefaultPerception = 24; + DefaultIntelligence = 20; + DefaultWisdom = 10; + DefaultCharisma = 12; + DefaultMana = 10; + TotalVolume = 160000; + TotalSize = 160; + NameSingular = "UTFA (Official)"; + Cloak = LEATHER cloak { Enchantment = 1; } + BodyArmor = HARDENED_LEATHER bodyarmor(PLATE_MAIL) { Enchantment = 2; } + Belt = BLACK_LEATHER belt; + RightWielded = IRON meleeweapon(WAR_HAMMER) { Enchantment = 2; } + RightGauntlet = LEATHER gauntlet { Enchantment = 1; } + RightBoot = HARDENED_LEATHER boot; + KnownCWeaponSkills = { 2, BLUNT_WEAPONS, KICK; } + CWeaponSkillHits = { 2, 500, 200; } + RightSWeaponSkillHits = 500; + LeftSWeaponSkillHits = 500; + PanicLevel = 10; + TorsoBitmapPos = 48, 48; + HeadBitmapPos = 96, 160; + ArmBitmapPos = 64, 16; + LegBitmapPos = 0, 64; + ClothColor = rgb16(100, 80, 54); + /*HairColor = rgb16(244, 84, 4);*/ + UsesLongArticle = false; + Inventory = { 6, stone, stone, stone, stone, stone, holybook(MELLIS); } + CanRead = true; + HostileReplies == "\"Naff off!\""; + IsUnique = false; + IsNameable = false; + CanBeCloned = false; + /*DefaultName = "";*/ /*Meriwether, Paul, Tiffany, Randolph, Junket*/ + AttackStyle = USE_ARMS|USE_LEGS; + CriticalModifier = 4; + TamingDifficulty = NO_TAMING; + AttachedGod = SILVA; + CanBeConfused = false; + IsPolymorphable = false; + FriendlyReplies == "\"Welcome brother! And solidarity to you!\""; + AutomaticallySeen = true; + ScienceTalkPossibility = 0; + UndeadVersions = false; + CanBeGenerated = false; +} diff --git a/Script/define.dat b/Script/define.dat new file mode 100644 index 0000000..f260216 --- /dev/null +++ b/Script/define.dat @@ -0,0 +1,1069 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* + * NOTICE!!! + * + * This file contains SPOILERS, which might ruin your IVAN experience + * totally. Also, editing anything can DESTROY GAME BALANCE or CAUSE + * OBSCURE BUGS if you don't know what you're doing. So from here on, + * proceed at your own risk! + */ + +/* Numerical defines for other script files */ + + + +#define NONE 0 +#define MIRROR 1 +#define FLIP 2 +#define ROTATE 4 + +#define RED 63488 +#define GREEN 2016 +#define BLUE 31 + +#define YELLOW 65504 +#define PINK 61470 + +#define WHITE 65535 +#define LIGHT_GRAY 46518 +#define DARK_GRAY 21130 +#define BLACK 0 + +#define TRANSPARENT_COLOR 63519 + +#define POLYMORPHED (1 << 0) +#define HASTE (1 << 1) +#define SLOW (1 << 2) +#define POLYMORPH_CONTROL (1 << 3) +#define LIFE_SAVED (1 << 4) +#define LYCANTHROPY (1 << 5) +#define INVISIBLE (1 << 6) +#define INFRA_VISION (1 << 7) +#define ESP (1 << 8) +#define POISONED (1 << 9) +#define TELEPORT (1 << 10) +#define POLYMORPH (1 << 11) +#define TELEPORT_CONTROL (1 << 12) +#define PANIC (1 << 13) +#define CONFUSED (1 << 14) +#define PARASITIZED (1 << 15) +#define SEARCHING (1 << 16) +#define GAS_IMMUNITY (1 << 17) +#define LEVITATION (1 << 18) +#define LEPROSY (1 << 19) +#define HICCUPS (1 << 20) +#define VAMPIRISM (1 << 21) +#define DETECTING (1 << 22) + +#define THROW_ITEM_TYPES 5 + /*ThrowFlags */ +#define THROW_BONE (1 << 0) +#define THROW_POTION (1 << 1) +#define THROW_AXE (1 << 2) +#define THROW_GAS_GRENADE (1 << 3) +#define THROW_WAND (1 << 4) + +#define HEAD 1 +#define TORSO 2 +#define RIGHT_ARM 4 +#define LEFT_ARM 8 +#define ARMS 12 +#define GROIN 16 +#define RIGHT_LEG 32 +#define LEFT_LEG 64 +#define LEGS 96 +#define OTHER 128 +#define ALL 255 + +#define PHYSICAL_DAMAGE 1 +#define SOUND 2 +#define ACID 4 +#define FIRE 8 +#define ELECTRICITY 16 +#define ENERGY 32 +#define POISON 64 +#define DRAIN 128 +#define MUSTARD_GAS_DAMAGE 256 +#define THROW 32768 + +#define UNDEFINED 0 +#define MALE 1 +#define FEMALE 2 +#define TRANSSEXUAL 3 + +#define TORSO_INDEX 0 +#define HEAD_INDEX 1 +#define RIGHT_ARM_INDEX 2 +#define LEFT_ARM_INDEX 3 +#define GROIN_INDEX 4 +#define RIGHT_LEG_INDEX 5 +#define LEFT_LEG_INDEX 6 + +#define ALPP 0 +#define ALP 1 +#define AL 2 +#define ALM 3 +#define ANP 4 +#define AN 5 +#define ANM 6 +#define ACP 7 +#define AC 8 +#define ACM 9 +#define ACMM 10 + +#define UNARTICLED 0 +#define PLURAL 1 +#define ARTICLE_BIT 2 +#define DEFINITE 2 +#define INDEFINE_BIT 4 +#define INDEFINITE 6 + +#define ANY_CATEGORY 2147483647 +#define HELMET (1 << 0) +#define AMULET (1 << 1) +#define CLOAK (1 << 2) +#define BODY_ARMOR (1 << 3) +#define WEAPON (1 << 4) +#define SHIELD (1 << 5) +#define RING (1 << 6) +#define GAUNTLET (1 << 7) +#define BELT (1 << 8) +#define BOOT (1 << 9) +#define FOOD (1 << 10) +#define POTION (1 << 11) +#define SCROLL (1 << 12) +#define BOOK (1 << 13) +#define WAND (1 << 14) +#define TOOL (1 << 15) +#define VALUABLE (1 << 16) +#define MISC (1 << 17) + +#define GOOD 1 +#define NEUTRAL 2 +#define EVIL 3 +#define TOPPLED 4 + +#define CT_FRUIT 1 +#define CT_MEAT 2 +#define CT_METAL 4 +#define CT_MINERAL 8 +#define CT_LIQUID 16 +#define CT_BONE 32 +#define CT_PROCESSED 64 +#define CT_MISC_ORGANIC 128 +#define CT_PLASTIC 256 +#define CT_GAS 512 + +#define LEFT 0 +#define DOWN 1 +#define UP 2 +#define RIGHT 3 +#define CENTER 4 + +#define HOSTILE 1 +#define UNCARING 2 +#define FRIEND 4 + +#define MARTIAL_SKILL_CATEGORIES 3 +#define WEAPON_SKILL_CATEGORIES 11 + +#define UNARMED 0 +#define KICK 1 +#define BITE 2 +#define UNCATEGORIZED 3 +#define SMALL_SWORDS 4 +#define LARGE_SWORDS 5 +#define BLUNT_WEAPONS 6 +#define AXES 7 +#define POLE_ARMS 8 +#define WHIPS 9 +#define SHIELDS 10 + +#define LOCKED 1 + +#define EFFECT_NOTHING 0 +#define EFFECT_POISON 1 +#define EFFECT_DARKNESS 2 +#define EFFECT_OMMEL_URINE 3 +#define EFFECT_PEPSI 4 +#define EFFECT_KOBOLD_FLESH 5 +#define EFFECT_HEAL 6 +#define EFFECT_LYCANTHROPY 7 +#define EFFECT_SCHOOL_FOOD 8 +#define EFFECT_ANTIDOTE 9 +#define EFFECT_CONFUSE 10 +#define EFFECT_POLYMORPH 11 +#define EFFECT_ESP 12 +#define EFFECT_SKUNK_SMELL 13 +#define EFFECT_MAGIC_MUSHROOM 14 +#define EFFECT_TRAIN_PERCEPTION 15 +#define EFFECT_HOLY_BANANA 16 +#define EFFECT_EVIL_WONDER_STAFF_VAPOUR 17 +#define EFFECT_GOOD_WONDER_STAFF_VAPOUR 18 +#define EFFECT_PEA_SOUP 19 +#define EFFECT_BLACK_UNICORN_FLESH 20 +#define EFFECT_GRAY_UNICORN_FLESH 21 +#define EFFECT_WHITE_UNICORN_FLESH 22 +#define EFFECT_TELEPORT_CONTROL 23 +#define EFFECT_MUSHROOM 24 +#define EFFECT_OMMEL_CERUMEN 25 +#define EFFECT_OMMEL_SWEAT 26 +#define EFFECT_OMMEL_TEARS 27 +#define EFFECT_OMMEL_SNOT 28 +#define EFFECT_OMMEL_BONE 29 +#define EFFECT_MUSTARD_GAS 30 +#define EFFECT_MUSTARD_GAS_LIQUID 31 +#define EFFECT_PANIC 32 +#define EFFECT_TELEPORT 33 +#define EFFECT_VAMPIRISM 34 +#define EFFECT_DETECTING 35 +#define EFFECT_HOLY_MANGO 36 + +/* CEM = Consume End Message */ + +#define CEM_NOTHING 0 +#define CEM_SCHOOL_FOOD 1 +#define CEM_BONE 2 +#define CEM_FROG_FLESH 3 +#define CEM_OMMEL 4 +#define CEM_PEPSI 5 +#define CEM_KOBOLD_FLESH 6 +#define CEM_HEALING_LIQUID 7 +#define CEM_ANTIDOTE 8 +#define CEM_ESP 9 +#define CEM_HOLY_BANANA 10 +#define CEM_PEA_SOUP 11 +#define CEM_BLACK_UNICORN_FLESH 12 +#define CEM_GRAY_UNICORN_FLESH 13 +#define CEM_WHITE_UNICORN_FLESH 14 +#define CEM_OMMEL_BONE 15 +#define CEM_LIQUID_HORROR 16 +#define CEM_HOLY_MANGO 17 + +/* HM = Hit Message */ + +#define HM_NOTHING 0 +#define HM_SCHOOL_FOOD 1 +#define HM_FROG_FLESH 2 +#define HM_OMMEL 3 +#define HM_PEPSI 4 +#define HM_KOBOLD_FLESH 5 +#define HM_HEALING_LIQUID 6 +#define HM_ANTIDOTE 7 +#define HM_CONFUSE 8 +#define HM_HOLY_BANANA 9 +#define HM_HOLY_MANGO 10 + +#define SOLID_ID 4096 + +#define VALPURIUM (SOLID_ID + 1) +#define GRAVEL (SOLID_ID + 2) +#define MORAINE (SOLID_ID + 3) +#define OCTIRON (SOLID_ID + 4) +#define GLASS (SOLID_ID + 5) +#define PARCHMENT (SOLID_ID + 6) +#define CLOTH (SOLID_ID + 7) +#define MITHRIL (SOLID_ID + 8) +#define MARBLE (SOLID_ID + 9) +#define GOLD (SOLID_ID + 10) +#define GRASS (SOLID_ID + 11) +#define LEATHER (SOLID_ID + 12) +#define LEAF (SOLID_ID + 13) +#define FABRIC (SOLID_ID + 14) +#define PALM_LEAF (SOLID_ID + 15) +#define SULFUR (SOLID_ID + 16) +#define UNICORN_HORN (SOLID_ID + 17) +#define DIAMOND (SOLID_ID + 18) +#define SILVER (SOLID_ID + 19) +#define SAPPHIRE (SOLID_ID + 20) +#define RUBY (SOLID_ID + 21) +#define BRONZE (SOLID_ID + 22) +#define COPPER (SOLID_ID + 23) +#define TIN (SOLID_ID + 24) +#define SPIDER_SILK (SOLID_ID + 25) +#define KEVLAR (SOLID_ID + 26) +#define OMMEL_HAIR (SOLID_ID + 27) +#define HARDENED_LEATHER (SOLID_ID + 28) +#define TROLL_HIDE (SOLID_ID + 29) +#define NYMPH_HAIR (SOLID_ID + 30) +#define ANGEL_HAIR (SOLID_ID + 31) +#define PHOENIX_FEATHER (SOLID_ID + 32) +#define GOLDEN_EAGLE_FEATHER (SOLID_ID + 33) +#define ICE (SOLID_ID + 34) +#define DRAGON_HIDE (SOLID_ID + 35) +#define ARCANITE (SOLID_ID + 36) +#define ILLITHIUM (SOLID_ID + 37) +#define BALSA_WOOD (SOLID_ID + 38) +#define PINE_WOOD (SOLID_ID + 39) +#define FIR_WOOD (SOLID_ID + 40) +#define BIRCH_WOOD (SOLID_ID + 41) +#define OAK_WOOD (SOLID_ID + 42) +#define TEAK_WOOD (SOLID_ID + 43) +#define EBONY_WOOD (SOLID_ID + 44) +#define BLUE_CRYSTAL (SOLID_ID + 45) +#define PURPLE_CRYSTAL (SOLID_ID + 46) +#define GREEN_CRYSTAL (SOLID_ID + 47) +#define SAND_STONE (SOLID_ID + 48) +#define LIME_STONE (SOLID_ID + 49) +#define CALCITE (SOLID_ID + 50) +#define OBSIDIAN (SOLID_ID + 51) +#define GNEISS (SOLID_ID + 52) +#define SLATE (SOLID_ID + 53) +#define GRANITE (SOLID_ID + 54) +#define BASALT (SOLID_ID + 55) +#define MILKY_QUARTZ (SOLID_ID + 56) +#define FLINT (SOLID_ID + 57) +#define QUARTZITE (SOLID_ID + 58) +#define AMETHYST (SOLID_ID + 59) +#define CITRINE (SOLID_ID + 60) +#define ROSE_QUARTZ (SOLID_ID + 61) +#define JASPER (SOLID_ID + 62) +#define ROCK_CRYSTAL (SOLID_ID + 63) +#define DARK_GRASS (SOLID_ID + 64) +#define LEAD (SOLID_ID + 65) +#define BLACK_GRANITE (SOLID_ID + 66) +#define BLACK_LEATHER (SOLID_ID + 67) +#define FLAWLESS_DIAMOND (SOLID_ID + 68) +#define EMERALD (SOLID_ID + 69) +#define SUN_CRYSTAL (SOLID_ID + 70) +#define BLACK_DIAMOND (SOLID_ID + 71) +#define PSYPHER (SOLID_ID + 72) +#define EXTRA_HARD_BASALT (SOLID_ID + 73) +#define DEAD_GRASS (SOLID_ID + 74) +#define KAURI_WOOD (SOLID_ID + 75) +#define RATA_WOOD (SOLID_ID + 76) +#define NEPHRITE (SOLID_ID + 77) +#define HESSIAN_CLOTH (SOLID_ID + 78) + + +#define ORGANIC_ID (4096 * 2) + +#define BANANA_FLESH (ORGANIC_ID + 1) +#define SCHOOL_FOOD (ORGANIC_ID + 2) +#define BANANA_PEEL (ORGANIC_ID + 3) +#define KIWI_FLESH (ORGANIC_ID + 4) +#define PINEAPPLE_FLESH (ORGANIC_ID + 5) +#define PLANT_FIBER (ORGANIC_ID + 6) +#define MUTANT_PLANT_FIBER (ORGANIC_ID + 7) +#define BONE (ORGANIC_ID + 8) +#define BREAD (ORGANIC_ID + 9) +#define HOLY_BANANA_FLESH (ORGANIC_ID + 10) +#define CARROT_FLESH (ORGANIC_ID + 11) +#define OMMEL_CERUMEN (ORGANIC_ID + 12) +#define OMMEL_BONE (ORGANIC_ID + 13) +#define OMMEL_TOOTH (ORGANIC_ID + 14) +#define MANGO_FLESH (ORGANIC_ID + 15) +#define HOLY_MANGO_FLESH (ORGANIC_ID + 16) + +#define GAS_ID (4096 * 3) + +#define AIR (GAS_ID + 1) +#define MAGICAL_AIR (GAS_ID + 2) +#define SMOKE (GAS_ID + 3) +#define SKUNK_SMELL (GAS_ID + 4) +#define GHOST (GAS_ID + 5) +#define MAGIC_VAPOUR (GAS_ID + 6) +#define EVIL_WONDER_STAFF_VAPOUR (GAS_ID + 7) +#define GOOD_WONDER_STAFF_VAPOUR (GAS_ID + 8) +#define FART (GAS_ID + 9) +#define MUSTARD_GAS (GAS_ID + 10) +#define VACUUM_BLADE_AIR (GAS_ID + 11) + +#define LIQUID_ID (4096 * 4) + +#define OMMEL_URINE (LIQUID_ID + 1) +#define PEPSI (LIQUID_ID + 2) +#define WATER (LIQUID_ID + 3) +#define HEALING_LIQUID (LIQUID_ID + 4) +#define BLOOD (LIQUID_ID + 5) +#define BROWN_SLIME (LIQUID_ID + 6) +#define POISON_LIQUID (LIQUID_ID + 7) +#define VALDEMAR (LIQUID_ID + 8) +#define ANTIDOTE_LIQUID (LIQUID_ID + 9) +#define VODKA (LIQUID_ID + 10) +#define TROLL_BLOOD (LIQUID_ID + 11) +#define DARK_FROG_BLOOD (LIQUID_ID + 12) +#define SPIDER_BLOOD (LIQUID_ID + 13) +#define VOMIT (LIQUID_ID + 14) +#define ACIDOUS_BLOOD (LIQUID_ID + 15) +#define SULPHURIC_ACID (LIQUID_ID + 16) +#define DOG_DROOL (LIQUID_ID + 17) +#define PEA_SOUP (LIQUID_ID + 18) +#define OMMEL_SWEAT (LIQUID_ID + 19) +#define OMMEL_TEARS (LIQUID_ID + 20) +#define OMMEL_SNOT (LIQUID_ID + 21) +#define SWEAT (LIQUID_ID + 22) +#define GLOWING_BLOOD (LIQUID_ID + 23) +#define YELLOW_SLIME (LIQUID_ID + 24) +#define SICK_BLOOD (LIQUID_ID + 25) +#define MUSTARD_GAS_LIQUID (LIQUID_ID + 26) +#define LIQUID_HORROR (LIQUID_ID + 27) +#define VINEGAR (LIQUID_ID + 28) +#define OMMEL_BLOOD (LIQUID_ID + 29) +#define CURDLED_OMMEL_BLOOD (LIQUID_ID + 30) + +#define FLESH_ID (4096 * 5) + +#define GOBLINOID_FLESH (FLESH_ID + 1) +#define PORK (FLESH_ID + 2) +#define BEEF (FLESH_ID + 3) +#define FROG_FLESH (FLESH_ID + 4) +#define ELPURI_FLESH (FLESH_ID + 5) +#define HUMAN_FLESH (FLESH_ID + 6) +#define DOLPHIN_FLESH (FLESH_ID + 7) +#define BEAR_FLESH (FLESH_ID + 8) +#define WOLF_FLESH (FLESH_ID + 9) +#define DOG_FLESH (FLESH_ID + 10) +#define ENNER_BEAST_FLESH (FLESH_ID + 11) +#define SPIDER_FLESH (FLESH_ID + 12) +#define JACKAL_FLESH (FLESH_ID + 13) +#define MUTANT_ASS_FLESH (FLESH_ID + 14) +#define BAT_FLESH (FLESH_ID + 15) +#define WERE_WOLF_FLESH (FLESH_ID + 16) +#define KOBOLD_FLESH (FLESH_ID + 17) +#define GIBBERLING_FLESH (FLESH_ID + 18) +#define CAT_FLESH (FLESH_ID + 19) +#define RAT_FLESH (FLESH_ID + 20) +#define ANGEL_FLESH (FLESH_ID + 21) +#define DWARF_FLESH (FLESH_ID + 22) +#define DAEMON_FLESH (FLESH_ID + 23) +#define MAMMOTH_FLESH (FLESH_ID + 24) +#define BLACK_UNICORN_FLESH (FLESH_ID + 25) +#define GRAY_UNICORN_FLESH (FLESH_ID + 26) +#define WHITE_UNICORN_FLESH (FLESH_ID + 27) +#define LION_FLESH (FLESH_ID + 28) +#define BUFFALO_FLESH (FLESH_ID + 29) +#define SNAKE_FLESH (FLESH_ID + 30) +#define ORC_FLESH (FLESH_ID + 31) +#define OSTRICH_FLESH (FLESH_ID + 32) +#define CHAMELEON_FLESH (FLESH_ID + 33) +#define FLOATING_EYE_FLESH (FLESH_ID + 34) +#define MUSHROOM_FLESH (FLESH_ID + 35) +#define MOOSE_FLESH (FLESH_ID + 36) +#define MAGPIE_FLESH (FLESH_ID + 37) +#define SKUNK_FLESH (FLESH_ID + 38) +#define HEDGEHOG_FLESH (FLESH_ID + 39) +#define MUTANT_BUNNY_FLESH (FLESH_ID + 40) +#define HATTIFATTENER_FLESH (FLESH_ID + 41) +#define BLINK_DOG_FLESH (FLESH_ID + 42) +#define MAGIC_MUSHROOM_FLESH (FLESH_ID + 43) +#define SICK_SPIDER_FLESH (FLESH_ID + 44) +#define MIND_WORM_FLESH (FLESH_ID + 45) +#define MUTANT_HEDGEHOG_FLESH (FLESH_ID + 46) +#define EAGLE_FLESH (FLESH_ID + 47) +#define KABOUTER_FLESH (FLESH_ID + 48) +#define ULDRA_FLESH (FLESH_ID + 49) +#define OKAPI_FLESH (FLESH_ID + 50) +#define VAMPIRE_FLESH (FLESH_ID + 51) +#define MOUSE_FLESH (FLESH_ID + 52) +#define FOX_FLESH (FLESH_ID + 53) +#define THUNDER_BIRD_FLESH (FLESH_ID + 54) + +#define POWDER_ID (4096 * 6) + +#define GUN_POWDER (POWDER_ID + 1) +#define SNOW (POWDER_ID + 2) +#define SAND (POWDER_ID + 3) + +#define IRON_ALLOY_ID (4096 * 7) + +#define IRON (IRON_ALLOY_ID + 1) +#define STEEL (IRON_ALLOY_ID + 2) +#define METEORIC_STEEL (IRON_ALLOY_ID + 3) +#define ADAMANT (IRON_ALLOY_ID + 4) +#define DAMASCUS_STEEL (IRON_ALLOY_ID + 5) + +#define UNARMED_ATTACK 0 +#define WEAPON_ATTACK 1 +#define KICK_ATTACK 2 +#define BITE_ATTACK 3 + +#define USE_ARMS 1 +#define USE_LEGS 2 +#define USE_HEAD 4 + +#define ATTRIBUTES 11 +#define BASE_ATTRIBUTES 7 + +#define ENDURANCE 0 +#define PERCEPTION 1 +#define INTELLIGENCE 2 +#define WISDOM 3 +#define WILL_POWER 4 +#define CHARISMA 5 +#define MANA 6 + +#define ARM_STRENGTH 7 +#define LEG_STRENGTH 8 +#define DEXTERITY 9 +#define AGILITY 10 + +#define HELMET_INDEX 0 +#define AMULET_INDEX 1 +#define CLOAK_INDEX 2 +#define BODY_ARMOR_INDEX 3 +#define BELT_INDEX 4 +#define RIGHT_WIELDED_INDEX 5 +#define LEFT_WIELDED_INDEX 6 +#define RIGHT_RING_INDEX 7 +#define LEFT_RING_INDEX 8 +#define RIGHT_GAUNTLET_INDEX 9 +#define LEFT_GAUNTLET_INDEX 10 +#define RIGHT_BOOT_INDEX 11 +#define LEFT_BOOT_INDEX 12 + +#define BROKEN 128 +#define WINDOW 1024 + +#define LONG_SWORD 1 +#define TWO_HANDED_SWORD 2 +#define TWO_HANDED_SCIMITAR 3 +#define SPEAR 4 +#define AXE 5 +#define HALBERD 6 +#define MACE 7 +#define WAR_HAMMER 8 +#define SICKLE 9 +#define DAGGER 10 +#define SHORT_SWORD 11 +#define BASTARD_SWORD 12 +#define BATTLE_AXE 13 +#define SCYTHE 14 +#define QUARTER_STAFF 15 +#define HAMMER 16 +#define KNIGHT_SWORD 17 +#define KATANA 18 +#define SPETUM 19 +#define TIP_SWORD 20 +#define GREAT_AXE 21 +#define RAPIER 22 + +#define GOROVITS_HAMMER 1 +#define GOROVITS_SICKLE 2 +#define GOROVITS_SCIMITAR 3 + +#define CHAIN_MAIL 1 +#define PLATE_MAIL 2 +#define ARMOR_OF_GREAT_HEALTH 3 +#define DRAGON_CUIRASS 4 +#define FILTHY_TUNIC 5 + +#define CHEAP 1 +#define EXPENSIVE 2 + +#define WAND_OF_POLYMORPH 1 +#define WAND_OF_STRIKING 2 +#define WAND_OF_FIRE_BALLS 3 +#define WAND_OF_TELEPORTATION 4 +#define WAND_OF_HASTE 5 +#define WAND_OF_SLOW 6 +#define WAND_OF_RESURRECTION 7 +#define WAND_OF_DOOR_CREATION 8 +#define WAND_OF_INVISIBILITY 9 +#define WAND_OF_CLONING 10 +#define WAND_OF_LIGHTNING 11 +#define WAND_OF_ACID_RAIN 12 +#define WAND_OF_MIRRORING 13 +#define WAND_OF_NECROMANCY 14 + +#define RUNED_WHIP 1 + +#define BIG_MINE 1 + +#define CLOAK_OF_INVISIBILITY 1 +#define CLOAK_OF_FIRE_RESISTANCE 2 +#define CLOAK_OF_ELECTRICITY_RESISTANCE 3 +#define CLOAK_OF_ACID_RESISTANCE 4 +#define CLOAK_OF_WERE_WOLF_FUR 5 + +#define BOOT_OF_STRENGTH 1 +#define BOOT_OF_AGILITY 2 +#define BOOT_OF_KICKING 3 + +#define GAUNTLET_OF_STRENGTH 1 +#define GAUNTLET_OF_DEXTERITY 2 + +#define RING_OF_FIRE_RESISTANCE 1 +#define RING_OF_POLYMORPH_CONTROL 2 +#define RING_OF_INFRA_VISION 3 +#define RING_OF_TELEPORTATION 4 +#define RING_OF_TELEPORT_CONTROL 5 +#define RING_OF_POLYMORPH 6 +#define RING_OF_POISON_RESISTANCE 7 +#define RING_OF_INVISIBILITY 8 +#define RING_OF_ELECTRICITY_RESISTANCE 9 +#define RING_OF_SEARCHING 10 +#define RING_OF_ACID_RESISTANCE 11 +#define RING_OF_THIEVES 12 + +#define AMULET_OF_LIFE_SAVING 1 +#define AMULET_OF_ESP 2 +#define AMULET_OF_WARDING 3 +#define AMULET_OF_VANITY 4 + + +#define FULL_HELMET 1 +#define HELM_OF_PERCEPTION 2 +#define HELM_OF_BRILLIANCE 4 +#define HELM_OF_ATTRACTIVITY 5 +#define GOROVITS_FAMILY_GAS_MASK 6 + +#define BELT_OF_CARRYING 1 +#define BELT_OF_LEVITATION 2 + +#define SMALL_CHEST 1 +#define CHEST 2 +#define LARGE_CHEST 3 +#define STRONG_BOX 4 + +#define BRAVERY 1 +#define FEAR 2 +#define CONFUSION 3 + +#define ROOKIE 1 +#define VETERAN 2 +#define EUNUCH 3 +#define PATROL 4 +#define SHOP 5 +#define ELITE 6 +#define MASTER 7 +#define GRAND_MASTER 8 +#define MONDEDR_GUARD 9 +#define DWARVEN_GUARD 10 +#define SENTINEL 11 +#define FOREST_SHOP 12 +#define ENQUIOX 128 + +#define DARK 1 +#define GREATER_DARK 2 +#define GIANT_DARK 3 +#define LIGHT 4 +#define GREATER_LIGHT 5 +#define GIANT_LIGHT 6 + +#define WARRIOR 1 +#define WAR_LORD 2 + +#define BERSERKER 1 +#define BUTCHER 2 +#define PRINCE 3 +#define KING 4 +#define JAILER 5 +#define PRISON_WARDEN 6 + +#define CONICAL 1 +#define FLAT 2 + +#define LARGE 1 +#define GIANT 2 +#define ARANEA 3 + +#define BLACK_BEAR 1 +#define GRIZZLY_BEAR 2 +#define CAVE_BEAR 3 +#define POLAR_BEAR 4 +#define PANDA_BEAR 5 + +#define ZOMBIE_OF_KHAZ_ZADM 1 + +#define TORTURING_CHIEF 1 +#define WHIP_CHAMPION 2 +#define WAR_LADY 3 +#define QUEEN 4 + +#define CHIEFTAIN 1 +#define LORD 2 +#define PATRIARCH 3 + +#define AMBULATORY 1 + +#define GREATER 1 +#define GIANT 2 +#define SHAMBLING 3 +#define LILY 4 + +#define GENEFOURX 1 + +#define SLAUGHTERER 1 +#define SQUAD_LEADER 2 +#define OFFICER 3 +#define GENERAL 4 +#define MARSHAL 5 + +#define APPRENTICE 1 +#define BATTLE_MAGE 2 +#define ELDER 3 +#define ARCH_MAGE 4 + +#define ROVER 1 +#define BAND_LEADER 2 + +#define FIELD_MOUSE 1 +#define LABORATORY_MOUSE 2 + +#define THIN_PIG 1 + +#define STARVED_OX 1 + +#define FLOATIE 1 + +/* Least significant bit defines sex */ + +#define SONIC 1 + +#define BABY_MALE 2 +#define BABY_FEMALE 3 +#define ADULT_MALE 4 +#define ADULT_FEMALE 5 + +#define APPRENTICE_NECROMANCER 1 +#define MASTER_NECROMANCER 2 + +#define HUSBAND 1 +#define WIFE 2 +#define CHILD 3 + +#define LIGHT_ASIAN_SIREN 1 +#define DARK_ASIAN_SIREN 2 +#define CAUCASIAN_SIREN 3 +#define DARK_SIREN 4 +#define GREEN_SIREN 5 + + +#define HATCHLING 1 +#define BOIL 2 + +#define PARQUET 1 +#define FLOOR 2 +#define GROUND 3 +#define GRASS_TERRAIN 4 +#define LANDING_SITE 5 +#define SNOW_TERRAIN 6 +#define DARK_GRASS_TERRAIN 7 +#define SAND_TERRAIN 8 +#define DEAD_GRASS_TERRAIN 9 + +#define POOL 1 +#define UNDERGROUND_LAKE 2 + +#define BRICK_FINE 1 +#define BRICK_PROPAGANDA 2 +#define BRICK_OLD 3 +#define BRICK_PRIMITIVE 4 +#define BRICK_PRIMITIVE_PROPAGANDA 5 +#define STONE_WALL 6 +#define ICE_WALL 7 +#define BROKEN_WALL 8 + +#define PINE 1 +#define FIR 2 +#define HOLY_TREE 3 +#define CARPET 4 +#define COUCH 5 +#define DOUBLE_BED 6 +#define POOL_BORDER 7 +#define POOL_CORNER 8 +#define PALM 9 +#define SNOW_PINE 10 +#define SNOW_FIR 11 +#define ANVIL 12 +#define SHARD 13 +#define CACTUS 14 +#define OAK 15 +#define BIRCH 16 +#define TEAK 17 +#define DWARF_BIRCH 18 + +#define SNOW_BOULDER 4 + +#define STAIRS_UP 100 +#define STAIRS_DOWN 200 +#define OREE_LAIR_ENTRY 300 +#define OREE_LAIR_EXIT 400 +#define SUMO_ARENA_ENTRY 700 +#define SUMO_ARENA_EXIT 800 +#define KHARAZ_ARAD_ENTRY 900 +#define KHARAZ_ARAD_EXIT 1000 +#define WAYPOINT_DEEPER 1100 +#define WAYPOINT_SHALLOWER 1200 +#define FOUNTAIN 65535 + +#define BOOK_CASE 1 +#define CHEST_OF_DRAWERS 2 +#define SHELF 3 + +#define BROKEN_BARWALL 1 + +#define BARDOOR 1 +#define SECRET_DOOR 2 + +#define WORLD_MAP 255 + +#define DEFAULT_TEAM 255 + +/* Hard-coded teams */ + +#define PLAYER_TEAM 0 +#define MONSTER_TEAM 1 +#define ATTNAM_TEAM 2 +#define SUMO_TEAM 3 +#define IVAN_TEAM 6 +#define NEW_ATTNAM_TEAM 7 +#define COLONIST_TEAM 8 +#define TOURIST_GUIDE_TEAM 9 +#define TOURIST_TEAM 10 +#define BETRAYED_TEAM 11 +#define MONDEDR_TEAM 12 +#define KHARAZ_ARAD_TEAM 13 +#define FORESTMAN_TEAM 14 +#define SOLICITUS_TEAM 15 +#define MORBE_TEAM 16 + +#define NOT_WALKABLE 1 +#define HAS_CHARACTER 2 +#define IN_ROOM 4 +#define NOT_IN_ROOM 8 +#define ATTACHABLE (16|NOT_IN_ROOM) /* overrides IN_ROOM */ +#define HAS_NO_OTERRAIN 32 + +#define RANDOM 0 +#define ELPURI_CAVE 1 +#define ATTNAM 2 +#define NEW_ATTNAM 3 +#define UNDER_WATER_TUNNEL 4 +#define MONDEDR 5 +#define DRAGON_TOWER 6 +#define DARK_FOREST 7 +#define MUNTUO 8 +#define XINROCH_TOMB 9 +#define KHARAZ_ARAD_SHOP 127 +#define UNDER_WATER_TUNNEL_EXIT 128 + +#define VESANA_LEVEL 2 +#define CRYSTAL_LEVEL 3 +#define SPIDER_LEVEL 4 +#define ENNER_BEAST_LEVEL 4 +#define ZOMBIE_LEVEL 5 +#define CITY_LEVEL 6 +#define FUSANGA_LEVEL 7 +#define IVAN_LEVEL 9 +#define DARK_LEVEL 10 +#define OREE_LAIR 14 +#define KHARAZ_ARAD 15 +#define PLANT_ENTRY_LEVEL 16 +#define PLANT_LEVEL 17 +#define GENEFOURX_LAIR 18 +#define POND_LEVEL 19 + +#define RECTANGLE 1 +#define ROUND_CORNERS 2 + +#define VALPURUS 1 +#define LEGIFER 2 +#define ATAVUS 3 +#define DULCIS 4 +#define SEGES 5 +#define SOPHOS 6 +#define TERRA 7 +#define SILVA 7 +#define LORICATUS 8 +#define MELLIS 9 +#define CLEPTIA 10 +#define NEFAS 11 +#define SCABIES 12 +#define INFUSCOR 13 +#define CRUENTUS 14 +#define MORTIFER 15 +#define ATHEIST 16 +#define SOLICITU 17 + +#define MAX_PRICE 2147483647 + +#define ROOM_NORMAL 1 +#define ROOM_SHOP 2 +#define ROOM_CATHEDRAL 3 +#define ROOM_LIBRARY 4 +#define ROOM_BANANA_DROP_AREA 5 +#define ROOM_SUMO_ARENA 6 +#define ROOM_VAULT 7 + +#define BEAM_POLYMORPH 0 +#define BEAM_STRIKE 1 +#define BEAM_FIRE_BALL 2 +#define BEAM_TELEPORT 3 +#define BEAM_HASTE 4 +#define BEAM_SLOW 5 +#define BEAM_RESURRECT 6 +#define BEAM_INVISIBILITY 7 +#define BEAM_DUPLICATE 8 +#define BEAM_LIGHTNING 9 +#define BEAM_DOOR_CREATION 10 +#define BEAM_ACID_RAIN 11 +#define BEAM_NECROMANCY 12 + +#define BEAM_STYLES 3 + +#define PARTICLE_BEAM 0 +#define LIGHTNING_BEAM 1 +#define SHIELD_BEAM 2 + +#define RANDOM_COLOR 65536 + +#define NO_LIMIT 65535 + +#define NO_BROKEN 1 +#define IGNORE_BROKEN_PRICE 2 + +#define N_LOCK_ID 1024 +#define S_LOCK_ID 16384 +#define LOCK_DELTA 1024 + +#define BROKEN_LOCK S_LOCK_ID + +/* Normal lock types, which can be randomized */ + +#define ROUND_LOCK (N_LOCK_ID + LOCK_DELTA * 1) +#define SQUARE_LOCK (N_LOCK_ID + LOCK_DELTA * 2) +#define TRIANGULAR_LOCK (N_LOCK_ID + LOCK_DELTA * 3) + +/* Special lock types, which must be generated in the script */ + +#define HEXAGONAL_LOCK (S_LOCK_ID + LOCK_DELTA * 1) +#define OCTAGONAL_LOCK (S_LOCK_ID + LOCK_DELTA * 2) +#define HEART_SHAPED_LOCK (S_LOCK_ID + LOCK_DELTA * 3) +#define PENTAGONAL_LOCK (S_LOCK_ID + LOCK_DELTA * 4) + +#define DESERT 1 +#define JUNGLE 2 +#define STEPPE 3 +#define LEAFY_FOREST 4 +#define EVERGREEN_FOREST 5 +#define TUNDRA 6 +#define GLACIER 7 + +#define NO_MOVE 0 +#define WALK 1 +#define SWIM 2 +#define FLY 4 +#define ETHEREAL 8 +#define ANY_MOVE 15 + +#define NOT_RUSTED 0 +#define SLIGHTLY_RUSTED 1 +#define RUSTED 2 +#define VERY_RUSTED 3 + +#define SKIN_COLOR 1 +#define CAP_COLOR 2 +#define HAIR_COLOR 4 +#define EYE_COLOR 8 +#define TORSO_MAIN_COLOR 16 +#define BELT_COLOR 32 +#define BOOT_COLOR 64 +#define TORSO_SPECIAL_COLOR 128 +#define ARM_MAIN_COLOR 256 +#define GAUNTLET_COLOR 512 +#define ARM_SPECIAL_COLOR 1024 +#define LEG_MAIN_COLOR 2048 +#define LEG_SPECIAL_COLOR 4096 +#define CLOTH_COLOR (CAP_COLOR|TORSO_MAIN_COLOR|ARM_MAIN_COLOR|GAUNTLET_COLOR|LEG_MAIN_COLOR) + +/* contentscript Flags */ + +#define IS_LEADER 1 +#define IS_MASTER 2 + +#define DEPENDS_ON_ATTRIBUTES 65535 + +#define FOLLOW_PLAYER 1 +#define FLEE_FROM_ENEMIES 2 +#define DONT_CHANGE_EQUIPMENT 4 +#define DONT_CONSUME_ANYTHING_VALUABLE 8 + +#define NO_PARAMETERS 255 + +#define GRAY_FRACTAL 0 +#define RED_FRACTAL 1 +#define GREEN_FRACTAL 2 +#define BLUE_FRACTAL 3 +#define YELLOW_FRACTAL 4 + +#define BLUNT 1 +#define SLASH 2 +#define PIERCE 4 + +/*************************/ +/* Common DataBase flags */ +/*************************/ + +/* CommonFlags */ +#define IS_ABSTRACT 1 +#define HAS_SECONDARY_MATERIAL 2 +#define CREATE_DIVINE_CONFIGURATIONS 4 +#define CAN_BE_WISHED 8 +#define CAN_BE_DESTROYED 16 +#define IS_VALUABLE 32 +#define CAN_BE_MIRRORED 64 + +/* NameFlags */ +#define USE_AN 1 +#define USE_ADJECTIVE_AN 2 +#define NO_ARTICLE 4 +#define FORCE_THE 8 +#define SHOW_MATERIAL 16 + +/***************************/ +/* Material DataBase flags */ +/***************************/ + +/* CommonFlags */ +/* NameFlags (only USE_AN) */ + +/* CategoryFlags */ +#define IS_METAL 1 +#define IS_BLOOD 2 +#define CAN_BE_TAILORED 4 +#define IS_SPARKLING 8 +#define IS_SCARY 16 +#define IS_GOLEM_MATERIAL 32 +#define IS_BEVERAGE 64 + +/* BodyFlags */ +#define IS_ALIVE 1 +#define IS_WARM 2 +#define CAN_HAVE_PARASITE 4 +#define USE_MATERIAL_ATTRIBUTES 8 +#define CAN_REGENERATE 16 + +/* InteractionFlags */ +#define CAN_BURN 1 +#define CAN_EXPLODE 2 +#define CAN_DISSOLVE 4 +#define AFFECT_INSIDE 8 +#define EFFECT_IS_GOOD 16 +#define IS_AFFECTED_BY_MUSTARD_GAS 32 + +/*************************/ +/* End of DataBase flags */ +/*************************/ + +#define BONUS_LIVES 0 + +/* room flags */ + +#define NO_MONSTER_GENERATION 1 + +#define NO_TAMING -1 + +; /* this line must be here */ \ No newline at end of file diff --git a/Script/dungeon.dat b/Script/dungeon.dat new file mode 100644 index 0000000..7f73bec --- /dev/null +++ b/Script/dungeon.dat @@ -0,0 +1,8518 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* + * NOTICE!!! + * + * This file contains SPOILERS, which might ruin your IVAN experience + * totally. Also, editing anything can DESTROY GAME BALANCE or CAUSE + * OBSCURE BUGS if you don't know what you're doing. So from here on, + * proceed at your own risk! + */ + +/* Team and dungeon data for the game */ + +Dungeons = 9; +Teams = 17; + +/* + * Description of hard-coded teams: + * 0 == PLAYER_TEAM == Player and pets + * 1 == MONSTER_TEAM == Monsters, default relation to other teams == hostile + * 2 == ATTNAM_TEAM == Residents of Attnam + * 3 == SUMO_TEAM == Huang Ming Pong during an arena figth + * 7 == NEW_ATTNAM_TEAM == natives and ostriches of New Attnam + * 8 == COLONIST_TEAM == colonists of New Attnam + * 9 == TOURIST_GUIDE_TEAM == dummy team which holds the village elder, who tourists follow + * 10 == TOURIST_TEAM == tourists of New Attnam + * 11 == BETRAYED_TEAM == pets which the player has angered + * 12 == KHARAZ_ARAD_TEAM + * 13 == MONDEDR_TEAM + * 14 == FORESTMAN_TEAM == Forestmen of the Deep Forest + * 15 == SOLICITUS_TEAM == Team on which Solicitus and friends hang out + * 16 == MORBE_TEAM == Team on which Morbe and friends hang out + */ + +Team ATTNAM_TEAM; +{ + Relation 0, UNCARING; + KillEvilness = 50; +} + +Team SUMO_TEAM; +{ + Relation 0, HOSTILE; +} + +Team 4; /* Spawned hostile angels */ +{ + Relation 0, HOSTILE; + Relation 1, UNCARING; +} + +Team 5; /* Dungeon shopkeepers */ +{ + Relation 1, UNCARING; + KillEvilness = 100; +} + +Team IVAN_TEAM; +{ + KillEvilness = 100; +} + +Team NEW_ATTNAM_TEAM; +{ + KillEvilness = 50; +} + +Team COLONIST_TEAM; +{ + Relation 7, FRIEND; +} + +Team TOURIST_GUIDE_TEAM; +{ + Relation 7, FRIEND; + Relation 8, FRIEND; + KillEvilness = 50; +} + +Team TOURIST_TEAM; +{ + Relation 7, FRIEND; + Relation 8, FRIEND; + KillEvilness = 10; +} + +Team BETRAYED_TEAM; +{ + Relation 0, HOSTILE; + KillEvilness = 10; +} + +Team KHARAZ_ARAD_TEAM; +{ + Relation 0, FRIEND; + KillEvilness = 50; +} + +Team MONDEDR_TEAM; +{ + Relation 0, FRIEND; + KillEvilness = 50; +} + +Team FORESTMAN_TEAM; +{ + Relation 1, UNCARING; + /*Relation 7, FRIEND; + Relation 8, FRIEND;*/ + KillEvilness = 50; +} + +Team SOLICITUS_TEAM; +{ + Relation 1, UNCARING; + KillEvilness = 50; +} + +Team MORBE_TEAM; +{ + Relation 1, UNCARING; +} +/* +Team PRISONER_TEAM; +{ + Relation 7, FRIEND; + Relation 8, FRIEND; + KillEvilness = 50; +} +*/ +Dungeon ELPURI_CAVE; +{ + Levels = 20; + Description = "gloomy cave"; + ShortDescription = "GC"; + + LevelDefault + { + FillSquare = solidterrain(GROUND), GNEISS earth; + TunnelSquare = solidterrain(GROUND), 0; + Size = 64, 36; + Rooms = 10:30; + Items = 25:50; + GenerateMonsters = true; + IsOnGround = false; + TeamDefault = MONSTER_TEAM; + LOSModifier = 16; + IgnoreDefaultSpecialSquares = false; + DifficultyBase = 50; + DifficultyDelta = 10; + MonsterAmountBase = 10; + MonsterAmountDelta = 2; + MonsterGenerationIntervalBase = 140; + MonsterGenerationIntervalDelta = -10; + CanGenerateBone = true; + ItemMinPriceBase = 20; + ItemMinPriceDelta = 10; + EnchantmentMinusChanceBase = 0; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 5; + EnchantmentPlusChanceDelta = 5; + BackGroundType = GRAY_FRACTAL; + IsCatacomb = false; + + Square, Random NOT_WALKABLE|NOT_IN_ROOM; + { + Items == stone; + Times = 25:50; + } + + Square, Random; + { + Items == mine { Team = MONSTER_TEAM; IsActive = true; } + Times = 0:3; + } + + Square, Random; + { + Items == beartrap { Team = MONSTER_TEAM; IsActive = true; } + Times = 0:3; + } + + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + AttachRequired = true; + } + + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + AttachRequired = true; + } + + RoomDefault + { + Pos = 2:XSize-5,2:YSize-5; + Size = 4:11,4:11; + AltarPossible = true; + WallSquare = solidterrain(GROUND), GRANITE wall(BRICK_OLD); + FloorSquare = solidterrain(PARQUET), 0; + DoorSquare = solidterrain(PARQUET), GRANITE door; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = true; + GenerateLanterns = true; + Type = ROOM_NORMAL; + GenerateFountains = true; + AllowLockedDoors = true; + AllowBoobyTrappedDoors = true; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + GenerateWards = false; + } + } + + Level 0; + { + FillSquare = solidterrain(GROUND), MORAINE earth; + + RoomDefault + { + Pos = 2:XSize-5,2:YSize-5; + WallSquare = solidterrain(GROUND), FIR_WOOD wall(BRICK_OLD); + FloorSquare = solidterrain(PARQUET), 0; + DoorSquare = solidterrain(PARQUET), FIR_WOOD door; + } + } + + RandomLevel 1:3; + { + Size = 80, 60; + + RoomDefault + { + Pos = 2:XSize-5,2:YSize-5; + Size = 10:20,10:20; + } + + Room + { + AltarPossible = false; + Size = 30,30; + Flags = NO_MONSTER_GENERATION; + + Square, Random; + { + OTerrain = STEEL sign { Text = "Experimental dwarven mine field. Thank you for participating in testing!"; } + } + + Square, Random; + { + Items == Random { MinPrice = 100; Chance = 50; } + Times = 20; + } + + Square, Random; + { + Items == Random { MinPrice = 500; Chance = 50; } + Times = 4; + } + + Square, Random; + { + Items == mine { Team = MONSTER_TEAM; IsActive = true; Chance = 25; } + Times = 40; + } + + Square, Random; + { + Items == mine(BIG_MINE) { Team = MONSTER_TEAM; IsActive = true; Chance = 25; } + Times = 20; + } + + Square, Random; + { + Items == backpack { Chance = 25; } + Times = 4; + } + } + } + + RandomLevel 1:2; + { + LevelMessage = "You hear many wolves howling. You shiver."; + + Room + { + Size = 7,6; + AltarPossible = false; + Shape = ROUND_CORNERS; + + CharacterMap + { + Pos = 1,1; + Size = 5,4; + + Types + { + w = wolf; + W = werewolfhuman { Inventory = { 3, scrollofenchantweapon, scrollofenchantarmor, solstone; } } + } + } + { + .www. + wwWww + wwwww + .www. + } + } + } + + RandomLevel 1:3; + { + + Room + { + Size = 10:14,10:14; + AltarPossible = false; + Shape = RECTANGLE; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + GenerateLanterns = false; + + Square, Random; + { + Items == Random { MinPrice = 500; MaxPrice = 2000; } + Times = 1; + } + + Square, Random; + { + OTerrain = decoration(FIR); + } + + Square, Random; + { + OTerrain = decoration(OAK); + } + + Square, Random; + { + OTerrain = decoration(BIRCH); + } + + Square, Random; + { + OTerrain = decoration(TEAK); + } + + Square, Random; + { + OTerrain = decoration(DWARF_BIRCH); + } + + Square, Random; + { + Character = magpie; + OTerrain = decoration(PINE); + Items == stone { Times = 0:1;} + Times = 5:10; + } + + } + } + + Level 3; + { + + Room + { + Size = 7,6; + AltarPossible = false; + Shape = ROUND_CORNERS; + + CharacterMap + { + Pos = 1,1; + Size = 5,4; + + Types + { + g = guard(DWARVEN_GUARD) { Team = 5; } + } + } + { + .g.g. + ..... + ..... + .g.g. + } + + Square, Pos 3,2; + { + OTerrain = stairs(STAIRS_DOWN) { AttachedArea = KHARAZ_ARAD; } + EntryIndex = STAIRS_DOWN + 1; + } + Square, Pos 3,0; + { + OTerrain = 0; + AttachRequired = true; + } + } + } + + + RandomLevel 1:3; + { + CanGenerateBone = false; + + Room + { + Size = 5,9; + AltarPossible = false; + DivineMaster = MELLIS; + Type = ROOM_SHOP; + GenerateFountains = false; + + CharacterMap + { + Pos = 1, 1; + Size = 3, 7; + + Types + { + g = guard(SHOP) { Team = 5; } + s = shopkeeper(ELPURI_CAVE) { Team = 5; Flags = IS_MASTER; } + } + } + { + g.g + ... + ... + .s. + ... + ... + g.g + } + + ItemMap + { + Pos = 1,1; + Size = 3,7; + + Types + { + g == Random { MinPrice = 100; MaxPrice = 10000; Category = GAUNTLET|BOOT; } + b == Random { MinPrice = 100; MaxPrice = 10000; Category = GAUNTLET|BOOT; } + a == Random { MinPrice = 500; MaxPrice = 10000; Category = HELMET|CLOAK|BODY_ARMOR|BELT; } + d == Random { MinPrice = 200; MaxPrice = 10000; Category = RING|AMULET; } + w == Random { MinPrice = 500; MaxPrice = 10000; Category = WEAPON|SHIELD; } + e == Random { MinPrice = 50; MaxPrice = 10000; Category = FOOD|POTION; } + u == Random { MinPrice = 200; MaxPrice = 10000; Category = WAND|TOOL; } + r == Random { MinPrice = 200; MaxPrice = 10000; Category = SCROLL|BOOK; } + s == wand(WAND_OF_STRIKING); + } + } + { + .g. + ddb + err + e.w + auw + auw + .s. + } + } + } + + RandomLevel 0:11; + { + /* I'm Evil, Bwahaha */ + + Square, Random; + { + Items == VALPURIUM VALPURIUM flamingsword(KNIGHT_SWORD) { Enchantment = 10; LifeExpectancy = 1000:10000; Chance = 5; } + } + } + + RandomLevel 0:11; + { + /* I'm Evil, Bwahaha */ + + Square, Random; + { + Items == itemcontainer(LARGE_CHEST) { LifeExpectancy = 100:10000; Chance = 5; ItemsInside == Random { MinPrice = 1500; Times = 10; Category = HELMET|AMULET|CLOAK|BODY_ARMOR|WEAPON|SHIELD|RING|GAUNTLET|BELT|BOOT|TOOL|VALUABLE; LifeExpectancy = 100:10000; } } + } + } + + RandomLevel 1:4; + { + CanGenerateBone = false; + + Room + { + Size = 9,9; + AltarPossible = false; + GenerateFountains = false; + + OTerrainMap + { + Size = 3,3; + Pos = 3,3; + Types + { + # = METEORIC_STEEL wall(BRICK_OLD); + } + } + { + ### + #.# + ### + } + + Square, Pos 4,4; + { + Items = { 2, MITHRIL beartrap { Team = MONSTER_TEAM; IsActive = true; Chance = 25; }, METEORIC_STEEL itemcontainer(CHEST|OCTAGONAL_LOCK) { Parameters = LOCKED; ItemsInside == Random { MinPrice = 1250; Times = 2; } } } + } + } + } + + Level ENNER_BEAST_LEVEL; + { + LevelMessage = "You hear a wailing scream in the distance. An enner beast must dwell in the level!"; + IgnoreDefaultSpecialSquares = true; + Items = 35:70; + EnterImage = "Enner.pcx"; + EnterTextDisplacement = -150, 0; + + Square, Random; + { + Items == mine(BIG_MINE) { Team = MONSTER_TEAM; IsActive = true; } + } + + Square, Random; + { + Items == METEORIC_STEEL beartrap { Team = MONSTER_TEAM; IsActive = true; } + Times = 2:6; + } + + Square, Random NOT_WALKABLE|NOT_IN_ROOM; + { + Items == stone; + Times = 25:50; + } + + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + AttachRequired = true; + } + + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + AttachRequired = true; + } + + Square, Random; + { + Items == bone; + Times = 20; + } + + Square, Random; + { + Items == skull; + Times = 5; + } + + Square, Random NOT_IN_ROOM; + { + Character = ennerbeast; + } + } + + Level ZOMBIE_LEVEL; + { + Rooms = 1; + IgnoreDefaultSpecialSquares = true; + MonsterAmountBase = 20; + LOSModifier = 24; + FillSquare = FLINT solidterrain(GROUND), GNEISS earth; + + Room + { + Pos = 2,2; + Size = 60,32; + AltarPossible = false; + WallSquare = FLINT solidterrain(GROUND), 0; + FloorSquare = FLINT solidterrain(GROUND), 0; + GenerateDoor = false; + GenerateTunnel = false; + GenerateLanterns = false; + GenerateFountains = false; + + Square, BoundedRandom 1, 1, 25, 58, HAS_NO_OTERRAIN; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + } + + Square, BoundedRandom 35, 1, 58, 58, HAS_NO_OTERRAIN; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + } + + Square, BoundedRandom 35, 1, 58, 58, 0; + { + Character = veterankamikazedwarf; + } + } + + Square, Random; + { + Character = zombie; + Times = 30; + } + } + + Level CITY_LEVEL; + { + Size = 45, 55; + GenerateMonsters = true; + Rooms = 30; + Items = 20:30; + IsOnGround = false; + TeamDefault = MONSTER_TEAM; + LOSModifier = 16; + IgnoreDefaultSpecialSquares = true; + CanGenerateBone = false; + EnchantmentMinusChanceBase = 0; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 0; + EnchantmentPlusChanceDelta = 0; + BackGroundType = GRAY_FRACTAL; + FillSquare = GRANITE solidterrain(GROUND), 0; + TunnelSquare = GRANITE solidterrain(DARK_GRASS_TERRAIN), 0; + IsCatacomb = false; + DifficultyBase = 0; + DifficultyDelta = 10; + MonsterAmountBase = 0; + MonsterAmountDelta = 5; + MonsterGenerationIntervalBase = 150; + MonsterGenerationIntervalDelta = -25; + + RoomDefault + { + Pos = 2:XSize-5,2:YSize-5; + Size = 5:9,5:9; + WallSquare = solidterrain(GROUND), BLACK_GRANITE wall(BRICK_OLD); + FloorSquare = solidterrain(PARQUET), 0; + DoorSquare = solidterrain(PARQUET), BLACK_GRANITE door; + AltarPossible = true; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = true; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + } + + Room + { + Pos = 2:XSize-5,2:YSize-5; + Size = 5,5; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = ROUND_CORNERS; + AltarPossible = false; + + Square, Pos 2,2; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + } + + Square, Pos 2,1; + { + Items == lantern; + } + } + + Room + { + Pos = 2:XSize-5,2:YSize-5; + Size = 31,23; + WallSquare = GRANITE solidterrain(GROUND), 0; + FloorSquare = GRANITE solidterrain(GROUND), 0; + DoorSquare = GRANITE solidterrain(GROUND), 0; + AltarPossible = false; + GenerateFountains = false; + Shape = RECTANGLE; + + GTerrainMap + { + Pos = 0,0; + Size = 31,23; + Types + { + # = TEAK_WOOD solidterrain(PARQUET) { IsInside = true; } + - = TEAK_WOOD solidterrain(PARQUET) { IsInside = true; } + ~ = FIR_WOOD solidterrain(PARQUET) { IsInside = true; } + @ = FIR_WOOD solidterrain(PARQUET) { IsInside = true; } + } + } + { + ###################....@@@@@@@@ + #-----------------#....@~~~~~~@ + #------#####------#....@~~~~~~@ + #-----##---##-----#....@~~~~~~@ + #-----#-----#-----#....@~~~~~~@ + #-----#-----#-----#....@~~~~~~@ + #-----#-----#-----#....@~~~~~~@ + #-----##---##-----#....@~~~~~~@ + #------#####------#....@~~~~~~@ + #-----------------#....@~~~~~~@ + ###################....@@@@@@@@ + #------#....................... + #------#....................... + #------#....................... + #------#....................... + #------#....................... + ########..............@@@@@@@.. + ......................@~~~~~@.. + ......................@~~~~~@.. + ......................@~~~~~@.. + ......................@~~~~~@.. + ......................@~~~~~@.. + ......................@@@@@@@.. + } +OTerrainMap + { + Pos = 0,0; + Size = 31,23; + + Types + { + # = EBONY_WOOD wall(BRICK_FINE); + - = 0; + | = decoration(CARPET); + D = TEAK_WOOD door(HEXAGONAL_LOCK); + d = FIR_WOOD door(HEXAGONAL_LOCK); + } + } + { + ###################....######## + #-----------------#....#------# + #------##D##------#....#------# + #-----##---##-----#....#------# + #-----#-----#-----#....#------# + #-----D-----D-----D....d------# + #-----#-----#-----#....#------# + #-----##---##-----#....#------# + #------##D##------#....#------# + #-----------------#....#------# + ###DD##############....######## + #------#....................... + #------#....................... + #------#....................... + #------#....................... + #------#....................... + ########..............###dd##.. + ......................#-----#.. + ......................#-----#.. + ......................#-----#.. + ......................#-----#.. + ......................#-----#.. + ......................#######.. + } +CharacterMap + { + Pos = 0,0; + Size = 31,23; + + Types + { + # = 0; + - = 0; + w = wisefarmer; + } + } + { + ###################....######## + #-----------------#....#------# + #------#####------#....#------# + #-----##---##-----#....#------# + #-----#-----#-----#....#------# + #-----#--w--#-----#....#------# + #-----#-----#-----#....#------# + #-----##---##-----#....#------# + #------#####------#....#------# + #-----------------#....#------# + ###################....######## + #------#....................... + #------#....................... + #------#....................... + #------#....................... + #------#....................... + ########..............#######.. + ......................#-----#.. + ......................#-----#.. + ......................#-----#.. + ......................#-----#.. + ......................#-----#.. + ......................#######.. + } + ItemMap + { + Pos = 0,0; + Size = 31, 23; + + Types + { + * == 0; + # == 0; + - == 0; + 1 == lantern { SquarePosition = UP; } + 2 == lantern { SquarePosition = DOWN; } + 3 == lantern { SquarePosition = RIGHT; } + 4 == lantern { SquarePosition = LEFT; } + $ == Random { MinPrice = 1000; MaxPrice = 5000; } + } + } + { + ###################....######## + #-----------------#....#------# + #------#2#2#------#....#------# + #-----##---##-----#....#------# + #-----3-----4-----#....#------# + #-----#--$--#-----#....#------# + #-----3-----4-----#....#------# + #-----##---##-----#....#------# + #------#1#1#------#....#------# + #-----------------#....#------# + ###################....######## + #------#....................... + #------#....................... + #------#....................... + #------#....................... + #------#....................... + ########..............#######.. + ......................#-----#.. + ......................#-----#.. + ......................#-----#.. + ......................#-----#.. + ......................#-----#.. + ......................#######.. + } + Square, Pos 3,13; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + } + } + + Square, Random; + { + Items == GRANITE stone; + Times = 50:100; + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = boulder(1); + Times = 50:100; + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = boulder(2); + Times = 50:100; + } + + Square, Random IN_ROOM|HAS_NO_OTERRAIN; + { + GTerrain = GRANITE solidterrain(GROUND); + Times = 100; + } + + Room + { + Pos = 2:XSize-5,2:YSize-5; + Size = 8,8; + WallSquare = GRANITE solidterrain(GROUND), 0; + FloorSquare = GRANITE solidterrain(GROUND), 0; + DoorSquare = GRANITE solidterrain(GROUND), 0; + AltarPossible = false; + GenerateFountains = false; + Shape = RECTANGLE; + + GTerrainMap + { + Pos = 0,0; + Size = 8,8; + Types + { + # = GRANITE solidterrain(GROUND) { IsInside = true; } + - = FIR_WOOD solidterrain(PARQUET) { IsInside = true; } + ~ = FIR_WOOD solidterrain(PARQUET) { IsInside = true; } + @ = FIR_WOOD solidterrain(PARQUET) { IsInside = true; } + } + } + { + ######## + #------. + #------. + #-----.. + #----... + #----... + #--..... + #-...... + } +OTerrainMap + { + Pos = 0,0; + Size = 8,8; + + Types + { + # = EBONY_WOOD wall(BRICK_FINE); + - = 0; + | = decoration(CARPET); + D = TEAK_WOOD door(HEXAGONAL_LOCK); + d = FIR_WOOD door(HEXAGONAL_LOCK); + } + } + { + #####--- + #------. + #------. + #-----.. + #----... + -----... + ---..... + --...... + } +CharacterMap + { + Pos = 0,0; + Size = 8,8; + + Types + { + # = 0; + - = 0; + } + } + { + ######## + #------. + #------. + #-----.. + #----... + #----... + #--..... + #-...... + } + ItemMap + { + Pos = 0,0; + Size = 8, 8; + + Types + { + * == 0; + # == 0; + - == 0; + 1 == lantern { SquarePosition = UP; } + 2 == lantern { SquarePosition = DOWN; } + 3 == lantern { SquarePosition = RIGHT; } + 4 == lantern { SquarePosition = LEFT; } + 5 == lantern; + / == EBONY_WOOD stick; + } + } + { + #####/// + #//----. + #/-----. + #-----.. + #----... + /----... + /--..... + /-...... + } + } + + Room + { + Pos = 2:XSize-5,2:YSize-5; + Size = 7,6; + WallSquare = solidterrain(GROUND),wall(BRICK_OLD); + FloorSquare = solidterrain(PARQUET), 0; + GenerateDoor = false; + Type = ROOM_NORMAL; + GenerateLanterns = false; + GenerateWindows = false; + AltarPossible = false; + + Square, Pos 3, 5; + { + GTerrain = solidterrain(PARQUET); + OTerrain = door; + } + + + + Square, Pos 3, 0; + { + Items == lantern { SquarePosition = DOWN; } + } + + Square, Pos 0, 5; + { + Items == lantern { SquarePosition = DOWN; } + } + + Square, Pos 6, 5; + { + Items == lantern { SquarePosition = DOWN; } + } + + OTerrainMap + { + Pos = 1,1; + Size = 5,4; + Types + { + = = olterraincontainer(BOOK_CASE) { ItemsInside == Random { MaxPrice = 500; Category = SCROLL|BOOK; } } + } + } + { + ===== + =...= + ==.== + =...= + } + } + + } + + RandomLevel 1:4; + { + Room /* vault */ + { + GenerateLanterns = false; + GenerateFountains = false; + GenerateDoor = false; + GenerateTunnel = false; + AltarPossible = false; + Size = 7,7; + UseFillSquareWalls = true; + Flags = NO_MONSTER_GENERATION; + + OTerrainMap + { + Pos = 1,1; + Size = 5,5; + Types + { + x = IRON wall(BRICK_OLD); + } + } + { + xxxxx + x...x + x...x + x...x + xxxxx + } + + Square, Random; + { + Items == mine { Team = MONSTER_TEAM; IsActive = true; Chance = 50; } + } + + Square, Random; + { + Items == mine(BIG_MINE) { Team = MONSTER_TEAM; IsActive = true; Chance = 50; } + } + + Square, Random; + { + Items == MITHRIL beartrap { Team = MONSTER_TEAM; IsActive = true; Chance = 50; } + Times = 2; + } + + Square, Pos 3,3; + { + Items == METEORIC_STEEL itemcontainer(CHEST|OCTAGONAL_LOCK) + { + Parameters = LOCKED; + ItemsInside = { 2, Random { MinPrice = 1250; Times = 2; }, + stone { Chance = 75; Times = 3; } } + } + } + } + } + + Level FUSANGA_LEVEL; +{ +Size = 60, 60; +Rooms = 100; +IgnoreDefaultSpecialSquares = true; + + Square, Pos 1, 1; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + AttachRequired = true; + } + + Square, Pos 58, 58; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + AttachRequired = true; + } + + Square, Random; + { + Character = magicmushroom { Team = MONSTER_TEAM; } + Times = 10; + } + + RoomDefault + { + AltarPossible = false; + Pos = 2:XSize-5,2:YSize-5; + Size = 3:3,3:3; + AltarPossible = false; + WallSquare = solidterrain(GROUND), 0; + FloorSquare = solidterrain(GROUND), 0; + DoorSquare = solidterrain(GROUND), 0; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = true; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = true; + AllowLockedDoors = true; + AllowBoobyTrappedDoors = true; + Shape = ROUND_CORNERS; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + } + Room + { + Size = 4,4; + Pos = 30,30; + GenerateFountains = false; + AltarPossible = false; + GenerateLanterns = false; + Shape = ROUND_CORNERS; + + CharacterMap + { + Size = 4,4; + Pos = 0,0; + Types + { + M = magicmushroom; + m = menatrixfusanga; + } + } + { + .MM. + Mm.M + M..M + .MM. + } + } +} + + Level 8; + { + IgnoreDefaultSpecialSquares = true; + FillSquare = solidterrain(GROUND), QUARTZITE earth; + + RoomDefault + { + WallSquare = solidterrain(GROUND), COPPER wall(BRICK_OLD); + FloorSquare = solidterrain(PARQUET), 0; + DoorSquare = solidterrain(PARQUET), COPPER door; + } + + Square, Random NOT_WALKABLE|NOT_IN_ROOM; + { + Items == stone; + Times = 25:50; + } + + Square, Random; + { + Items == mine { Team = MONSTER_TEAM; IsActive = true; } + Times = 0:3; + } + + Square, Random; + { + Items == mine(BIG_MINE) { Team = MONSTER_TEAM; IsActive = true; Chance = 50; } + } + + Square, Random; + { + Items == METEORIC_STEEL beartrap { Team = MONSTER_TEAM; IsActive = true; } + Times = 2:6; + } + + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + AttachRequired = true; + } + + Room + { + Size = 7,7; + AltarPossible = false; + GenerateFountains = false; + GenerateDoor = false; + GenerateLanterns = false; + WallSquare = solidterrain(GROUND),IRON wall(BRICK_OLD); + FloorSquare = IRON solidterrain(PARQUET), 0; + DoorSquare = IRON solidterrain(PARQUET),IRON door { Parameters = LOCKED; } + + Square, Pos 3,6; + { + GTerrain = solidterrain(PARQUET); + OTerrain = IRON door { Parameters = LOCKED; } + AttachRequired = true; + } + + Square, Pos 3,3; + { + Character = mysticfrog(DARK); + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + } + + Square, Random; + { + Character = frog(GREATER_DARK); + Times = 2; + } + + Square, Random; + { + Character = frog(DARK); + Times = 4; + } + } + } + + Level IVAN_LEVEL; + { + LevelMessage = "You hear someone singing loudly: \"Uncle Lenin lives in Russia...\""; + FillSquare = solidterrain(GROUND), QUARTZITE earth; + /*EnterImage = "ivlad.pcx"; + EnterTextDisplacement = 264, -64;*/ + + RoomDefault + { + WallSquare = solidterrain(GROUND), BRONZE wall(BRICK_OLD); + FloorSquare = solidterrain(PARQUET), 0; + DoorSquare = solidterrain(PARQUET), BRONZE door; + } + + Square, Random; + { + Items == mine(BIG_MINE) { Team = MONSTER_TEAM; IsActive = true; } + Times = 0:2; + } + + Square, Random NOT_IN_ROOM; + { + Character = communist { Team = IVAN_TEAM; Flags = IS_LEADER; } + } + + Square, Random; + { + Character = vladimir { Team = IVAN_TEAM; } + } + } + + RandomLevel 6:8; + { + Room /* vault */ + { + GenerateLanterns = false; + GenerateFountains = false; + GenerateDoor = false; + GenerateTunnel = false; + AltarPossible = false; + Size = 7,7; + UseFillSquareWalls = true; + Flags = NO_MONSTER_GENERATION; + + OTerrainMap + { + Pos = 1,1; + Size = 5,5; + Types + { + x = STEEL wall(BRICK_OLD); + } + } + { + xxxxx + x...x + x...x + x...x + xxxxx + } + + Square, Random; + { + Items == mine { Team = MONSTER_TEAM; IsActive = true; } + } + + Square, Random; + { + Items == mine(BIG_MINE) { Team = MONSTER_TEAM; IsActive = true; Chance = 75; } + } + + Square, Random; + { + Items == OCTIRON beartrap { Team = MONSTER_TEAM; IsActive = true; Chance = 50; } + Times = 4; + } + + Square, Pos 3,3; + { + Items == ADAMANT itemcontainer(CHEST|OCTAGONAL_LOCK) + { + Parameters = LOCKED; + ItemsInside == Random { MinPrice = 2000; Times = 2; } + } + } + } + } + + Level DARK_LEVEL; + { + Description = "dark level"; + ShortDescription = "DarkLevel"; + LevelMessage = "You shudder as you sense a being of pure darkness nearby. Your goal is near."; + FillSquare = STEEL solidterrain(FLOOR), STEEL wall(BRICK_OLD); + TunnelSquare = STEEL solidterrain(FLOOR), 0; + Items = 0; + IgnoreDefaultSpecialSquares = true; + + Square, Random; + { + Items == mine(BIG_MINE) { Team = MONSTER_TEAM; IsActive = true; } + Times = 1:3; + } + + RoomDefault + { + AltarPossible = false; + FloorSquare = STEEL solidterrain(PARQUET), 0; + WallSquare = STEEL solidterrain(FLOOR), STEEL wall(BRICK_OLD); + DoorSquare = STEEL solidterrain(PARQUET), STEEL door; + GenerateLanterns = false; + GenerateFountains = false; + } + + Room + { + Size = 11, 11; + DivineMaster = SCABIES; + + Square, Pos 5, 5; + { + OTerrain = portal(DARK_LEVEL) { AttachedArea = DARK_LEVEL + 1; AttachedEntry = STAIRS_UP; } + Character = elpuri; + } + + Square, Random; + { + Character = frog(DARK); + Times = 8; + } + + Square, Random; + { + Character = frog(GREATER_DARK); + Times = 4; + } + + Square, Random; + { + Character = frog(GIANT_DARK); + Times = 2; + } + + Square, Random; + { + Character = mysticfrog(DARK); + } + + Square, Random; + { + Items == HUMAN_FLESH lump; + Times = 5; + } + + Square, Random; + { + Items == bone; + Times = 10; + } + + Square, Random; + { + Items == skull; + Times = 5; + } + } + + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + AttachRequired = true; + } + } + + RandomLevel 9:11; + { + Room + { + Size = 7,7; + AltarPossible = false; + Shape = ROUND_CORNERS; + + CharacterMap + { + Pos = 1,1; + Size = 5,5; + + Types + { + G = golem; + V = golem(VALPURIUM) { Inventory = { 2, scrollofgolemcreation, scrollofgolemcreation { Chance = 10; Times = 4; } } } + } + } + { + .GGG. + GGGGG + GGVGG + GGGGG + .GGG. + } + } + } + + RandomLevel 9:12; + { + Room + { + Size = 9,7; + AltarPossible = false; + GenerateFountains = false; + Shape = ROUND_CORNERS; + + CharacterMap + { + Pos = 1,1; + Size = 7,5; + + Types + { + G = orc(GENERAL) { Inventory = { 2, Random { MinPrice = 100; Category = POTION; Times = 4; }, Random { MinPrice = 200; Category = POTION; Times = 2; } } } + O = orc(OFFICER) { Inventory == Random { MinPrice = 100; Category = POTION; Times = 2; } } + S = orc(SQUAD_LEADER); + s = orc(SLAUGHTERER); + o = orc; + } + } + { + .ooooo. + osSOSso + ossGsso + osSOSso + .ooooo. + } + } + } + + RandomLevel 12:13; + { + Room + { + Size = 5,12; + AltarPossible = false; + GenerateFountains = false; + GenerateDoor = false; + GenerateTunnel = false; + FloorSquare = ARCANITE solidterrain(PARQUET), 0; + WallSquare = ARCANITE solidterrain(FLOOR), OCTIRON wall(BRICK_OLD); + + CharacterMap + { + Pos = 1,1; + Size = 3,10; + + Types + { + E = darkmage(ELDER) { Inventory == Random { MinPrice = 750; Category = WAND|SCROLL; } } + B = darkmage(BATTLE_MAGE); + A = darkmage(APPRENTICE); + M = mysticfrog(DARK); + N = necromancer(MASTER_NECROMANCER); + n = necromancer(APPRENTICE_NECROMANCER); + a = golem(ARCANITE); + O = golem(OCTIRON); + } + } + { + aOa + nnn + AAA + BMB + NEN + NEN + BMB + AAA + nnn + aOa + } + + Square, Pos 2, 0; + { + GTerrain = ARCANITE solidterrain(PARQUET); + OTerrain = OCTIRON door(OCTAGONAL_LOCK) { Parameters = LOCKED; } + AttachRequired = true; + } + + Square, Pos 2, 11; + { + GTerrain = ARCANITE solidterrain(PARQUET); + OTerrain = OCTIRON door(OCTAGONAL_LOCK) { Parameters = LOCKED; } + AttachRequired = true; + } + } + } + + Level 11; + { + Size = 20, 100; + IgnoreDefaultSpecialSquares = true; + DifficultyBase = 60; + MonsterAmountBase = 12; + ItemMinPriceBase = 30; + Rooms = 20:40; + FillSquare = MILKY_QUARTZ solidterrain(GROUND), ROCK_CRYSTAL earth; + TunnelSquare = MILKY_QUARTZ solidterrain(GROUND), 0; + BackGroundType = RED_FRACTAL; + + RoomDefault + { + Pos = 2:XSize-5,2:YSize-5; + Size = 3:5,3:5; + WallSquare = ROSE_QUARTZ solidterrain(GROUND), ROSE_QUARTZ wall(BRICK_OLD); + FloorSquare = ROSE_QUARTZ solidterrain(GROUND), 0; + DoorSquare = ROSE_QUARTZ solidterrain(GROUND), ROSE_QUARTZ door; + } + + Room + { + Pos = 2:XSize-5, 2:YSize/4-5; + + Square, Random; + { + EntryIndex = STAIRS_UP; + } + } + + Square, Random; + { + Items == mine(BIG_MINE) { Team = MONSTER_TEAM; IsActive = true; } + Times = 2:6; + } + + Square, Random; + { + Items == MITHRIL beartrap { Team = MONSTER_TEAM; IsActive = true; } + Times = 2:6; + } + + Square, BoundedRandom 1, YSize * 3 / 4, XSize - 2, YSize - 2, NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + AttachRequired = true; + } + } + + Level 12; + { + Size = 20, 100; + IgnoreDefaultSpecialSquares = true; + DifficultyBase = 70; + MonsterAmountBase = 14; + ItemMinPriceBase = 40; + Rooms = 20:40; + Items = 35:70; + FillSquare = MILKY_QUARTZ solidterrain(GROUND), ROCK_CRYSTAL earth; + TunnelSquare = MILKY_QUARTZ solidterrain(GROUND), 0; + BackGroundType = BLUE_FRACTAL; + + RoomDefault + { + Pos = 2:XSize-5,2:YSize-5; + Size = 3:5,3:5; + WallSquare = AMETHYST solidterrain(GROUND), AMETHYST wall(BRICK_OLD); + FloorSquare = AMETHYST solidterrain(GROUND), 0; + DoorSquare = AMETHYST solidterrain(GROUND), AMETHYST door; + } + + Square, Random; + { + Items == mine(BIG_MINE) { Team = MONSTER_TEAM; IsActive = true; } + Times = 2:6; + } + + Square, Random; + { + Items == MITHRIL beartrap { Team = MONSTER_TEAM; IsActive = true; } + Times = 2:6; + } + + Square, BoundedRandom 1, YSize * 3 / 4, XSize - 2, YSize - 2, NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + AttachRequired = true; + } + + Square, BoundedRandom 1, 1, XSize - 2, YSize/4 - 2, NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + AttachRequired = true; + } + } + + Level 13; + { + Size = 64,36; + IgnoreDefaultSpecialSquares = true; + DifficultyBase = 80; + MonsterAmountBase = 50; + MonsterAmountDelta = 0; + ItemMinPriceBase = 50; + Items = 45:90; + FillSquare = ARCANITE solidterrain(GROUND), ARCANITE earth; + TunnelSquare = ARCANITE solidterrain(GROUND), 0; + BackGroundType = YELLOW_FRACTAL; + + RoomDefault + { + Pos = 2:XSize-5,2:YSize-5; + Size = 6:20,6:20; + WallSquare = ARCANITE solidterrain(GROUND), OCTIRON wall(BRICK_OLD); + FloorSquare = ARCANITE solidterrain(PARQUET), 0; + DoorSquare = ARCANITE solidterrain(PARQUET), OCTIRON door; + } + + Square, Random; + { + Items == mine(BIG_MINE) { Team = MONSTER_TEAM; IsActive = true; } + Times = 2:6; + } + + Square, Random; + { + Items == MITHRIL beartrap { Team = MONSTER_TEAM; IsActive = true; } + Times = 2:6; + } + + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + AttachRequired = true; + } + + Room + { + Size = 21,21; + GenerateFountains = false; + AltarPossible = false; + + Square, Pos 10,10; + { + OTerrain = portal(OREE_LAIR_ENTRY) { AttachedArea = OREE_LAIR; AttachedEntry = STAIRS_UP; } + } + + Square, Pos 10,10; + { + EntryIndex = STAIRS_DOWN; + } + } + + Square, Random; + { + Items == pickaxe; + } + + Room /* vault */ + { + GenerateLanterns = false; + GenerateFountains = false; + GenerateDoor = false; + GenerateTunnel = false; + AltarPossible = false; + Size = 7,7; + UseFillSquareWalls = true; + Flags = NO_MONSTER_GENERATION; + + OTerrainMap + { + Pos = 1,1; + Size = 5,5; + Types + { + x = ADAMANT wall(BRICK_OLD); + } + } + { + xxxxx + x...x + x...x + x...x + xxxxx + } + + Square, Random; + { + Items == mine(BIG_MINE) { Team = MONSTER_TEAM; IsActive = true; Chance = 75; } + Times = 3; + } + + Square, Random; + { + Items == ADAMANT beartrap { Team = MONSTER_TEAM; IsActive = true; Chance = 75; } + Times = 4; + } + + Square, Pos 3,3; + { + Items == ADAMANT itemcontainer(CHEST|OCTAGONAL_LOCK) + { + Parameters = LOCKED; + ItemsInside == Random { MinPrice = 3000; Times = 2; } + } + } + } + } + + Level OREE_LAIR; + { + Description = "Oree's lair"; + ShortDescription = "OreeLair"; + LevelMessage = "\"Welcome to my lair, mortal! There's no escape now!\""; + FillSquare = BLOOD liquidterrain(UNDERGROUND_LAKE), 0; + TunnelSquare = solidterrain(GROUND), 0; + Rooms = 5; + GenerateMonsters = false; + Items = 0; + IgnoreDefaultSpecialSquares = true; + CanGenerateBone = false; + DifficultyBase = 90; + BackGroundType = RED_FRACTAL; + + RoomDefault + { + Pos = 2:XSize-5,2:YSize-5; + Size = 5:7,5:7; + WallSquare = RUBY solidterrain(PARQUET), RUBY wall(BRICK_FINE); + FloorSquare = RUBY solidterrain(PARQUET), 0; + DoorSquare = RUBY solidterrain(PARQUET), RUBY door(OCTAGONAL_LOCK); + Shape = ROUND_CORNERS; + AltarPossible = false; + DivineMaster = CRUENTUS; + GenerateFountains = false; + GenerateLanterns = false; + AllowLockedDoors = false; + IsInside = false; + } + + Room + { + Size = 9,9; + GenerateDoor = false; + + Square, Pos 4,0; + { + OTerrain = RUBY door(OCTAGONAL_LOCK) { Parameters = LOCKED; } + AttachRequired = true; + } + + Square, Pos 4,4; + { + EntryIndex = STAIRS_UP; + } + + CharacterMap + { + Pos = 1,1; + Size = 7,7; + + Types + { + B = darkknight(ELITE); + G = golem(ACIDOUS_BLOOD); + } + } + { + ..G.G.. + ....... + G..B..G + ..B.B.. + G..B..G + ....... + ..G.G.. + } + } + + Room + { + Size = 9,9; + DivineMaster = CRUENTUS; + + Square, Pos 4,1; + { + OTerrain = STEEL altar(CRUENTUS); + } + + Square, Pos 1,4; + { + OTerrain = portal(OREE_LAIR_EXIT) { AttachedArea = 0; AttachedEntry = RANDOM; } + } + + Square, Pos 7,4; + { + OTerrain = monsterportal; + } + + CharacterMap + { + Pos = 1,1; + Size = 7,7; + + Types + { + O = oree; + A = angel(CRUENTUS); + } + } + { + ..O.A.. + ....... + ....... + ....... + ....... + ....... + ....... + } + } + + Square, Random NOT_IN_ROOM; + { + Character = darkknight(ELITE); + Times = 4; + } + + Square, Random NOT_IN_ROOM; + { + Character = frog(GIANT_DARK); + Times = 2; + } + + Square, Random NOT_IN_ROOM; + { + Character = mysticfrog(DARK); + Times = 1; + } + + Square, Random NOT_IN_ROOM; + { + Character = darkmage(APPRENTICE); + Times = 4; + } + + Square, Random NOT_IN_ROOM; + { + Character = darkmage(BATTLE_MAGE); + Times = 2; + } + + Square, Random NOT_IN_ROOM; + { + Character = darkmage(ELDER); + Times = 1; + } + } + +Level KHARAZ_ARAD; + { + Description = "Kharaz-Arad"; + ShortDescription = "KharazArad"; + TeamDefault = KHARAZ_ARAD_TEAM; + FillSquare = solidterrain(GROUND), MORAINE earth; + TunnelSquare = GRAVEL solidterrain(GROUND), 0; + Size = 70, 70; + Rooms = 25; + GenerateMonsters = false; + Items = 0; + IgnoreDefaultSpecialSquares = true; + CanGenerateBone = false; + DifficultyBase = 90; + + RoomDefault + { + Pos = 2:XSize-5,2:YSize-5; + Size = 5:9,5:9; + AltarPossible = false; + WallSquare = solidterrain(GROUND), GRANITE wall(BRICK_FINE); + FloorSquare = solidterrain(PARQUET), 0; + DoorSquare = solidterrain(PARQUET), GRANITE door; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = true; + GenerateLanterns = true; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + + Square, Random; + { + Items == Random { MinPrice = 1; MaxPrice = 2000; } + Times = 0:1; + } + + Square, Random; + { + Items == Random { MinPrice = 1; MaxPrice = 1000; } + Times = 0:1; + } +} + + Room + { + Pos = 2:XSize-5,2:YSize-5; + Size = 6:10,6:10; + AltarPossible = false; + WallSquare = GRAVEL solidterrain(GROUND), MORAINE earth; + FloorSquare = GRAVEL solidterrain(GROUND), 0; + DoorSquare = GRAVEL solidterrain(GROUND), 0; + GenerateDoor = false; + DivineMaster = 0; + GenerateTunnel = true; + GenerateLanterns = true; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = ROUND_CORNERS; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + + + Square, Random; + { + Items == stone; + Times = 1:2; + } + + Square, Random; + { + Items == solstone; + Times = 1:2; + } +} + + Room + { + Size = 11,11; + AllowLockedDoors = true; + AllowBoobyTrappedDoors = true; + GenerateFountains = false; + Shape = ROUND_CORNERS; + WallSquare = GRAVEL solidterrain(GROUND), MORAINE earth; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + GenerateDoor = false; + GenerateLanterns = false; + AltarPossible = false; + + + + GTerrainMap + { + Pos = 3,3; + Size = 5,5; + Types + { + # = WATER liquidterrain(UNDERGROUND_LAKE); + } + } + { + ..#.. + .#.#. + #...# + .#.#. + ..#.. + } + + Square, Pos 5,5; + { + OTerrain = stairs(STAIRS_DOWN) { AttachedArea = PLANT_ENTRY_LEVEL; } + EntryIndex = STAIRS_DOWN; + } + + Square, Pos 5,0; + { + OTerrain = 0; + AttachRequired = true; + } + + } + + Room + { + Size = 7,6; + GenerateFountains = false; + AltarPossible = false; + Shape = ROUND_CORNERS; + + CharacterMap + { + Pos = 1,1; + Size = 5,4; + + Types + { + g = guard(ELITE) { Team = 5; } + } + } + { + ..... + ..... + ..... + ..... + } + Square, Pos 3,2; + { + OTerrain = stairs(STAIRS_UP) { AttachedArea = 3; AttachedEntry = STAIRS_DOWN + 1; } + EntryIndex = STAIRS_UP; + } + } + + Room + { + Size = 9,9; + AltarPossible = false; + DivineMaster = MELLIS; + Type = ROOM_SHOP; + GenerateFountains = false; + Shape = RECTANGLE; + GenerateLanterns = false; + + CharacterMap + { + Pos = 1, 1; + Size = 7, 7; + + Types + { + g = guard(SHOP) { Team = 5; } + s = shopkeeper(KHARAZ_ARAD_SHOP) { Team = 5; Flags = IS_MASTER; } + } + } + { + g.....g + ....... + ....... + ...s... + ....... + ....... + g.....g + } + + ItemMap + { + Pos = 1,1; + Size = 7,7; + + Types + { + g == Random { MinPrice = 100; MaxPrice = 10000; Category = GAUNTLET|BOOT; } + b == Random { MinPrice = 500; MaxPrice = 10000; Category = GAUNTLET|BOOT; } + a == Random { MinPrice = 200; MaxPrice = 10000; Category = HELMET|CLOAK|BODY_ARMOR|BELT; } + d == Random { MinPrice = 100; MaxPrice = 10000; Category = RING|AMULET; } + w == Random { MinPrice = 100; MaxPrice = 10000; Category = WEAPON|SHIELD; } + e == Random { MinPrice = 100; MaxPrice = 10000; Category = FOOD|POTION; } + u == Random { MinPrice = 100; MaxPrice = 10000; Category = WAND|TOOL; } + r == Random { MinPrice = 100; MaxPrice = 10000; Category = SCROLL|BOOK; } + R == Random { MinPrice = 300; MaxPrice = 10000; Category = WEAPON; } + W == Random { MinPrice = 750; MaxPrice = 10000; Category = WEAPON; } + 1 == lantern { SquarePosition = UP; } + 2 == lantern { SquarePosition = DOWN; } + 3 == lantern { SquarePosition = RIGHT; } + 4 == lantern { SquarePosition = LEFT; } + } + } + { + 2WRRgg2 + ddddbbb + errrrrr + eeeeeww + aaauuww + aaauuww + 1aauuu1 + } + } + + Room + { + Type = ROOM_VAULT; + Size = 37,20; + AltarPossible = false; + GenerateFountains = false; + Shape = RECTANGLE; + GTerrainMap + { + Pos = 2,2; + Size = 33,16; + Types + { + # = STEEL solidterrain(FLOOR) { IsInside = true; } + - = STEEL solidterrain(FLOOR) { IsInside = true; } + } + } + { + ################################# + #-----#--#-------------#--#-----# + #-----#--#-------------#--#-----# + #-----#--#-------------#--#-----# + #-----#--#-------------#--#-----# + #-----#--#-------------#--#-----# + #-----#--#-------------#--#-----# + #######--###############--####### + #-----#-------------------#-----# + #-----#-------------------#-----# + #-----#####################-----# + #-----#-------------------#-----# + #-----#-------------------#-----# + #-----#-------------------#-----# + #-----#-------------------#-----# + ################################# + } + +OTerrainMap + { + Pos = 2,2; + Size = 33,16; + + Types + { + # = STEEL wall(BRICK_FINE); + - = 0; + T = throne; + | = decoration(CARPET); + A = ADAMANT altar(LORICATUS); + D = STEEL door(HEXAGONAL_LOCK); + } + } + { + ################################# + #-----D--#------T------#--D-----# + #-----#--#------|------#--#-----# + #-----#--#------|------#--#-----# + #-----#--#------|------#--#-----# + #-----#--#------|------#--#-----# + #-----#--#------|------#--#-----# + #######--#######D#######--####### + #-----D---------|---------D-----# + #-----#---------|---------#-----# + #-----##########D##########-----# + #-----#-------------------#-----# + #-----#-------------------#-----# + #-----#-------------------#-----# + #-----#-------------------#-----# + ################D################ + } + ItemMap + { + Pos = 2,2; + Size = 33,16; + Types + { + # == 0; + - == 0; + S == goldenjaguarshirt; + P == phoenixshield; + 1 == lantern { SquarePosition = UP; } + 2 == lantern { SquarePosition = DOWN; } + 3 == lantern { SquarePosition = RIGHT; } + 4 == lantern { SquarePosition = LEFT; } + b == backpack; + } + } + { + ########2#####2###2#####2######## + 3-----#--#-------------#--#-----4 + #-----4--#-------------#--3-----# + #-----#--#-------------#--#-----# + #--S--#--3-------------4--#--P--# + #-----#--#-------------#--#-----# + 3-----4--#-------------#--3-----4 + #######--######1#1######--####### + 3-----#-------------------#-----4 + #b---b3-------------------4-----# + #b---b#########1#1#########-----# + #b---b#-------------------#-----# + #b---b3-------------------4-----# + #b---b#-------------------#-----# + 3-----4-------------------3-----4 + ###############1#1############### + } + CharacterMap + { + Pos = 2,2; + Size = 33,16; + Types + { + # = 0; + - = 0; + g = guard(DWARVEN_GUARD); + D = denim; + E = guard(ENQUIOX); + K = kamikazedwarf(LORICATUS); + } + } + { + ################################# + #g----#-g#g-----DE----g#g-#----g# + #-----#--#-------------#--#-----# + #-----#--#-----g-g-----#--#-----# + #-----#--#-------------#--#-----# + #-----#--#-----g-g-----#--#-----# + #g---g#--#-------------#--#g---g# + #######--###############--####### + #-----#-------------------#-----# + #-----#-------------------#-----# + #-----#####################-----# + #-----#-------------------#-----# + #-----#-------------------#-----# + #-----#-------------------#-----# + #KKKKK#-------------------#-----# + ################################# + } + } + + Room + { + Size = 7,7; + AltarPossible = false; + GenerateFountains = false; + Shape = ROUND_CORNERS; + GenerateLanterns = false; + + Square, Pos 3, 2; + { + Character = smith(KHARAZ_ARAD) { Flags = IS_MASTER; } + } + + Square, Pos 3, 3; + { + OTerrain = decoration(ANVIL); + } + Square, Pos 1, 1; + { + Items == lantern { SquarePosition = RIGHT; } + } + + Square, Pos 5, 1; + { + Items == lantern { SquarePosition = LEFT; } + } + + Square, Pos 5, 1; + { + Items == lantern { SquarePosition = DOWN; } + } + + Square, Pos 1, 1; + { + Items == lantern { SquarePosition = DOWN; } + } + + Square, Pos 1, 5; + { + Items == lantern { SquarePosition = UP; } + } + + Square, Pos 5, 5; + { + Items == lantern { SquarePosition = UP; } + } + + Square, Pos 1, 5; + { + Items == lantern { SquarePosition = RIGHT; } + } + + Square, Pos 5, 5; + { + Items == lantern { SquarePosition = LEFT; } + } + } +} + +Level PLANT_ENTRY_LEVEL; + { + EnchantmentMinusChanceBase = 9; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 7; + EnchantmentPlusChanceDelta = 0; + IgnoreDefaultSpecialSquares = true; + + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_UP) { AttachedArea = KHARAZ_ARAD; } + EntryIndex = STAIRS_UP; + AttachRequired = true; + } + + Room + { + Size = 11,11; + AllowLockedDoors = true; + AllowBoobyTrappedDoors = true; + GenerateFountains = false; + Shape = ROUND_CORNERS; + WallSquare = WATER liquidterrain(UNDERGROUND_LAKE), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + GenerateDoor = false; + GenerateLanterns = false; + AltarPossible = false; + + GTerrainMap + { + Pos = 3,3; + Size = 5,5; + Types + { + # = WATER liquidterrain(UNDERGROUND_LAKE); + } + } + { + ..#.. + .###. + ##.## + .#.#. + ..... + } + + Square, Pos 5,5; + { + OTerrain = stairs(STAIRS_DOWN) { AttachedArea = PLANT_LEVEL; } + EntryIndex = PLANT_ENTRY_LEVEL; + } + + Square, Pos 5,0; + { + OTerrain = 0; + AttachRequired = true; + } + } + } + + Level PLANT_LEVEL; + { + FillSquare = solidterrain(GROUND), SLATE earth; + TunnelSquare = solidterrain(GRASS_TERRAIN), 0; + Size = 48, 48; + Rooms = 15:20; + Items = 35:40; + GenerateMonsters = false; + IgnoreDefaultSpecialSquares = true; + CanGenerateBone = true; + EnchantmentMinusChanceBase = 8; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 8; + EnchantmentPlusChanceDelta = 0; + + Square, Random; + { + Character = carnivorousplant; + Times = 10; + } + + Square, Random; + { + Character = carnivorousplant(GREATER); + Times = 12; + } + + Square, Random; + { + Character = carnivorousplant(GIANT); + Times = 14; + } + + Square, Random; + { + Character = carnivorousplant(SHAMBLING); + Times = 16; + } + + RoomDefault + { + Pos = 2:XSize-5,2:YSize-5; + Size = 4:11,4:11; + AltarPossible = false; + WallSquare = solidterrain(GRASS_TERRAIN), 0; + FloorSquare = WATER liquidterrain(UNDERGROUND_LAKE), 0; + DoorSquare = solidterrain(GRASS_TERRAIN), 0; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = true; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + Shape = ROUND_CORNERS; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + + Square, Random NOT_WALKABLE; + { + Character = carnivorousplant(LILY); + Times = 1:3; + } + } + + Room + { + Pos = 2:XSize-6, 2:YSize-6; + Size = 5,5; + AltarPossible = false; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(GRASS_TERRAIN), 0; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = true; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + Shape = ROUND_CORNERS; + Square, Pos 2,2; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + } + } + + Room + { + Pos = 2:XSize-6, 2:YSize-6; + Size = 5,5; + AltarPossible = false; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = true; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + Shape = ROUND_CORNERS; + Square, Pos 2,2; + { + OTerrain = stairs(STAIRS_UP) { AttachedArea = PLANT_ENTRY_LEVEL; AttachedEntry = PLANT_ENTRY_LEVEL; } + EntryIndex = STAIRS_UP; + } + } + + } + + Level GENEFOURX_LAIR; + { + Description = "Genefourx Versana's Lair"; + ShortDescription = "GenefourxLair"; + LevelMessage = "You feel a fairly big evil rooted here."; + FillSquare = WATER liquidterrain(UNDERGROUND_LAKE), 0; + TunnelSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + Size = 46, 36; + Rooms = 25:30; + Items = 35:40; + GenerateMonsters = false; + IgnoreDefaultSpecialSquares = true; + BackGroundType = GRAY_FRACTAL; + EnchantmentMinusChanceBase = 7; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 8; + EnchantmentPlusChanceDelta = 0; + + Square, Random NOT_WALKABLE; + { + Character = carnivorousplant(LILY); + Times = 14; + } + + Square, Random; + { + Character = carnivorousplant(GREATER); + Times = 8; + } + + Square, Random; + { + Character = carnivorousplant(GIANT); + Times = 14; + } + + Square, Random; + { + Character = carnivorousplant(SHAMBLING); + Times = 20; + } + + RoomDefault + { + Pos = 2:XSize-8,2:YSize-8; + Size = 3:7,3:7; + AltarPossible = false; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = true; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = true; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Shape = ROUND_CORNERS; + Flags = 0; + } + + Room + { + Pos = 2:XSize-6, 2:YSize-6; + Size = 5,5; + AltarPossible = false; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = true; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + Shape = ROUND_CORNERS; + Square, Pos 2,2; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + } + } + + Room + { + Size = 8,8; + Pos = 2:XSize-9,2:YSize-9; + FloorSquare = solidterrain(DEAD_GRASS_TERRAIN), 0; + WallSquare = solidterrain(DEAD_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DEAD_GRASS_TERRAIN), 0; + GenerateFountains = false; + AltarPossible = false; + GenerateLanterns = false; + GenerateDoor = false; + + Square, Pos 4,4; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + } + + GTerrainMap + { + Pos = 1,1; + Size = 6,6; + Types + { + # = POISON_LIQUID liquidterrain(UNDERGROUND_LAKE); + } + } + { + .#..#. + #....# + ...... + ...... + #....# + .#..#. + } + + CharacterMap + { + Size = 8,8; + Pos = 0,0; + Types + { + c = carnivorousplant(GIANT); + g = genefourxvesana; + s = carnivorousplant(SHAMBLING); + } + } + { + ...ss... + .s....s. + ...cc... + s.cg.c.s + s.c..c.s + ...cc... + .s....s. + ...ss... + } + + + } + } + + Level POND_LEVEL; + { + Description = "Tranquil pond"; + ShortDescription = "TranquilPond"; + LevelMessage = "You find yourself in a calm lagoon."; + FillSquare = solidterrain(GRASS_TERRAIN), 0; + TunnelSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + Size = 18, 18; + Rooms = 10:15; + Items = 5; + GenerateMonsters = false; + IgnoreDefaultSpecialSquares = true; + BackGroundType = GRAY_FRACTAL; + DifficultyBase = 70; + EnchantmentMinusChanceBase = 2; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 8; + EnchantmentPlusChanceDelta = 0; + ItemMinPriceBase = 150; + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = fountain; + Times = 1:4; + } + + Square, Pos 8, 8; + { + OTerrain = fountain; + Times = 1; + } + + RoomDefault + { + /* Inperceptable - for paths */ + Pos = 2:XSize-4,2:YSize-4; + Size = 3,3; + AltarPossible = false; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = WATER liquidterrain(UNDERGROUND_LAKE), 0; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = true; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = true; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + } + + Square, Random; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + } + + Room + { + /* Massive pond */ + Pos = XSize/4,YSize/4; + Size = XSize/2,YSize/2; + WallSquare = WATER liquidterrain(UNDERGROUND_LAKE), 0; + FloorSquare = WATER liquidterrain(UNDERGROUND_LAKE), 0; + DoorSquare = WATER liquidterrain(UNDERGROUND_LAKE), 0; + Shape = ROUND_CORNERS; + } + } + +} + +Dungeon ATTNAM; +{ + Levels = 5; + Description = "Attnamese catacombs"; + ShortDescription = "AC"; + + LevelDefault + { + Size = 30, 30; + GenerateMonsters = true; + Rooms = 10; + Items = 0; + IsOnGround = false; + TeamDefault = ATTNAM_TEAM; + LOSModifier = 16; + IgnoreDefaultSpecialSquares = false; + CanGenerateBone = false; + EnchantmentMinusChanceBase = 0; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 0; + EnchantmentPlusChanceDelta = 0; + BackGroundType = GRAY_FRACTAL; + FillSquare = GRANITE solidterrain(GROUND), BLACK_GRANITE earth; + TunnelSquare = GRANITE solidterrain(GROUND), 0; + IsCatacomb = true; + DifficultyBase = 0; + DifficultyDelta = 10; + MonsterAmountBase = 0; + MonsterAmountDelta = 5; + MonsterGenerationIntervalBase = 150; + MonsterGenerationIntervalDelta = -25; + + RoomDefault + { + Pos = 2:XSize-5,2:YSize-5; + WallSquare = GRANITE solidterrain(GROUND), BLACK_GRANITE wall(BRICK_OLD); + FloorSquare = GRANITE solidterrain(GROUND), 0; + DoorSquare = GRANITE solidterrain(GROUND), 0; + + Size = 4:11,4:11; + AltarPossible = false; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = true; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + GenerateWards = false; + } + } + + Level 0; + { + Description = Attnam; + ShortDescription = Attnam; + FillSquare = solidterrain(SNOW_TERRAIN), 0; + Size = 61, 67; + GenerateMonsters = false; + Rooms = 20:25; + Items = 0; + IsOnGround = true; + TeamDefault = ATTNAM_TEAM; + LOSModifier = 48; + IgnoreDefaultSpecialSquares = false; + CanGenerateBone = false; + DifficultyBase = 150; + DifficultyDelta = 0; + EnchantmentMinusChanceBase = 0; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 0; + EnchantmentPlusChanceDelta = 0; + BackGroundType = GRAY_FRACTAL; + + RoomDefault + { + Pos = 2:XSize-5,2:51; + Size = 4:11,4:11; + AltarPossible = false; + WallSquare = solidterrain(SNOW_TERRAIN),wall(BRICK_FINE); + FloorSquare = solidterrain(PARQUET), 0; + DoorSquare = solidterrain(PARQUET),door; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = false; + GenerateLanterns = true; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = true; + UseFillSquareWalls = false; + Flags = 0; + } + + Square, Pos 0, 0; + { + Character = guard(PATROL) { WayPoint = { 4, 0, 0, XSize - 1, 0, XSize - 1, YSize - 1, 0, YSize - 1; } } + } + + Square, Pos 4, 59; { Character = guard(VETERAN); } + Square, Pos 6, 59; { Character = guard(VETERAN); } + Square, Pos 23, 59; { Character = guard(VETERAN); } + Square, Pos 25, 59; { Character = guard(VETERAN); } + Square, Pos 35, 59; { Character = guard(VETERAN); } + Square, Pos 37, 59; { Character = guard(VETERAN); } + Square, Pos 54, 59; { Character = guard(VETERAN); } + Square, Pos 56, 59; { Character = guard(VETERAN); } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(SNOW_PINE); + Times = 200; + } + + Square, Random NOT_IN_ROOM; + { + Character = hunter; + Times = 10; + } + + Square, Random NOT_IN_ROOM; + { + Character = bear(POLAR_BEAR); + } + + Square, Random NOT_IN_ROOM; + { + Character = farmer; + Times = 5; + } + + Square, Random NOT_IN_ROOM; + { + Character = housewife; + Times = 5; + } + + Square, Pos 30,YSize - 1; + { + EntryIndex = STAIRS_UP; + } + + Room + { + Pos = 10,10; + Size = 41,42; + WallSquare = solidterrain(SNOW_TERRAIN), GRANITE wall(BRICK_FINE); + FloorSquare = solidterrain(SNOW_TERRAIN), 0; + GenerateDoor = false; + DivineMaster = VALPURUS; + GenerateLanterns = false; + Type = ROOM_CATHEDRAL; + IsInside = false; + GenerateWindows = false; + + Square, Pos 1, 1; + { + Character = floatingeye { WayPoint = { 4, 11, 11, 49, 11, 49, 50, 11, 50; } } + } + + Square, Pos 19, 41; + { + OTerrain = 0; + } + + Square, Pos 20, 41; + { + OTerrain = 0; + } + + Square, Pos 21, 41; + { + OTerrain = 0; + } + + GTerrainMap + { + Pos = 4,4; + Size = 33,34; + Types + { + # = MARBLE solidterrain(FLOOR) { IsInside = true; } + - = MARBLE solidterrain(FLOOR) { IsInside = true; } + ~ = liquidterrain(POOL) { IsInside = true; } + } + } + { + ............#########............ + ............#-------#............ + ####......###-------###......#### + #--#......#-----------#......#--# + #--#....###-----------###....#--# + #--#....#---------------#....#--# + #--###..#---------------#..###--# + #----#..#---------------#..#----# + #----#..#---------------#..#----# + ###--#..###-----------###..#--### + ..#--###..#-----------#..###--#.. + ..#----#..#-----------#..#----#.. + ..###--####-----------####--###.. + ....#-----#-----------#-----#.... + ....#-----#-----------#-----#.... + ....####--#-----------#--####.... + .......#--#-----------#--#....... + ......###-#-----------#-###...... + ..#####-#-#-----------#-#-#####.. + .##-------#-----------#-------##. + .#---~~~#-#-----------#-#------#. + .#---~~~#-#-----------#-#------#. + .##--~~~#-#-----------#-#-----##. + ..##-~~~#-#-----------#-#----##.. + ...##~~~#-######-######-#---##... + ....#~~~#---------------#---#.... + ....#~~~#---------------#---#.... + ...#######---###-###---#######... + ####---------#.....#---------#### + #--------#####.....#####--------# + #--------#.............#--------# + ##---#####.............#####---## + .##--#.....................#--##. + ..####.....................####.. + } + + OTerrainMap + { + Pos = 1,1; + Size = 39,40; + Types + { + # = MARBLE wall(BRICK_FINE); + @ = MARBLE wall(BRICK_PROPAGANDA); + T = throne; + | = decoration(CARPET); + A = VALPURIUM altar(VALPURUS); + D = MARBLE door(HEXAGONAL_LOCK); + L = MARBLE door(HEXAGONAL_LOCK) { Parameters = LOCKED; } + b = decoration(SNOW_FIR); + 1 = decoration(POOL_CORNER) { VisualEffects = NONE; } + 2 = decoration(POOL_CORNER) { VisualEffects = MIRROR; } + 3 = decoration(POOL_CORNER) { VisualEffects = MIRROR | FLIP; } + 4 = decoration(POOL_CORNER) { VisualEffects = FLIP; } + 5 = decoration(POOL_BORDER) { VisualEffects = NONE; } + 6 = decoration(POOL_BORDER) { VisualEffects = ROTATE; } + 7 = decoration(POOL_BORDER) { VisualEffects = FLIP; } + 8 = decoration(POOL_BORDER) { VisualEffects = MIRROR | ROTATE; } + s = decoration(COUCH); + f = fountain { SecondaryMaterial = ICE; } + d = decoration(DOUBLE_BED); + B = GOLD boulder(1); + } + } + { + b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b + ....................................... + b...b...b..b...b.b.b.b.b...b..b...b...b + ..b...b........#########........b...b.. + b....b.......bb#.......#bb.......b....b + ...####b..b..###.......###..b..b####... + b.b#..#.b....#...........#....b.#.B#b.b + ...#..#....###...........###....#..#... + b.b#..#b..b#...............#b..b#..#b.b + ...#..###..#.....T...T.....#..###.B#... + b.b#....#.b#.......|.......#b.#....#b.b + ...#....#..#.......|.......#..#B...#... + b.b###..#b.###.....|.....###.b#..###b.b + ....b#..#@#.b#.....|.....#b.#@#.B#b.... + b....#....#..#.....|.....#..#..B.#....b + ..bb.###..####.....|.....####..###.bb.. + b.....b#.....#.....|.....#...B.#b.....b + .b..b..#.....#.....|.....#.B...#..b..b. + b..b...####..#.....|.....#..####...b..b + b....b....#..#.....|.....#.B#....b....b + .b.b....b###D#.....|.....#D###b....b.b. + b...b#####.#.#.....|.....#.#.#####b.b.b + ....##.....D.#.....|.....#.D.....##.... + b..b#.s.152#.#.....|.....#.#......#b..b + .b..#...8.6#.#.....|.....#.#..A...#..b. + b...##..8.6#.#.....|.....#.#.....##...b + ....b##.8.6#.#...........#.#....##b.... + b.b..b##8.6#.######D######.#...##b..b.b + .......#8.6#...............#...#....... + b...b.b#473#...............#...#b.b...b + .b....##@####...###D###...####@##....b. + b..####.s.s.D...#.b.b.#...D.....####..b + ..b#.d......#####.f.f.#####........#b.. + b..#....s.s.#b..b.....b..b#........#..b + .b.##...#####.bb..b.b..bb.#####...##.b. + b..b##s.#b.b......f.f......b.b#..##b..b + ....b####....b...........b....####b.... + b.b...b...b.......b.b.......b...b...b.b + ...............bb.f.f.bb............... + b.b.b.b.b.b.b.b..b...b..b.b.b.b.b.b.b.b + } + + ItemMap + { + Pos = 4,4; + Size = 33,34; + Types + { + # == 0; + a == avatarofvalpurus; + b == banana; + k == kiwi; + p == pineapple; + e == backpack; + g == GOLD stone; + i == SILVER stone; + h == SAPPHIRE stone; + r == RUBY stone; + d == DIAMOND stone; + 1 == lantern { SquarePosition = UP; } + 2 == lantern { SquarePosition = DOWN; } /*christmaslight???*/ + 3 == lantern { SquarePosition = RIGHT; } + 4 == lantern { SquarePosition = LEFT; } + C == GOLD itemcontainer(CHEST) { ItemsInside = { 3, GOLD helmet, stone { Times = 10; }, GOLD banana; } } + F == ring(RING_OF_FIRE_RESISTANCE); + I == ring(RING_OF_INVISIBILITY); + E == amulet(AMULET_OF_ESP); + S == GOLD GOLD meleeweapon(LONG_SWORD); + U == DIAMOND helmet(HELM_OF_BRILLIANCE); + A == GOLD helmet(HELM_OF_ATTRACTIVITY); + L == oillamp; + P == GOLD bodyarmor(PLATE_MAIL); + O == GOLD cloak; + W == wand(WAND_OF_POLYMORPH); + w == Random { MinPrice = 350; Category = WEAPON|SHIELD; ConfigFlags = NO_BROKEN; Enchantment = 1; } + c == Random { MinPrice = 700; Category = WEAPON|SHIELD; ConfigFlags = NO_BROKEN; Enchantment = 3; } + m == Random { MinPrice = 350; Category = HELMET|CLOAK|BODY_ARMOR|BELT|BOOT|GAUNTLET; ConfigFlags = NO_BROKEN; Enchantment = 1; } + } + } + { + ............##2###2##............ + ............#.......#............ + ####......###.......###......#### + #..#......#...........#......#r.# + #..#....###...........###....#gi# + #..#....#...r.......r...#....#hC# + #..###..#.........a.....#..###I.# + #...e#..#......b.b......#..#EOLF# + #...e#..#......k.k......#..#.dPS# + ##1.e#..##1.h..p.p..h.1##..#Ug1## + ..#.e4##..#....b.b....#..##3r.#.. + ..#....#..#....k.k....#..#Wh.L#.. + ..###..4###....p.p....###3Fi###.. + ....#.....3.d..b.b..d.4P.A.I#.... + ....#.....#....k.k....#r.gEd#.... + ....###3..#....p.p....#Sd4###.... + .......#..#....b.b....#h.#....... + ......###.3.g..k.k..g.4.###...... + ..#####.#.#....p.p....#.#.#####.. + .##.......#....b.b....#.......##. + .#......3.#....k.k....#.4......#. + .#......#.3.i..p.p..i.4.#......#. + .##.....4.#....b.b....#.3.....##. + ..##....#.#...........#.#....##.. + ...##...#.##2###.###2##.#...##... + ....#...4...............3...#.... + ....#...#...............#...#.... + ...###2###...##2.2##...###2###... + ####.........#.....#.........#### + #........#####.....#####........# + #........#.............#........# + ##...#####.............#####...## + .##..#.....................#..##. + ..####.....................####.. + } + + CharacterMap + { + Pos = 4,4; + Size = 33,34; + Types + { + # = 0; + P = petrus { Flags = IS_MASTER; } + g = guard(VETERAN); + e = guard(EUNUCH); + E = guard(ELITE); + m = guard(MASTER); + p = priest(VALPURUS); + d = dolphin; + l = frog(LIGHT); + f = frog(GREATER_LIGHT); + i = frog(GIANT_LIGHT); + y = mysticfrog(LIGHT); + s = femaleslave(ATTNAM); + 1 = petrusswife(1); + 2 = petrusswife(2); + 3 = petrusswife(3); + 4 = petrusswife(4); + 5 = petrusswife(5); + 6 = petrusswife(6); + k = kamikazedwarf(VALPURUS); + K = veterankamikazedwarf(VALPURUS); + } + } + { + ............#########............ + ............#g.....g#............ + ####......###..ymy..###......#### + #.K#......#g....l....g#......#..# + #..#....###...........###....#..# + #k.#....#E.............E#....#..# + #k.###..#....sPs.s.s....#..###..# + #k...#..#..f....i....f..#..#....# + #k...#..#E.............E#..#....# + ###..#..###...........###..#..### + ..#..###..#...........#..###..#.. + ..#....#..#g.l.....l.g#..#....#.. + ..###k.####...........####..###.. + ....#k...k#...........#.E...#.... + ....#k...k#...........#.....#.... + ....####.k#g.........g#..####.... + .......#..#...........#..#....... + ......###.#...........#.###...... + ..#####E#.#...........#.#.#####.. + .##.......#g.........g#.......##. + .#......#.#...........#.#..p...#. + .#....d.#.#...........#.#......#. + .##...d.#.#...........#.#.....##. + ..##.d.d#.#g.........g#.#....##.. + ...##.d.#.######.######.#...##... + ....#.d.#......g.g......#...#.... + ....#...#...............#...#.... + ...#######...###.###...#######... + ####e3.5.....#g...g#.........#### + #.1......#####.....#####........# + #....4.6.#.............#........# + ##...#####.............#####...## + .##2.#.....................#..##. + ..####.....................####.. + } + } + + Square, Pos 44,44; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + } + + Room + { + Pos = 33,53; + Size = 7,6; + WallSquare = solidterrain(SNOW_TERRAIN),wall(BRICK_FINE); + FloorSquare = solidterrain(PARQUET), 0; + GenerateDoor = false; + DivineMaster = MELLIS; + Type = ROOM_SHOP; + GenerateLanterns = false; + GenerateWindows = false; + + Square, Pos 3, 5; + { + GTerrain = solidterrain(PARQUET); + OTerrain = door; + } + + Square, Pos 1, 1; + { + Character = slave; + } + + Square, Pos 1, 4; + { + Items == banana; + Times = 100; + } + + Square, Pos 5, 1; + { + Character = shopkeeper(ATTNAM) { Flags = IS_MASTER; } + } + + Square, Pos 3, 0; + { + OTerrain = GRANITE wall(BRICK_PROPAGANDA); + } + + ItemMap + { + Pos = 0,0; + Size = 7,6; + Types + { + # == 0; + a == Random { MinPrice = 250; MaxPrice = 2000; Category = HELMET|CLOAK|BODY_ARMOR|BELT|BOOT|GAUNTLET; } + d == Random { MinPrice = 50; MaxPrice = 2000; Category = RING|AMULET; } + w == Random { MinPrice = 250; MaxPrice = 2000; Category = WEAPON|SHIELD; } + e == Random { MinPrice = 10; MaxPrice = 2000; Category = FOOD|POTION; } + u == Random { MinPrice = 50; MaxPrice = 2000; Category = WAND|TOOL; } + v == Random { MinPrice = 500; MaxPrice = 2000; } + p == lump { MainMaterial = BEAR_FLESH { Volume = 20000; } } + l == lantern; + 2 == lantern { SquarePosition = DOWN; } + 3 == lantern { SquarePosition = RIGHT; } + 4 == lantern { SquarePosition = LEFT; } + } + } + { + ##2#2## + #.aaa.# + #edduw# + 3eeluw4 + #.epvw# + 2##.##2 + } + } + + Room + { + Pos = 52,53; + Size = 7,6; + WallSquare = solidterrain(SNOW_TERRAIN),wall(BRICK_FINE); + FloorSquare = solidterrain(PARQUET), 0; + GenerateDoor = false; + DivineMaster = SOPHOS; + Type = ROOM_LIBRARY; + GenerateLanterns = false; + GenerateWindows = false; + + Square, Pos 3, 5; + { + GTerrain = solidterrain(PARQUET); + OTerrain = door; + } + + Square, Pos 3, 0; + { + OTerrain = GRANITE wall(BRICK_PROPAGANDA); + } + + Square, Pos 3, 3; + { + Character = librarian { Flags = IS_MASTER; } + } + + Square, Pos 3, 0; + { + Items == lantern { SquarePosition = DOWN; } + } + + Square, Pos 0, 5; + { + Items == lantern { SquarePosition = DOWN; } + } + + Square, Pos 6, 5; + { + Items == lantern { SquarePosition = DOWN; } + } + + OTerrainMap + { + Pos = 1,1; + Size = 5,4; + Types + { + = = olterraincontainer(BOOK_CASE) { ItemsInside = { 2, Random { MaxPrice = 500; Category = SCROLL|BOOK; }, Random { MaxPrice = 500; Category = SCROLL|BOOK; } } } + } + } + { + ===== + =...= + =...= + =...= + } + } + + Room + { + Pos = 21,53; + Size = 7,6; + AltarPossible = false; + WallSquare = solidterrain(SNOW_TERRAIN),wall(BRICK_FINE); + FloorSquare = solidterrain(FLOOR), 0; + DoorSquare = solidterrain(FLOOR), door; + GenerateDoor = false; + DivineMaster = LORICATUS; + GenerateLanterns = false; + GenerateWindows = false; + + Square, Pos 3, 5; + { + GTerrain = solidterrain(FLOOR); + OTerrain = door; + } + + Square, Pos 3, 0; + { + OTerrain = GRANITE wall(BRICK_PROPAGANDA); + } + + Square, Pos 0, 3; + { + Items == lantern { SquarePosition = RIGHT; } + } + + Square, Pos 6, 3; + { + Items == lantern { SquarePosition = LEFT; } + } + + Square, Pos 2, 0; + { + Items == lantern { SquarePosition = DOWN; } + } + + Square, Pos 4, 0; + { + Items == lantern { SquarePosition = DOWN; } + } + + Square, Pos 0, 5; + { + Items == lantern { SquarePosition = DOWN; } + } + + Square, Pos 6, 5; + { + Items == lantern { SquarePosition = DOWN; } + } + + Square, Pos 3, 2; + { + Character = smith { Flags = IS_MASTER; } + } + + Square, Pos 3, 3; + { + OTerrain = decoration(ANVIL); + } + } + + Room + { + Pos = 2,53; + Size = 7,6; + AltarPossible = false; + WallSquare = solidterrain(SNOW_TERRAIN),wall(BRICK_FINE); + FloorSquare = solidterrain(PARQUET), 0; + GenerateDoor = false; + DivineMaster = LORICATUS; + GenerateLanterns = false; + GenerateWindows = false; + + Square, Pos 3, 5; + { + GTerrain = solidterrain(PARQUET); + OTerrain = door; + } + + Square, Pos 3, 0; + { + OTerrain = GRANITE wall(BRICK_PROPAGANDA); + } + + Square, Pos 0, 3; + { + Items == lantern { SquarePosition = RIGHT; } + } + + Square, Pos 6, 3; + { + Items == lantern { SquarePosition = LEFT; } + } + + Square, Pos 2, 0; + { + Items == lantern { SquarePosition = DOWN; } + } + + Square, Pos 4, 0; + { + Items == lantern { SquarePosition = DOWN; } + } + + Square, Pos 0, 5; + { + Items == lantern { SquarePosition = DOWN; } + } + + Square, Pos 6, 5; + { + Items == lantern { SquarePosition = DOWN; } + } + + Square, Pos 3, 2; + { + Character = tailor { Flags = IS_MASTER; } + } + } + + Room + { + FloorSquare = solidterrain(FLOOR), 0; + WallSquare = solidterrain(SNOW_TERRAIN), FIR_WOOD wall(BRICK_FINE); + DoorSquare = solidterrain(FLOOR), door; + } + + Room + { + FloorSquare = solidterrain(FLOOR), 0; + WallSquare = solidterrain(SNOW_TERRAIN), FIR_WOOD wall(BRICK_FINE); + DoorSquare = solidterrain(FLOOR), door; + } + + Room + { + FloorSquare = solidterrain(FLOOR), 0; + WallSquare = solidterrain(SNOW_TERRAIN), FIR_WOOD wall(BRICK_FINE); + DoorSquare = solidterrain(FLOOR), door; + } + + /*Square, Random; + { + Character = communist { Team = IVAN_TEAM; } + Times = 3; + } + + Square, Random; + { + Character = vladimir { Team = IVAN_TEAM; } + Times = 3; + } + + Square, Random; + { + Character = imperialist { Team = ATTNAM_TEAM; } + Times = 3; + } + + Square, Random; + { + Character = mistress(QUEEN) { Team = ATTNAM_TEAM; } + Times = 3; + } + + Square, Random; + { + Character = genetrixvesana { Team = ATTNAM_TEAM; } + Times = 10; + } + + Square, Random; + { + Character = golem(VALPURIUM) { Team = ATTNAM_TEAM; } + Times = 3; + } + + Square, Random; + { + Character = kobold(PATRIARCH) { Team = MONSTER_TEAM; } + Times = 3; + } + + Square, Random; + { + Character = goblin(KING) { Team = MONSTER_TEAM; } + Times = 3; + } + + Square, Random; + { + Character = ennerbeast { Team = MONSTER_TEAM; } + Times = 3; + } + + Square, Random; + { + Character = eddy { Team = MONSTER_TEAM; } + Times = 25; + } + + Square, Random; + { + Character = skunk { Team = MONSTER_TEAM; } + Times = 50; + } + + Square, Random; + { + Character = orc(MARSHAL) { Team = MONSTER_TEAM; } + Times = 3; + } + + Square, Random; + { + Character = darkknight(GRAND_MASTER) { Team = MONSTER_TEAM; } + Times = 3; + } + + Square, Random; + { + Character = oree { Team = MONSTER_TEAM; } + Times = 3; + } + + Square, Random; + { + Character = elpuri { Team = MONSTER_TEAM; } + Times = 3; + } + + Square, Random; + { + Character = darkmage(ARCH_MAGE) { Team = MONSTER_TEAM; } + Times = 10; + } + + Square, Random; + { + Character = skeleton(WAR_LORD) { Team = MONSTER_TEAM; } + Times = 3; + } + + Square, Random; + { + Character = spider { Team = MONSTER_TEAM; } + Times = 3; + }*/ + } + + Level 1; + { + Description = Cellar; + ShortDescription = Cellar; + Size = 42, 48; + GenerateMonsters = false; + Rooms = 1; + Items = 0; + IsOnGround = false; + TeamDefault = ATTNAM_TEAM; + LOSModifier = 16; + IgnoreDefaultSpecialSquares = false; + CanGenerateBone = false; + DifficultyBase = 150; + DifficultyDelta = 0; + EnchantmentMinusChanceBase = 0; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 0; + EnchantmentPlusChanceDelta = 0; + BackGroundType = GRAY_FRACTAL; + FillSquare = solidterrain(GROUND), MORAINE earth; + TunnelSquare = solidterrain(GROUND), 0; + + Room + { + Size = 38, 44; + Pos = 2, 2; + WallSquare = solidterrain(GROUND), 0; + FloorSquare = solidterrain(GROUND), 0; + AltarPossible = false; + GenerateDoor = false; + DivineMaster = VALPURUS; + GenerateTunnel = false; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + + OTerrainMap + { + Size = 39, 44; + Pos = 0, 0; + Types + { + * = MORAINE earth; + # = BLACK_GRANITE wall(BRICK_OLD); + @ = IRON barwall; + $ = IRON barwall(BROKEN_BARWALL); + % = MARBLE door(BARDOOR); + B = MARBLE brokendoor(BARDOOR); + e = MARBLE wall(BRICK_OLD); + > = stairs(STAIRS_DOWN); + < = stairs(STAIRS_UP); + N = GRANITE wall(BRICK_FINE); + b = boulder(1); + S = BLACK_GRANITE door(SECRET_DOOR); + X = BLACK_GRANITE wall(BROKEN_WALL); + W = olterraincontainer(SHELF) + { + ItemsInside = { 1, potion { Times = 1:4; SecondaryMaterial = VODKA; } } + } + I = ironmaiden; + } + } + { + ************#######################XX## + ************X...X..@....X....@....$...# + ************#......@....#....@@B@@@...# + ************#......@@B@@#@@%@@b...%...# + ************X...#.................XX### + ************#######..#.##X...#XX#.%...# + ******************#>.#***#..bX**X.@...# + ******************####***#NNN#**#.$...X + ************************##...##*#.#XXX# + ************************#.....#*#.%...X + #########################.....#*#.@...# + #.............................#*#.@...# + #.............................#*#S##### + #@%@###@%@###@%@###@%@###.....#*#.WW#** + #...#*#...#*#...#*#...#*#.....#*#..W#** + #...#*#...#*#...#*#...#*#.....#*#.WW#** + #####*#####*#####*#####*##...##*#..W#** + *************************#...#**#.WW#** + #####*#####*#####*#####*##...##*#..W#** + #...#*#...#*#...#*#...#*#.....#*#.WW#** + #...#*#...#*#...#*#...#*#.....#*#.###** + #@%@###@%@###@%@###@%@###.....#*#.#**** + #.............................###.##*** + #..................................#*** + #########################.....###..#*** + ************************#.....#*#..#*** + ************************##...##*#..#*** + *************************#...#**#..#*** + **********************####...##*#.<#*** + **********************#.......#*####*** + **********************#.I.....#******** + **********************#.......#******** + **********************#.......#******** + **********************#.......#******** + **********************#.......#******** + **********************#.......#******** + **********************###.#####******** + ************************#.#************ + **********************###.###********** + **********************#.....#********** + **********************#.....#********** + **********************#.....#********** + **********************#.....#********** + **********************#######********** + } + + ItemMap + { + Size = 39, 44; + Pos = 0, 0; + Types + { + * == 0; + # == 0; + 1 == lantern { SquarePosition = UP; } + 2 == lantern { SquarePosition = DOWN; } + 3 == lantern { SquarePosition = RIGHT; } + 4 == lantern { SquarePosition = LEFT; } + r == GRANITE stone; + b == lantern(BROKEN); + } + } + { + ************########################### + ************#..b#..#....#....#....#...# + ************3....r.#....#....##.###...# + ************#......##.#####.##........# + ************#..b#...............b.##### + ************#######..#r###...####.....# + ******************#.b#***#...#**#.#...# + ******************####***#...#**#.#...# + ************************##...##*#.##### + ************************#.....#*#.....# + ##2#####2#####2#####2###3.....#*#.#...# + #.............................#*#.#...# + #.............................#*####### + ##.#####.#####.#####.####.....#*#...#** + #...#*#...#*#...#*#...#*3.....#*#...#** + #...#*#...#*#...#*#...#*#.....#*#...#** + #####*#####*#####*#####*##...##*#...#** + *************************#...#**#...#** + #####*#####*#####*#####*##...##*3...#** + #...#*#...#*#...#*#...#*#.....#*#...#** + #...#*#...#*#...#*#...#*3.....#*#.###** + ##.#####.#####.#####.####.....#*#.#**** + #.............................###.##*** + #..................................#*** + ##1#####1#####1#####1###3.....###..#*** + ************************#.....#*#..#*** + ************************##...##*#..#*** + *************************#...#**#..#*** + **********************####...##*#..#*** + **********************#.......#*####*** + **********************3.......4******** + **********************#.......#******** + **********************#.......#******** + **********************#.......#******** + **********************3.......4******** + **********************#.......#******** + **********************###.#####******** + ************************#.#************ + **********************###.###********** + **********************#.....#********** + **********************#.....#********** + **********************#.....4********** + **********************#.....#********** + **********************#######********** + } + } + Square, Pos 36,30; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + } + } + + Level 2; + { + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + AttachRequired = true; + } + + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + AttachRequired = true; + } + } + Level 3; + { + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + AttachRequired = true; + } + + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + AttachRequired = true; + } + } + Level 4; + { + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + AttachRequired = true; + } + Square, Random IN_ROOM; + { + OTerrain coffin; + } + } +} + +Dungeon NEW_ATTNAM; +{ + Levels = 2; + + Level 0; + { + LevelMessage = "You hear someone shouting: \"Faster! Faster! The Empire wants yer bananas!\""; + Description = "New Attnam"; + ShortDescription = "New Attnam"; + FillSquare = solidterrain(GRASS_TERRAIN), 0; + Size = 55, 55; + GenerateMonsters = false; + Rooms = 6:10; + Items = 0; + IsOnGround = true; + TeamDefault = NEW_ATTNAM_TEAM; + LOSModifier = 48; + IgnoreDefaultSpecialSquares = false; + AutoReveal = true; + CanGenerateBone = false; + DifficultyBase = 50; + DifficultyDelta = 0; + EnchantmentMinusChanceBase = -15; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 0; + EnchantmentPlusChanceDelta = 0; + BackGroundType = GREEN_FRACTAL; + + RoomDefault + { + Pos = 2:36,2:36; + Size = 4:6,4:6; + AltarPossible = false; + WallSquare = solidterrain(GRASS_TERRAIN), wall(BRICK_PRIMITIVE); + FloorSquare = solidterrain(GRASS_TERRAIN), 0; + DoorSquare = solidterrain(GRASS_TERRAIN), BALSA_WOOD door; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = false; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = ROUND_CORNERS; + IsInside = true; + GenerateWindows = true; + UseFillSquareWalls = false; + Flags = 0; + GenerateWards = false; + } + + Room + { + Type = ROOM_BANANA_DROP_AREA; + Pos = 4,4; + Size = 17,19; + WallSquare = solidterrain(GRASS_TERRAIN), EBONY_WOOD wall(BRICK_FINE); + FloorSquare = BALSA_WOOD solidterrain(PARQUET), 0; + GenerateDoor = false; + DivineMaster = SILVA; + GenerateLanterns = false; + GenerateWindows = false; + GenerateWards = false; + + OTerrainMap + { + Pos = 0,0; + Size = 17,19; + Types + { + _ = 0; + # = 0; + p = GOLD wall(BRICK_PROPAGANDA); + $ = GOLD wall(BRICK_FINE); + ^ = decoration(HOLY_TREE); + c = decoration(COUCH); + d = EBONY_WOOD door; + o = EBONY_WOOD wall(BRICK_FINE|WINDOW); + } + } + { + .#o##o##o##o##o#. + ##.............## + o...............o + #.....$$p$$.....# + #...$$$___$$$...# + #..$$_______$$..# + o..$____c____$..o + #..$___^_^___$..# + #..$_________$..# + o..$$_______$$..o + #...$$_____$$...# + #....$$___$$....# + o.....$___$.....o + #.....$___$.....# + #.....$___$.....# + #.....$___$.....# + o...............o + ##.............## + .#o##o##d##o##o#. + } + + CharacterMap + { + Pos = 3,3; + Size = 11,13; + Types + { + _ = 0; + # = 0; + I = imperialist { Team = COLONIST_TEAM; Flags = IS_MASTER; } + g = guard(ROOKIE) { Team = COLONIST_TEAM; } + f = femaleslave(NEW_ATTNAM) { Team = COLONIST_TEAM; } + } + } + { + ...#####... + .###_g_###. + ##_______## + #g__fIf__g# + #_________# + #g_______g# + ##_______## + .##_____##. + ..##g_g##.. + ...#___#... + ...#___#... + ...#___#... + ...#g_g#... + } + + GTerrainMap + { + Pos = 3,3; + Size = 11,16; + Types + { + # = SILVER solidterrain(FLOOR); + _ = SILVER solidterrain(FLOOR); + - = BALSA_WOOD solidterrain(PARQUET); + } + } + { + ...#####... + .###___###. + ##_______## + #_________# + #_________# + #_________# + ##_______## + .##_____##. + ..##___##.. + ...#___#... + ...#___#... + ...#___#... + ...#___#... + ........... + ........... + .....-..... + } + + ItemMap + { + Pos = 3,3; + Size = 11,13; + Types + { + # == 0; + _ == 0; + 2 == lantern { SquarePosition = DOWN; } + 3 == lantern { SquarePosition = RIGHT; } + 4 == lantern { SquarePosition = LEFT; } + } + } + { + ...#2#2#... + .###___###. + ##_______## + #_________# + 3_________4 + #_________# + #3_______4# + .##_____##. + ..##___##.. + ...#___#... + ...#___#... + ...#___#... + ...2___2... + } + } + + Room + { + Type = ROOM_BANANA_DROP_AREA; + Size = 3, 3; + Pos = 44, 44; + AltarPossible = false; + WallSquare = solidterrain(GRASS_TERRAIN), decoration(PALM); + FloorSquare = solidterrain(LANDING_SITE), 0; + GenerateDoor = false; + GenerateTunnel = false; + GenerateLanterns = false; + GenerateFountains = false; + GenerateWindows = false; + Shape = RECTANGLE; + + Square, Pos 1, 1; + { + Items == banana { Times = 10; } + } + } + + Room + { + Size = 4, 4; + Pos = 25, 35; + GenerateDoor = false; + Type = ROOM_BANANA_DROP_AREA; + FloorSquare = solidterrain(FLOOR), 0; + GenerateWindows = false; + + Square, Pos 1, 1; + { + Character = sumowrestler { Flags = IS_MASTER; } + } + + Square, Pos 2, 1; + { + OTerrain = stairs(SUMO_ARENA_ENTRY) { AttachedArea = 1; AttachedEntry = STAIRS_UP; } + EntryIndex = STAIRS_DOWN; + } + + Square, Pos 1, 3; + { + GTerrain = solidterrain(FLOOR); + } + + OTerrainMap + { + Pos = 0,0; + Size = 4,4; + Types + { + # = 0; + p = wall(BRICK_PRIMITIVE_PROPAGANDA); + o = wall(BRICK_PRIMITIVE|WINDOW); + d = BALSA_WOOD door(BROKEN_LOCK); + } + } + { + #p## + #..# + #..# + #do# + } + } + + Room + { + Pos = 9,33; + Size = 7,7; + WallSquare = solidterrain(GRASS_TERRAIN), TEAK_WOOD wall(BRICK_PRIMITIVE); + FloorSquare = solidterrain(FLOOR), 0; + GenerateDoor = false; + DivineMaster = MELLIS; + Type = ROOM_SHOP; + GenerateWindows = false; + + Square, Pos 3, 3; + { + Character = shopkeeper(NEW_ATTNAM) { Team = COLONIST_TEAM; Flags = IS_MASTER; } + } + + Square, Pos 3, 6; + { + GTerrain = solidterrain(FLOOR); + } + + OTerrainMap + { + Pos = 0,0; + Size = 7,7; + Types + { + # = 0; + p = TEAK_WOOD wall(BRICK_PRIMITIVE_PROPAGANDA); + o = TEAK_WOOD wall(BRICK_PRIMITIVE|WINDOW); + d = TEAK_WOOD door; + } + } + { + .##p##. + ##...## + #.....# + o.....o + #.....# + ##...## + .##d##. + } + + ItemMap + { + Pos = 0,0; + Size = 7,7; + Types + { + # == 0; + b == banana { Chance = 50; SpoilPercentage = 49; Times = 10; } + k == kiwi { Times = 10; } + p == pineapple { Times = 3; } + 2 == lantern { SquarePosition = DOWN; } + 3 == lantern { SquarePosition = RIGHT; } + 4 == lantern { SquarePosition = LEFT; } + } + } + { + .##2##. + ##bbb## + #bbbbb# + #bk.pb# + 3bbbbb4 + ##bbb## + .#####. + } + } + + Room + { + Pos = 25,20; + Size = 4,3; + GenerateDoor = false; + DivineMaster = SILVA; + GenerateWindows = false; + + Square, Pos 2, 1; + { + Character = priest(SILVA) { Flags = IS_MASTER; } + } + + OTerrainMap + { + Pos = 0,0; + Size = 4,3; + Types + { + # = 0; + p = wall(BRICK_PRIMITIVE_PROPAGANDA); + o = wall(BRICK_PRIMITIVE|WINDOW); + d = BALSA_WOOD door; + } + } + { + #p## + #..o + #d## + } + } + + Square, Pos 0, 33; + { + EntryIndex = STAIRS_UP; + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = boulder(1); + Times = 10; + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = boulder(2); + Times = 10; + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(PALM); + Times = 100; + } + + Square, Pos 20, 44; + { + Character = encourager { Team = COLONIST_TEAM; } + } + + Square, Pos 44, 2; + { + Character = mysteryman { Team = MONDEDR_TEAM; } + } + + Square, Random NOT_IN_ROOM; + { + Character = pig(THIN_PIG) { Team = NEW_ATTNAM_TEAM; } + } + + Square, Random NOT_IN_ROOM; + { + Character = ox(STARVED_OX) { Team = NEW_ATTNAM_TEAM; } + } + + Square, Pos 0, 45; + { + Character = bananagrower; + } + + Square, Pos 10, 45; + { + Character = bananagrower; + } + + Square, Pos 20, 45; + { + Character = bananagrower; + } + + Square, Pos 30, 45; + { + Character = bananagrower; + } + + Square, Pos 40, 45; + { + Character = bananagrower; + } + + Square, Pos 45, 0; + { + Character = ostrich; + } + + Square, Pos 45, 15; + { + Character = ostrich; + } + + Square, Pos 45, 30; + { + Character = ostrich; + } + + Square, Pos 12, 50; + { + Character = elder { Team = TOURIST_GUIDE_TEAM; Flags = IS_LEADER; } + } + + Square, Pos 10, 50; + { + Character = tourist(HUSBAND) { Team = TOURIST_TEAM; } + } + + Square, Pos 14, 50; + { + Character = tourist(WIFE) { Team = TOURIST_TEAM; Flags = IS_LEADER; } + } + + Square, Pos 12, 52; + { + Character = tourist(CHILD) { Team = TOURIST_TEAM; } + } + + Square, Pos 11, 23; + { + OTerrain = sign { Text = "The mansion of Viceroy Richel Decos, formerly a temple of nature."; } + } + + Square, Pos 11, 40; + { + OTerrain = sign { Text = "Zulko's wholesale banana market. We offer quality bananas straight from the jungle at a bargain price and even some other exotic fruits for most demanding customers."; } + } + + Square, Pos 25, 39; + { + OTerrain = sign { Text = "The home of Huang Ming Pong, the guardian of the natives' sacred and mysterious sumo fighting techniques."; } + } + + Square, Pos 25, 23; + { + OTerrain = sign { Text = "The village's new temple of nature."; } + } + + Square, Pos 43, 43; + { + OTerrain = sign { Text = "New Attnam's main ostrich landing site. The delicious fruits of Decos Bananas Co. are gathered here by native professionals and then distributed aerially all over the civilized world."; } + } + } + + Level 1; + { + Description = "New Attnam's sumo wrestling arena"; + ShortDescription = "SumoArena"; + FillSquare = solidterrain(GROUND), MORAINE earth; + Size = 55, 55; + Rooms = 1; + Items = 0; + GenerateMonsters = false; + IsOnGround = false; + TeamDefault = NEW_ATTNAM_TEAM; + LOSModifier = 32; + IgnoreDefaultSpecialSquares = false; + CanGenerateBone = false; + DifficultyBase = 50; + DifficultyDelta = 0; + EnchantmentMinusChanceBase = -15; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 0; + EnchantmentPlusChanceDelta = 0; + BackGroundType = GREEN_FRACTAL; + + Room + { + Pos = 19,12; + Size = 13,13; + WallSquare = solidterrain(GROUND), BALSA_WOOD wall(BRICK_FINE); + FloorSquare = solidterrain(GROUND), 0; + GenerateDoor = false; + DivineMaster = ATHEIST; + GenerateLanterns = false; + Type = ROOM_SUMO_ARENA; + GenerateFountains = false; + AltarPossible = false; + Shape = RECTANGLE; + GenerateTunnel = false; + GenerateWindows = false; + IsInside = true; + UseFillSquareWalls = false; + Flags = 0; + GenerateWards = false; + + Square, Pos 6, 6; + { + OTerrain = stairs(SUMO_ARENA_EXIT) { AttachedArea = 0; AttachedEntry = STAIRS_DOWN; } + EntryIndex = STAIRS_UP; + } + + Square, Pos 6, 0; + { + OTerrain = wall(BRICK_PRIMITIVE_PROPAGANDA); + Items == lantern { SquarePosition = DOWN; } + } + + Square, Pos 0, 6; + { + Items == lantern { SquarePosition = RIGHT; } + } + + Square, Pos 12, 6; + { + Items == lantern { SquarePosition = LEFT; } + } + + Square, Pos 6, 12; + { + Items == lantern { SquarePosition = UP; } + } + + OTerrainMap + { + Pos = 3, 3; + Size = 7, 7; + Types + { + # = GLASS wall(BRICK_FINE); + } + } + { + ####### + #.....# + #.....# + #.....# + #.....# + #.....# + ####### + } + } + } +} + + +Dungeon MONDEDR; +{ + Levels = 6; + Description = "Underground Mondedr"; + ShortDescription = "UM"; + + LevelDefault + { + Size = 40, 40; + GenerateMonsters = true; + Rooms = 40; + Items = 0; + IsOnGround = false; + TeamDefault = ATTNAM_TEAM; + LOSModifier = 16; + IgnoreDefaultSpecialSquares = false; + CanGenerateBone = false; + EnchantmentMinusChanceBase = 0; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 0; + EnchantmentPlusChanceDelta = 0; + BackGroundType = GRAY_FRACTAL; + FillSquare = GRANITE solidterrain(GROUND), BLACK_GRANITE earth; + TunnelSquare = GRANITE solidterrain(GROUND), 0; + IsCatacomb = false; + DifficultyBase = 0; + DifficultyDelta = 10; + MonsterAmountBase = 0; + MonsterAmountDelta = 5; + MonsterGenerationIntervalBase = 150; + MonsterGenerationIntervalDelta = -25; + + RoomDefault + { + Pos = 2:XSize-5,2:YSize-5; + WallSquare = GRANITE solidterrain(GROUND), BLACK_GRANITE wall(BRICK_OLD); + FloorSquare = GRANITE solidterrain(GROUND), 0; + DoorSquare = GRANITE solidterrain(GROUND), 0; + + Size = 4:11,4:11; + AltarPossible = false; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = true; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + GenerateWards = false; + } + } + + Level 0; + { + Size = 61, 67; + Description = Mondedr; + ShortDescription = Mondedr; + FillSquare = solidterrain(GRASS_TERRAIN), 0; + Size = 61, 67; + GenerateMonsters = false; + Rooms = 50:60; + Items = 0; + IsOnGround = true; + TeamDefault = MONDEDR_TEAM; + LOSModifier = 48; + IgnoreDefaultSpecialSquares = false; + CanGenerateBone = false; + DifficultyBase = 150; + DifficultyDelta = 0; + EnchantmentMinusChanceBase = 0; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 0; + EnchantmentPlusChanceDelta = 0; + BackGroundType = GREEN_FRACTAL; + + RoomDefault + { + Pos = 2:XSize-5,2:51; + Size = 4:8,4:8; + AltarPossible = false; + WallSquare = solidterrain(GRASS_TERRAIN),wall(BRICK_FINE); + FloorSquare = solidterrain(PARQUET), 0; + DoorSquare = solidterrain(PARQUET),door; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = false; + GenerateLanterns = true; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = true; + UseFillSquareWalls = false; + Flags = 0; + } + + Square, Pos 0, 0; + { + Character = guard(MONDEDR_GUARD) { WayPoint = { 4, 0, 0, XSize - 1, 0, XSize - 1, YSize - 1, 0, YSize - 1; } } + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(FIR); + Times = 100; + } + + Square, Random NOT_IN_ROOM; + { + Character = farmer(MONDEDR); + Times = 5; + } + + Square, Random NOT_IN_ROOM; + { + Character = housewife(MONDEDR); + Times = 5; + } + + Square, Pos 30,YSize - 1; + { + EntryIndex = STAIRS_UP; + } + + Room + { + Pos = 34,33; + Size = 7,6; + WallSquare = solidterrain(GRASS_TERRAIN),wall(BRICK_FINE); + FloorSquare = solidterrain(PARQUET), 0; + GenerateDoor = false; + DivineMaster = MELLIS; + Type = ROOM_SHOP; + GenerateLanterns = false; + GenerateWindows = false; + + Square, Pos 3, 5; + { + GTerrain = solidterrain(PARQUET); + OTerrain = door; + } + + Square, Pos 1, 4; + { + Items == ring(RING_OF_FIRE_RESISTANCE); + } + + Square, Pos 3, 1; + { + Character = shopkeeper(MONDEDR) { Flags = IS_MASTER; } + } + + ItemMap + { + Pos = 0,0; + Size = 7,6; + Types + { + # == 0; + a == Random { MinPrice = 250; MaxPrice = 2000; Category = HELMET|CLOAK|BODY_ARMOR|BELT|BOOT|GAUNTLET; } + d == Random { MinPrice = 50; MaxPrice = 2000; Category = RING|AMULET; } + w == Random { MinPrice = 250; MaxPrice = 2000; Category = WEAPON|SHIELD; } + e == Random { MinPrice = 10; MaxPrice = 2000; Category = FOOD|POTION; } + u == Random { MinPrice = 50; MaxPrice = 2000; Category = WAND|TOOL; } + v == Random { MinPrice = 500; MaxPrice = 2000; } + l == lantern; + 2 == lantern { SquarePosition = DOWN; } + 3 == lantern { SquarePosition = RIGHT; } + 4 == lantern { SquarePosition = LEFT; } + } + } + { + ##2#2## + #aa.ad# + #wwwwd# + 3uuuvd4 + #.eevd# + 2##.##2 + } + } + + Square, Pos 36, 39; + { + Character = guard(MONDEDR_GUARD); + } + + Room + { + Pos = 16,16; + Size = 30,16; + AltarPossible = false; + GenerateFountains = false; + Shape = RECTANGLE; + + GTerrainMap + { + Pos = 0,0; + Size = 30,46; + Types + { + # = EBONY_WOOD solidterrain(FLOOR) { IsInside = true; } + - = 0; + ~ = GRASS solidterrain(GROUND) { IsInside = true; } + } + } + { + ############################## + #---------------#------------# + #---------------#------------# + ##############--#------------# + #------------#--#------------# + #------------#--#------------# + #------------#--#------------# + #------------#--############## + #------------#---------------# + #------------#---------------# + #------------#--############## + #------------#--#------------# + #------------#--#------------# + #------------#--#------------# + #------------#--#------------# + ############################## + ..............~~.............. + ..............~~.............. + ..............~~.............. + ..............~~.............. + ..............~~.............. + ..............~~.............. + ............~~~~~~~~~~........ + ...........~~.~~....~~........ + ..........~~..~~.....~........ + .........~~...~~.....~~~~..... + .......~~.....~~.....~..~~~... + .......~......~~......~...~~.. + .....~~.......~~.......~...~.. + ..~~~..~......~~......~....~.. + ....~...~.....~~.......~...... + ....~....~....~~.....~........ + ...~....~~....~~......~....... + ..~~~~~~~~....~~.....~~~...... + ..~...~~.....~~.........~~.... + ..~.....~~....~~........~~.... + ..~~..........~~.........~~... + ..~~..........~~.........~.... + ....~.........~~.........~.... + ...~..........~~.....~~~~..... + ...~..........~~.....~..~..... + ...~..........~~....~~...~~~.. + ..............~~....~...~~.... + ..............~~...~.....~.... + ..............~~.............. + ..............~~.............. + } +OTerrainMap + { + Pos = 0,0; + Size = 30,46; + + Types + { + # = EBONY_WOOD wall(BRICK_FINE); + - = 0; + | = decoration(CARPET); + D = EBONY_WOOD door(HEXAGONAL_LOCK); + } + } + { + ############################## + #---------------#------------# + #---------------#------------# + ######D#######--#------------# + #------------#--#------------# + #------------#--#------------# + #------------#--#------------# + #------------#--######D####### + #------------#---------------# + #------------#---------------# + #------------#--######D####### + #------------#--#------------# + #------------#--#------------# + #------------#--#------------# + #------------#--#------------# + ##############DD############## + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + + } +CharacterMap + { + Pos = 0,0; + Size = 30,46; + + Types + { + # = 0; + - = 0; + g = guard(MONDEDR_GUARD); + } + } + { + ############################## + #g--------------#g----------g# + #g--------------#------------# + ##############--#------------# + #g----------g#--#------------# + #------------#--#------------# + #------------#--#g----------g# + #------------#--############## + #------------#---------------# + #------------#---------------# + #------------#--############## + #------------#--#g----------g# + #------------#--#------------# + #------------#--#------------# + #g----------g#--#g----------g# + ############################## + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + .............................. + } + Square, Pos 6,10; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + } + } + } + +Level 1; + { + Description = Cellar; + ShortDescription = Cellar; + Size = 42, 48; + GenerateMonsters = false; + Rooms = 1; + Items = 0; + IsOnGround = false; + TeamDefault = MONDEDR_TEAM; + LOSModifier = 16; + IgnoreDefaultSpecialSquares = false; + CanGenerateBone = false; + DifficultyBase = 150; + DifficultyDelta = 0; + EnchantmentMinusChanceBase = 0; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 0; + EnchantmentPlusChanceDelta = 0; + BackGroundType = GRAY_FRACTAL; + FillSquare = solidterrain(GROUND), MORAINE earth; + TunnelSquare = solidterrain(GROUND), 0; + + Room + { + Size = 38, 44; + Pos = 2, 2; + WallSquare = solidterrain(GROUND), 0; + FloorSquare = solidterrain(GROUND), 0; + AltarPossible = false; + GenerateDoor = false; + DivineMaster = CLEPTIA; + GenerateTunnel = false; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + + OTerrainMap + { + Size = 39, 44; + Pos = 0, 0; + Types + { + * = MORAINE earth; + # = BLACK_GRANITE wall(BRICK_OLD); + @ = IRON barwall; + $ = IRON barwall(BROKEN_BARWALL); + % = MARBLE door(BARDOOR) { Parameters = LOCKED; } + B = MARBLE brokendoor(BARDOOR); + e = MARBLE wall(BRICK_OLD); + > = stairs(STAIRS_DOWN); + < = stairs(STAIRS_UP); + N = GRANITE wall(BRICK_FINE); + b = boulder(1); + D = BLACK_GRANITE door(HEXAGONAL_LOCK); + X = BLACK_GRANITE wall(BROKEN_WALL); + I = ironmaiden; + A = OCTIRON altar(CLEPTIA); + T = OCTIRON throne; + | = decoration(CARPET); + } + } + { + *************************************** + **########*********###############***** + **#......#*********#...#...#...#.#***** + **#......#*********#...#...#...#.#***** + **#......#*********#@%@#@%@#@%@#.#***** + **###D####*********#.............#***** + ****#.#************#######D#######***** + ****#.#******************#.#*********** + ****#.#******************#.#*********** + ****#.#******************#.#*********** + **###D####*********#######D#######***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#......###########.............#***** + **#......D.........D......T......#***** + **#......###########.............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **###D####*********######D#D######***** + ****#.#*************#.....#.....#****** + ****#.#*************#.....#.....#****** + ****#.#*************#.....#.....#****** + ****#.#*************#.....#.....#****** + ****#.#*************#############****** + ****#.#******************************** + ****#.#******************************** + **###D####*********##############****** + **#......#*********#............#****** + **#......###########............#****** + **#......D.........D.....A......#****** + **#......###########............#****** + **#......#*********#............#****** + **########*********##############****** + *************************************** + *************************************** + *************************************** + *************************************** + } + + ItemMap + { + Size = 39, 44; + Pos = 0, 0; + Types + { + * == 0; + # == 0; + 1 == lantern { SquarePosition = UP; } + 2 == lantern { SquarePosition = DOWN; } + 3 == lantern { SquarePosition = RIGHT; } + 4 == lantern { SquarePosition = LEFT; } + r == GRANITE stone; + b == lantern(BROKEN); + } + } + { + *************************************** + **###2####*********#############2#***** + **#......#*********#...#...#...#.#***** + **3......4*********#...#...#...#.#***** + **#......#*********#############.#***** + **########*********3.............4***** + ****3.4************#1###########1#***** + ****#.#******************3*4*********** + ****#.#******************#*#*********** + ****3.4******************3*4*********** + **#2###2##*********#######2#######***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **3......4*********3.............4***** + **#......#*********#.............#***** + **#......#2#######2#.............#***** + **3......#.........#.............4***** + **#......#1#######1#.............#***** + **#......#*********#.............#***** + **3......4*********3.............4***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#1###1##*********#######1#######***** + ****3.4*************#.....#.....#****** + ****#.#*************#.....#.....#****** + ****#.#*************#.....#.....#****** + ****#.#*************#.....#.....#****** + ****#.#*************#############****** + ****#.#******************************** + ****3.4******************************** + **########*********######2#######****** + **#......4*********3............#****** + **#......#2#######2#............#****** + **3......#.........#............4****** + **#......#1#######1#............#****** + **#......4*********3............#****** + **###1####*********######1#######****** + *************************************** + *************************************** + *************************************** + *************************************** + } + CharacterMap + { + Size = 39, 44; + Pos = 0, 0; + Types + { + * = 0; + # = 0; + R = raven { Flags = IS_MASTER; } + g = guard(MONDEDR_GUARD); + P = priest(CLEPTIA); + } + } + { + *************************************** + **########*********###############***** + **#......#*********#...#...#...#g#***** + **#......#*********#...#...#...#.#***** + **#.g.g..#*********#############.#***** + **########*********#.............#***** + ****#.#************###############***** + ****#.#******************#.#*********** + ****#.#******************#.#*********** + ****#.#******************#.#*********** + **########*********###############***** + **#.g.g..#*********#g...........g#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#.....g###########g............#***** + **#......#.........#......R......#***** + **#.....g###########g............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#......#*********#.............#***** + **#.g.g..#*********#g...........g#***** + **########*********###############***** + ****#.#*************#.....#.....#****** + ****#.#*************#.....#.....#****** + ****#.#*************#.....#.....#****** + ****#.#*************#.....#.....#****** + ****#.#*************#############****** + ****#.#******************************** + ****#.#******************************** + **########*********##############****** + **#.g.g..#*********#............#****** + **#.....g###########............#****** + **#......#.........#......P.....#****** + **#.....g###########............#****** + **#......#*********#............#****** + **########*********##############****** + *************************************** + *************************************** + *************************************** + *************************************** + } + + } + Square, Pos 7,5; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + } + Square, Pos 32,30; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + } + } + Level 2; + { + FillSquare = WATER liquidterrain(UNDERGROUND_LAKE), 0; + Rooms = 60; + Items = 0; + + Square, Random; + { + Character = rogue { Team = MONSTER_TEAM; } + Times = 1; + } + + Square, Random; + { + Items == Random { MinPrice = 250; MaxPrice = 2000; } + Times = 10; + } + + Square, Random ATTACHABLE; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + AttachRequired = true; + } + + Square, Random ATTACHABLE; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + AttachRequired = true; + } + } + Level 3; + { + + Square, Random; + { + Character = rogue { Team = MONSTER_TEAM; } + Times = 2; + } + + Square, Random; + { + Items == Random { MinPrice = 500; MaxPrice = 2000; } + Times = 10; + } + + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + AttachRequired = true; + } + + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + AttachRequired = true; + } + } + Level 4; + { + + Square, Random; + { + Character = rogue { Team = MONSTER_TEAM; } + Times = 3; + } + + Square, Random; + { + Character = assassin { Team = MONSTER_TEAM; } + Times = 1; + } + + Square, Random; + { + Items == Random { MinPrice = 500; MaxPrice = 2000; } + Times = 10; + } + + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + AttachRequired = true; + } + + Square, Random NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + AttachRequired = true; + } + } + Level 5; + { + Size = 61, 67; + GenerateMonsters = false; + Rooms = 1; + Items = 0; + IsOnGround = false; + TeamDefault = MONDEDR_TEAM; + LOSModifier = 16; + IgnoreDefaultSpecialSquares = false; + CanGenerateBone = false; + DifficultyBase = 150; + DifficultyDelta = 0; + EnchantmentMinusChanceBase = 0; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 0; + EnchantmentPlusChanceDelta = 0; + BackGroundType = GRAY_FRACTAL; + FillSquare = solidterrain(GROUND), BLACK_GRANITE earth; + TunnelSquare = solidterrain(GROUND), 0; + + Room + { + Pos = 6,6; + Size = 45, 42; + WallSquare = solidterrain(GROUND), 0; + FloorSquare = solidterrain(GROUND), 0; + AltarPossible = false; + GenerateDoor = false; + DivineMaster = CLEPTIA; + GenerateTunnel = false; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + + GTerrainMap + { + Size = 45, 54; + Pos = 0, 0; + Types + { + * = 0; + # = 0; + ~ = WATER liquidterrain(UNDERGROUND_LAKE); + } + } + { + **************#################************** + *************##...............##************* + ************##.................##************ + ***********##...................##*********** + **********##.....................##********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#~.....................~#********** + **********#~~...................~~#********** + **********#~~~.......~.~.......~~~#********** + **********#~~~~.....~~.~~......~~~#********** + **********#~~~~~~~~~~...~~~~~~~~~~#********** + **********#~~~~~~~~~~...~~~~~~~~~~#********** + **********#~~~......~~.~~......~~~#********** + **********#~~........~.~........~~#********** + **********#~.....................~#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********##.....................##********** + ***********##...................##*********** + ************##.................##************ + *************##...............##************* + **************#################************** + ******************##.....##****************** + ******************#~.....~#****************** + ******************##.....##****************** + ******************#~.....~#****************** + ******************##.....##****************** + ******************#~.....~#****************** + ******************##.....##****************** + **************#################************** + *#######******#...............#*****########* + *#.....########...............#######.....#** + *#.....#......#...............#.....#.....#** + *#.....#......#...............#.....#.....#** + *#.....########...............#######.....#** + *#######******#...............#*****#######** + **************#################************** + *******************##..##******************** + *******************#~..~#******************** + *******************##..##******************** + *******************#~..~#******************** + *******************##..##******************** + ******************########******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************########******************* + + + + } + OTerrainMap + { + Size = 45, 54; + Pos = 0, 0; + Types + { + * = BLACK_GRANITE earth; + # = BLACK_GRANITE wall(BRICK_OLD); + @ = IRON barwall; + $ = IRON barwall(BROKEN_BARWALL); + % = MARBLE door(BARDOOR) { Parameters = LOCKED; } + B = MARBLE brokendoor(BARDOOR); + e = MARBLE wall(BRICK_OLD); + > = stairs(STAIRS_DOWN); + < = stairs(STAIRS_UP); + N = GRANITE wall(BRICK_FINE); + b = boulder(1); + D = BLACK_GRANITE door(HEXAGONAL_LOCK); + X = BLACK_GRANITE wall(BROKEN_WALL); + I = ironmaiden; + A = OCTIRON altar(CLEPTIA); + T = OCTIRON throne; + 1 = decoration(POOL_CORNER) { VisualEffects = NONE; } + 2 = decoration(POOL_CORNER) { VisualEffects = MIRROR; } + 3 = decoration(POOL_CORNER) { VisualEffects = MIRROR | FLIP; } + 4 = decoration(POOL_CORNER) { VisualEffects = FLIP; } + 5 = decoration(POOL_BORDER) { VisualEffects = NONE; } + 6 = decoration(POOL_BORDER) { VisualEffects = ROTATE; } + 7 = decoration(POOL_BORDER) { VisualEffects = FLIP; } + 8 = decoration(POOL_BORDER) { VisualEffects = MIRROR | ROTATE; } + | = decoration(CARPET); + } + } + { + **************#################************** + *************##...............##************* + ************##.................##************ + ***********##...................##*********** + **********##.....................##********** + **********#...........T...........#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#@@@@@@@@@@...@@@@@@@@@@#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********##.....................##********** + ***********##...................##*********** + ************##.................##************ + *************##...............##************* + **************########D########************** + ******************##.....##****************** + ******************#.......#****************** + ******************##.....##****************** + ******************#.......#****************** + ******************##.....##****************** + ******************#.......#****************** + ******************##.....##****************** + **************########D########************** + ########******#...............#*****######### + #......########...............#######.......# + #......D......D...............D.....D.......# + #......D......D...............D.....D.......# + #......########...............#######.......# + ########******#...............#*****######### + **************#######DD########************** + *******************##..##******************** + *******************#....#******************** + *******************##..##******************** + *******************#....#******************** + *******************##..##******************** + ******************###DD###******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************########******************* + + + + } +CharacterMap + { + Pos = 0,0; + Size = 45, 54; + + Types + { + * = 0; + # = 0; + - = 0; + B = blinkdog { Team = MONSTER_TEAM; } + F = floatingeye { Team = MONSTER_TEAM; } + A = assassin { Team = MONSTER_TEAM; } + V = vulcan { Team = MONSTER_TEAM; } + S = invisiblestalker { Team = MONSTER_TEAM; } + f = frog(DARK) { Team = MONSTER_TEAM; } + } + } + { + **************#################************** + *************##...............##************* + ************##.................##************ + ***********##...................##*********** + **********##.....................##********** + **********#.........F.V.F.........#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#f.....................f#********** + **********#..........f.f..........#********** + **********#f.....................f#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********##.....................##********** + ***********##...................##*********** + ************##.................##************ + *************##....A.....A....##************* + **************#################************** + ******************##f...f##****************** + ******************#.......#****************** + ******************##.....##****************** + ******************#.......#****************** + ******************##.....##****************** + ******************#.......#****************** + ******************##.....##****************** + **************#################************** + ########******#S.............S#*****######### + #......########...............#######.......# + #......#......#...............#.....#.......# + #......#......#...............#.....#.......# + #......########...............#######.......# + ########******#S.............S#*****######### + **************#################************** + *******************##..##******************** + *******************#....#******************** + *******************##..##******************** + *******************#....#******************** + *******************##..##******************** + ******************########******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************########******************* + } + ItemMap + { + Pos = 0,0; + Size = 45, 54; + + Types + { + * == 0; + # == 0; + - == 0; + 1 == lantern { SquarePosition = UP; } + 2 == lantern { SquarePosition = DOWN; } + 3 == lantern { SquarePosition = RIGHT; } + 4 == lantern { SquarePosition = LEFT; } + } + } + { + **************#######2#2#######************** + *************##...............##************* + ************##.................##************ + ***********##...................##*********** + **********##.....................##********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********3.......................4********** + **********#.......................#********** + **********3.......................4********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********#.......................#********** + **********##.....................##********** + ***********##...................##*********** + ************##.................##************ + *************##...............##************* + **************#######1#1#######************** + ******************##.....##****************** + ******************3.......4****************** + ******************##.....##****************** + ******************3.......4****************** + ******************##.....##****************** + ******************3.......4****************** + ******************##.....##****************** + **************#################************** + ########******#...............#*****######### + #......#2####2#...............#2###2#.......# + #......#......#...............#.....#.......# + #......#......#...............#.....#.......# + #......#1####1#...............#1###1#.......# + ########******#...............#*****######### + **************#################************** + *******************##..##******************** + *******************#....#******************** + *******************##..##******************** + *******************#....#******************** + *******************##..##******************** + ******************########******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************#......#******************* + ******************########******************* + } + Square, Pos 22,36; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + } + } + } + +} + +Dungeon UNDER_WATER_TUNNEL; +{ + Levels = 5; + Description = "underwater tunnel"; + ShortDescription = "UT"; + + LevelDefault + { + FillSquare = solidterrain(GROUND), SAND_STONE earth; + TunnelSquare = solidterrain(GROUND), 0; + Size = 80, 20; + Rooms = 5:10; + Items = 15:30; + GenerateMonsters = true; + IsOnGround = false; + TeamDefault = MONSTER_TEAM; + LOSModifier = 16; + IgnoreDefaultSpecialSquares = false; + DifficultyBase = 5; + DifficultyDelta = 15; + MonsterAmountDelta = 0; + MonsterGenerationIntervalBase = 200; + MonsterGenerationIntervalDelta = -25; + CanGenerateBone = true; + ItemMinPriceBase = 0; + ItemMinPriceDelta = 5; + EnchantmentMinusChanceBase = 10; + EnchantmentMinusChanceDelta = -5; + EnchantmentPlusChanceBase = 1; + EnchantmentPlusChanceDelta = 1; + BackGroundType = GRAY_FRACTAL; + IsCatacomb = false; + + Square, Random; + { + Items == beartrap { Team = MONSTER_TEAM; IsActive = true; } + Times = 0:3; + } + + Square, Random; + { + Items == Random { Category = FOOD; } + Times = 1:2; + } + + RoomDefault + { + Pos = 2:XSize-5,2:YSize-5; + Size = 4:11,4:11; + AltarPossible = true; + WallSquare = solidterrain(GROUND), BALSA_WOOD wall(BRICK_OLD); + FloorSquare = BALSA_WOOD solidterrain(PARQUET), 0; + DoorSquare = BALSA_WOOD solidterrain(PARQUET), BALSA_WOOD door; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = true; + GenerateLanterns = true; + Type = ROOM_NORMAL; + GenerateFountains = true; + AllowLockedDoors = true; + AllowBoobyTrappedDoors = true; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + GenerateWards = false; + } + } + +RandomLevel 0:2; + { + Room /* vault */ + { + GenerateLanterns = false; + GenerateFountains = false; + GenerateDoor = false; + GenerateTunnel = false; + AltarPossible = false; + Size = 7,7; + Flags = NO_MONSTER_GENERATION; + UseFillSquareWalls = true; + + Square, Random; + { + Items == Random { MinPrice = 250; MaxPrice = 2000; } + Times = 3:9; + } + + OTerrainMap + { + Pos = 1,1; + Size = 5,5; + Types + { + x = BALSA_WOOD wall(BRICK_OLD); + } + } + { + xxxxx + x...x + x...x + x...x + xxxxx + } + } + } + + Level 0; + { + IgnoreDefaultSpecialSquares = true; + MonsterAmountBase = 5; + CanGenerateBone = false; + + RoomDefault + { + AltarPossible = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + } + + Square, Random; + { + Character = spider(LARGE); + Times = 2; + } + + Square, Random; + { + Character = kobold; + Times = 2; + } + + Square, Random; + { + Character = carnivorousplant; + Times = 2; + } + + Square, Random; + { + Character = hedgehog; + Times = 2; + } + + Square, BoundedRandom 61, 1, 78, 18, NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + AttachRequired = true; + } + + Room + { + Pos = 2:XSize/4-5,2:YSize-5; + Size = 5,5; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = ROUND_CORNERS; + + Square, Pos 2,2; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + } + + Square, Pos 2,1; + { + Items == lantern; + } + } + + } + + Level 1; + { + Size = 160, 20; + MonsterAmountBase = 15; + Items = 20:40; + + RoomDefault + { + Pos = 2:XSize-5,2:YSize-5; + } + + Square, Random; + { + Items == Random { MinPrice = 50; MaxPrice = 500; Category = BODY_ARMOR; } + } + + Square, Random; + { + Items == Random { MinPrice = 50; MaxPrice = 500; Category = HELMET|CLOAK|BELT|BOOT|GAUNTLET; } + } + + Square, BoundedRandom 1, 1, 18, 18, NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + AttachRequired = true; + } + + Square, BoundedRandom 141, 1, 158, 18, NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_UP) { AttachedArea = 2; } + EntryIndex = STAIRS_UP + 1; + AttachRequired = true; + } + + Room + { + Size = 11,11; + AllowLockedDoors = true; + AllowBoobyTrappedDoors = true; + GenerateFountains = false; + Shape = ROUND_CORNERS; + WallSquare = solidterrain(GROUND), SAND_STONE earth; + FloorSquare = solidterrain(GROUND), 0; + GenerateDoor = false; + GenerateLanterns = false; + AltarPossible = false; + + OTerrainMap + { + Pos = 3,3; + Size = 5,5; + Types + { + # = SAND_STONE earth; + } + } + { + ..#.. + .###. + ##.## + .###. + ..#.. + } + + Square, Pos 5,5; + { + OTerrain = stairs(STAIRS_DOWN) { AttachedArea = 3; } + EntryIndex = STAIRS_DOWN; + } + + Square, Pos 5,0; + { + OTerrain = 0; + AttachRequired = true; + } + } + } + + Level VESANA_LEVEL; + { + MonsterAmountBase = 10; + LevelMessage = "This level seems to be somehow alive. You feel you want to leave it as soon as possible."; + FillSquare = solidterrain(GROUND), LIME_STONE earth; + + Square, BoundedRandom 1, 1, 18, 18, NOT_WALKABLE|ATTACHABLE; + { + OTerrain = stairs(STAIRS_DOWN) { AttachedArea = 1; AttachedEntry = STAIRS_UP + 1; } + EntryIndex = STAIRS_DOWN; + AttachRequired = true; + } + + Room + { + Size = 6,6; + Pos = 3*XSize/4:XSize/4-5,2:YSize-5; + FloorSquare = solidterrain(GRASS_TERRAIN), 0; + GenerateFountains = false; + AltarPossible = false; + GenerateLanterns = false; + + Square, Pos 3,3; + { + OTerrain = stairs(STAIRS_UP) { AttachedArea = WORLD_MAP; AttachedEntry = UNDER_WATER_TUNNEL_EXIT; } + EntryIndex = STAIRS_UP; + } + + CharacterMap + { + Size = 4,4; + Pos = 1,1; + Types + { + c = carnivorousplant(GREATER); + g = genetrixvesana; + } + } + { + .cc. + cg.c + c..c + .cc. + } + } + } + + Level CRYSTAL_LEVEL; + { + Rooms = 17:30; + Size = 64, 36; + LevelMessage = "The air feels thick and damp, and there are strange crystals sprouting from the ground, emitting soft glows of many colors."; + FillSquare = solidterrain(DARK_GRASS_TERRAIN), BASALT earth; + TunnelSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DifficultyBase = 100; + DifficultyDelta = 0; + MonsterAmountBase = 25; + MonsterGenerationIntervalBase = 60; + MonsterGenerationIntervalDelta = 0; + ItemMinPriceBase = 70; + ItemMinPriceDelta = 0; + EnchantmentMinusChanceBase = 0; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 40; + EnchantmentPlusChanceDelta = 0; + BackGroundType = BLUE_FRACTAL; + + RoomDefault + { + Size = 8:17,8:17; + Pos = 2:XSize-5,2:YSize-5; + GenerateLanterns = false; + Shape = ROUND_CORNERS; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), BASALT earth; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + AltarPossible = false; + } + + Room + { + Square, Random IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = stairs(STAIRS_UP) { AttachedArea = 1; } + EntryIndex = STAIRS_UP; + } + + Square, Random IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = stairs(STAIRS_DOWN); + EntryIndex = STAIRS_DOWN; + } + } + + Room + { + DivineMaster = TERRA; + + Square, Random HAS_NO_OTERRAIN; + { + OTerrain = BASALT altar(TERRA); + } + } + + Square, Random HAS_NO_OTERRAIN; + { + OTerrain = decoration(SHARD); + Times = 10:30; + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = boulder(1); + Times = 5:15; + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = boulder(2); + Times = 5:15; + } + + Square, Random; + { + Items == stone; + Times = 0:10; + } + + Square, Random IN_ROOM; + { + Character = golem(BLUE_CRYSTAL); + } + + Square, Random IN_ROOM; + { + Character = golem(PURPLE_CRYSTAL); + } + + Square, Random IN_ROOM; + { + Character = golem(GREEN_CRYSTAL); + } + + Square, Random; + { + Character = spider(LARGE); + Times = 15; + } + } + + Level SPIDER_LEVEL; + { + Rooms = 25:30; + Size = 64, 36; + LevelMessage = "The air feels thick and damp, and there are strange crystals sprouting from the ground, emitting soft glows of many colors."; + FillSquare = solidterrain(DARK_GRASS_TERRAIN), EXTRA_HARD_BASALT earth; + TunnelSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DifficultyBase = 100; + DifficultyDelta = 0; + MonsterAmountBase = 25; + MonsterGenerationIntervalBase = 60; + MonsterGenerationIntervalDelta = 0; + ItemMinPriceBase = 70; + ItemMinPriceDelta = 0; + EnchantmentMinusChanceBase = 0; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 40; + EnchantmentPlusChanceDelta = 0; + BackGroundType = BLUE_FRACTAL; + + RoomDefault + { + Size = 3:5,3:5; + Pos = 2:XSize-5,2:YSize-5; + GenerateLanterns = false; + Shape = ROUND_CORNERS; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), EXTRA_HARD_BASALT earth; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + AltarPossible = false; + } + + Square, Random; + { + Character = spider(GIANT); + Times = 50; + } + +Square, Random IN_ROOM|HAS_NO_OTERRAIN|NOT_IN_ROOM; + { + OTerrain = stairs(STAIRS_UP) { AttachedArea = 3; } + EntryIndex = STAIRS_UP; + } + + +Room + { + Size = 10:14,7:9; + GenerateFountains = false; + AltarPossible = false; + Shape = ROUND_CORNERS; + + Square, Random; + { + Items == SPIDER_SILK bodyarmor(PLATE_MAIL); + } + + Square, Random; + { + Items == solstone; + } + + Square, Random; + { + Character = lobhse { Team = MONSTER_TEAM; } + } + + } + + + } +} + +Dungeon DRAGON_TOWER; +{ + Levels = 2; + + Level 0; + { + LevelMessage = "Please end my suffering"; + Description = "Dragon Tower"; + ShortDescription = "DT1"; + FillSquare = solidterrain(GRASS_TERRAIN), 0; + Size = 30, 30; + GenerateMonsters = false; + Rooms = 1; + Items = 0; + IsOnGround = true; + TeamDefault = MONSTER_TEAM; + LOSModifier = 48; + IgnoreDefaultSpecialSquares = false; + AutoReveal = true; + CanGenerateBone = false; + DifficultyBase = 50; + DifficultyDelta = 0; + EnchantmentMinusChanceBase = -15; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 0; + EnchantmentPlusChanceDelta = 0; + BackGroundType = GREEN_FRACTAL; + + RoomDefault + { + Pos = 2:36,2:36; + Size = 4:6,4:6; + AltarPossible = false; + WallSquare = solidterrain(GRASS_TERRAIN), wall(BRICK_OLD); + FloorSquare = MARBLE solidterrain(PARQUET), 0; + DoorSquare = solidterrain(GRASS_TERRAIN), BALSA_WOOD door; + GenerateDoor = false; + DivineMaster = 0; + GenerateTunnel = false; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = ROUND_CORNERS; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + GenerateWards = false; + } + + Room + { + Pos = 7,7; + Size = 15,15; + AltarPossible = false; + GenerateFountains = false; + Shape = ROUND_CORNERS; + + Square, Pos 7,7; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + } + + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(FIR); + Times = 50; + } + } +} + + +Dungeon DARK_FOREST; +{ + Levels = 11; + Description = "deep forest"; + ShortDescription = "DF"; + + LevelDefault + { + FillSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + Size = 60, 60; + Rooms = 5; + Items = 0; /* this must be zero for these levels */ + GenerateMonsters = true; + IsOnGround = true; + TeamDefault = MONSTER_TEAM; + LOSModifier = 64; + IgnoreDefaultSpecialSquares = false; + DifficultyBase = 30; + DifficultyDelta = 5; + MonsterAmountBase = 6; + MonsterAmountDelta = 3; + MonsterGenerationIntervalBase = 200; + MonsterGenerationIntervalDelta = -20; + CanGenerateBone = true; + ItemMinPriceBase = 10; + ItemMinPriceDelta = 5; + EnchantmentMinusChanceBase = -5; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 6; + EnchantmentPlusChanceDelta = 2; + BackGroundType = GREEN_FRACTAL; + AutoReveal = false; + IsCatacomb = false; + + RoomDefault + { + Pos = 2:XSize-5,2:YSize-5; + Size = 4:7,4:7; + AltarPossible = false; + WallSquare = solidterrain(GRASS_TERRAIN), LIME_STONE wall(BRICK_OLD); + FloorSquare = FIR_WOOD solidterrain(PARQUET), 0; + DoorSquare = solidterrain(GRASS_TERRAIN), FIR_WOOD door; + GenerateDoor = true; + DivineMaster = 0; + GenerateTunnel = false; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = true; + AllowBoobyTrappedDoors = true; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + GenerateWards = false; + } + } + + Level 0; + { + Size = 50, 50; + LevelMessage = "You step inside the deep forest"; + Rooms = 2; + Items = 1:5; + + RoomDefault + { + AllowLockedDoors = false; + } + + Room + { + Pos = 10,10; + Size = 6,4; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + WallSquare = solidterrain(GROUND), BASALT earth; + FloorSquare = solidterrain(GROUND), 0; + GenerateDoor = false; + + OTerrainMap + { + Pos = 0,0; + Size = 6,4; + Types + { + # = 0; + $ = GRANITE wall(BRICK_OLD); + d = FIR_WOOD door; + % = BASALT earth; + } + } + { + %%%%%% + %....% + %..$.% + %$$$d$ + } + + Square, Pos 2,2; + { + OTerrain = stairs(STAIRS_DOWN) { AttachedArea = 1; } + EntryIndex = STAIRS_DOWN; + } + } + + Room + { + Pos = 25:XSize-10,25:YSize-10; + Size = 5,5; + WallSquare = solidterrain(GRASS_TERRAIN), GRANITE wall(BRICK_OLD); + GenerateWindows = false; + AllowLockedDoors = false; + GenerateDoor = false; + GenerateLanterns = true; + + OTerrainMap + { + Pos = 0,0; + Size = 5,5; + Types + { + # = 0; + $ = GRANITE wall(BRICK_OLD); + d = FIR_WOOD door; + } + } + { + $$$$$ + $...$ + $...$ + $...$ + $$d$$ + } + + CharacterMap + { + Pos = 1,1; + Size = 3,3; + Types + { + _ = 0; + # = 0; + b = fruitbat; + + } + } + { + ..b + b.. + .b. + } + + GTerrainMap + { + Pos = 1,1; + Size = 3,3; + Types + { + # = FIR_WOOD solidterrain(PARQUET); + } + } + { + ### + ### + ### + } + + ItemMap + { + Pos = 1,1; + Size = 3,3; + Types + { + C == OAK_WOOD itemcontainer(LARGE_CHEST) { ItemsInside = { 3, Random { MaxPrice = 150; Category = WEAPON|SHIELD; ConfigFlags = NO_BROKEN; Times = 3;}, Random { MaxPrice = 150; Category = HELMET|BELT; ConfigFlags = NO_BROKEN; Enchantment = 1; Times = 3;}, Random { MaxPrice = 63; Category = CLOAK|BODY_ARMOR; ConfigFlags = NO_BROKEN; Times = 2;} } } + } + } + { + .C. + ... + ... + } + } + + Square, Pos 13, 14; + { + OTerrain = sign { Text = "UTFA Headquarters"; } + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(FIR); + Times = 300; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(PINE); + Times = 60; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(BIRCH); + Times = 30; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(OAK); + Times = 10; + } + + Square, Pos 45,5; + { + OTerrain = stairs(WAYPOINT_DEEPER) { AttachedArea = 2; AttachedEntry = WAYPOINT_SHALLOWER; } + EntryIndex = WAYPOINT_DEEPER; /*EntryIndex = STAIRS_DOWN + 1;*/ + } + + Square, Pos 12, 45; + { + Character = forestman(ROVER) { Team = FORESTMAN_TEAM; } + } + + Square, Pos 12, 43; + { + Character = forestman(ROVER) { Team = FORESTMAN_TEAM; } + } + Square, Pos 10, 43; + { + Character = forestman(ROVER) { Team = FORESTMAN_TEAM; } + } + Square, Pos 10, 45; + { + Character = forestman(ROVER) { Team = FORESTMAN_TEAM; } + } + Square, Pos 11, 44; + { + Character = forestman(BAND_LEADER) { Team = FORESTMAN_TEAM; Flags = IS_LEADER; } + } + + Square, Pos 0, 45; + { + EntryIndex = STAIRS_UP; + } + + Square, Pos 5, 45; + { + OTerrain = sign { Text = "The Deep Forest"; } + } + Square, Pos 6, 45; + { + Items == lantern; + } + + Square, Pos 32, 45; + { + Character = kobold; + } + + Square, Pos 32, 43; + { + Character = kobold; + } + Square, Pos 30, 43; + { + Character = kobold; + } + Square, Pos 30, 45; + { + Character = kobold(CHIEFTAIN); + } + Square, Pos 31, 44; + { + Character = kobold(LORD) { Flags = IS_LEADER; } + } + } + + Level 1;/* underground store */ + { + FillSquare = solidterrain(GROUND), BASALT earth; + TunnelSquare = solidterrain(GROUND), 0; + LevelMessage = "It is very dark in this underground store"; + /*Description = "Underground Store";*/ + ShortDescription = "DF Store"; + Size = 44, 38; + Rooms = 2; + IsOnGround = false; + LOSModifier = 16; + BackGroundType = GRAY_FRACTAL; + CanGenerateBone = false; + GenerateMonsters = false; + + RoomDefault + { + /*Pos = 2:XSize-5,2:YSize-5; + Size = 4:6,4:6;*/ + WallSquare = solidterrain(GROUND), GRANITE wall(BRICK_OLD); + FloorSquare = FIR_WOOD solidterrain(PARQUET), 0; + DoorSquare = FIR_WOOD solidterrain(GROUND), FIR_WOOD door; + GenerateTunnel = true; + GenerateWards = false; + } + + Room + { + Pos = 2:XSize-10,2:YSize-8; + Size = 8,5; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + GenerateTunnel = true; + + OTerrainMap + { + Pos = 0,0; + Size = 8,5; + Types + { + # = 0; + $ = GRANITE wall(BRICK_OLD); + d = FIR_WOOD door; + } + } + { + $$$$$$$$ + $......$ + $......$ + $......$ + $$$$$$$$ + } + + Square, Random IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = stairs(STAIRS_UP) { AttachedArea = 0; } + EntryIndex = STAIRS_UP; + } + + Square, Random; + { + Items == brokenbottle; + Times = 3; + } + + } + + Room + { + Pos = 2:XSize-12,2:YSize-14; + Size = 11,13; + GenerateDoor = true; + GenerateTunnel = true; + Flags = NO_MONSTER_GENERATION; + + OTerrainMap + { + Pos = 0,0; + Size = 11,13; + Types + { + # = 0; + $ = GRANITE wall(BRICK_OLD); + d = FIR_WOOD door; + = = olterraincontainer(BOOK_CASE) { ItemsInside = { 3, Random { MaxPrice = 500; Category = SCROLL; }, Random { MaxPrice = 500; Category = BOOK; }, scrollofdetectmaterial; } } + } + } + { + ........... + ........... + ..$$$$$$$.. + ..=.....$.. + ..$.....$.. + ..$.....$.. + ..$.....$.. + ..$.....$.. + ..$.....$.. + ..$.....$.. + ..$$$$$$$.. + ........... + ........... + } + + CharacterMap + { + Pos = 3,3; + Size = 5,7; + Types + { + _ = 0; + # = 0; + R = regii { Team = FORESTMAN_TEAM; } + O = UTFAOfficial { Team = FORESTMAN_TEAM; } + } + } + { + ..... + O.... + ...O. + .O... + ....O + O.... + ..R.. + } + + Square, Random; + { + Items == can { SecondaryMaterial = BANANA_FLESH; } + Times = 2; + } + } + } + + RandomLevel 2:3; + { + /*Room Dolphin Pond*/ + Room + { + Pos = 20:XSize-30,20:YSize-30; + Size = 7,7; + GenerateLanterns = false; + GenerateWindows = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + GenerateFountains = false; + AltarPossible = false; + GenerateDoor = false; + GenerateLanterns = false; + GenerateWards = false; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + + OTerrainMap + { + Pos = 0,0; + Size = 7,7; + Types + { + $ = GRANITE wall(BRICK_OLD); + } + } + { + ....... + ....... + ....... + ....... + ....... + ....... + ....... + } + + GTerrainMap + { + Pos = 0,0; + Size = 7,7; + Types + { + # = WATER liquidterrain(POOL); + B = FIR_WOOD solidterrain(PARQUET); + } + } + { + #####.. + ######. + ####### + ####### + ####### + .###### + ..##### + } + CharacterMap + { + Pos = 0,0; + Size = 7,7; + Types + { + d = dolphin; + } + } + { + ....... + ....... + ..d.... + ....... + ....... + ....... + ....... + } + } + + Square, Random NOT_IN_ROOM; + { + Character = haastseagle; + Times = 1; + } + } + + RandomLevel 2:3; + { + /*Dwarven minefield A*/ + Room + { + Pos = 2:XSize-8,2:YSize-8; + Size = 6:10, 5:8; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = ROUND_CORNERS; + GenerateDoor = false; + GenerateLanterns = false; + AltarPossible = false; + GenerateWards = false; + IsInside = false; + Flags = NO_MONSTER_GENERATION; + UseFillSquareWalls = true; + GenerateWards = false; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + + Square, Random; + { + OTerrain = STEEL sign { Text = "Dwarven mine field 214"; } + } + + Square, Random; + { + Items == Random { MinPrice = 100; Chance = 50; } + Times = 8; + } + + Square, Random; + { + Items == Random { MinPrice = 500; Chance = 50; } + Times = 2; + } + + Square, Random; + { + Items == mine { Team = MONSTER_TEAM; IsActive = true; Chance = 25; } + Times = 18; + } + + Square, Random; + { + Items == mine(BIG_MINE) { Team = MONSTER_TEAM; IsActive = true; Chance = 25; } + Times = 6; + } + } + } + + RandomLevel 3:4; + { + /*Dwarven minefield B*/ + Room + { + Pos = 2:XSize-10,2:YSize-10; + Size = 5:8, 8:10; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = ROUND_CORNERS; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + GenerateDoor = false; + GenerateLanterns = false; + AltarPossible = false; + GenerateWards = false; + IsInside = false; + Flags = NO_MONSTER_GENERATION; + UseFillSquareWalls = true; + GenerateWards = false; + + Square, Random; + { + OTerrain = STEEL sign { Text = "Dwarven mine field 625"; } + } + + Square, Random; + { + Items == Random { MinPrice = 200; Chance = 50; } + Times = 12; + } + + Square, Random; + { + Items == Random { MinPrice = 500; Chance = 50; } + Times = 2; + } + + Square, Random; + { + Items == mine { Team = MONSTER_TEAM; IsActive = true; Chance = 25; } + Times = 18; + } + + Square, Random; + { + Items == mine(BIG_MINE) { Team = MONSTER_TEAM; IsActive = true; Chance = 25; } + Times = 6; + } + } + } + + RandomLevel 3:4; + { + /*Room Rock cave with cave bears*/ + + Room /* mountain */ + { + Pos = 2:XSize-10,2:YSize-10; + Size = 8, 8; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + GenerateFountains = false; + Shape = ROUND_CORNERS; + GenerateDoor = false; + GenerateLanterns = false; + AltarPossible = false; + GenerateWards = false; + UseFillSquareWalls = true; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + + OTerrainMap + { + Pos = 0,0; + Size = 8,8; + Types + { + % = LIME_STONE earth; + d = GRANITE door; + } + } + { + .%%%%%%. + %%..%.%% + %...d..% + %..%%..% + %%%%..%% + .%%...%. + ..%..%%. + ..%..%.. + } + + CharacterMap + { + Pos = 0,0; + Size = 8,8; + + Types + { + c = bear(CAVE_BEAR); + } + } + { + ........ + ........ + ..c..... + .c...... + ........ + ........ + ........ + ........ + } + } + } + + RandomLevel 4:5; + { + Square, Random NOT_IN_ROOM; + { + Character = necromancer(APPRENTICE); + Times = 1; + } + } + + /*level 2 = chaotic shrine*/ + /*level 3 = guaranteed benign Uldra*/ + /*level 4 = shop (wherever this is, you can't generate bones)*/ + /*level 5 = Rock formation and Lawful shrine*/ + /*level 6 = catacomb with mutant bunny room and guaranteed vampire*/ + /*level 7 = Morbe's abode*/ + /*level 8 = Bridge level*/ + /*level 9 = Underground Passage*/ + /*level 10 = Prison Level*/ + + Level 2; + { + LevelMessage = "You step further inside the deep forest"; + LOSModifier = 48; + + RoomDefault + { + GenerateWards = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + } + + Room + { + Pos = 5:15,5:15; + Size = 1, 1; + GenerateDoor = false; + GenerateLanterns = false; + AltarPossible = false; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + IsInside = false; + Flags = NO_MONSTER_GENERATION; + UseFillSquareWalls = true; + GenerateWards = false; + + Square, Pos 0, 0; + { + OTerrain = stairs(WAYPOINT_SHALLOWER) { AttachedArea = 0; AttachedEntry = WAYPOINT_DEEPER; } /*STAIRS_DOWN + 1;*/ + EntryIndex = WAYPOINT_SHALLOWER; /*EntryIndex = STAIRS_UP;*/ + } + } + + Room + { + Pos = XSize-15:XSize-5,XSize-15:XSize-5; + Size = 1, 1; + GenerateDoor = false; + GenerateLanterns = false; + AltarPossible = false; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + IsInside = false; + Flags = NO_MONSTER_GENERATION; + UseFillSquareWalls = true; + GenerateWards = false; + + Square, Pos 0, 0; + { + OTerrain = stairs(WAYPOINT_DEEPER) { AttachedArea = 3; AttachedEntry = WAYPOINT_SHALLOWER; } + EntryIndex = WAYPOINT_DEEPER; + } + } + + /*holy shrine number 1*/ + Room + { + Pos = 2:XSize-8,2:YSize-9; + Size = 4:7, 4:7; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = ROUND_CORNERS; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + GenerateDoor = false; + GenerateLanterns = false; + AltarPossible = false; + GenerateWards = true; + IsInside = false; + Flags = NO_MONSTER_GENERATION; + UseFillSquareWalls = true; + + Square, Random; + { + OTerrain = BASALT altar; + } + Square, Random; + { + Items == bone; + Times = 5; + } + Square, Random; + { + Items == skull; + Times = 2; + } + + Square, Random; + { + Items == Random { MinPrice = 20; Chance = 50; } + Times = 4; + } + + Square, Random; + { + Items == lantern; + Times = 2; + } + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(FIR); + Times = 600; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(PINE); + Times = 180; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(BIRCH); + Times = 90; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(OAK); + Times = 30; + } + + Square, Random NOT_IN_ROOM; + { + Character = kobold; + Times = 4:8; + } + Square, Random NOT_IN_ROOM; + { + Character = kobold(CHIEFTAIN); + Times = 1:2; + } + Square, Random NOT_IN_ROOM; + { + Items == TIN beartrap { Team = MONSTER_TEAM; IsActive = true; } + Times = 4:8; + } + Square, Random NOT_IN_ROOM; + { + Items == BRONZE beartrap { Team = MONSTER_TEAM; IsActive = true; } + Times = 2:4; + } + Square, Random NOT_IN_ROOM; + { + Items == METEORIC_STEEL beartrap { Team = MONSTER_TEAM; IsActive = true; } + Times = 0:2; + } + } + + Level 3; + { + LOSModifier = 32; + LevelMessage = "You step further inside the deep forest"; + + RoomDefault + { + GenerateWards = false; + } + + Room + { + Pos = 5:15,(YSize-15):(YSize-5); + Size = 1, 1; + GenerateDoor = false; + GenerateLanterns = false; + AltarPossible = false; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + IsInside = false; + Flags = NO_MONSTER_GENERATION; + UseFillSquareWalls = true; + GenerateWards = false; + + Square, Pos 0, 0; + { + OTerrain = stairs(WAYPOINT_SHALLOWER) { AttachedArea = 2; AttachedEntry = WAYPOINT_DEEPER; } + EntryIndex = WAYPOINT_SHALLOWER; + } + } + + Room + { + Pos = XSize-15:XSize-5,5:15; + Size = 1, 1; + GenerateDoor = false; + GenerateLanterns = false; + AltarPossible = false; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + IsInside = false; + Flags = NO_MONSTER_GENERATION; + UseFillSquareWalls = true; + GenerateWards = false; + + Square, Pos 0, 0; + { + OTerrain = stairs(WAYPOINT_DEEPER) { AttachedArea = 4; AttachedEntry = WAYPOINT_SHALLOWER; } + EntryIndex = WAYPOINT_DEEPER; + } + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(FIR); + Times = 600; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(PINE); + Times = 180; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(BIRCH); + Times = 190; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(OAK); + Times = 30; + } + + Square, Random; + { + Items == METEORIC_STEEL beartrap { Team = MONSTER_TEAM; IsActive = true; } + Times = 2:6; + } + + Square, Random NOT_IN_ROOM; + { + Character = bat; + Times = 4; + } + Square, Random NOT_IN_ROOM; + { + Character = magpie; + Times = 2; + } + Square, Random NOT_IN_ROOM; + { + Character = kobold; + Times = 4:8; + } + Square, Random NOT_IN_ROOM; + { + Character = kobold(CHIEFTAIN); + Times = 1:2; + } + Square, Random NOT_IN_ROOM; + { + Character = kobold(LORD); + Times = 1:2; + } + Square, Random NOT_IN_ROOM; + { + Character = hunter; + Times = 1; + } + Square, Random NOT_IN_ROOM; + { + Character = bear(BLACK_BEAR); + Times = 3:6; + } + } + + Level 4; + { + LevelMessage = "You step further inside the deep forest"; + LOSModifier = 32; + Size = 80, 40; + CanGenerateBone = false; + + RoomDefault + { + GenerateWards = false; + } + + Room + { + Pos = 1:7,5:YSize-5; + Size = 1, 1; + GenerateDoor = false; + GenerateLanterns = false; + AltarPossible = false; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + IsInside = false; + Flags = NO_MONSTER_GENERATION; + UseFillSquareWalls = true; + GenerateWards = false; + + Square, Pos 0, 0; + { + OTerrain = stairs(WAYPOINT_SHALLOWER) { AttachedArea = 3; AttachedEntry = WAYPOINT_DEEPER; } + EntryIndex = WAYPOINT_SHALLOWER; + } + } + + Room + { + Pos = 70:78,5:YSize-5; + Size = 1, 1; + GenerateDoor = false; + GenerateLanterns = false; + AltarPossible = false; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + IsInside = false; + Flags = NO_MONSTER_GENERATION; + UseFillSquareWalls = true; + GenerateWards = false; + + Square, Pos 0, 0; + { + OTerrain = stairs(WAYPOINT_DEEPER) { AttachedArea = 5; AttachedEntry = WAYPOINT_SHALLOWER; } + EntryIndex = WAYPOINT_DEEPER; + } + } + + Room + { + Size = 7,6; + AltarPossible = false; + DivineMaster = MELLIS; + Type = ROOM_SHOP; + GenerateFountains = false; + WallSquare = solidterrain(GRASS_TERRAIN), LIME_STONE wall(BRICK_OLD); + FloorSquare = FIR_WOOD solidterrain(PARQUET), 0; + DoorSquare = solidterrain(GRASS_TERRAIN), FIR_WOOD door; + UseFillSquareWalls = false; + GenerateDoor = true; + GenerateTunnel = false; + GenerateLanterns = true; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + Flags = 0; + GenerateWards = false; + + CharacterMap + { + Pos = 1, 1; + Size = 5, 4; + + Types + { + g = guard(FOREST_SHOP) { Team = 5; } + s = shopkeeper(DARK_FOREST) { Team = 5; Flags = IS_MASTER; } + } + } + { + g.s.g + ..... + ..... + g...g + } + + ItemMap + { + Pos = 1, 1; + Size = 5, 4; + + Types + { + b == Random { MinPrice = 100; MaxPrice = 10000; Category = GAUNTLET|BOOT; } + a == Random { MinPrice = 500; MaxPrice = 10000; Category = HELMET|CLOAK|BODY_ARMOR|BELT; } + d == Random { MinPrice = 200; MaxPrice = 10000; Category = RING|AMULET; } + w == Random { MinPrice = 500; MaxPrice = 10000; Category = WEAPON|SHIELD; } + e == Random { MinPrice = 50; MaxPrice = 10000; Category = FOOD|POTION; } + u == Random { MinPrice = 200; MaxPrice = 10000; Category = WAND|TOOL; } + r == Random { MinPrice = 200; MaxPrice = 10000; Category = SCROLL|BOOK; } + f == wand(WAND_OF_FIRE_BALLS); + } + } + { + .e.f. + wrwwu + uarae + .abd. + } + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(FIR); + Times = 600; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(PINE); + Times = 180; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(BIRCH); + Times = 120; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(OAK); + Times = 100; + } + + Square, Random; + { + Items == METEORIC_STEEL beartrap { Team = MONSTER_TEAM; IsActive = true; } + Times = 2:6; + } + + Square, Random NOT_IN_ROOM; + { + Character = eddy; + Times = 10; + } + } + + Level 5; /* mountain level */ + { + FillSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + Size = 50, 50; + LevelMessage = "You step further inside the deep forest. In the distance you see an ominous looking rocky outcropping."; + GenerateMonsters = true; + Rooms = 3; + Items = 10:30; + IsOnGround = true; + TeamDefault = MONSTER_TEAM; + LOSModifier = 24; + IgnoreDefaultSpecialSquares = false; + CanGenerateBone = true; + BackGroundType = GREEN_FRACTAL; + DifficultyBase = 60; + IsCatacomb = false; + + RoomDefault + { + GenerateWards = false; + } + + Room /* mountain */ + { + Pos = 19, 17; + Size = 25, 25; + AllowLockedDoors = true; + AllowBoobyTrappedDoors = true; + GenerateFountains = false; + Shape = ROUND_CORNERS; + GenerateDoor = false; + GenerateLanterns = false; + AltarPossible = false; + GenerateWards = false; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(GROUND), SAND_STONE earth; + + OTerrainMap + { + Pos = 0,0; + Size = 25,25; + Types + { + % = OBSIDIAN earth; + d = OBSIDIAN door { Parameters = LOCKED; } + } + } + { + ....%%%%%%%%%%%%%..%%%%.. + .%%%%%%%%%%%%%%%%%%%%%%%. + .%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%..%%%%%%%%%%%%. + %%%%%%%%%....%%%%%%%%%%%% + ..%%%%%%%%..%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%. + %%%%%%%%%%%%%%%%%%%%%%%.. + .%%%%%%%%%%%%%%%%%%%%%... + %%%%%%%%...%%%%%%%%%%.... + %%%%%%%.....%%%%%%%%..... + .%%%%%%.....d.....d...... + %%%%%%%.....%%%%%%%%..... + %%%%%%%%...%%%%%%%%%%.... + %%%%%%%%%%%%%%%%%%%%%%... + .%%%%%%%%%%%%%%%%%%%%%%.. + ..%%%%%%%%%%%%%%%%%%%%%%. + %%%%%%%%%%%%%%%%%%%%%%%.. + %%%%%%%%%%%%%%%%%%%%%.... + .%%%%%%%%%%%%%%%%%%..%%.. + ..%%%%%%%%%%%%%%%%%...... + ..%%%%%%%%%%%%%%%%%...... + ...%%%%%%%%%%%%%%........ + ......%%%%%%%%%%%%%...... + ........%%%%%..%%%%...... + } + + GTerrainMap + { + Pos = 0,0; + Size = 25,25; + Types + { + % = LIME_STONE solidterrain(FLOOR); + _ = LIME_STONE solidterrain(FLOOR); + - = OAK_WOOD solidterrain(PARQUET); + } + } + { + ....%%%%%%%%%%%%%..%%%%.. + .%%%%%%%%%%%%%%%%%%%%%%%. + .%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%__%%%%%%%%%%%%. + %%%%%%%%%____%%%%%%%%%%%% + ..%%%%%%%%__%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%. + %%%%%%%%%%%%%%%%%%%%%%%.. + .%%%%%%%%%%%%%%%%%%%%%... + %%%%%%%%___%%%%%%%%%%.... + %%%%%%%_____%%%%%%%%..... + .%%%%%%____________...... + %%%%%%%_____%%%%%%%%..... + %%%%%%%%___%%%%%%%%%%.... + %%%%%%%%%%%%%%%%%%%%%%... + .%%%%%%%%%%%%%%%%%%%%%%.. + ..%%%%%%%%%%%%%%%%%%%%%%. + %%%%%%%%%%%%%%%%%%%%%%%.. + %%%%%%%%%%%%%%%%%%%%%.... + .%%%%%%%%%%%%%%%%%%..%%.. + ..%%%%%%%%%%%%%%%%%...... + ..%%%%%%%%%%%%%%%%%...... + ...%%%%%%%%%%%%%%........ + ......%%%%%%%%%%%%%...... + ........%%%%%..%%%%...... + } + + Square, Pos 9, 12; + { + OTerrain = stairs(STAIRS_DOWN) { AttachedArea = 6; } + EntryIndex = STAIRS_DOWN; + } + + } + + /*holy shrine number 2*/ + Room + { + Pos = 2:XSize-9,2:YSize-9; + Size = 6:8, 6:8; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = ROUND_CORNERS; + WallSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + FloorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + DoorSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + GenerateDoor = false; + GenerateLanterns = false; + AltarPossible = false; + GenerateWards = true; + IsInside = false; + Flags = NO_MONSTER_GENERATION; + UseFillSquareWalls = true; + + Square, Random; + { + OTerrain = BASALT altar; + }/* + Square, Random; + { + Items == wand; + } + Square, Random; + { + Items == scroll; + Times = 2; + }*/ + Square, Random; + { + Items == Random { MinPrice = 100; Chance = 50; } + Times = 4; + } + Square, Random; + { + Items == lantern; + Times = 2; + } + } + + Square, Pos 2,25; + { + OTerrain = stairs(WAYPOINT_SHALLOWER) { AttachedArea = 4; AttachedEntry = WAYPOINT_DEEPER; } + EntryIndex = WAYPOINT_SHALLOWER; + } + + Square, Pos 47,25; + { + OTerrain = stairs(WAYPOINT_DEEPER) { AttachedArea = 8; AttachedEntry = WAYPOINT_SHALLOWER; } + EntryIndex = WAYPOINT_DEEPER; /*STAIRS_DOWN + 1;*/ + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(FIR); + Times = 300; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(PINE); + Times = 90; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(BIRCH); + Times = 45; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(OAK); + Times = 15; + } + } + + Level 6; /* 'Catacomb' style level */ + { + FillSquare = solidterrain(GROUND), OBSIDIAN earth; + TunnelSquare = solidterrain(GROUND), 0; + LevelMessage = "It is very dark and smells musty here."; + /*Description = "Haunted Lair";*/ + ShortDescription = "DF Cave"; + Size = 36, 64; + Rooms = 12:16; + IsOnGround = false; + LOSModifier = 16; + BackGroundType = GRAY_FRACTAL; + CanGenerateBone = true; + IsCatacomb = true; + GenerateMonsters = true; + TeamDefault = MONSTER_TEAM; + IgnoreDefaultSpecialSquares = false; + MonsterGenerationIntervalBase = 100; + Items = 10:15; + + RoomDefault + { + Pos = 5:XSize-10,5:YSize-10; + Size = 5:8,5:8; + WallSquare = solidterrain(GROUND), GRANITE wall(BRICK_OLD); + FloorSquare = FIR_WOOD solidterrain(PARQUET), 0; + DoorSquare = FIR_WOOD solidterrain(PARQUET), FIR_WOOD door; + GenerateTunnel = true; + GenerateWards = true; + AltarPossible = true; + GenerateDoor = true; + DivineMaster = 0; + GenerateLanterns = true; + Type = ROOM_NORMAL; + GenerateFountains = true; + AllowLockedDoors = true; + AllowBoobyTrappedDoors = true; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + } + + Room + { + Size = 7,7; + AltarPossible = false; + AllowLockedDoors = false; + Shape = ROUND_CORNERS; + + CharacterMap + { + Pos = 1,1; + Size = 5,5; + + Types + { + f = bunny(BABY_FEMALE); + m = bunny(BABY_MALE); + F = bunny(ADULT_FEMALE); + M = bunny(ADULT_MALE); + V = vampire { Inventory = { 3, scrollofenchantweapon, scrollofenchantarmor, solstone; } } + } + } + { + .fff. + mF.Fm + mFVFm + fMFMf + .mmm. + } + } + + Square, Random; + { + Items == can { SecondaryMaterial = BANANA_FLESH; } + Times = 2; + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = stairs(STAIRS_UP) { AttachedArea = 5; AttachedEntry = STAIRS_DOWN; } + EntryIndex = STAIRS_UP; + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = stairs(STAIRS_DOWN) { AttachedArea = 7; } + EntryIndex = STAIRS_DOWN; + } + + } + + Level 7; /* Morbe's level */ + { + FillSquare = solidterrain(GROUND), OBSIDIAN earth; + TunnelSquare = solidterrain(GROUND), 0; + LevelMessage = "You smell the stench of all the world's pestillence."; + Description = "Morbe's Abode"; + /*ShortDescription = "DF Cave";*/ + Size = 40, 24; + Rooms = 1; + IsOnGround = false; + LOSModifier = 16; + BackGroundType = GRAY_FRACTAL; + AutoReveal = false; + CanGenerateBone = false; + IsCatacomb = false; + Items = 0; + GenerateMonsters = false; + TeamDefault = MONSTER_TEAM; + IgnoreDefaultSpecialSquares = false; + MonsterAmountBase = 0; + MonsterAmountDelta = 0; + MonsterGenerationIntervalBase = 0; + MonsterGenerationIntervalDelta = 0; + DifficultyBase = 30; + DifficultyDelta = 5; + + RoomDefault + { + Pos = 5:XSize-15,5:YSize-15; + Size = 5:10,5:10; + WallSquare = solidterrain(GROUND), GRANITE wall(BRICK_OLD); + FloorSquare = LIME_STONE solidterrain(GROUND), 0; + DoorSquare = LIME_STONE solidterrain(GROUND), GRANITE door; + GenerateTunnel = false; + GenerateWards = false; + AltarPossible = false; + GenerateDoor = false; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = true; + Flags = NO_MONSTER_GENERATION; + } + + Room + { + Pos = 15,7; + Size = 9,9; + DivineMaster = SCABIES; + GenerateWards = false; + + OTerrainMap + { + Pos = 1,1; + Size = 7,7; + Types + { + _ = 0; + # = 0; + $ = TIN wall(BRICK_OLD); + ^ = decoration(HOLY_TREE); + c = decoration(COUCH); + d = OAK_WOOD door; + A = BLACK_DIAMOND altar(SCABIES); + } + } + { + ...A... + ....... + ....... + ....... + ....... + ....... + ....... + } + + CharacterMap + { + Pos = 1,1; + Size = 7,7; + Types + { + # = 0; + X = morbe { Team = MORBE_TEAM; } + } + } + { + ....... + ..X.... + ....... + ....... + ....... + ....... + ....... + } + + ItemMap + { + Pos = 1,1; + Size = 7,7; + Types + { + C == COPPER OMMEL_BLOOD cauldron; + } + } + { + ....... + ....C.. + ....... + ....... + ....... + ....... + ....... + } + + Square, Pos = 4, 6; + { + OTerrain = stairs(STAIRS_UP) { AttachedArea = 6; AttachedEntry = STAIRS_DOWN; } + EntryIndex = STAIRS_UP; + } + } + } + + Level 8; /* The bridge level */ + { + FillSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + Size = 60, 30; + LevelMessage = "You step further inside the deep forest"; + GenerateMonsters = true; + Rooms = 1; + Items = 5:10; + IsOnGround = true; + TeamDefault = MONSTER_TEAM; + LOSModifier = 24; + IgnoreDefaultSpecialSquares = false; + CanGenerateBone = true; + BackGroundType = GREEN_FRACTAL; + DifficultyBase = 70; + DifficultyDelta = 0; + MonsterAmountBase = 21; + MonsterGenerationIntervalBase = 80; + MonsterGenerationIntervalDelta = 0; + IsCatacomb = false; + + RoomDefault + { + Pos = 36:50,1:20; + Size = 4:6,4:6; + AltarPossible = false; + WallSquare = WATER liquidterrain(POOL), 0; + FloorSquare = WATER liquidterrain(POOL), 0; + DoorSquare = solidterrain(GRASS_TERRAIN), FIR_WOOD door; + GenerateDoor = false; + DivineMaster = 0; + GenerateTunnel = false; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = true; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + } + + Room + { + Pos = 20:XSize-20,1; + Size = 1,1; + WallSquare = WATER liquidterrain(POOL), 0; + GenerateLanterns = false; + GenerateWindows = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + GenerateFountains = false; + AltarPossible = false; + GenerateDoor = false; + GenerateLanterns = false; + + OTerrainMap + { + Pos = 0,-1; + Size = 15,30; + Types + { + $ = GRANITE wall(BRICK_OLD); + } + } + { + ............... + ............... + ............... + ............... + ............... + ............... + ............... + ............... + ............... + ............... + ............... + ............... + ............... + ............... + ............... + ....$$$$$$$.... + ............... + ............... + ....$$$$$$$.... + ............... + ............... + ............... + ............... + ............... + ............... + ............... + ............... + ............... + ............... + ............... + } + + GTerrainMap + { + Pos = 0,-1; + Size = 15,30; + Types + { + # = WATER liquidterrain(POOL); + B = FIR_WOOD solidterrain(PARQUET); + } + } + { + #####.......... + .#####......... + ..#####........ + ...#####....... + ...#####....... + ...#####....... + ..#####........ + .#####......... + .#####......... + ..#####........ + ..#####........ + ...#####....... + ...#####....... + ....#####...... + .....#####..... + .....#####..... + ....BBBBBBB.... + ....BBBBBBB.... + .....#####..... + .....#####..... + ....#####...... + ...#####....... + ...#####....... + ...#####....... + ....#####...... + ....#####...... + ...#####....... + ...#####....... + ...#####....... + ..#####........ + } + + } + + Square, Pos 4,14; + { + OTerrain = stairs(WAYPOINT_SHALLOWER) { AttachedArea = 5; AttachedEntry = WAYPOINT_DEEPER;/* + 1; */} + EntryIndex = WAYPOINT_SHALLOWER; + } + + Square, Pos 56,20; + { + OTerrain = stairs(STAIRS_DOWN) { AttachedArea = 9; } + EntryIndex = STAIRS_DOWN; + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(FIR); + Times = 150; + } + Square, Random; + { + Items == METEORIC_STEEL beartrap { Team = MONSTER_TEAM; IsActive = true; } + Times = 0:3; + } + Square, Random NOT_IN_ROOM; + { + Items == STEEL beartrap { Team = MONSTER_TEAM; IsActive = true; } + Times = 0:3; + } + Square, Random NOT_IN_ROOM; + { + Items == ADAMANT beartrap { Team = MONSTER_TEAM; IsActive = true; } + Times = 0:1; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(BIRCH); + Times = 50; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(OAK); + Times = 20; + } + } + + Level 9; /* Underground Passage */ + { + FillSquare = solidterrain(GROUND), OBSIDIAN earth; + TunnelSquare = solidterrain(GROUND), 0; + /*Description = "Underground Passage";*/ + ShortDescription = "DF Passage"; + Size = 54, 54; + Rooms = 8:12; + Items = 5:10; + IsOnGround = false; + LOSModifier = 16; + BackGroundType = GRAY_FRACTAL; + CanGenerateBone = true; + IsCatacomb = false; + GenerateMonsters = true; + TeamDefault = MONSTER_TEAM; + IgnoreDefaultSpecialSquares = false; + MonsterAmountBase = 24; + MonsterGenerationIntervalBase = 60; + DifficultyBase = 90; + + RoomDefault + { + Pos = 2:XSize-10,2:YSize-10; + Size = 5:8,5:8; + WallSquare = solidterrain(GROUND), GRANITE wall(BRICK_OLD); + FloorSquare = FIR_WOOD solidterrain(PARQUET), 0; + DoorSquare = FIR_WOOD solidterrain(PARQUET), FIR_WOOD door; + GenerateTunnel = true; + GenerateWards = false; + AltarPossible = true; + GenerateDoor = true; + DivineMaster = 0; + GenerateLanterns = true; + Type = ROOM_NORMAL; + GenerateFountains = true; + AllowLockedDoors = true; + AllowBoobyTrappedDoors = true; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + } + + Room + { + Size = 7,7; + AltarPossible = false; + AllowLockedDoors = false; + Shape = ROUND_CORNERS; + + CharacterMap + { + Pos = 1,1; + Size = 5,5; + + Types + { + a = alchemist; + w = warlock; + s = shaman; + } + } + { + ..... + .awa. + .s.s. + .awa. + ..... + } + } + + Room + { + Size = 6,6; + AltarPossible = false; + AllowLockedDoors = false; + Shape = ROUND_CORNERS; + + ItemMap + { + Pos = 1,1; + Size = 4,4; + + Types + { + a == Random { MinPrice = 40; MaxPrice = 400; Category = HELMET|CLOAK|BODY_ARMOR; } + w == Random { MinPrice = 20; MaxPrice = 500; Category = WEAPON; } + } + } + { + .aa. + waaw + wwww + .ww. + } + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = stairs(STAIRS_UP) { AttachedArea = 8; AttachedEntry = STAIRS_DOWN; } + EntryIndex = STAIRS_UP; + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = stairs(STAIRS_UP) { AttachedArea = 10; AttachedEntry = STAIRS_UP; } + EntryIndex = STAIRS_UP + 1; + } + } + + Level 10; /* Prison Level */ + { + FillSquare = solidterrain(DARK_GRASS_TERRAIN), 0; + Size = 48, 48; + LevelMessage = "You sneak into the prison within the deep forest. You enter through the sewer, (ugh)."; + Description = "the deep forest Prison level"; + ShortDescription = "DF Prison"; + GenerateMonsters = true; + Rooms = 1; + Items = 1:5; + IsOnGround = true; + TeamDefault = MONSTER_TEAM; + LOSModifier = 32; + IgnoreDefaultSpecialSquares = false; + CanGenerateBone = false; + BackGroundType = GREEN_FRACTAL; + DifficultyBase = 70; + DifficultyDelta = 0; + MonsterAmountBase = 40; + MonsterAmountDelta = 0; + MonsterGenerationIntervalBase = 60; + MonsterGenerationIntervalDelta = 0; + IsCatacomb = false; + + RoomDefault + { + Pos = 2:36,2:36; + Size = 4:6,4:6; + AltarPossible = false; + WallSquare = solidterrain(GRASS_TERRAIN), wall(BRICK_OLD); + FloorSquare = FIR_WOOD solidterrain(PARQUET), 0; + DoorSquare = solidterrain(GRASS_TERRAIN), FIR_WOOD door; + GenerateDoor = false; + DivineMaster = 0; + GenerateTunnel = false; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = true; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + + } + + Room /* PRISON */ + { + Pos = 7,3; + Size = 34, 23; + WallSquare = solidterrain(GRASS_TERRAIN), GRANITE wall(BRICK_OLD); + FloorSquare = FIR_WOOD solidterrain(PARQUET), 0; + GenerateLanterns = true; + GenerateWindows = false; + AllowLockedDoors = true; + AllowBoobyTrappedDoors = true; + GenerateFountains = false; + AltarPossible = false; + GenerateDoor = false; + GenerateLanterns = false; + Flags = NO_MONSTER_GENERATION; + + OTerrainMap + { + Pos = 0,0; + Size = 34,23; + Types + { + # = 0; + $ = GRANITE wall(BRICK_OLD); + % = OCTIRON wall(BRICK_OLD); + d = FIR_WOOD door; + D = STEEL door(PENTAGONAL_LOCK) { Parameters = LOCKED; } + f = fountain; + b = STEEL barwall; + @ = STEEL door(BARDOOR|PENTAGONAL_LOCK) { Parameters = LOCKED; } + B = STEEL brokendoor(BARDOOR); + k = OCTIRON barwall; + } + } + { + $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + $%%%%%%%...$$$$$$$$$$$$$.......$.$ + $%..%..%$$$$..$..$..$..$$$%%%%%%$$ + $%..%..D...$..$..$..$..$$$%....%.$ + $%%%%%%%...$b@$bB$b@$b@$$$%....%.$ + $%..%..D..................D....%.$ + $%..%..k..................%....%.$ + $%%D%%%%.....$b@$b@$b@$bB$%%%%%%.$ + $.$.......$..$..$..$..$..$$$$$$$.$ + $.$.......$..$..$..$..$..$$....$.$ + $$$$$$$$$$$..$$$$$$$$$$$$$$$$$$$$$ + $.$.....$......................$.$ + $.$.....$......................$.$ + $.$.....d......................$.$ + $.$.....$.....$.$.$.$..........$.$ + $.$.....$....$$$$$$$$....$.....$.$ + $.$.....$....$.$.$.$.....$.....$.$ + $.$$$$$$$................$..f..$.$ + $.$.....d................$.....$.$ + $.$.....$................$.....$.$ + $$$$$$$$$$$$$$$$d$$$$$$$$$$$$$$$$$ + $.$............$.$.............$.$ + $$$$$$$$$$$$$$$$d$$$$$$$$$$$$$$$$$ + } + + CharacterMap + { + Pos = 0,0; + Size = 34, 23; + Types + { + _ = 0; + $ = 0; + % = 0; + b = 0; + B = 0; + d = 0; + D = 0; + @ = 0; + k = 0; + o = orc(OFFICER); + j = goblin(JAILER); + W = goblin(PRISON_WARDEN); + m = mistress { Team = NEW_ATTNAM_TEAM; } + w = werewolfwolf { Team = NEW_ATTNAM_TEAM; } + c = chameleon { Team = NEW_ATTNAM_TEAM; } + s = slave(DARK_FOREST) { Team = NEW_ATTNAM_TEAM; } + S = femaleslave(NEW_ATTNAM) { Team = NEW_ATTNAM_TEAM; } + F = forestman(ROVER) { Team = NEW_ATTNAM_TEAM; } + C = dog; + H = housewife(DARK_FOREST) {Team = FORESTMAN_TEAM;} + M = mangogrower {Team = NEW_ATTNAM_TEAM;} + } + } + { + $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + $%%%%%%%...$$$$$$$$$$$$$.......$.$ + $%..%w.%$$$$H.$..$s.$M.$$$%%%%%%$$ + $%..%..D..j$..$..$..$..$$$%....%.$ + $%%%%%%%...$b@$bB$b@$b@$$$%....%.$ + $%c.%m.D..................D....%.$ + $%..%..k.................j%....%.$ + $%%D%%%%.....$b@$b@$b@$bB$%%%%%%.$ + $.$.......$..$..$..$..$..$$$$$$$.$ + $.$.j.....$..$.S$.s$C.$..$$....$.$ + $$$$$$$$$$$..$$$$$$$$$$$$$$$$$$$$$ + $.$.....$......................$.$ + $.$.....$......................$.$ + $.$.j...d......................$.$ + $.$.....$.....$.$.$.$..........$.$ + $.$.....$....$$$$$$$$....$.....$.$ + $.$..W..$....$.$.$.$.....$.....$.$ + $.$$$$$$$................$.....$.$ + $.$.....d................$.....$.$ + $.$.....$................$.....$.$ + $$$$$$$$$$$$$$$$d$$$$$$$$$$$$$$$$$ + $.$............$.$.............$.$ + $$$$$$$$$$$$$$$$d$$$$$$$$$$$$$$$$$ + } + + ItemMap + { + Pos = 0,0; + Size = 34,23; + Types + { + $ == 0; + % == 0; + b == bone; + s == skull; + B == backpack; + C == TIN itemcontainer(SMALL_CHEST) { Parameters = ~LOCKED; ItemsInside = { 1, key(PENTAGONAL_LOCK); } } + 2 == lantern { SquarePosition = DOWN; } + 3 == lantern { SquarePosition = RIGHT; } + 4 == lantern { SquarePosition = LEFT; } + 5 == lantern { SquarePosition = UP; } + a == Random { MinPrice = 40; MaxPrice = 80; Category = HELMET|CLOAK|BODY_ARMOR; } + w == Random { MinPrice = 20; MaxPrice = 60; Category = WEAPON; } + c == constitution; + M == mangoseedling; + } + } + { + $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + $%%%%%%%...$$$$$$$$$$$$$..b....$.$ + $%.b%..%$2$$..$b.$..$..$$$%%%%%%$$ + $%s.%......$..$..$..$..$$$%M..c%.$ + $%%%%%%%...2..$..$..$..$2$%....%.$ + $%..%..........................%.$ + $%..%.....................%...B%.$ + $%%.%%%%.....$..5..$..$..$%%%%%%.$ + $.$.......$..$..$..$b.$..$$$$$$$.$ + $.$.......$..$..$..$.s$..$$....$.$ + $$$$$$5$$$$..$$$2$$$$$$$$2$$$$$$$$ + $.$.....$......................$.$ + $.$.....$......................$.$ + $.4............................$s$ + $.$.....$.....$.$.$.5..........$.$ + $.$.....4....$$$$$$$$....3.....4.$ + $b$C....$....$.$.$.$.....$.....$.$ + $.$$$$$$$................$.....$.$ + $b$aw.a..................$.....$.$ + $.$.aw.w$................$.....$.$ + $$$$$$$$$$$$5$$$.$$$$5$$$$$$$$$$$$ + $.$....s...b...$.$......b..b...$.$ + $$$$$$$$$$$$$$$$.$$$$$$$$$$$$$$$$$ + } + } + + Square, Pos 35, 21; /*entry via the sewer, no returns */ + { + EntryIndex = STAIRS_UP; + } + +/* this works, for posterity + Square, Pos 5,5; + { + OTerrain = stairs(STAIRS_UP) { AttachedArea = 9; AttachedEntry = STAIRS_UP + 1; } + EntryIndex = STAIRS_UP; + }*/ + + Square, Random NOT_IN_ROOM; + { + Character = goblin(BERSERKER); + Times = 8; + } + Square, Random NOT_IN_ROOM; + { + Character = goblin(BUTCHER); + Times = 4; + } + Square, Random NOT_IN_ROOM; + { + Character = orc(SLAUGHTERER); + Times = 4; + } + Square, Random NOT_IN_ROOM; + { + Character = orc(SQUAD_LEADER); + Times = 2; + } + Square, Random NOT_IN_ROOM; + { + Character = orc; + Times = 12; + } + Square, Random NOT_IN_ROOM; + { + Character = orc(OFFICER); + Times = 1; + } + Square, Random NOT_IN_ROOM; + { + Character = bat; + Times = 2; + } + Square, Random NOT_IN_ROOM; + { + Character = magpie; + Times = 2; + } + Square, Random NOT_IN_ROOM; + { + Character = kobold(LORD); + Times = 12; + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(FIR); + Times = 100; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(BIRCH); + Times = 30; + } + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(OAK); + Times = 10; + } + } +} + +Dungeon MUNTUO; +{ + Levels = 2; + + Level 0; + { + LevelMessage = "You descend into a clearing full of tame animals."; + Description = "Muntuo, the Fastness of Solicitus"; + ShortDescription = "Muntuo"; + FillSquare = solidterrain(GRASS_TERRAIN), 0; + Size = 50, 42; + GenerateMonsters = false; + Rooms = 1; + Items = 0; + IsOnGround = true; + TeamDefault = SOLICITUS_TEAM; + LOSModifier = 48; + IgnoreDefaultSpecialSquares = false; + CanGenerateBone = false; + DifficultyBase = 50; + DifficultyDelta = 0; + EnchantmentMinusChanceBase = -15; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 0; + EnchantmentPlusChanceDelta = 0; + BackGroundType = GREEN_FRACTAL; + + RoomDefault + { + Pos = 2:36,2:36; + Size = 4:6,4:6; + AltarPossible = false; + WallSquare = solidterrain(GRASS_TERRAIN), wall(BRICK_OLD); + FloorSquare = MARBLE solidterrain(PARQUET), 0; + DoorSquare = solidterrain(GRASS_TERRAIN), BALSA_WOOD door; + GenerateDoor = false; + DivineMaster = 0; + GenerateTunnel = false; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = RECTANGLE; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + GenerateWards = false; + } + + Room + { + Type = ROOM_NORMAL; + Pos = 4,5; + Size = 42,16; + WallSquare = solidterrain(GRASS_TERRAIN), TIN wall(BRICK_OLD); + FloorSquare = LIME_STONE solidterrain(PARQUET), 0; + GenerateDoor = false; + DivineMaster = LEGIFER; + GenerateLanterns = false; + GenerateWindows = false; + + OTerrainMap + { + Pos = 0,0; + Size = 42,16; + Types + { + _ = 0; + # = 0; + $ = TIN wall(BRICK_OLD); + ^ = decoration(HOLY_TREE); + c = decoration(COUCH); + d = OAK_WOOD door; + f = fountain { SecondaryMaterial = LIQUID_HORROR; } + } + } + { + ########################################## + #................$$$$$$$$................# + #................$$$$$$$$................# + #.................$$..$$.................# + #................$$$..$$$................# + #.$$$$$.........$$$....$$$.........$$$$$.# + #$$...$$......$$$........$$$......$$...$$# + #$.....$....$$$............$$$....$.....$# + #$..f..$$$$$$................$$$$$$.....$# + #.......$$$$..................$$$$.......# + #........$$....................$$........# + #........................................# + #$.......$$....................$$.......$# + #$$.....$$$....................$$$.....$$# + #.$$$..$$.$$..................$$.$$..$$$.# + ####################dd#################### + } + + CharacterMap + { + Pos = 0,0; + Size = 42,16; + Types + { + # = 0; + g = guard(SENTINEL) { Team = SOLICITUS_TEAM; } + S = solicitus{ Team = SOLICITUS_TEAM; Flags = IS_MASTER; } + A = insudo { Team = SOLICITUS_TEAM; } + P = priest(SOLICITU) { Team = SOLICITUS_TEAM; } + /*X = morbe { Team = MORBE_TEAM; }*/ + } + } + { + ########################################## + #................########................# + #................########................# + #.................##S.##.................# + #................###..###................# + #.#####.........###....###.........#####.# + ###...##......###.g....g.###......##...### + ##.....#....###.....P......###....#.....## + ##.....######................######..A..## + #.......####..................####.......# + #.......g##....................##g.......# + #........................................# + ##......g##....................##g......## + ###.....###....................###.....### + #.###..##.##......g....g......##.##..###.# + ####################..#################### + } + + GTerrainMap + { + Pos = 0,0; + Size = 42,16; + Types + { + # = LIME_STONE solidterrain(FLOOR); + _ = LIME_STONE solidterrain(FLOOR); + - = OAK_WOOD solidterrain(PARQUET); + } + } + { + ########################################## + #................########................# + #................########................# + #.................##__##.................# + #................###__###................# + #.#####.........###____###.........#####.# + ###___##......###________###......##___### + ##_____#....###____________###....#_____## + ##_____######________________######_____## + #_______####__________________####_______# + #________##____________________##________# + #________________________________________# + ##_______##____________________##_______## + ###_____###____________________###_____### + #.###__##.##__________________##.##__###.# + ####################--#################### + } + + ItemMap + { + Pos = 0,0; + Size = 42,16; + Types + { + # == 0; + _ == 0; + 2 == lantern { SquarePosition = DOWN; } + 3 == lantern { SquarePosition = RIGHT; } + 4 == lantern { SquarePosition = LEFT; } + 5 == lantern { SquarePosition = UP; } + /*E == EMERALD OCTIRON eptyron { Enchantment = 5; LifeExpectancy = 2000:5000;} */ + /*f == GLASS LIQUID_HORROR potion;*/ + /*v == GLASS VINEGAR potion;*/ + /*C == COPPER OMMEL_BLOOD cauldron;*/ + /*e == COPPER WATER cauldron;*/ + /*t == taiaha;*/ + } + } + { + ########################################## + #................########................# + #................########................# + #.................##..##.................# + #................###..###................# + #.##2##.........###....###.........##2##.# + ###...##......###........###......##...### + ##.....#....###............###....#.....## + ##.....######................######.....## + #.......####..................####.......# + 3........##....................##........4 + #........................................# + ##.......#3....................4#.......## + ###.....###....................###.....### + #.##5..##.##..................##.##..5##.# + #################5##..##5################# + } + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(BIRCH); + Times = 50; + } + + Square, Random NOT_IN_ROOM; + { + Character = okapi { Team = SOLICITUS_TEAM; } + } + + Square, Random NOT_IN_ROOM; + { + Character = mouse(FIELD_MOUSE) { Team = SOLICITUS_TEAM; } + } + + Square, Random NOT_IN_ROOM; + { + Character = firefox { Team = SOLICITUS_TEAM; } + } + + Square, Random NOT_IN_ROOM; + { + Character = carnivorousplant { Team = SOLICITUS_TEAM; } + Times = 9:18; + } + + Square, Random NOT_IN_ROOM; + { + Character = carnivorousplant(GREATER) { Team = SOLICITUS_TEAM; } + Times = 3:6; + } + + Square, Random NOT_IN_ROOM; + { + Character = carnivorousplant(GIANT) { Team = SOLICITUS_TEAM; } + Times = 1:2; + } + + Square, Pos 25, 41; + { + EntryIndex = STAIRS_UP; + } + } +} + +Dungeon XINROCH_TOMB; +{ + Levels = 2; + + Level 0; + { + LevelMessage = "Please end my suffering also"; + Description = "Tomb of Xinroch"; + ShortDescription = "TX"; + FillSquare = solidterrain(GRASS_TERRAIN), 0; + Size = 30, 30; + GenerateMonsters = false; + Rooms = 1; + Items = 0; + IsOnGround = true; + TeamDefault = ATTNAM_TEAM; + LOSModifier = 48; + IgnoreDefaultSpecialSquares = false; + AutoReveal = false; + CanGenerateBone = false; + DifficultyBase = 50; + DifficultyDelta = 0; + EnchantmentMinusChanceBase = -15; + EnchantmentMinusChanceDelta = 0; + EnchantmentPlusChanceBase = 0; + EnchantmentPlusChanceDelta = 0; + BackGroundType = GREEN_FRACTAL; + + RoomDefault + { + Pos = 2:36,2:36; + Size = 4:6,4:6; + AltarPossible = false; + WallSquare = solidterrain(GRASS_TERRAIN), wall(BRICK_OLD); + FloorSquare = MARBLE solidterrain(PARQUET), 0; + DoorSquare = solidterrain(GRASS_TERRAIN), BALSA_WOOD door; + GenerateDoor = false; + DivineMaster = 0; + GenerateTunnel = false; + GenerateLanterns = false; + Type = ROOM_NORMAL; + GenerateFountains = false; + AllowLockedDoors = false; + AllowBoobyTrappedDoors = false; + Shape = ROUND_CORNERS; + IsInside = true; + GenerateWindows = false; + UseFillSquareWalls = false; + Flags = 0; + GenerateWards = false; + } + + Room + { + Pos = 7,7; + Size = 15,15; + AltarPossible = false; + GenerateFountains = false; + Shape = ROUND_CORNERS; + + Square, Pos 7,7; + { + OTerrain = stairs(STAIRS_UP); + EntryIndex = STAIRS_UP; + } + + } + + Square, Random NOT_IN_ROOM|HAS_NO_OTERRAIN; + { + OTerrain = decoration(FIR); + Times = 50; + } + + } +} + diff --git a/Script/glterra.dat b/Script/glterra.dat new file mode 100644 index 0000000..890cc80 --- /dev/null +++ b/Script/glterra.dat @@ -0,0 +1,156 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* + * NOTICE!!! + * + * This file contains SPOILERS, which might ruin your IVAN experience + * totally. Also, editing anything can DESTROY GAME BALANCE or CAUSE + * OBSCURE BUGS if you don't know what you're doing. So from here on, + * proceed at your own risk! + */ + +/* Numerical glterrain data loaded during game startup */ + +/* Default values: */ + +glterrain +{ + /* Obligatory: BitmapPos */ + UsesLongArticle = false; + Adjective = ""; + UsesLongAdjectiveArticle = false; + /* Obligatory: NameSingular */ + /* NameSingular + "s" by default: NamePlural */ + PostFix = ""; + ArticleMode = 0; + /* Obligatory: MainMaterialConfig */ + /* Obligatory: SecondaryMaterialConfig */ + /* Obligatory if multiple material configurations defined: MaterialConfigChances */ + IsAbstract = true; /* This is false by default and does not inherit! */ + OKVisualEffects = NONE; + MaterialColorB = 0; + MaterialColorC = 0; + MaterialColorD = 0; + SitMessage = "You sit for some time. Nothing happens."; + ShowMaterial = true; + AttachedGod = NONE; + HasSecondaryMaterial = false; + UseBorderTiles = false; + BorderTilePriority = 0; +} + +solidterrain /* glterrain-> */ +{ + Walkability = WALK|FLY|ETHEREAL; + + IsAbstract = true; + + Config PARQUET; + { + MainMaterialConfig == FIR_WOOD; + NameSingular = "floor"; + BitmapPos = 32, 0; + BorderTilePriority = 100; + } + + Config FLOOR; + { + OKVisualEffects = MIRROR|FLIP|ROTATE; + MainMaterialConfig == GRAVEL; + NameSingular = "floor"; + BitmapPos = 0, 0; + } + + Config GROUND; + { + OKVisualEffects = MIRROR|FLIP|ROTATE; + MainMaterialConfig == GRAVEL; + NameSingular = "floor"; + BitmapPos = 0, 0; + } + + Config GRASS_TERRAIN; + { + OKVisualEffects = MIRROR|FLIP|ROTATE; + MainMaterialConfig == GRASS; + NameSingular = "ground"; + BitmapPos = 16, 16; + } + + Config DEAD_GRASS_TERRAIN; + { + OKVisualEffects = MIRROR|FLIP|ROTATE; + MainMaterialConfig == DEAD_GRASS; + NameSingular = "ground"; + BitmapPos = 16, 16; + } + + Config LANDING_SITE; + { + MainMaterialConfig == GRASS; + MaterialColorB = rgb16(230, 230, 230); + UsesLongAdjectiveArticle = true; + Adjective = "ostrich"; + NameSingular = "landing site"; + BitmapPos = 32, 16; + ShowMaterial = false; + } + + Config SNOW_TERRAIN; + { + OKVisualEffects = MIRROR|FLIP|ROTATE; + MainMaterialConfig == SNOW; + NameSingular = "ground"; + BitmapPos = 16, 16; + } + + Config DARK_GRASS_TERRAIN; + { + OKVisualEffects = MIRROR|FLIP|ROTATE; + MainMaterialConfig == DARK_GRASS; + NameSingular = "ground"; + BitmapPos = 16, 16; + } + + Config SAND_TERRAIN; + { + OKVisualEffects = MIRROR|FLIP|ROTATE; + MainMaterialConfig == SAND; + NameSingular = "ground"; + BitmapPos = 32, 32; + } +} + +liquidterrain /* glterrain-> */ +{ + MainMaterialConfig == WATER; + IsAbstract = true; + Walkability = SWIM|FLY|ETHEREAL; + ShowMaterial = false; + + Config POOL; + { + NameSingular = "pool"; + SitMessage = "You sit on the pool. Oddly enough, you sink. You feel stupid."; + } + + Config UNDERGROUND_LAKE; + { + UsesLongAdjectiveArticle = true; + Adjective = "underground"; + NameSingular = "lake"; + /*SitMessage = "You sit on the pool. Oddly enough, you sink. You feel stupid.";*/ + UseBorderTiles = true; + BorderTilePriority = 10; + } +} \ No newline at end of file diff --git a/Script/item.dat b/Script/item.dat new file mode 100644 index 0000000..d922661 --- /dev/null +++ b/Script/item.dat @@ -0,0 +1,5501 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* + * NOTICE!!! + * + * This file contains SPOILERS, which might ruin your IVAN experience + * totally. Also, editing anything can DESTROY GAME BALANCE or CAUSE + * OBSCURE BUGS if you don't know what you're doing. So from here on, + * proceed at your own risk! + */ + +/* Numerical item data loaded during game startup */ + +/* Default values: */ + +item +{ + Possibility = 0; + IsDestroyable = true; + CanBeWished = true; + IsMaterialChangeable = true; + WeaponCategory = UNCATEGORIZED; + IsPolymorphSpawnable = true; + IsAutoInitializable = true; + Category = MISC; + FireResistance = 0; + PoisonResistance = 0; + ElectricityResistance = 0; + AcidResistance = 0; + StrengthModifier = 0; + /* Obligatory: FormModifier */ + /* Obligatory: DefaultSize */ + DefaultMainVolume = 0; + DefaultSecondaryVolume = 0; + /* Obligatory: BitmapPos */ + Price = 0; + BaseEmitation = 0; + UsesLongArticle = false; + Adjective = ""; + UsesLongAdjectiveArticle = false; + /* Obligatory: NameSingular */ + /* NameSingular + "s" by default: NamePlural */ + PostFix = ""; + ArticleMode = 0; + /* Obligatory: MainMaterialConfig */ + /* Obligatory: SecondaryMaterialConfig */ + /* Obligatory if multiple material configurations defined: MaterialConfigChances */ + IsAbstract = true; /* This is false by default and does not inherit! */ + IsPolymorphable = true; + OKVisualEffects = NONE; + CanBeGeneratedInContainer = true; + /* Obligatory: Roundness */ + GearStates = 0; + IsTwoHanded = false; + CreateDivineConfigurations = false; + ForcedVisualEffects = 0; + CanBeCloned = true; + CanBeBroken = false; + MinCharges = 0; + MaxCharges = 0; + CanBePiled = true; + StorageVolume = 0; + MaxGeneratedContainedItems = 0; + AffectsArmStrength = false; + AffectsLegStrength = false; + AffectsDexterity = false; + AffectsAgility = false; + AffectsEndurance = false; + AffectsPerception = false; + AffectsIntelligence = false; + AffectsWisdom = false; + AffectsWillPower = false; + AffectsCharisma = false; + AffectsMana = false; + BaseEnchantment = 0; + PriceIsProportionalToEnchantment = false; + CanBeUsedBySmith = false; + AffectsCarryingCapacity = false; + HandleInPairs = false; + CanBeEnchanted = false; + /* Obligatory if wand: BeamColor */ + /* Obligatory if wand: BeamEffect */ + BeamStyle = PARTICLE_BEAM; + WearWisdomLimit = NO_LIMIT; + AttachedGod = NONE; + /* Obligatory: AttachedGod */ + WieldedBitmapPos = 176, 0; + IsQuestItem = false; + IsGoodWithPlants = true; + CreateLockConfigurations = false; /* Can't be overridden by Configs */ + CanBePickedUp = true; + /* Obligatory if helmet: CoverPercentile */ + /* Obligatory for respective armor: + TorsoArmorBitmapPos + ArmArmorBitmapPos + LegArmorBitmapPos + HelmetBitmapPos + CloakBitmapPos + BeltBitmapPos + GauntletBitmapPos + BootBitmapPos */ + HasSecondaryMaterial = false; + AllowEquip = true; + /* Obligatory if scroll: ReadDifficulty */ + IsValuable = true; + EnchantmentMinusChance = 0; + EnchantmentPlusChance = 0; + TeleportPriority = 100; + HasNormalPictureDirection = true; + DamageFlags = BLUNT; + IsKamikazeWeapon = false; + FlexibilityIsEssential = false; + IsSadistWeapon = false; + IsThrowingWeapon = false; + ThrowItemTypes = NONE; /*Possibilities: THROW_BONE|THROW_POTION|THROW_AXE|THROW_GAS_GRENADE|THROW_WAND;*/ +} + +materialcontainer +{ + IsAbstract = true; + HasSecondaryMaterial = true; +} + +lantern +{ + DefaultSize = 30; + Possibility = 100; + Category = TOOL; + DefaultMainVolume = 400; + StrengthModifier = 50; + FormModifier = 20; + Price = 10; + BaseEmitation = rgb24(125, 125, 105); + NameSingular = "lantern"; + MainMaterialConfig == GLASS; + Alias == "red lantern"; + Roundness = 75; + CanBeBroken = true; + BitmapPos = 0, 256; + WallBitmapPos = 0, 192; + AttachedGod = LEGIFER; + WieldedBitmapPos = 160, 160; + + Config BROKEN; + { + DefaultSize = 15; + Possibility = 10; + FormModifier = 25; + Price = 0; + BaseEmitation = 0; + BitmapPos = 0, 304; + WallBitmapPos = 0, 208; + WieldedBitmapPos = 176, 160; + } +} +/* +christmaslight +{ + DefaultSize = 30; + Possibility = 0; + Category = TOOL; + DefaultMainVolume = 400; + StrengthModifier = 50; + FormModifier = 20; + Price = 10; + BaseEmitation = rgb24(125, 125, 105); + NameSingular = "Atavus' Day light"; + MainMaterialConfig == GLASS; + Alias == "christmas light"; + Roundness = 75; + CanBeBroken = true; + BitmapPos = 0, 256; + WallBitmapPos = 0, 192; + AttachedGod = LEGIFER; + WieldedBitmapPos = 160, 160; + + Config BROKEN; + { + DefaultSize = 15; + Possibility = 10; + FormModifier = 25; + Price = 0; + BaseEmitation = 0; + BitmapPos = 0, 304; + WallBitmapPos = 0, 208; + WieldedBitmapPos = 176, 160; + } +} +*/ +/*theredlantern +{ + DefaultSize = 30; + Possibility = 1; + Category = TOOL; + DefaultMainVolume = 400; + StrengthModifier = 50; + FormModifier = 30; + Price = 10; + BaseEmitation = rgb24(125, 125, 105); + NameSingular = "lantern"; + MainMaterialConfig == RUBY; + Alias == "light"; + Roundness = 85; + CanBeBroken = true; + BitmapPos = 0, 256; + WallBitmapPos = 0, 192; + AttachedGod = LEGIFER; + WieldedBitmapPos = 160, 160; + + Config BROKEN; + { + DefaultSize = 15; + Possibility = 10; + FormModifier = 25; + Price = 0; + BaseEmitation = 0; + BitmapPos = 0, 304; + WallBitmapPos = 0, 208; + WieldedBitmapPos = 176, 160; + } +} +*/ +can /* materialcontainer-> */ +{ + DefaultSize = 10; + Possibility = 450; + Category = FOOD; + DefaultMainVolume = 50; + DefaultSecondaryVolume = 500; + StrengthModifier = 50; + /* BitmapPos depends on being full or not */ + FormModifier = 15; + NameSingular = "can"; + /* Adjective and PostFix overridden */ + MainMaterialConfig == IRON; + SecondaryMaterialConfig = {10 , BANANA_FLESH, SCHOOL_FOOD, MUSHROOM_FLESH, PEA_SOUP, OMMEL_CERUMEN, OMMEL_SNOT, FLOATING_EYE_FLESH, CAT_FLESH, RAT_FLESH, PINEAPPLE_FLESH; } + MaterialConfigChances = { 10, 750, 5, 25, 25, 10, 10, 25, 20, 20, 100; } + Roundness = 60; + AttachedGod = NONE; + WieldedBitmapPos = 176, 144; + IsValuable = false; +} + +lump +{ + DefaultSize = 10; + Possibility = 400; + Category = FOOD; + DefaultMainVolume = 500; + StrengthModifier = 75; + BitmapPos = 16, 48; + FormModifier = 10; + NameSingular = "lump"; + MainMaterialConfig = { 2, BANANA_FLESH, SCHOOL_FOOD; } + MaterialConfigChances = { 2, 75, 5; } + OKVisualEffects = MIRROR|FLIP; + Roundness = 75; + AttachedGod = NONE; + WieldedBitmapPos = 160, 112; + IsValuable = false; +} + +meleeweapon +{ + Category = WEAPON; + IsAbstract = true; + CanBeBroken = true; + CanBePiled = false; + CanBeEnchanted = true; + HasSecondaryMaterial = true; + TeleportPriority = 500; + + Config LONG_SWORD; + { + DefaultSize = 120; + Possibility = 100; + WeaponCategory = LARGE_SWORDS; + DefaultMainVolume = 130; + DefaultSecondaryVolume = 45; + BitmapPos = 16, 336; + FormModifier = 90; + StrengthModifier = 100; + NameSingular = "long sword"; + MainMaterialConfig = { 10, BIRCH_WOOD, OAK_WOOD, TEAK_WOOD, EBONY_WOOD, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig = { 10, BIRCH_WOOD, OAK_WOOD, TEAK_WOOD, EBONY_WOOD, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + MaterialConfigChances = { 10, 200, 150, 100, 50, 1000, 750, 100, 25, 25, 5; } + Roundness = 10; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 160, 16; + EnchantmentPlusChance = 5; + DamageFlags = SLASH|PIERCE; + } + + Config BROKEN|LONG_SWORD; + { + Possibility = 10; + BitmapPos = 32, 208; + WieldedBitmapPos = 176, 80; + MainMaterialConfig = { 5, EBONY_WOOD, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + SecondaryMaterialConfig = { 5, EBONY_WOOD, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + MaterialConfigChances = { 5, 25, 100, 25, 25, 10; } + EnchantmentPlusChance = 10; + } + + Config TWO_HANDED_SWORD; + { + DefaultSize = 150; + Possibility = 75; + WeaponCategory = LARGE_SWORDS; + DefaultMainVolume = 350; + DefaultSecondaryVolume = 150; + StrengthModifier = 200; + BitmapPos = 0, 0; + FormModifier = 135; + NameSingular = "two-handed sword"; + MainMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + MaterialConfigChances = { 5, 2000, 100, 25, 25, 5; } + Roundness = 15; + IsTwoHanded = true; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 160, 16; + EnchantmentMinusChance = 5; + EnchantmentPlusChance = 5; + DamageFlags = SLASH; + } + + Config BROKEN|TWO_HANDED_SWORD; + { + Possibility = 8; + BitmapPos = 32, 80; + WieldedBitmapPos = 176, 80; + MaterialConfigChances = { 4, 0, 250, 25, 25; } + EnchantmentMinusChance = 10; + EnchantmentPlusChance = 10; + } + + Config KNIGHT_SWORD; + { + DefaultSize = 120; + Possibility = 1; + WeaponCategory = LARGE_SWORDS; + DefaultMainVolume = 200; + DefaultSecondaryVolume = 100; + BitmapPos = 110,257; + FormModifier = 145; + StrengthModifier = 120; + NameSingular = "knight sword"; + MainMaterialConfig = { 7, BONE, TIN, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL; } + SecondaryMaterialConfig = { 7, BONE, TIN, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL; } + MaterialConfigChances = { 7, 25, 50, 250, 500, 1000, 250, 25; } + EnchantmentMinusChance = 15; + Roundness = 20; + IsTwoHanded = true; + AttachedGod = LEGIFER; + WieldedBitmapPos = 150, 475; + DamageFlags = SLASH|PIERCE; + } + + Config BROKEN|KNIGHT_SWORD; + { + Possibility = 10; + BitmapPos = 110, 279; + WieldedBitmapPos = 176, 80; + MainMaterialConfig = { 7, BRONZE, IRON, STEEL, METEORIC_STEEL, DAMASCUS_STEEL, MITHRIL, OMMEL_TOOTH;} + SecondaryMaterialConfig = { 7, BRONZE, IRON, STEEL, METEORIC_STEEL, DAMASCUS_STEEL, MITHRIL, OMMEL_TOOTH;} + MaterialConfigChances = { 7, 50, 250, 500, 1000, 250, 25, 25; } + EnchantmentMinusChance = 10; + } + +Config TIP_SWORD; + { + DefaultSize = 120; + Possibility = 1; + WeaponCategory = LARGE_SWORDS; + DefaultMainVolume = 145; + DefaultSecondaryVolume = 50; + BitmapPos = 86, 330; + FormModifier = 125; + StrengthModifier = 120; + NameSingular = "tip sword"; + MainMaterialConfig = { 7, BRONZE, IRON, STEEL, METEORIC_STEEL, DAMASCUS_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig == EBONY_WOOD; + MaterialConfigChances = { 7, 500, 2000, 500, 150, 50, 25, 1; } + EnchantmentMinusChance = 15; + Roundness = 30; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 140, 536; + DamageFlags = SLASH|PIERCE; + } + + Config BROKEN|TIP_SWORD; + { + Possibility = 10; + BitmapPos = 87, 267; + WieldedBitmapPos = 160, 536; + MainMaterialConfig = { 7, STEEL, METEORIC_STEEL, DAMASCUS_STEEL, MITHRIL, ILLITHIUM, ADAMANT, OMMEL_TOOTH; } + SecondaryMaterialConfig == EBONY_WOOD; + MaterialConfigChances = { 7, 500, 2000, 500, 150, 50, 25, 1; } + EnchantmentMinusChance = 10; + } + + Config KATANA; + { + DefaultSize = 100; + Possibility = 25; + WeaponCategory = LARGE_SWORDS; + DefaultMainVolume = 110; + DefaultSecondaryVolume = 30; + BitmapPos = 110, 209; + FormModifier = 115; + StrengthModifier = 100; + NameSingular = "katana"; + MainMaterialConfig = { 7, EBONY_WOOD, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig = { 7, EBONY_WOOD, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + MaterialConfigChances = { 7, 50, 1000, 750, 100, 50, 25, 5; } + Roundness = 20; + AttachedGod = LEGIFER; + WieldedBitmapPos = 176, 112; + EnchantmentPlusChance = 5; + DamageFlags = SLASH|PIERCE; + } + + Config BROKEN|KATANA; + { + Possibility = 3; + BitmapPos = 110, 225; + WieldedBitmapPos = 176, 80; + MainMaterialConfig = { 4, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + SecondaryMaterialConfig = { 4, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + MaterialConfigChances = { 4, 100, 50, 25, 10; } + EnchantmentPlusChance = 10; + } + + Config TWO_HANDED_SCIMITAR; + { + DefaultSize = 140; + Possibility = 25; + WeaponCategory = LARGE_SWORDS; + DefaultMainVolume = 400; + DefaultSecondaryVolume = 150; + StrengthModifier = 175; + BitmapPos = 0, 16; + FormModifier = 160; + NameSingular = "two-handed scimitar"; + MainMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + MaterialConfigChances = { 5, 2000, 100, 25, 25, 5; } + Roundness = 15; + IsTwoHanded = true; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 176, 112; + EnchantmentMinusChance = 5; + EnchantmentPlusChance = 5; + DamageFlags = SLASH; + } + + Config BROKEN|TWO_HANDED_SCIMITAR; + { + Possibility = 3; + BitmapPos = 32, 96; + WieldedBitmapPos = 176, 80; + MaterialConfigChances = { 4, 0, 250, 25, 25; } + EnchantmentMinusChance = 10; + EnchantmentPlusChance = 10; + } + + Config SPEAR; + { + DefaultSize = 190; + Possibility = 600; + WeaponCategory = POLE_ARMS; + StrengthModifier = 70; + BitmapPos = 16, 144; + FormModifier = 85; + DefaultMainVolume = 100; + DefaultSecondaryVolume = 2500; + NameSingular = "spear"; + MainMaterialConfig = { 13, BIRCH_WOOD, OAK_WOOD, TEAK_WOOD, EBONY_WOOD, BONE, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_BONE, OMMEL_TOOTH; } + SecondaryMaterialConfig == PINE_WOOD; + MaterialConfigChances = { 13, 250, 200, 150, 100, 250, 650, 500, 350, 200, 50, 50, 25, 10; } + Roundness = 10; + IsTwoHanded = true; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 160, 96; + EnchantmentPlusChance = 15; + DamageFlags = PIERCE; + } + + Config BROKEN|SPEAR; + { + Possibility = 60; + BitmapPos = 32, 224; + WieldedBitmapPos = 176, 96; + MainMaterialConfig = { 7, STEEL, METEORIC_STEEL, MITHRIL, SAPPHIRE, RUBY, DIAMOND, ADAMANT; } + MaterialConfigChances = { 7, 150, 25, 25, 5, 5, 5, 5; } + EnchantmentPlusChance = 30; + } + + Config SPETUM; + { + DefaultSize = 180; + Possibility = 5; + WeaponCategory = POLE_ARMS; + StrengthModifier = 70; + BitmapPos = 110, 345; + FormModifier = 160; + DefaultMainVolume = 100; + DefaultSecondaryVolume = 1800; + NameSingular = "spetum"; + MainMaterialConfig = { 10, TIN, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL, DAMASCUS_STEEL, MITHRIL, OMMEL_BONE, OMMEL_TOOTH; } + SecondaryMaterialConfig == TEAK_WOOD; + MaterialConfigChances = { 10, 50, 100, 250, 500, 1000, 250, 100, 50, 10, 5; } + EnchantmentMinusChance = 15; + Roundness = 25; + IsTwoHanded = true; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 149, 512; + DamageFlags = PIERCE; + } + + Config BROKEN|SPETUM; + { + Possibility = 50; + BitmapPos = 84, 349; + WieldedBitmapPos = 165, 512; + MainMaterialConfig = { 12, BRONZE, IRON, STEEL, METEORIC_STEEL, DAMASCUS_STEEL, MITHRIL, ILLITHIUM, RUBY, SAPPHIRE, EMERALD, DIAMOND, ADAMANT; } + MaterialConfigChances = { 12, 50, 100, 250, 500, 1000, 250, 100, 10, 10, 10, 5, 1; } + EnchantmentMinusChance = 10; + } + + Config AXE; + { + DefaultSize = 60; + Possibility = 400; + WeaponCategory = AXES; + DefaultMainVolume = 200; + DefaultSecondaryVolume = 500; + StrengthModifier = 80; + BitmapPos = 16, 256; + FormModifier = 60; + NameSingular = "axe"; + UsesLongArticle = true; + MainMaterialConfig = { 7, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig == PINE_WOOD; + MaterialConfigChances = { 7, 1250, 1000, 500, 200, 50, 50, 10; } + Roundness = 20; + IsTwoHanded = true; + AttachedGod = LORICATUS; + WieldedBitmapPos = 160, 176; + EnchantmentPlusChance = 20; + DamageFlags = SLASH; + IsThrowingWeapon = true; + ThrowItemTypes = THROW_AXE; + } + + Config BROKEN|AXE; + { + Possibility = 40; + BitmapPos = 32, 176; + WieldedBitmapPos = 176, 176; + MainMaterialConfig = { 4, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + MaterialConfigChances = { 4, 250, 50, 50, 10; } + EnchantmentPlusChance = 30; + } + + Config HALBERD; + { + DefaultSize = 170; + Possibility = 100; + WeaponCategory = POLE_ARMS; + DefaultMainVolume = 375; + DefaultSecondaryVolume = 1500; + StrengthModifier = 150; + BitmapPos = 0, 80; + FormModifier = 115; + NameSingular = "halberd"; + MainMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig == BIRCH_WOOD; + MaterialConfigChances = { 5, 2000, 100, 25, 25, 5; } + Roundness = 20; + IsTwoHanded = true; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 160, 256; + EnchantmentMinusChance = 5; + EnchantmentPlusChance = 5; + DamageFlags = SLASH|PIERCE; + } + + Config BROKEN|HALBERD; + { + Possibility = 10; + BitmapPos = 32, 144; + WieldedBitmapPos = 176, 256; + MainMaterialConfig = { 3, STEEL, METEORIC_STEEL, MITHRIL; } + MaterialConfigChances = { 3, 250, 25, 25; } + EnchantmentMinusChance = 10; + EnchantmentPlusChance = 10; + } + + Config MACE; + { + StrengthModifier = 125; + FormModifier = 75; + DefaultSize = 70; + DefaultMainVolume = 300; + DefaultSecondaryVolume = 500; + BitmapPos = 0, 32; + Possibility = 300; + WeaponCategory = BLUNT_WEAPONS; + NameSingular = "mace"; + MainMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig == FIR_WOOD; + MaterialConfigChances = { 5, 1500, 100, 25, 25, 5; } + Roundness = 30; /* we count the handle, too */ + IsTwoHanded = true; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 160, 192; + EnchantmentPlusChance = 10; + } + + Config BROKEN|MACE; + { + Possibility = 30; + BitmapPos = 32, 112; + WieldedBitmapPos = 176, 192; + MainMaterialConfig = { 4, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + MaterialConfigChances = { 4, 250, 25, 25, 5; } + EnchantmentPlusChance = 20; + } + + Config WAR_HAMMER; + { + DefaultSize = 60; + Possibility = 200; + WeaponCategory = BLUNT_WEAPONS; + DefaultMainVolume = 250; + DefaultSecondaryVolume = 500; + StrengthModifier = 110; + BitmapPos = 80, 16; + FormModifier = 55; + NameSingular = "war hammer"; + MainMaterialConfig = { 8, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OCTIRON, OMMEL_TOOTH; } + SecondaryMaterialConfig == FIR_WOOD; + MaterialConfigChances = { 8, 1000, 750, 500, 100, 50, 50, 50, 10; } + Roundness = 20; + IsTwoHanded = true; + CanBeUsedBySmith = true; + AttachedGod = LORICATUS; + WieldedBitmapPos = 176, 320; + EnchantmentPlusChance = 20; + } + + Config BROKEN|WAR_HAMMER; + { + Possibility = 20; + BitmapPos = 80, 32; + WieldedBitmapPos = 176, 336; + MainMaterialConfig = { 4, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + MaterialConfigChances = { 4, 250, 50, 50, 10; } + EnchantmentPlusChance = 30; + } + + Config SICKLE; + { + DefaultSize = 40; + Possibility = 50; + WeaponCategory = SMALL_SWORDS; + DefaultMainVolume = 100; + DefaultSecondaryVolume = 50; + BitmapPos = 48, 144; + FormModifier = 60; + StrengthModifier = 40; + NameSingular = "sickle"; + MainMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig == FIR_WOOD; + MaterialConfigChances = { 5, 500, 200, 50, 50, 10; } + Roundness = 20; + AttachedGod = SEGES; + WieldedBitmapPos = 176, 32; + IsGoodWithPlants = true; + EnchantmentPlusChance = 20; + DamageFlags = SLASH; + } + + Config BROKEN|SICKLE; + { + Possibility = 5; + BitmapPos = 48, 176; + WieldedBitmapPos = 176, 240; + MainMaterialConfig = { 6, METEORIC_STEEL, MITHRIL, SAPPHIRE, RUBY, DIAMOND, ADAMANT; } + MaterialConfigChances = { 6, 50, 50, 10, 10, 10, 10; } + EnchantmentPlusChance = 40; + } + + Config DAGGER; + { + DefaultSize = 40; + Possibility = 300; + WeaponCategory = SMALL_SWORDS; + DefaultMainVolume = 25; + DefaultSecondaryVolume = 25; + BitmapPos = 48, 256; + FormModifier = 75; + StrengthModifier = 60; + NameSingular = "dagger"; + MainMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + MaterialConfigChances = { 5, 1000, 200, 50, 50, 10; } + Roundness = 20; + AttachedGod = CLEPTIA; + WieldedBitmapPos = 160, 288; + EnchantmentPlusChance = 25; + DamageFlags = SLASH|PIERCE; + IsThrowingWeapon = true; + } + + Config BROKEN|DAGGER; + { + Possibility = 30; + BitmapPos = 48, 288; + WieldedBitmapPos = 176, 288; + MainMaterialConfig = { 7, STEEL, METEORIC_STEEL, MITHRIL, SAPPHIRE, RUBY, DIAMOND, ADAMANT; } + SecondaryMaterialConfig = { 7, STEEL, METEORIC_STEEL, MITHRIL, SAPPHIRE, RUBY, DIAMOND, ADAMANT; } + MaterialConfigChances = { 7, 100, 25, 25, 5, 5, 5, 5; } + EnchantmentPlusChance = 50; + } + + Config SHORT_SWORD; + { + DefaultSize = 70; + Possibility = 200; + WeaponCategory = SMALL_SWORDS; + DefaultMainVolume = 70; + DefaultSecondaryVolume = 30; + BitmapPos = 48, 240; + FormModifier = 80; + StrengthModifier = 90; + NameSingular = "short sword"; + MainMaterialConfig = { 7, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig = { 7, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + MaterialConfigChances = { 7, 1000, 750, 500, 100, 25, 25, 5; } + Roundness = 15; + AttachedGod = CLEPTIA; + WieldedBitmapPos = 160, 32; + EnchantmentPlusChance = 15; + DamageFlags = SLASH|PIERCE; + } + + Config BROKEN|SHORT_SWORD; + { + Possibility = 20; + BitmapPos = 48, 272; + WieldedBitmapPos = 176, 208; + MainMaterialConfig = { 4, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + SecondaryMaterialConfig = { 4, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + MaterialConfigChances = { 4, 250, 25, 25, 5; } + EnchantmentPlusChance = 30; + } + + Config BASTARD_SWORD; + { + DefaultSize = 130; + Possibility = 75; + WeaponCategory = LARGE_SWORDS; + DefaultMainVolume = 225; + DefaultSecondaryVolume = 100; + StrengthModifier = 150; + BitmapPos = 64, 160; + FormModifier = 115; + NameSingular = "bastard sword"; + MainMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + MaterialConfigChances = { 5, 2000, 100, 50, 50, 10; } + Roundness = 15; + IsTwoHanded = true; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 160, 16; + EnchantmentPlusChance = 5; + DamageFlags = SLASH; + } + + Config BROKEN|BASTARD_SWORD; + { + Possibility = 8; + BitmapPos = 64, 192; + WieldedBitmapPos = 176, 80; + MaterialConfigChances = { 4, 0, 250, 40, 40; } + EnchantmentPlusChance = 10; + } + + Config BATTLE_AXE; + { + DefaultSize = 110; + Possibility = 200; + WeaponCategory = AXES; + DefaultMainVolume = 250; + DefaultSecondaryVolume = 500; + StrengthModifier = 100; + BitmapPos = 64, 80; + FormModifier = 85; + NameSingular = "battle-axe"; + MainMaterialConfig = { 7, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig == OAK_WOOD; + MaterialConfigChances = { 7, 1500, 1000, 750, 100, 50, 50, 10; } + Roundness = 30; + IsTwoHanded = true; + AttachedGod = LORICATUS; + WieldedBitmapPos = 160, 272; + EnchantmentPlusChance = 10; + DamageFlags = SLASH; + } + + Config BROKEN|BATTLE_AXE; + { + Possibility = 20; + BitmapPos = 64, 96; + WieldedBitmapPos = 176, 272; + MainMaterialConfig = { 4, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + MaterialConfigChances = { 4, 250, 25, 25, 5; } + EnchantmentPlusChance = 20; + } + +Config GREAT_AXE; + { + DefaultSize = 150; + Possibility = 6; + WeaponCategory = AXES; + DefaultMainVolume = 400; + DefaultSecondaryVolume = 800; + StrengthModifier = 100; + BitmapPos = 131, 6; + FormModifier = 165; + NameSingular = "great axe"; + MainMaterialConfig = { 7, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig == OAK_WOOD; + MaterialConfigChances = { 7, 0, 0, 0, 100, 50, 50, 10; } + Roundness = 30; + IsTwoHanded = true; + AttachedGod = LORICATUS; + WieldedBitmapPos = 149, 419; + EnchantmentPlusChance = 10; + DamageFlags = SLASH; + } + + Config BROKEN|GREAT_AXE; + { + Possibility = 2; + BitmapPos = 130, 33; + WieldedBitmapPos = 165, 419; + MainMaterialConfig = { 2, ILLITHIUM, ADAMANT; } + MaterialConfigChances = { 2, 1, 1; } + EnchantmentPlusChance = 20; + } + + Config SCYTHE; + { + DefaultSize = 200; + Possibility = 50; + WeaponCategory = POLE_ARMS; + DefaultMainVolume = 150; + DefaultSecondaryVolume = 1800; + BitmapPos = 64, 112; + FormModifier = 80; + StrengthModifier = 40; + NameSingular = "scythe"; + MainMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig == FIR_WOOD; + MaterialConfigChances = { 5, 500, 100, 50, 50, 10; } + Roundness = 10; + IsTwoHanded = true; + AttachedGod = MORTIFER; + WieldedBitmapPos = 176, 48; + IsGoodWithPlants = true; + EnchantmentPlusChance = 20; + DamageFlags = SLASH; + } + + Config BROKEN|SCYTHE; + { + Possibility = 5; + BitmapPos = 64, 128; + WieldedBitmapPos = 160, 48; + MainMaterialConfig = { 4, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + MaterialConfigChances = { 4, 50, 50, 50, 10; } + EnchantmentPlusChance = 40; + } + + Config QUARTER_STAFF; + { + DefaultSize = 220; + Possibility = 200; + WeaponCategory = BLUNT_WEAPONS; + StrengthModifier = 125; + BitmapPos = 64, 240; + FormModifier = 90; + DefaultMainVolume = 3000; + DefaultSecondaryVolume = 200; + NameSingular = "quarterstaff"; + MainMaterialConfig = { 10, BALSA_WOOD, PINE_WOOD, FIR_WOOD, BIRCH_WOOD, OAK_WOOD, TEAK_WOOD, EBONY_WOOD, ARCANITE, OCTIRON, OMMEL_BONE; } + SecondaryMaterialConfig = { 10, BALSA_WOOD, PINE_WOOD, FIR_WOOD, BIRCH_WOOD, OAK_WOOD, TEAK_WOOD, EBONY_WOOD, ARCANITE, OCTIRON, OMMEL_BONE; } + MaterialConfigChances = { 10, 100, 350, 300, 250, 200, 150, 100, 75, 25, 5; } + Roundness = 10; + IsTwoHanded = true; + AttachedGod = SILVA; + WieldedBitmapPos = 160, 304; + EnchantmentPlusChance = 20; + } + + Config BROKEN|QUARTER_STAFF; + { + Possibility = 20; + BitmapPos = 64, 256; + WieldedBitmapPos = 176, 304; + MainMaterialConfig = { 3, EBONY_WOOD, ARCANITE, OCTIRON; } + SecondaryMaterialConfig = { 3, EBONY_WOOD, ARCANITE, OCTIRON; } + MaterialConfigChances = { 3, 500, 200, 25; } + EnchantmentPlusChance = 40; + } + + Config HAMMER; + { + DefaultSize = 40; + Possibility = 50; + WeaponCategory = BLUNT_WEAPONS; + DefaultMainVolume = 150; + DefaultSecondaryVolume = 300; + StrengthModifier = 90; + BitmapPos = 48, 160; + FormModifier = 50; + NameSingular = "hammer"; + MainMaterialConfig = { 7, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OCTIRON, OMMEL_TOOTH; } + SecondaryMaterialConfig == FIR_WOOD; + MaterialConfigChances = { 7, 750, 500, 100, 50, 50, 20, 10; } + Roundness = 20; + CanBeUsedBySmith = true; + AttachedGod = LORICATUS; + WieldedBitmapPos = 176, 16; + EnchantmentPlusChance = 20; + } + + Config BROKEN|HAMMER; + { + Possibility = 5; + BitmapPos = 48, 192; + WieldedBitmapPos = 176, 224; + MainMaterialConfig = { 4, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + MaterialConfigChances = { 4, 100, 50, 50, 10; } + EnchantmentPlusChance = 40; + } + + Config RAPIER; + { + DefaultSize = 120; + Possibility = 25; + WeaponCategory = SMALL_SWORDS; + DefaultMainVolume = 100; + DefaultSecondaryVolume = 95; + BitmapPos = 129, 63; + FormModifier = 70; + StrengthModifier = 75; + NameSingular = "rapier"; + MainMaterialConfig = { 6, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, ILLITHIUM; } + SecondaryMaterialConfig = { 6, TEAK_WOOD, TEAK_WOOD, TEAK_WOOD, TEAK_WOOD, TEAK_WOOD, TEAK_WOOD; } + MaterialConfigChances = { 6, 100, 90, 80, 70, 25, 4; } + Roundness = 30; + AttachedGod = LEGIFER; + WieldedBitmapPos = 149, 449; + EnchantmentPlusChance = 8; + DamageFlags = PIERCE; + } + + Config BROKEN|RAPIER; + { + Possibility = 5; + BitmapPos = 132, 94; + WieldedBitmapPos = 166, 449; + MainMaterialConfig = { 5, EBONY_WOOD, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + SecondaryMaterialConfig = { 5, TEAK_WOOD, TEAK_WOOD, TEAK_WOOD, TEAK_WOOD, TEAK_WOOD; } + MaterialConfigChances = { 5, 0, 75, 25, 25, 10; } + EnchantmentPlusChance = 10; + } + +} + +banana /* materialcontainer-> */ +{ + Possibility = 150; + WeaponCategory = BLUNT_WEAPONS; + DefaultMainVolume = 40; + DefaultSecondaryVolume = 150; + StrengthModifier = 75; + Category = FOOD; + BitmapPos = 0, 112; + FormModifier = 25; + DefaultSize = 20; + NameSingular = "banana"; + MainMaterialConfig == BANANA_PEEL; + SecondaryMaterialConfig == BANANA_FLESH; + Alias == "gun"; + Roundness = 15; + CanBeBroken = true; + /* MinCharges & MaxCharges overridden */ + AttachedGod = NEFAS; + WieldedBitmapPos = 160, 320; + AllowEquip = false; + IsValuable = false; + IsSadistWeapon = true; + + Config BROKEN; + { + Possibility = 10; + BitmapPos = 32, 272; + WieldedBitmapPos = 176, 320; + } +} + +holybanana /* materialcontainer->banana-> */ +{ + Possibility = 5; + DefaultMainVolume = 100; + DefaultSecondaryVolume = 500; + StrengthModifier = 150; + FormModifier = 250; + DefaultSize = 30; + Price = 1000; + Adjective = "holy"; + PostFix = "of Oily Orpiv"; + ArticleMode = FORCE_THE; + IsPolymorphable = false; + Roundness = 20; + WieldedBitmapPos = 160, 320; + CanBePiled = false; + CanBeBroken = false; + SecondaryMaterialConfig == HOLY_BANANA_FLESH; + BaseEmitation = rgb24(160, 140, 110); + CanBeCloned = false; + CanBeMirrored = true; /* will create a peel */ + AllowEquip = true; + IsValuable = true; +} + +holymango +{ + WeaponCategory = BLUNT_WEAPONS; + Possibility = 2; + Category = FOOD; + DefaultMainVolume = 250; + StrengthModifier = 150; + FormModifier = 250; + DefaultSize = 30; + Price = 1000; + Adjective = "holy"; + NameSingular = "mango"; + NamePlural = "mangoes"; + PostFix = "of Oren Ordent"; + ArticleMode = FORCE_THE; + IsPolymorphable = false; + Roundness = 90; + BitmapPos = 0, 432; + CanBePiled = false; + CanBeBroken = false; + MainMaterialConfig == HOLY_MANGO_FLESH; + BaseEmitation = rgb24(160, 140, 110); + WieldedBitmapPos = 176, 144; + CanBeCloned = false; + CanBeMirrored = false; + AllowEquip = true; + IsValuable = true; + AttachedGod = SILVA; +} + +justifier /* meleeweapon-> */ +{ + DefaultSize = 125; + Possibility = 0; + WeaponCategory = LARGE_SWORDS; + DefaultMainVolume = 200; + DefaultSecondaryVolume = 100; + BitmapPos = 0, 64; + FormModifier = 275; + StrengthModifier = 275; + CanBeWished = false; + IsMaterialChangeable = false; + NameSingular = "broadsword"; + Adjective = "holy"; + PostFix = "named Valpurus's Justifier"; + ArticleMode = FORCE_THE; + MainMaterialConfig == VALPURIUM; + SecondaryMaterialConfig == VALPURIUM; + IsPolymorphable = false; + Alias == "Justifier"; + Roundness = 20; + CanBeCloned = false; + CanBeMirrored = true; + CanBeBroken = false; + BaseEnchantment = 16; + CanBeEnchanted = false; + GearStates = HASTE|INFRA_VISION|ESP|POLYMORPH_CONTROL|TELEPORT_CONTROL; + AttachedGod = VALPURUS; + WieldedBitmapPos = 160, 16; + IsQuestItem = true; + DamageFlags = SLASH|PIERCE; +} + +neercseulb /* meleeweapon-> */ +{ + StrengthModifier = 200; + FormModifier = 200; + DefaultSize = 100; + Possibility = 0; + CanBeWished = false; + BitmapPos = 0, 32; + WeaponCategory = BLUNT_WEAPONS; + IsMaterialChangeable = false; + DefaultMainVolume = 500; + DefaultSecondaryVolume = 500; + Adjective = "ancient"; + NameSingular = "mace"; + UsesLongAdjectiveArticle = true; + PostFix = "named Neerc Se-ulb"; + ArticleMode = FORCE_THE; + MainMaterialConfig == MITHRIL; + SecondaryMaterialConfig == MITHRIL; + IsPolymorphable = false; + Alias == "Neerc Se-ulb"; + Roundness = 35; + IsTwoHanded = true; + CanBeCloned = false; + CanBeMirrored = true; + CanBeBroken = false; + AttachedGod = MORTIFER; + WieldedBitmapPos = 160, 192; + IsQuestItem = true; +} + +pickaxe /* meleeweapon-> */ +{ + Category = TOOL; + DefaultSize = 75; + Possibility = 100; + DefaultMainVolume = 225; + DefaultSecondaryVolume = 400; + BitmapPos = 0, 96; + FormModifier = 35; + NameSingular = "pick-axe"; + Alias == "pick-ax"; + Roundness = 20; + WeaponCategory = AXES; + StrengthModifier = 75; + MainMaterialConfig = { 7, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig == FIR_WOOD; + MaterialConfigChances = { 7, 2000, 1500, 750, 100, 25, 25, 5; } + IsTwoHanded = true; + Price = 50; /* bonus for being able to dig */ + AttachedGod = LORICATUS; + WieldedBitmapPos = 160, 64; + EnchantmentPlusChance = 10; + DamageFlags = PIERCE; + + Config BROKEN; + { + Possibility = 10; + BitmapPos = 32, 160; + Price = 0; + WieldedBitmapPos = 176, 64; + MainMaterialConfig = { 4, IRON, STEEL, METEORIC_STEEL, MITHRIL; } + MaterialConfigChances = { 4, 250, 100, 25, 25; } + EnchantmentPlusChance = 20; + } +} + +armor +{ + IsAbstract = true; + CanBePiled = false; + CanBeEnchanted = true; +} + +bodyarmor /* armor-> */ +{ + Category = BODY_ARMOR; + FormModifier = 15; + IsAbstract = true; + Roundness = 70; + IsTwoHanded = true; + CanBeBroken = true; + AttachedGod = LORICATUS; + WieldedBitmapPos = 160, 144; + TeleportPriority = 200; + + Config CHAIN_MAIL; + { + Possibility = 50; + StrengthModifier = 125; + DefaultSize = 75; + DefaultMainVolume = 2000; + BitmapPos = 16, 96; + NameSingular = "chain mail"; + MainMaterialConfig = { 8, BRONZE, IRON, STEEL, METEORIC_STEEL, ARCANITE, MITHRIL, OMMEL_BONE, OMMEL_TOOTH; } + MaterialConfigChances = { 8, 450, 300, 150, 25, 20, 25, 10, 5; } + InElasticityPenaltyModifier = 10; + TorsoArmorBitmapPos = 32, 432; + ArmArmorBitmapPos = 64, 432; + AthleteArmArmorBitmapPos = 64, 464; + LegArmorBitmapPos = 0, 432; + EnchantmentPlusChance = 10; + } + + Config BROKEN|CHAIN_MAIL; + { + Possibility = 5; + BitmapPos = 48, 208; + FormModifier = 20; + StrengthModifier = 75; + MainMaterialConfig = { 6, STEEL, METEORIC_STEEL, ARCANITE, MITHRIL, OCTIRON, ADAMANT; } + MaterialConfigChances = { 6, 50, 25, 20, 25, 5, 5; } + EnchantmentPlusChance = 20; + InElasticityPenaltyModifier = 15; + TorsoArmorBitmapPos = 32, 512; + ArmArmorBitmapPos = 64, 512; + AthleteArmArmorBitmapPos = 64, 544; + LegArmorBitmapPos = 0, 512; + } + + Config PLATE_MAIL; + { + StrengthModifier = 200; + DefaultSize = 75; + DefaultMainVolume = 4000; + BitmapPos = 16, 128; + Possibility = 250; + NameSingular = "plate mail"; + FlexibleNameSingular = "armor"; + MainMaterialConfig = { 11, LEATHER, HARDENED_LEATHER, NYMPH_HAIR, TROLL_HIDE, OMMEL_HAIR, BRONZE, IRON, STEEL, METEORIC_STEEL, OMMEL_BONE, OMMEL_TOOTH; } + MaterialConfigChances = { 11, 5000, 2500, 250, 1000, 100, 200, 200, 100, 25, 10, 5; } + InElasticityPenaltyModifier = 20; + EnchantmentPlusChance = 5; + TorsoArmorBitmapPos = 32, 416; + ArmArmorBitmapPos = 80, 416; + AthleteArmArmorBitmapPos = 80, 448; + LegArmorBitmapPos = 16, 416; + } + + Config BROKEN|PLATE_MAIL; + { + Possibility = 20; + StrengthModifier = 100; + BitmapPos = 0, 352; + FormModifier = 20; + MainMaterialConfig = { 6, NYMPH_HAIR, OMMEL_HAIR, IRON, STEEL, METEORIC_STEEL, DRAGON_HIDE; } + MaterialConfigChances = { 6, 250, 100, 0, 100, 25, 10; } + EnchantmentPlusChance = 10; + InElasticityPenaltyModifier = 25; + TorsoArmorBitmapPos = 32, 496; + ArmArmorBitmapPos = 80, 496; + AthleteArmArmorBitmapPos = 80, 528; + LegArmorBitmapPos = 16, 496; + } + + Config ARMOR_OF_GREAT_HEALTH; + { + StrengthModifier = 150; + DefaultSize = 80; + DefaultMainVolume = 5000; + BitmapPos = 64, 208; + Possibility = 5; + UsesLongArticle = true; + NameSingular = "armor"; + PostFix = "of great health"; + MainMaterialConfig = { 7, LEATHER, HARDENED_LEATHER, NYMPH_HAIR, TROLL_HIDE, OMMEL_HAIR, OMMEL_BONE, OMMEL_TOOTH; } + MaterialConfigChances = { 7, 150, 75, 25, 50, 10, 25, 10; } + InElasticityPenaltyModifier = 40; + AffectsEndurance = true; + BaseEnchantment = 1; + Price = 500; + PriceIsProportionalToEnchantment = true; + AttachedGod = SEGES; + EnchantmentPlusChance = 2; + Alias == "AOgH"; + TorsoArmorBitmapPos = 32, 464; + ArmArmorBitmapPos = 80, 464; + AthleteArmArmorBitmapPos = 80, 480; + LegArmorBitmapPos = 16, 464; + } + + Config BROKEN|ARMOR_OF_GREAT_HEALTH; + { + Possibility = 10; + StrengthModifier = 75; + BitmapPos = 64, 224; + FormModifier = 20; + Price = 50; + AffectsEndurance = false; + MainMaterialConfig = { 6, LEATHER, HARDENED_LEATHER, NYMPH_HAIR, TROLL_HIDE, OMMEL_HAIR, DRAGON_HIDE; } + MaterialConfigChances = { 6, 150, 75, 25, 50, 10, 1; } + EnchantmentPlusChance = 4; + InElasticityPenaltyModifier = 50; + TorsoArmorBitmapPos = 32, 544; + ArmArmorBitmapPos = 80, 544; + AthleteArmArmorBitmapPos = 80, 560; + LegArmorBitmapPos = 16, 544; + } + + Config DRAGON_CUIRASS; + { + StrengthModifier = 225; + DefaultSize = 100; + DefaultMainVolume = 5000; + BitmapPos = 86, 306; + Possibility = 3; + NameSingular = "dragon"; + PostFix = "cuirass"; + Alias == "dragon cuirass"; + CanBeBroken = false; + MainMaterialConfig = { 4, DRAGON_HIDE, MITHRIL, OCTIRON, DAMASCUS_STEEL;} + MaterialConfigChances = { 4, 10, 1, 1, 0; } + InElasticityPenaltyModifier = 20; + EnchantmentPlusChance = 5; + TorsoArmorBitmapPos = 5, 576; + ArmArmorBitmapPos = 80, 416; + AthleteArmArmorBitmapPos = 80, 448; + LegArmorBitmapPos = 16, 416; + } + + Config FILTHY_TUNIC; + { + StrengthModifier = 50; + DefaultSize = 5; + DefaultMainVolume = 500; + BitmapPos = 48, 432; + Possibility = 50; + Alias == "tunic"; + NameSingular = "filthy tunic"; + MainMaterialConfig == HESSIAN_CLOTH; + InElasticityPenaltyModifier = 45; + EnchantmentPlusChance = 5; + TorsoArmorBitmapPos = 32, 736; + LegArmorBitmapPos = 0, 432; + /*ArmArmorBitmapPos = 80, 416; + AthleteArmArmorBitmapPos = 80, 448;*/ + AttachedGod = SCABIES; + Price = 0; + } + + Config BROKEN|FILTHY_TUNIC; + { + Possibility = 15; + StrengthModifier = 100; + BitmapPos = 64, 432; + FormModifier = 20; + EnchantmentPlusChance = 10; + InElasticityPenaltyModifier = 50; + TorsoArmorBitmapPos = 32, 752; + LegArmorBitmapPos = 0, 432; + /*ArmArmorBitmapPos = 80, 496; + AthleteArmArmorBitmapPos = 80, 528; + LegArmorBitmapPos = 16, 496;*/ + } + +} + +goldeneagleshirt /* armor->bodyarmor-> */ +{ + DefaultSize = 60; + BitmapPos = 16, 112; + Possibility = 0; + CanBeWished = false; + IsDestroyable = false; + IsMaterialChangeable = false; + DefaultMainVolume = 1000; + BaseEmitation = rgb24(112, 112, 166); + StrengthModifier = 200; + NameSingular = "Shirt of the Golden Eagle"; + MainMaterialConfig == GOLDEN_EAGLE_FEATHER; + ArticleMode = FORCE_THE; + IsPolymorphable = false; + CanBeBroken = false; + CanBeCloned = false; + CanBeMirrored = true; + BaseEnchantment = 10; + InElasticityPenaltyModifier = 30; + CanBeEnchanted = false; + AttachedGod = VALPURUS; + IsQuestItem = true; + TorsoArmorBitmapPos = 32, 416; + ArmArmorBitmapPos = 80, 416; + AthleteArmArmorBitmapPos = 80, 448; + LegArmorBitmapPos = 16, 416; +} + +potion /* materialcontainer-> */ +{ + DefaultSize = 30; + Possibility = 800; + WeaponCategory = BLUNT_WEAPONS; + Category = POTION; + DefaultMainVolume = 60; + DefaultSecondaryVolume = 1000; + StrengthModifier = 50; + BitmapPos = 0, 160; + FormModifier = 25; + NameSingular = "bottle"; + MainMaterialConfig == GLASS; + SecondaryMaterialConfig = { 11, WATER, HEALING_LIQUID, OMMEL_URINE, POISON_LIQUID, VALDEMAR, ANTIDOTE_LIQUID, VODKA, TROLL_BLOOD, OMMEL_SWEAT, OMMEL_TEARS, SULPHURIC_ACID; } + MaterialConfigChances = { 11, 75, 50, 5, 50, 2, 10, 5, 25, 5, 5, 25; } + Alias = { 2, "potion", "drink"; } + Roundness = 70; + CanBeBroken = true; + AttachedGod = NONE; + WieldedBitmapPos = 176, 144; + IsValuable = false; + TeleportPriority = 200; + ThrowItemTypes = THROW_POTION; +} + +kleinbottle /* materialcontainer-> */ +{ + DefaultSize = 30; + Possibility = 0; + WeaponCategory = BLUNT_WEAPONS; + Category = POTION; + DefaultMainVolume = 60; + DefaultSecondaryVolume = 1000; + StrengthModifier = 50; + BitmapPos = 80, 464; + FormModifier = 25; + NameSingular = "klein bottle"; + MainMaterialConfig == GLASS; + SecondaryMaterialConfig == WATER; + /*MaterialConfigChances == 0;*/ + /*Alias = { 2, "potion", "drink"; }*/ + Roundness = 70; + CanBeBroken = false; + AttachedGod = SOPHOS; + WieldedBitmapPos = 176, 144; + IsValuable = true; + TeleportPriority = 200; + CanBeCloned = false; + CanBeMirrored = true; + IsQuestItem = true; + CanBeWished = false; + IsMaterialChangeable = true; + IsPolymorphSpawnable = false; + IsPolymorphable = false; +} + +cauldron /* materialcontainer-> */ +{ + Possibility = 0; + Category = POTION; + StrengthModifier = 350; + DefaultSize = 150; + DefaultMainVolume = 800000; + WeaponCategory = BLUNT_WEAPONS; + /*Category = POTION;*/ + DefaultSecondaryVolume = 26000; + BitmapPos = 32, 432; + /*FormModifier = 25;*/ + NameSingular = "cauldron"; + MainMaterialConfig == COPPER; + SecondaryMaterialConfig = { 11, WATER, HEALING_LIQUID, OMMEL_URINE, POISON_LIQUID, VALDEMAR, ANTIDOTE_LIQUID, VODKA, TROLL_BLOOD, OMMEL_SWEAT, OMMEL_TEARS, SULPHURIC_ACID; } + MaterialConfigChances = { 11, 75, 50, 5, 50, 2, 10, 5, 25, 5, 5, 25; } + Roundness = 100; + CanBeBroken = false; + AttachedGod = NONE; + WieldedBitmapPos = 176, 144; + IsValuable = false; + TeleportPriority = 200; + ThrowItemTypes = THROW_POTION; + CanBeGeneratedInContainer = false; + CanBePickedUp = false; +} + +bananapeels +{ + DefaultSize = 20; + Possibility = 100; + Category = MISC; + DefaultMainVolume = 40; + StrengthModifier = 50; + BitmapPos = 0, 128; + FormModifier = 35; + NameSingular = "banana peel"; + MainMaterialConfig == BANANA_PEEL; + Roundness = 10; + WeaponCategory = WHIPS; + AttachedGod = NEFAS; + WieldedBitmapPos = 160, 112; + AllowEquip = false; + IsValuable = false; + IsSadistWeapon = true; +} + +brokenbottle /* cannot contain anything, so NOT bottle-> */ +{ + DefaultSize = 10; + Possibility = 100; + Category = MISC; + DefaultMainVolume = 60; + StrengthModifier = 50; + BitmapPos = 16, 160; + FormModifier = 50; + NameSingular = "bottle"; + Adjective = "broken"; + MainMaterialConfig == GLASS; + Roundness = 70; + AttachedGod = NEFAS; + WieldedBitmapPos = 160, 128; + IsValuable = false; + DamageFlags = PIERCE; + IsSadistWeapon = true; +} + +scroll +{ + Category = SCROLL; + DefaultMainVolume = 250; + StrengthModifier = 25; + BitmapPos = 16, 176; + FormModifier = 20; + DefaultSize = 30; + NameSingular = "scroll"; + MainMaterialConfig == PARCHMENT; + IsAbstract = true; + Roundness = 15; + WieldedBitmapPos = 176, 144; + AllowEquip = false; + TeleportPriority = 300; +} + +scrollofteleportation /* scroll-> */ +{ + Possibility = 100; + Price = 50; + PostFix = "of teleportation"; + Alias == "scroll of teleport"; + AttachedGod = SOPHOS; + ReadDifficulty = 50; +} + +scrolloffireballs /* scroll-> */ +{ + Possibility = 15; + Price = 1000; + PostFix = "of fireballs"; + Alias == "sofb"; + AttachedGod = LEGIFER; + ReadDifficulty = 1000; + IsPolymorphSpawnable = false; + CanBeCloned = false; +} + +scrollofcharging /* scroll-> */ +{ + Possibility = 25; + Price = 500; + PostFix = "of charging"; + CanBeCloned = false; + IsPolymorphSpawnable = false; + AttachedGod = SOPHOS; + ReadDifficulty = 500; +} + +locationmap +{ + Category = SCROLL; + DefaultMainVolume = 250; + StrengthModifier = 25; + BitmapPos = 32, 448; + FormModifier = 20; + DefaultSize = 30; + NameSingular = "map"; + MainMaterialConfig == PARCHMENT; + IsAbstract = true; + Roundness = 15; + WieldedBitmapPos = 176, 144; + AllowEquip = false; + TeleportPriority = 300; +} + +maptotombofxinroch /* locationmap-> */ +{ + Possibility = 0; + Price = 50; + PostFix = "to the Tomb of Xinroch"; + AttachedGod = SOPHOS; + ReadDifficulty = 50; + CanBeWished = false; + CanBeCloned = false; + IsPolymorphSpawnable = false; +} + +nut +{ + DefaultSize = 3; + Possibility = 5; + Category = FOOD; + DefaultMainVolume = 25; + StrengthModifier = 50; + BitmapPos = 16, 208; + FormModifier = 10; + MainMaterialConfig == HUMAN_FLESH; + NameSingular = "nut"; + Roundness = 90; + AttachedGod = NEFAS; + WieldedBitmapPos = 160, 368; + IsValuable = false; + IsSadistWeapon = true; +} + +leftnutofpetrus /* nut-> */ +{ + DefaultSize = 10; + Possibility = 0; + Category = MISC; + DefaultMainVolume = 500; + StrengthModifier = 2500; + IsDestroyable = false; + BaseEmitation = rgb24(166, 112, 112); + Adjective = "left"; + PostFix = "of Petrus"; + ArticleMode = FORCE_THE; + IsPolymorphable = false; + CanBeWished = false; + CanBeCloned = false; + FormModifier = 15; + AttachedGod = VALPURUS; + IsQuestItem = true; + IsValuable = true; +} + +bone +{ + DefaultSize = 50; + Possibility = 250; + WeaponCategory = BLUNT_WEAPONS; + DefaultMainVolume = 500; + StrengthModifier = 80; + BitmapPos = 16, 240; + FormModifier = 25; + Category = MISC; + NameSingular = "bone"; + MainMaterialConfig = { 2, BONE, OMMEL_BONE; } + MaterialConfigChances = { 2, 100, 1; } + Roundness = 20; + IsTwoHanded = true; + AttachedGod = MORTIFER; + WieldedBitmapPos = 160, 336; + IsValuable = false; +} + +loaf +{ + DefaultSize = 40; + Possibility = 500; + WeaponCategory = BLUNT_WEAPONS; + Category = FOOD; + DefaultMainVolume = 500; + StrengthModifier = 100; + BitmapPos = 0, 272; + FormModifier = 20; + NameSingular = "loaf"; + NamePlural = "loaves"; + MainMaterialConfig = { 3, BEEF, PORK, BREAD; } + MaterialConfigChances = { 3, 50, 50, 200; } + Alias == "food"; + Roundness = 20; + AttachedGod = NONE; + WieldedBitmapPos = 160, 352; + IsValuable = false; +} + +scrollofwishing /* scroll-> */ +{ + Possibility = 3; + CanBeWished = false; + Price = 3000; + IsPolymorphSpawnable = false; + PostFix = "of wishing"; + CanBeCloned = false; + AttachedGod = ATAVUS; + ReadDifficulty = 2500; +} + +copyofleftnutofpetrus /* nut-> */ +{ + DefaultSize = 10; + Category = MISC; + DefaultMainVolume = 500; + NameSingular = "copy of the left nut of Petrus"; + NamePlural = "copies of the left nut of Petrus"; + FormModifier = 15; + AttachedGod = MELLIS; + IsValuable = false; + IsAbstract = true; + + Config CHEAP; + { + Possibility = 10; + Price = 50; + MainMaterialConfig == GLASS; /* should be plastic */ + Adjective = "cheap"; + Alias == "left nut"; + } + + Config EXPENSIVE; + { + Possibility = 5; + Price = 500; + MainMaterialConfig == DIAMOND; + UsesLongAdjectiveArticle = true; + Adjective = "expensive"; + } +} + +wand +{ + Category = WAND; + DefaultMainVolume = 125; + StrengthModifier = 50; + BitmapPos = 0, 288; + FormModifier = 30; + DefaultSize = 30; + NameSingular = "wand"; + IsAbstract = true; + Roundness = 5; + CanBePiled = false; + BreakEffectRangeSquare = 2; + WieldedBitmapPos = 160, 352; + TeleportPriority = 1000; + + Config WAND_OF_POLYMORPH; + { + Possibility = 20; + Price = 500; + IsPolymorphSpawnable = false; + PostFix = "of polymorph"; + MainMaterialConfig == OCTIRON; + BeamRange = 5; + MinCharges = 2; + MaxCharges = 5; + BeamColor = RANDOM_COLOR; + BeamEffect = BEAM_POLYMORPH; + CanBeCloned = false; + AttachedGod = SCABIES; + IsKamikazeWeapon = true; + BreakMsg = "shatters, its pieces transforming into a myriad of exotic forms before vanishing"; + } + + Config WAND_OF_STRIKING; + { + Possibility = 30; + Price = 500; + PostFix = "of striking"; + MainMaterialConfig == IRON; + BeamRange = 10; + MinCharges = 2; + MaxCharges = 5; + BeamColor = WHITE; + BeamEffect = BEAM_STRIKE; + AttachedGod = INFUSCOR; + IsKamikazeWeapon = true; + BreakMsg = "explodes, releasing all of its energy at once"; + } + + Config WAND_OF_FIRE_BALLS; + { + Possibility = 40; + Price = 500; + PostFix = "of fireballs"; + MainMaterialConfig == BRONZE; + BeamRange = 50; + MinCharges = 1; + MaxCharges = 4; + BeamColor = YELLOW; + BeamEffect = BEAM_FIRE_BALL; + AttachedGod = INFUSCOR; + BreakEffectRangeSquare = 0; + IsKamikazeWeapon = true; + BreakMsg = "turns into a fiery ball of rapidly expanding plasma"; + } + + Config WAND_OF_TELEPORTATION; + { + Possibility = 60; + Price = 500; + PostFix = "of teleportation"; + MainMaterialConfig == SILVER; + Alias == "wand of teleport"; + BeamRange = 5; + MinCharges = 3; + MaxCharges = 6; + BeamColor = BLUE; + BeamEffect = BEAM_TELEPORT; + AttachedGod = SOPHOS; + BreakEffectRangeSquare = 9; + BreakMsg = "breaks apart and a thousand splinters scatter in random directions, each cutting a tiny hole in space and time"; + } + + Config WAND_OF_HASTE; + { + Possibility = 40; + Price = 500; + PostFix = "of haste"; + MainMaterialConfig == TIN; + BeamRange = 5; + MinCharges = 3; + MaxCharges = 6; + BeamColor = RED; + BeamEffect = BEAM_HASTE; + AttachedGod = CLEPTIA; + BreakMsg = "breaks, speeding up everything around it"; + } + + Config WAND_OF_SLOW; + { + Possibility = 40; + Price = 500; + PostFix = "of slow"; + MainMaterialConfig == GRANITE; + BeamRange = 5; + MinCharges = 3; + MaxCharges = 6; + BeamColor = BLUE; + BeamEffect = BEAM_SLOW; + AttachedGod = INFUSCOR; + BreakMsg = "breaks, dilating time around it"; + } + + Config WAND_OF_RESURRECTION; + { + Possibility = 20; + Price = 750; + PostFix = "of resurrection"; + MainMaterialConfig == PHOENIX_FEATHER; + BeamRange = 5; + MinCharges = 1; + MaxCharges = 3; + BeamColor = BLACK; + BeamEffect = BEAM_RESURRECT; + AttachedGod = SEGES; + BreakMsg = "breaks, releasing countless green rays of highly compressed life energy"; + } + + Config WAND_OF_DOOR_CREATION; + { + Possibility = 20; + Price = 250; + PostFix = "of door creation"; + MainMaterialConfig == OAK_WOOD; + /* BeamRange has no effect */ + MinCharges = 3; + MaxCharges = 6; + BeamColor = LIGHT_GRAY; + BeamEffect = BEAM_DOOR_CREATION; + BeamStyle = SHIELD_BEAM; + AttachedGod = LORICATUS; + BreakMsg = "releases its powers, causing a number of highly unexpected effects to occur around itself"; + } + + Config WAND_OF_INVISIBILITY; + { + Possibility = 40; + Price = 250; + PostFix = "of invisibility"; + MainMaterialConfig == GLASS; + BeamRange = 10; + MinCharges = 3; + MaxCharges = 6; + BeamColor = TRANSPARENT_COLOR; /* invisible */ + BeamEffect = BEAM_INVISIBILITY; + AttachedGod = CLEPTIA; + BreakMsg = "becomes increasingly transparent and then vanishes abruptly"; + } + + Config WAND_OF_CLONING; + { + Possibility = 5; + Price = 2000; + IsPolymorphSpawnable = false; + PostFix = "of cloning"; + MainMaterialConfig == RUBY; + BeamRange = 5; + MinCharges = 1; + MaxCharges = 3; + CanBeCloned = false; + BeamColor = PINK; + BeamEffect = BEAM_DUPLICATE; + AttachedGod = INFUSCOR; + BreakEffectRangeSquare = 0; + BreakMsg = "breaks up, its dispersing splinters multiplying infinitely"; + } + + Config WAND_OF_LIGHTNING; + { + Possibility = 60; + Price = 250; + PostFix = "of lightning"; + MainMaterialConfig == GOLD; + BeamRange = 15; + MinCharges = 2; + MaxCharges = 5; + BeamColor = WHITE; + BeamEffect = BEAM_LIGHTNING; + BeamStyle = LIGHTNING_BEAM; + AttachedGod = INFUSCOR; + BreakEffectRangeSquare = 9; + IsKamikazeWeapon = true; + BreakMsg = "breaks and releases all of its stored electricity at once"; + } + + Config WAND_OF_ACID_RAIN; + { + Possibility = 30; + Price = 500; + PostFix = "of acid rain"; + MainMaterialConfig == SULFUR; + BeamRange = 15; + MinCharges = 2; + MaxCharges = 5; + BeamColor = GREEN; + BeamEffect = BEAM_ACID_RAIN; + AttachedGod = SCABIES; + BreakEffectRangeSquare = 1; + IsKamikazeWeapon = true; + BreakMsg = "vaporizes in an instant, unleashing a deadly rainstorm of polluted acid"; + } + + Config WAND_OF_MIRRORING; + { + Possibility = 15; + Price = 1000; + IsPolymorphSpawnable = false; + PostFix = "of mirroring"; + MainMaterialConfig == SAPPHIRE; + BeamRange = 5; + MinCharges = 1; + MaxCharges = 3; + CanBeCloned = false; + BeamColor = PINK; + BeamEffect = BEAM_DUPLICATE; /* difference from cloning overridden */ + AttachedGod = INFUSCOR; + BreakEffectRangeSquare = 0; + BreakMsg = "shatters, its slivers reflecting each other recursively ad infinitum"; + } + + Config WAND_OF_NECROMANCY; + { + Possibility = 30; + Price = 500; + PostFix = "of necromancy"; + MainMaterialConfig == BONE; + BeamRange = 5; + MinCharges = 2; + MaxCharges = 5; + BeamColor = BLACK; + BeamEffect = BEAM_NECROMANCY; + AttachedGod = INFUSCOR; + BreakMsg = "emits a gross of unholy rays and self-obliterates"; + } +} + +scrollofchangematerial /* scroll-> */ +{ + Possibility = 15; + Price = 2000; + PostFix = "of change material"; + Alias == "SoCM"; + AttachedGod = LORICATUS; + ReadDifficulty = 2000; +} + +avatarofvalpurus +{ + DefaultSize = 10; + Possibility = 0; + CanBeWished = false; + IsDestroyable = false; + IsMaterialChangeable = false; + Price = 1000000; + Category = MISC; + DefaultMainVolume = 250; + StrengthModifier = 500; + BitmapPos = 0, 320; + FormModifier = 15; + UsesLongArticle = true; + NameSingular = "Avatar"; + PostFix = "of Valpurus"; + ArticleMode = FORCE_THE; + MainMaterialConfig == VALPURIUM; + IsPolymorphable = false; + Roundness = 80; + CanBeCloned = false; + AttachedGod = VALPURUS; + WieldedBitmapPos = 160, 128; + IsQuestItem = true; +} + +kiwi +{ + DefaultSize = 10; + Possibility = 75; + Category = FOOD; + DefaultMainVolume = 50; + StrengthModifier = 50; + BitmapPos = 0, 384; + FormModifier = 10; + NameSingular = "kiwi"; + MainMaterialConfig == KIWI_FLESH; + Roundness = 80; + AttachedGod = SILVA; + WieldedBitmapPos = 160, 368; + AllowEquip = false; + IsValuable = false; +} + +pineapple +{ + DefaultSize = 20; + Possibility = 75; + Category = FOOD; + DefaultMainVolume = 1000; + StrengthModifier = 100; + BitmapPos = 0, 368; + FormModifier = 10; + NameSingular = "pineapple"; + MainMaterialConfig == PINEAPPLE_FLESH; + Roundness = 90; + IsTwoHanded = true; + AttachedGod = SILVA; + WieldedBitmapPos = 176, 144; + IsValuable = false; +} + +mango +{ + WeaponCategory = BLUNT_WEAPONS; + DefaultSize = 20; + Possibility = 0; + Category = FOOD; + DefaultMainVolume = 250; + StrengthModifier = 100; + BitmapPos = 0, 432; + FormModifier = 10; + NameSingular = "mango"; + NamePlural = "mangoes"; + MainMaterialConfig == MANGO_FLESH; + Roundness = 90; + AttachedGod = SILVA; + WieldedBitmapPos = 176, 144; + IsValuable = false; + CanBeBroken = false; + AllowEquip = false; + IsSadistWeapon = true; +} + +palmbranch +{ + DefaultSize = 80; + Possibility = 0; + Category = MISC; + DefaultMainVolume = 4000; + StrengthModifier = 50; + BitmapPos = 0, 240; + FormModifier = 15; + NameSingular = "palm branch"; + NamePlural = "palm branches"; + MainMaterialConfig == PALM_LEAF; + Roundness = 115; + IsTwoHanded = true; + AttachedGod = SILVA; + WieldedBitmapPos = 160, 208; + IsValuable = false; +} + +whip /* meleeweapon-> */ +{ + DefaultSize = 210; + Possibility = 50; + WeaponCategory = WHIPS; + DefaultMainVolume = 500; + DefaultSecondaryVolume = 20; + StrengthModifier = 10; + BitmapPos = 32, 0; + NameSingular = "whip"; + MainMaterialConfig = { 4, LEATHER, HARDENED_LEATHER, NYMPH_HAIR, OMMEL_HAIR; } + SecondaryMaterialConfig == EBONY_WOOD; + MaterialConfigChances = { 4, 75, 50, 25, 10; } + Roundness = 2; + FormModifier = 10; /* this is multiplied by MainMaterial's flexibility */ + CanBeBroken = false; + AttachedGod = NEFAS; + WieldedBitmapPos = 160, 224; + EnchantmentPlusChance = 30; + FlexibilityIsEssential = true; + DamageFlags = SLASH; + IsSadistWeapon = true; + + Config RUNED_WHIP; + { + DefaultSize = 230; + Possibility = 25; + DefaultMainVolume = 750; + DefaultSecondaryVolume = 50; + StrengthModifier = 20; + Adjective = "runed"; + FormModifier = 15; + EnchantmentPlusChance = 20; + } +} + +backpack /* materialcontainer-> */ +{ + DefaultSize = 80; + Possibility = 25; + Category = TOOL; + DefaultMainVolume = 1000; + DefaultSecondaryVolume = 10000; + StrengthModifier = 250; + BitmapPos = 32, 16; + FormModifier = 10; + NameSingular = "backpack"; + MainMaterialConfig == LEATHER; + SecondaryMaterialConfig == GUN_POWDER; + Alias == "explosives"; + Roundness = 70; + IsTwoHanded = true; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 160, 144; + IsKamikazeWeapon = true; +} + +holybook +{ + DefaultSize = 25; + Possibility = 15; /* this is for each config */ + Category = BOOK; + DefaultMainVolume = 2000; + StrengthModifier = 150; + BitmapPos = 32, 32; + FormModifier = 20; + Adjective = "holy"; + NameSingular = "book"; + ArticleMode = FORCE_THE; + MainMaterialConfig == PARCHMENT; + Price = 200; + Roundness = 70; + IsTwoHanded = true; + CreateDivineConfigurations = true; + IsAbstract = true; + AttachedGod = NONE; + WieldedBitmapPos = 176, 144; + ReadDifficulty = 1000; +} + +fiftymillionroubles +{ + DefaultSize = 20; + Possibility = 0; + Price = 2; + Category = VALUABLE; /* ??? */ + DefaultMainVolume = 1000; + StrengthModifier = 25; + BitmapPos = 0, 336; + FormModifier = 15; + NameSingular = "pile"; + PostFix = "of 50 million roubles"; + MainMaterialConfig == PARCHMENT; + Alias = { 2, "money", "riches"; } + Roundness = 60; + AttachedGod = MELLIS; + WieldedBitmapPos = 160, 128; + IsValuable = false; +} + +oillamp +{ + DefaultSize = 30; + Possibility = 15; + CanBeWished = false; + Category = TOOL; + DefaultMainVolume = 150; + StrengthModifier = 50; + BitmapPos = 32, 48; + FormModifier = 20; + BaseEmitation = rgb24(110, 110, 90); + UsesLongArticle = true; + NameSingular = "oil lamp"; + MainMaterialConfig == GOLD; + Roundness = 40; + CanBePiled = false; /* otherwise you could summon a genie only from the first piled item */ + Price = 100; + Alias == "lamp"; + WeaponCategory = BLUNT_WEAPONS; + AttachedGod = ATAVUS; + WieldedBitmapPos = 160, 128; +} + +stone +{ + DefaultSize = 30; + Possibility = 25; + Category = VALUABLE; + DefaultMainVolume = 100; + StrengthModifier = 100; + BitmapPos = 0, 48; + FormModifier = 10; + NameSingular = "stone"; + MainMaterialConfig = { 5, SILVER, GOLD, SAPPHIRE, RUBY, DIAMOND; } + MaterialConfigChances = { 5, 300, 200, 150, 100, 50; } + Alias == "jewel"; + Roundness = 90; + AttachedGod = NONE; + WieldedBitmapPos = 160, 368; + IsValuable = false; + HasNormalPictureDirection = false; +} + +solstone +{ + DefaultSize = 30; + Possibility = 25; + Category = VALUABLE; + DefaultMainVolume = -417; + StrengthModifier = 100; + BitmapPos = 0, 48; + FormModifier = 10; + Adjective = "sol"; + NameSingular = "stone"; + Price = 100; + MainMaterialConfig == OBSIDIAN; + Roundness = 90; + AttachedGod = NONE; + WieldedBitmapPos = 160, 368; + IsValuable = true; + HasNormalPictureDirection = false; + TeleportPriority = 20000; +} + +scrolloftaming /* scroll-> */ +{ + Possibility = 25; + Price = 500; + PostFix = "of taming"; + AttachedGod = DULCIS; + ReadDifficulty = 100; +} + +mine /*materialcontainer-> */ +{ + DefaultSize = 50; + Possibility = 50; + Category = TOOL; + DefaultMainVolume = 100; + DefaultSecondaryVolume = 5000; + StrengthModifier = 50; + BitmapPos = 32, 288; + FormModifier = 10; + NameSingular = "mine"; + MainMaterialConfig == IRON; + SecondaryMaterialConfig == GUN_POWDER; + Roundness = 100; + IsTwoHanded = true; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 160, 128; + + Config BIG_MINE; + { + DefaultSize = 65; + Possibility = 10; + DefaultMainVolume = 250; + DefaultSecondaryVolume = 12500; + NameSingular = "big mine"; + BitmapPos = 80, 288; + } +} + +key +{ + /* Bug: CreateLockConfigurations = true before key doesn't work! */ + + DefaultSize = 10; + Possibility = 0; + Price = 50; + Category = TOOL; + DefaultMainVolume = 15; + StrengthModifier = 50; + BitmapPos = 16, 416; + FormModifier = 30; + NameSingular = "key"; + MainMaterialConfig == BRONZE; + Roundness = 10; + AttachedGod = LORICATUS; + WieldedBitmapPos = 160, 368; + IsAbstract = true; + + Config ROUND_LOCK; { Adjective = "round"; Possibility = 25; } + Config SQUARE_LOCK; { Adjective = "square"; Possibility = 25; } + Config TRIANGULAR_LOCK; { Adjective = "triangular"; Possibility = 25; } + Config PENTAGONAL_LOCK; + { + MainMaterialConfig == IRON; + Adjective = "pentagonal"; + } + Config HEXAGONAL_LOCK; { Adjective = "hexagonal"; } + Config OCTAGONAL_LOCK; + { + MainMaterialConfig == OCTIRON; + UsesLongAdjectiveArticle = true; + Adjective = "octagonal"; + } + Config HEART_SHAPED_LOCK; + { + MainMaterialConfig == GOLD; + Adjective = "heart shaped"; + } +} + +shield /* armor-> */ +{ + DefaultSize = 60; + Possibility = 250; + Category = SHIELD; + DefaultMainVolume = 350; + StrengthModifier = 150; + BitmapPos = 32, 336; + FormModifier = 15; + NameSingular = "shield"; + MainMaterialConfig = { 14, PINE_WOOD, FIR_WOOD, BIRCH_WOOD, OAK_WOOD, TEAK_WOOD, EBONY_WOOD, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_BONE, OMMEL_TOOTH; } + MaterialConfigChances = { 14, 350, 300, 250, 200, 150, 100, 1000, 750, 500, 100, 25, 25, 10, 5; } + Roundness = 95; + WeaponCategory = SHIELDS; + CanBeBroken = true; + WieldedBitmapPos = 128, 32; + EnchantmentPlusChance = 15; + + Config BROKEN; + { + Possibility = 25; + DefaultSize = 40; + FormModifier = 20; + BitmapPos = 48, 224; + WieldedBitmapPos = 128, 48; + MainMaterialConfig = { 7, EBONY_WOOD, STEEL, METEORIC_STEEL, ARCANITE, MITHRIL, OCTIRON, ADAMANT; } + MaterialConfigChances = { 7, 100, 150, 25, 10, 25, 5, 5; } + EnchantmentPlusChance = 30; + } +} + +cloak /* armor-> */ +{ + DefaultSize = 200; + Possibility = 250; + Category = CLOAK; + DefaultMainVolume = 2000; + StrengthModifier = 125; + BitmapPos = 32, 352; + FormModifier = 20; + NameSingular = "cloak"; + MainMaterialConfig = { 5, LEATHER, HARDENED_LEATHER, TROLL_HIDE, NYMPH_HAIR, OMMEL_HAIR; } + MaterialConfigChances = { 5, 2000, 1000, 500, 250, 100; } + Roundness = 60; + IsTwoHanded = true; + InElasticityPenaltyModifier = 50; + AttachedGod = CLEPTIA; + WieldedBitmapPos = 160, 144; + CanBeBroken = true; + CloakBitmapPos = 48, 416; + EnchantmentPlusChance = 10; + + Config BROKEN; + { + Possibility = 25; + BitmapPos = 64, 304; + InElasticityPenaltyModifier = 40; + MainMaterialConfig = { 4, TROLL_HIDE, NYMPH_HAIR, OMMEL_HAIR, DRAGON_HIDE; } + MaterialConfigChances = { 4, 500, 250, 100, 25; } + EnchantmentPlusChance = 20; + CloakBitmapPos = 48, 496; + } + + Config CLOAK_OF_INVISIBILITY; + { + Possibility = 1; + GearStates = INVISIBLE; + PostFix = "of invisibility"; + Price = 2500; + MainMaterialConfig == LEATHER; + MaterialConfigChances == 100; + EnchantmentPlusChance = 2; + } + + Config BROKEN|CLOAK_OF_INVISIBILITY; + { + Possibility = 2; + BitmapPos = 64, 304; + InElasticityPenaltyModifier = 40; + Price = 500; + GearStates = 0; + EnchantmentPlusChance = 4; + CloakBitmapPos = 48, 496; + } + + Config CLOAK_OF_FIRE_RESISTANCE; + { + Possibility = 20; + PostFix = "of fire resistance"; + Price = 250; + FireResistance = 25; + EnchantmentPlusChance = 5; + } + + Config BROKEN|CLOAK_OF_FIRE_RESISTANCE; + { + Possibility = 2; + BitmapPos = 64, 304; + InElasticityPenaltyModifier = 40; + FireResistance = 10; + Price = 150; + MainMaterialConfig = { 4, TROLL_HIDE, NYMPH_HAIR, OMMEL_HAIR, DRAGON_HIDE; } + MaterialConfigChances = { 4, 300, 200, 100, 50; } + EnchantmentPlusChance = 10; + CloakBitmapPos = 48, 496; + } + + Config CLOAK_OF_ELECTRICITY_RESISTANCE; + { + Possibility = 15; + PostFix = "of electricity resistance"; + Price = 250; + ElectricityResistance = 25; + EnchantmentPlusChance = 5; + } + + Config BROKEN|CLOAK_OF_ELECTRICITY_RESISTANCE; + { + Possibility = 2; + BitmapPos = 64, 304; + InElasticityPenaltyModifier = 40; + ElectricityResistance = 10; + Price = 150; + MainMaterialConfig = { 4, TROLL_HIDE, NYMPH_HAIR, OMMEL_HAIR, DRAGON_HIDE; } + MaterialConfigChances = { 4, 300, 200, 100, 50; } + EnchantmentPlusChance = 10; + CloakBitmapPos = 48, 496; + } + + Config CLOAK_OF_ACID_RESISTANCE; + { + Possibility = 10; + PostFix = "of acid resistance"; + Price = 250; + AcidResistance = 4; + EnchantmentPlusChance = 5; + TeleportPriority = 1000; + } + + Config BROKEN|CLOAK_OF_ACID_RESISTANCE; + { + Possibility = 1; + BitmapPos = 64, 304; + InElasticityPenaltyModifier = 40; + AcidResistance = 2; + Price = 150; + MainMaterialConfig = { 4, TROLL_HIDE, NYMPH_HAIR, OMMEL_HAIR, DRAGON_HIDE; } + MaterialConfigChances = { 4, 300, 200, 100, 50; } + EnchantmentPlusChance = 10; + CloakBitmapPos = 48, 496; +} + + Config CLOAK_OF_WERE_WOLF_FUR; + { + Possibility = 5; + NameSingular = "werewolf fur cloak"; + MainMaterialConfig == WERE_WOLF_FLESH; + GearStates = LYCANTHROPY; + EnchantmentPlusChance = 5; + Alias == "werewolf fur cloak"; + } + + Config BROKEN|CLOAK_OF_WERE_WOLF_FUR; + { + Possibility = 0; + BitmapPos = 64, 304; + InElasticityPenaltyModifier = 40; + AcidResistance = 2; + EnchantmentPlusChance = 10; + CloakBitmapPos = 48, 496; + } +} + +boot /* armor-> */ +{ + DefaultSize = 30; + Possibility = 250; + Category = BOOT; + DefaultMainVolume = 400; + StrengthModifier = 150; + BitmapPos = 0, 400; + FormModifier = 20; + NameSingular = "boot"; + MainMaterialConfig = { 9, LEATHER, HARDENED_LEATHER, TROLL_HIDE, NYMPH_HAIR, OMMEL_HAIR, BRONZE, IRON, STEEL, METEORIC_STEEL; } + MaterialConfigChances = { 9, 2000, 1000, 500, 250, 100, 1000, 500, 100, 25; } + Roundness = 50; + CanBePiled = true; + InElasticityPenaltyModifier = 10; + HandleInPairs = true; + WieldedBitmapPos = 160, 128; + BootBitmapPos = 16, 432; + CanBeBroken = true; + EnchantmentPlusChance = 10; + + Config BROKEN; + { + DefaultSize = 30; + FormModifier = 30; + BitmapPos = 80, 368; + Possibility = 25; + MainMaterialConfig = { 9, OMMEL_HAIR, IRON, STEEL, METEORIC_STEEL, DRAGON_HIDE, ARCANITE, MITHRIL, OCTIRON, ADAMANT; } + MaterialConfigChances = { 9, 100, 500, 100, 25, 10, 10, 25, 5, 1; } + EnchantmentPlusChance = 20; + InElasticityPenaltyModifier = 30; + BootBitmapPos = 16, 512; + } + + Config BOOT_OF_STRENGTH; + { + Possibility = 10; + PostFix = "of strength"; + MainMaterialConfig == TROLL_HIDE; + MaterialConfigChances == 100; + AffectsLegStrength = true; + Price = 50; + BaseEnchantment = 1; + PriceIsProportionalToEnchantment = true; + EnchantmentPlusChance = 2; + } + + Config BROKEN|BOOT_OF_STRENGTH; + { + DefaultSize = 30; + FormModifier = 30; + BitmapPos = 80, 368; + Possibility = 20; + AffectsLegStrength = false; + Price = 10; + EnchantmentPlusChance = 4; + InElasticityPenaltyModifier = 30; + BootBitmapPos = 16, 512; + } + + Config BOOT_OF_AGILITY; + { + Possibility = 10; + PostFix = "of agility"; + MainMaterialConfig == LEATHER; + MaterialConfigChances == 100; + AffectsAgility = true; + Price = 75; + BaseEnchantment = 1; + PriceIsProportionalToEnchantment = true; + AttachedGod = CLEPTIA; + EnchantmentPlusChance = 2; + } + + Config BROKEN|BOOT_OF_AGILITY; + { + DefaultSize = 30; + FormModifier = 30; + BitmapPos = 80, 368; + Possibility = 20; + AffectsAgility = false; + Price = 25; + EnchantmentPlusChance = 4; + InElasticityPenaltyModifier = 30; + BootBitmapPos = 16, 512; + } + + Config BOOT_OF_KICKING; + { + Possibility = 10; + PostFix = "of kicking"; + MainMaterialConfig == IRON; + MaterialConfigChances == 100; + Price = 50; + FormModifier = 50; + EnchantmentPlusChance = 5; + } + + Config BROKEN|BOOT_OF_KICKING; + { + DefaultSize = 30; + FormModifier = 30; + BitmapPos = 80, 368; + Possibility = 20; + MainMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + MaterialConfigChances = { 5, 1000, 100, 10, 5, 1; } + EnchantmentPlusChance = 10; + InElasticityPenaltyModifier = 30; + BootBitmapPos = 16, 512; + } +} + +gauntlet /* armor-> */ +{ + DefaultSize = 30; + Possibility = 250; + Category = GAUNTLET; + DefaultMainVolume = 300; + StrengthModifier = 125; + BitmapPos = 32, 368; + FormModifier = 20; + NameSingular = "gauntlet"; + MainMaterialConfig = { 8, LEATHER, HARDENED_LEATHER, NYMPH_HAIR, OMMEL_HAIR, BRONZE, IRON, STEEL, METEORIC_STEEL; } + MaterialConfigChances = { 8, 2000, 1000, 200, 100, 500, 250, 50, 10; } + Roundness = 60; + CanBePiled = true; + InElasticityPenaltyModifier = 20; + HandleInPairs = true; + WieldedBitmapPos = 160, 128; + GauntletBitmapPos = 80, 432; + CanBeBroken = true; + EnchantmentPlusChance = 10; + HasNormalPictureDirection = false; + + Config BROKEN; + { + Possibility = 25; + MainMaterialConfig = { 7, OMMEL_HAIR, BRONZE, IRON, STEEL, METEORIC_STEEL, DRAGON_HIDE, MITHRIL; } + MaterialConfigChances = { 7, 100, 500, 250, 50, 10, 5, 10; } + DefaultSize = 30; + FormModifier = 30; + BitmapPos = 80, 400; + EnchantmentPlusChance = 20; + InElasticityPenaltyModifier = 30; + GauntletBitmapPos = 80, 512; + } + + Config GAUNTLET_OF_STRENGTH; + { + Possibility = 10; + PostFix = "of strength"; + MainMaterialConfig == TROLL_HIDE; + MaterialConfigChances == 100; + AffectsArmStrength = true; + Price = 75; + BaseEnchantment = 1; + PriceIsProportionalToEnchantment = true; + AttachedGod = CRUENTUS; + EnchantmentPlusChance = 2; + } + + Config BROKEN|GAUNTLET_OF_STRENGTH; + { + DefaultSize = 20; + FormModifier = 30; + BitmapPos = 80, 400; + AffectsArmStrength = false; + Price = 25; + EnchantmentPlusChance = 4; + InElasticityPenaltyModifier = 30; + GauntletBitmapPos = 80, 512; + } + + Config GAUNTLET_OF_DEXTERITY; + { + Possibility = 10; + PostFix = "of dexterity"; + MainMaterialConfig == LEATHER; + MaterialConfigChances == 100; + AffectsDexterity = true; + Price = 100; + BaseEnchantment = 1; + PriceIsProportionalToEnchantment = true; + AttachedGod = CLEPTIA; + EnchantmentPlusChance = 2; + } + + Config BROKEN|GAUNTLET_OF_DEXTERITY; + { + DefaultSize = 20; + FormModifier = 30; + BitmapPos = 80, 400; + AffectsDexterity = false; + Price = 25; + EnchantmentPlusChance = 4; + InElasticityPenaltyModifier = 30; + GauntletBitmapPos = 80, 512; + } +} + +belt /* armor-> */ +{ + DefaultSize = 150; + Possibility = 250; + WeaponCategory = WHIPS; + Category = BELT; + DefaultMainVolume = 250; + StrengthModifier = 100; + BitmapPos = 32, 384; + NameSingular = "belt"; + MainMaterialConfig = { 4, HARDENED_LEATHER, TROLL_HIDE, NYMPH_HAIR, OMMEL_HAIR; } + MaterialConfigChances = { 4, 1500, 500, 250, 100; } + Roundness = 10; + FormModifier = 5; /* this is multiplied by MainMaterial's flexibility */ + WieldedBitmapPos = 160, 224; + BeltBitmapPos = 48, 432; + CanBeBroken = true; + EnchantmentPlusChance = 10; + DamageFlags = SLASH; + IsSadistWeapon = true; + + Config BROKEN; + { + DefaultSize = 150; + Possibility = 25; + MainMaterialConfig = { 5, TROLL_HIDE, NYMPH_HAIR, OMMEL_HAIR, DRAGON_HIDE, MITHRIL; } + MaterialConfigChances = { 5, 500, 250, 100, 25, 10; } + BitmapPos = 80, 384; + EnchantmentPlusChance = 20; + BeltBitmapPos = 48, 512; + } + + Config BELT_OF_CARRYING; + { + Possibility = 10; + PostFix = "of carrying"; + AffectsCarryingCapacity = true; + Price = 75; + BaseEnchantment = 1; + PriceIsProportionalToEnchantment = true; + AttachedGod = MELLIS; + MainMaterialConfig == TROLL_HIDE; + MaterialConfigChances == 100; + EnchantmentPlusChance = 2; + } + + Config BROKEN|BELT_OF_CARRYING; + { + DefaultSize = 150; + Possibility = 20; + AffectsCarryingCapacity = false; + Price = 25; + BitmapPos = 80, 384; + EnchantmentPlusChance = 4; + BeltBitmapPos = 48, 512; + } + + Config BELT_OF_LEVITATION; + { + DefaultMainVolume = 1; + Possibility = 0; + PostFix = "of levitation"; + GearStates = LEVITATION; + Price = 500; + AttachedGod = SILVA; + MainMaterialConfig == ANGEL_HAIR; + MaterialConfigChances == 100; + EnchantmentPlusChance = 0; + } + + Config BROKEN|BELT_OF_LEVITATION; + { + DefaultMainVolume = 1; + DefaultSize = 150; + GearStates = 0; + Price = 100; + BitmapPos = 80, 384; + BeltBitmapPos = 48, 512; + } +} + +ring +{ + DefaultSize = 2; + Category = RING; + DefaultMainVolume = 3; + StrengthModifier = 100; + BitmapPos = 16, 400; + FormModifier = 10; + NameSingular = "ring"; + MainMaterialConfig == DIAMOND; + Roundness = 10; /* it is considered here opened */ + IsAbstract = true; + WieldedBitmapPos = 160, 368; + TeleportPriority = 300; + + Config RING_OF_FIRE_RESISTANCE; + { + Possibility = 40; + FireResistance = 15; + PostFix = "of fire resistance"; + MainMaterialConfig == DRAGON_HIDE; + Price = 250; + AttachedGod = LORICATUS; + } + + Config RING_OF_POLYMORPH_CONTROL; + { + Possibility = 5; + PostFix = "of polymorph control"; + MainMaterialConfig == SAPPHIRE; + GearStates == POLYMORPH_CONTROL; + Price = 750; + AttachedGod = SCABIES; + Alias == "polycontrol"; + } + + Config RING_OF_INFRA_VISION; + { + Possibility = 10; + PostFix = "of infravision"; + MainMaterialConfig == SILVER; + GearStates = INFRA_VISION; + Price = 500; + AttachedGod = LEGIFER; + } + + Config RING_OF_TELEPORTATION; + { + Possibility = 30; + PostFix = "of teleportation"; + GearStates = TELEPORT; + MainMaterialConfig == MITHRIL; + Alias == "ring of teleport"; + Price = 250; + WearWisdomLimit = 15; + AttachedGod = SOPHOS; + } + + Config RING_OF_TELEPORT_CONTROL; + { + Possibility = 10; + PostFix = "of teleport control"; + MainMaterialConfig == RUBY; + GearStates = TELEPORT_CONTROL; + Price = 500; + AttachedGod = SOPHOS; + TeleportPriority = 1000; + Alias == "telecontrol"; + } + + Config RING_OF_POLYMORPH; + { + Possibility = 30; + PostFix = "of polymorph"; + MainMaterialConfig == ARCANITE; + GearStates = POLYMORPH; + Price = 250; + WearWisdomLimit = 15; + AttachedGod = SCABIES; + } + + Config RING_OF_POISON_RESISTANCE; + { + Possibility = 30; + PoisonResistance = 2; + PostFix = "of poison resistance"; + MainMaterialConfig == TIN; + Price = 250; + AttachedGod = SEGES; + } + + Config RING_OF_INVISIBILITY; + { + Possibility = 1; + PostFix = "of invisibility"; + MainMaterialConfig == GLASS; + GearStates = INVISIBLE; + Price = 2500; + AttachedGod = CLEPTIA; + } + + Config RING_OF_ELECTRICITY_RESISTANCE; + { + Possibility = 30; + ElectricityResistance = 15; + PostFix = "of electricity resistance"; + MainMaterialConfig == EBONY_WOOD; + Price = 250; + AttachedGod = LORICATUS; + } + + Config RING_OF_SEARCHING; + { + Possibility = 20; + PostFix = "of searching"; + MainMaterialConfig == OAK_WOOD; + Price = 500; + AttachedGod = LEGIFER; + GearStates = SEARCHING; + } + + Config RING_OF_ACID_RESISTANCE; + { + Possibility = 20; + AcidResistance = 2; + PostFix = "of acid resistance"; + MainMaterialConfig == GOLD; + Price = 250; + AttachedGod = LORICATUS; + TeleportPriority = 1000; + } +} + +/*ringofthieves + { + DefaultSize = 2; + DefaultMainVolume = 3; + StrengthModifier = 100; + BitmapPos = 16, 400; + FormModifier = 10; + Category = MISC; + NameSingular = "ring"; + PostFix = "of thieves"; + Roundness = 10; + IsAbstract = true; + WieldedBitmapPos = 160, 368; + TeleportPriority = 10000; + Possibility = 0; + ElectricityResistance = 5; + FireResistance = 5; + AcidResistance = 1; + PoisonResistance = 1; + MainMaterialConfig == EMERALD; + Price = 5000; + AttachedGod = CLEPTIA; + Alias == "ring of thieves"; + CanBeWished = false; + IsQuestItem = false; + ArticleMode = FORCE_THE; + }*/ + +amulet +{ + DefaultSize = 20; + Category = AMULET; + DefaultMainVolume = 30; + StrengthModifier = 100; + BitmapPos = 32, 400; + FormModifier = 10; + UsesLongArticle = true; + NameSingular = "amulet"; + MainMaterialConfig == GOLD; + Roundness = 10; /* it is considered here opened */ + IsAbstract = true; + WieldedBitmapPos = 160, 112; + HasNormalPictureDirection = false; + + Config AMULET_OF_LIFE_SAVING; + { + Possibility = 5; + PostFix = "of life saving"; + MainMaterialConfig == GOLD; + GearStates = LIFE_SAVED; + CanBeCloned = false; + Price = 5000; + AttachedGod = SEGES; + } + + Config AMULET_OF_ESP; + { + Possibility = 10; + PostFix = "of ESP"; + MainMaterialConfig == ILLITHIUM; + GearStates = ESP; + Price = 1000; + AttachedGod = LEGIFER; + } + + Config AMULET_OF_WARDING; + { + Possibility = 4; + PostFix = "of elemental protection"; + MainMaterialConfig == ILLITHIUM; + CanBeCloned = false; + Price = 4000; + AttachedGod = LEGIFER; + FireResistance = 30; + ElectricityResistance = 30; + AcidResistance = 5; + PoisonResistance = 5; + } + + Config AMULET_OF_VANITY; + { + Possibility = 5; + PostFix = "of vanity"; + MainMaterialConfig == FLAWLESS_DIAMOND; + Price = 0; + AttachedGod = NEFAS; + AffectsCharisma = true; + BaseEnchantment = 5; + EnchantmentPlusChance = 2; + } +} + +bodypart +{ + Possibility = 0; + Category = FOOD; + IsAutoInitializable = false; + IsAbstract = true; + IsTwoHanded = true; + /*Adjective = "severed";*/ /* only used if severed */ + CanBeWished = false; /* temporary */ + AttachedGod = NONE; + IsValuable = false; + IsSadistWeapon = true; +} + +head /* bodypart-> */ +{ + StrengthModifier = 200; + FormModifier = 10; + NameSingular = "head"; + Roundness = 70; + WieldedBitmapPos = 160, 128; +} + +torso /* bodypart-> */ +{ + StrengthModifier = 250; + FormModifier = 10; + NameSingular = "torso"; + IsAbstract = true; + Roundness = 60; + WieldedBitmapPos = 160, 144; +} + +normaltorso /* bodypart->torso-> */ +{ +} + +humanoidtorso /* bodypart->torso-> */ +{ +} + +arm /* bodypart-> */ +{ + StrengthModifier = 100; + FormModifier = 15; + IsAbstract = true; + Roundness = 15; + WeaponCategory = BLUNT_WEAPONS; + WieldedBitmapPos = 160, 352; +} + +rightarm /* bodypart->arm-> */ +{ + NameSingular = "right arm"; +} + +leftarm /* bodypart->arm-> */ +{ + NameSingular = "left arm"; +} + +groin /* bodypart-> */ +{ + StrengthModifier = 150; + FormModifier = 10; + NameSingular = "groin"; + Roundness = 40; + WieldedBitmapPos = 160, 128; +} + +leg /* bodypart-> */ +{ + StrengthModifier = 125; + FormModifier = 15; + IsAbstract = true; + Roundness = 20; + WeaponCategory = BLUNT_WEAPONS; + WieldedBitmapPos = 160, 352; +} + +rightleg /* bodypart->leg-> */ +{ + NameSingular = "right leg"; +} + +leftleg /* bodypart->leg-> */ +{ + NameSingular = "left leg"; +} + +headofelpuri /* cannot wear helmet etc, so NOT head-> */ +{ + Possibility = 0; + CanBeWished = false; + DefaultSize = 60; + IsDestroyable = false; + Category = MISC; + StrengthModifier = 150; + FormModifier = 15; + BitmapPos = 16, 0; + DefaultMainVolume = 25000; + NameSingular = "head"; + PostFix = "of Elpuri"; + ArticleMode = FORCE_THE; + MainMaterialConfig == ELPURI_FLESH; + IsPolymorphable = false; + Roundness = 80; + IsTwoHanded = true; + CanBeCloned = false; + AttachedGod = SCABIES; + WieldedBitmapPos = 160, 144; + IsQuestItem = true; +} + +corpse +{ + Possibility = 0; + Category = FOOD; + IsAutoInitializable = false; + IsPolymorphSpawnable = false; + StrengthModifier = 100; + FormModifier = 15; + NameSingular = "corpse"; + OKVisualEffects = ROTATE|MIRROR|FLIP; + Roundness = 50; + IsTwoHanded = true; + CanBeWished = false; /* temporary */ + IsMaterialChangeable = false; + AttachedGod = NONE; + WieldedBitmapPos = 160, 144; + /* BitmapPos overridden */ + IsValuable = false; +} + +eddytorso /* normaltorso-> */ +{ +} + +largetorso /* normaltorso-> */ +{ + AttachedGod = NONE; + CanBePickedUp = false; +} + +largecorpse /* corpse -> */ +{ + BitmapPos = 48, 0; + CanBePickedUp = false; + OKVisualEffects = NONE; +} + +playerkindhead { } +playerkindtorso { } +playerkindrightarm { } +playerkindleftarm { } +playerkindgroin { } +playerkindrightleg { } +playerkindleftleg { } + +magicmushroomtorso /* normaltorso-> */ +{ +} + +menatrixtorso /* normaltorso-> */ +{ +} + +dogtorso /* normaltorso-> */ +{ +} + +blinkdogtorso /* dogtorso-> */ +{ +} + +mysticfrogtorso /* normaltorso-> */ +{ +} + +lobhsetorso /* largetorso-> */ +{ +} + +battorso /* normaltorso-> */ +{ +} + +spidertorso /* normaltorso-> */ +{ +} + +whistle +{ + Possibility = 50; + Category = TOOL; + DefaultMainVolume = 50; + StrengthModifier = 50; + BitmapPos = 0, 416; + FormModifier = 25; + DefaultSize = 30; + NameSingular = "whistle"; + MainMaterialConfig == TIN; + Roundness = 10; + Price = 25; + AttachedGod = DULCIS; + WieldedBitmapPos = 160, 368; +} + +magicalwhistle /* whistle-> */ +{ + Possibility = 5; + MainMaterialConfig == RUBY; + NameSingular = "whistle"; + Adjective = "magical"; + Price = 1500; + Alias == "magic whistle"; + CanBePiled = false; + AttachedGod = DULCIS; + TeleportPriority = 10000; +} + +itemcontainer +{ + CanBeGeneratedInContainer = false; + Roundness = 60; + IsTwoHanded = true; + FormModifier = 15; + MainMaterialConfig == FIR_WOOD; + Roundness = 60; + CanBePiled = false; + IsAbstract = true; + IsPolymorphSpawnable = false; + IsDestroyable = false; + AttachedGod = ATAVUS; + WieldedBitmapPos = 160, 144; + CreateLockConfigurations = true; + + Config SMALL_CHEST; + { + NameSingular = "chest"; + Adjective = "small"; + DefaultSize = 50; + StorageVolume = 10000; + DefaultMainVolume = 5000; + Price = 75; + Possibility = 100; + MaxGeneratedContainedItems = 3; + BitmapPos = 64, 48; + DamageDivider = 2; + StrengthModifier = 100; + } + + Config CHEST; + { + NameSingular = "chest"; + StorageVolume = 100000; + DefaultMainVolume = 30000; + DefaultSize = 100; + Price = 150; + Possibility = 50; + MaxGeneratedContainedItems = 5; + BitmapPos = 48, 80; + DamageDivider = 3; + StrengthModifier = 150; + } + + Config LARGE_CHEST; + { + NameSingular = "chest"; + Adjective = "large"; + DefaultSize = 200; + StorageVolume = 1000000; + DefaultMainVolume = 240000; + Price = 250; + Possibility = 10; + MaxGeneratedContainedItems = 10; + BitmapPos = 48, 64; + DamageDivider = 4; + StrengthModifier = 200; + } + + Config STRONG_BOX; + { + Adjective = "small"; + NameSingular = "strong-box"; + DefaultSize = 30; + StorageVolume = 5000; + DefaultMainVolume = 250; + Price = 250; + Possibility = 10; + MaxGeneratedContainedItems = 3; + MainMaterialConfig == IRON; + BitmapPos = 48, 368; + DamageDivider = 5; + StrengthModifier = 150; + } +} + +beartrap +{ + MainMaterialConfig == IRON; + NameSingular = "bear trap"; + Possibility = 100; + /* Program overrides BitmapPos always */ + IsTwoHanded = true; + DefaultSize = 50; + DefaultMainVolume = 500; + FormModifier = 30; + Roundness = 30; + Price = 25; + CanBePiled = false; + CanBeBroken = true; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 160, 128; + IsPolymorphable = false; + MainMaterialConfig = { 7, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + MaterialConfigChances = { 7, 200, 300, 500, 100, 50, 25, 1; } + + Config BROKEN; + { + Possibility = 10; + BitmapPos = 48, 48; + Price = 0; + MainMaterialConfig = { 4, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + MaterialConfigChances = { 4, 100, 50, 25, 1; } + } +} + +helmet +{ + DefaultSize = 30; + Possibility = 350; + Category = HELMET; + DefaultMainVolume = 150; + StrengthModifier = 100; + BitmapPos = 16, 384; + FormModifier = 10; + NameSingular = "helmet"; + MainMaterialConfig = { 23, LEATHER, HARDENED_LEATHER, TROLL_HIDE, BALSA_WOOD, PINE_WOOD, FIR_WOOD, BIRCH_WOOD, OAK_WOOD, TEAK_WOOD, EBONY_WOOD, BONE, OBSIDIAN, TIN, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL, DRAGON_HIDE, ARCANITE, MITHRIL, OMMEL_BONE, OMMEL_TOOTH; } + MaterialConfigChances = { 23, 500, 750, 300, 20, 70, 60, 50, 40, 30, 20, 300, 100, 100, 750, 500, 250, 100, 25, 25, 25, 25, 10, 5; } + Roundness = 60; + CanBeBroken = true; + WieldedBitmapPos = 160, 128; + CoverPercentile = 50; + HelmetBitmapPos = 112, 416; + EnchantmentPlusChance = 15; + + Config BROKEN; + { + BitmapPos = 64, 64; + Possibility = 35; + MainMaterialConfig = { 7, STEEL, METEORIC_STEEL, DRAGON_HIDE, ARCANITE, MITHRIL, OCTIRON, ADAMANT; } + MaterialConfigChances = { 7, 100, 25, 25, 25, 25, 10, 5; } + HelmetBitmapPos = 112, 496; + EnchantmentPlusChance = 30; + } + + Config FULL_HELMET; + { + DefaultSize = 40; + DefaultMainVolume = 250; + StrengthModifier = 150; + FormModifier = 25; + BitmapPos = 48, 32; + Possibility = 35; + Adjective = "full"; + MainMaterialConfig = { 9, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL, ARCANITE, MITHRIL, OMMEL_BONE, OMMEL_TOOTH; } + MaterialConfigChances = { 9, 750, 500, 250, 100, 25, 25, 25, 10, 5; } + CoverPercentile = 90; + HelmetBitmapPos = 96, 432; + EnchantmentPlusChance = 10; + } + + Config BROKEN|FULL_HELMET; + { + BitmapPos = 64, 32; + Possibility = 4; + MainMaterialConfig = { 7, IRON, STEEL, METEORIC_STEEL, ARCANITE, MITHRIL, OCTIRON, ADAMANT; } + MaterialConfigChances = { 7, 250, 100, 25, 25, 25, 10, 5; } + HelmetBitmapPos = 96, 512; + EnchantmentPlusChance = 20; + } + + Config HELM_OF_PERCEPTION; + { + DefaultSize = 40; + DefaultMainVolume = 250; + StrengthModifier = 150; + FormModifier = 25; + BitmapPos = 48, 32; + Possibility = 1; + PostFix = "of piercing perception"; + AffectsPerception = true; + MainMaterialConfig == ILLITHIUM; + MaterialConfigChances == 100; + BaseEnchantment = 1; + Price = 100; + PriceIsProportionalToEnchantment = true; + AttachedGod = LEGIFER; + CoverPercentile = 90; + HelmetBitmapPos = 96, 432; + EnchantmentPlusChance = 2; + } + + Config BROKEN|HELM_OF_PERCEPTION; + { + BitmapPos = 64, 32; + Possibility = 2; + Price = 25; + AffectsPerception = false; + HelmetBitmapPos = 96, 512; + EnchantmentPlusChance = 4; + } + + Config HELM_OF_BRILLIANCE; + { + DefaultSize = 40; + DefaultMainVolume = 75; + StrengthModifier = 100; + FormModifier = 25; + BitmapPos = 82, 213; + Possibility = 1; + PostFix = "of brilliance"; + AffectsWisdom = true; + AffectsIntelligence = true; + MainMaterialConfig == UNICORN_HORN; + MaterialConfigChances == 100; + BaseEnchantment = 1; + Price = 100; + PriceIsProportionalToEnchantment = true; + CoverPercentile = 90; + HelmetBitmapPos = 112, 416; + EnchantmentPlusChance = 2; + } + + Config BROKEN|HELM_OF_BRILLIANCE; + { + BitmapPos = 92, 228; + Possibility = 2; + Price = 25; + AffectsIntelligence = false; + HelmetBitmapPos = 112, 496; + EnchantmentPlusChance = 4; + } + + Config HELM_OF_ATTRACTIVITY; + { + DefaultSize = 40; + DefaultMainVolume = 250; + StrengthModifier = 150; + FormModifier = 25; + BitmapPos = 48, 32; + Possibility = 5; + PostFix = "of attractivity"; + AffectsCharisma = true; + MainMaterialConfig == GOLD; + MaterialConfigChances == 100; + BaseEnchantment = 5; + Price = 1; + PriceIsProportionalToEnchantment = true; + AttachedGod = MELLIS; + CoverPercentile = 90; + HelmetBitmapPos = 96, 432; + EnchantmentPlusChance = 2; + } + + Config BROKEN|HELM_OF_ATTRACTIVITY; + { + BitmapPos = 64, 32; + Possibility = 5; + Price = 25; + AffectsCharisma = false; + HelmetBitmapPos = 96, 512; + EnchantmentPlusChance = 4; + } + + Config GOROVITS_FAMILY_GAS_MASK; + { + DefaultSize = 50; + DefaultMainVolume = 300; + StrengthModifier = 125; + FormModifier = 20; + BitmapPos = 64, 400; + Possibility = 0; + Adjective = "Gorovits family"; + NameSingular = "gas mask"; + MainMaterialConfig == KEVLAR; + MaterialConfigChances == 100; + Price = 50; + AttachedGod = LORICATUS; + CanBeBroken = false; + CanBeWished = false; + ArticleMode = FORCE_THE; + GearStates = GAS_IMMUNITY; + IsQuestItem = true; + CoverPercentile = 100; + HelmetBitmapPos = 112, 432; + EnchantmentPlusChance = 0; + } +} + +stethoscope +{ + DefaultSize = 40; + Possibility = 25; + Category = TOOL; + DefaultMainVolume = 200; + StrengthModifier = 50; + BitmapPos = 48, 96; + FormModifier = 10; + Roundness = 20; + NameSingular = "stethoscope"; + Price = 250; + MainMaterialConfig == UNICORN_HORN; + AttachedGod = SEGES; + WieldedBitmapPos = 160, 128; + HasNormalPictureDirection = false; +} + +flamingsword /* meleeweapon-> */ +{ + Adjective = "flaming"; + BaseEmitation = rgb24(150, 120, 90); + IsAbstract = true; + + Config LONG_SWORD; + { + DefaultSize = 120; + Possibility = 8; + WeaponCategory = LARGE_SWORDS; + DefaultMainVolume = 130; + DefaultSecondaryVolume = 45; + BitmapPos = 16, 336; + FormModifier = 90; + StrengthModifier = 100; + NameSingular = "long sword"; + MainMaterialConfig = { 6, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig = { 6, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + MaterialConfigChances = { 6, 1000, 750, 100, 25, 25, 5; } + Roundness = 10; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 160, 16; + EnchantmentPlusChance = 5; + DamageFlags = SLASH|PIERCE; + } + + Config BROKEN|LONG_SWORD; + { + Possibility = 4; + BitmapPos = 32, 208; + WieldedBitmapPos = 176, 80; + MainMaterialConfig = { 4, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + SecondaryMaterialConfig = { 4, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + MaterialConfigChances = { 4, 0, 25, 25, 10; } + EnchantmentPlusChance = 10; + } + + Config TWO_HANDED_SWORD; + { + DefaultSize = 150; + Possibility = 2; + WeaponCategory = LARGE_SWORDS; + DefaultMainVolume = 350; + DefaultSecondaryVolume = 150; + StrengthModifier = 200; + BitmapPos = 0, 0; + FormModifier = 135; + NameSingular = "two-handed sword"; + MainMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + MaterialConfigChances = { 5, 2000, 100, 25, 25, 5; } + Roundness = 15; + IsTwoHanded = true; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 160, 16; + EnchantmentMinusChance = 5; + EnchantmentPlusChance = 5; + DamageFlags = SLASH; + } + + Config BROKEN|TWO_HANDED_SWORD; + { + Possibility = 2; + BitmapPos = 32, 80; + WieldedBitmapPos = 176, 80; + MaterialConfigChances = { 4, 0, 250, 25, 25; } + EnchantmentMinusChance = 10; + EnchantmentPlusChance = 10; + } + + Config KNIGHT_SWORD; + { + DefaultSize = 120; + Possibility = 1; + WeaponCategory = LARGE_SWORDS; + DefaultMainVolume = 200; + DefaultSecondaryVolume = 100; + BitmapPos = 110,257; + FormModifier = 145; + StrengthModifier = 120; + NameSingular = "knight sword"; + MainMaterialConfig = { 7, BONE, TIN, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL; } + SecondaryMaterialConfig = { 7, BONE, TIN, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL; } + MaterialConfigChances = { 7, 25, 50, 250, 500, 1000, 250, 25; } + Roundness = 20; + IsTwoHanded = true; + AttachedGod = LEGIFER; + WieldedBitmapPos = 150, 475; + EnchantmentMinusChance = 10; + EnchantmentPlusChance = 5; + DamageFlags = SLASH|PIERCE; + } + + Config BROKEN|KNIGHT_SWORD; + { + Possibility = 1; + BitmapPos = 110, 279; + WieldedBitmapPos = 176, 80; + MainMaterialConfig = { 7, BRONZE, IRON, STEEL, METEORIC_STEEL, DAMASCUS_STEEL, MITHRIL, OMMEL_TOOTH;} + SecondaryMaterialConfig = { 7, BRONZE, IRON, STEEL, METEORIC_STEEL, DAMASCUS_STEEL, MITHRIL, OMMEL_TOOTH;} + MaterialConfigChances = { 7, 50, 250, 500, 1000, 250, 25, 25; } + EnchantmentMinusChance = 5; + EnchantmentPlusChance = 10; + } + + Config TWO_HANDED_SCIMITAR; + { + DefaultSize = 140; + Possibility = 1; + WeaponCategory = LARGE_SWORDS; + DefaultMainVolume = 400; + DefaultSecondaryVolume = 150; + StrengthModifier = 175; + BitmapPos = 0, 16; + FormModifier = 160; + NameSingular = "two-handed scimitar"; + MainMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + MaterialConfigChances = { 5, 2000, 100, 25, 25, 5; } + Roundness = 15; + IsTwoHanded = true; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 176, 112; + EnchantmentMinusChance = 5; + EnchantmentPlusChance = 5; + DamageFlags = SLASH; + } + + Config BROKEN|TWO_HANDED_SCIMITAR; + { + Possibility = 1; + BitmapPos = 32, 96; + WieldedBitmapPos = 176, 80; + MaterialConfigChances = { 4, 0, 250, 25, 25; } + EnchantmentMinusChance = 10; + EnchantmentPlusChance = 10; + } + +} + +mjolak /* meleeweapon-> */ +{ + DefaultSize = 180; + Possibility = 1; + WeaponCategory = POLE_ARMS; + DefaultMainVolume = 400; + DefaultSecondaryVolume = 1000; + StrengthModifier = 200; + BitmapPos = 0, 80; + FormModifier = 115; + MainMaterialConfig == METEORIC_STEEL; + SecondaryMaterialConfig == EBONY_WOOD; + Roundness = 20; + IsTwoHanded = true; + Adjective = "unholy"; + NameSingular = "halberd"; + PostFix = "named Mjolak"; + ArticleMode = FORCE_THE; + Alias == "Mjolak"; + CanBeCloned = false; + CanBeMirrored = true; + Price = 750; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 160, 256; + EnchantmentPlusChance = 2; + DamageFlags = SLASH|PIERCE; + + Config BROKEN; + { + Possibility = 0; + BitmapPos = 32, 144; + Price = 150; + WieldedBitmapPos = 176, 256; + EnchantmentPlusChance = 4; + } +} + +vermis /* meleeweapon-> */ +{ + DefaultSize = 220; + Possibility = 1; + WeaponCategory = POLE_ARMS; + StrengthModifier = 100; + BitmapPos = 16, 144; + FormModifier = 200; + DefaultMainVolume = 60; + DefaultSecondaryVolume = 1000; + Adjective = "runed"; + NameSingular = "spear"; + PostFix = "named Vermis"; + MainMaterialConfig == DIAMOND; + SecondaryMaterialConfig == OAK_WOOD; + Roundness = 10; + IsTwoHanded = true; + Alias == "Vermis"; + ArticleMode = FORCE_THE; + CanBeCloned = false; + CanBeMirrored = true; + Price = 500; + AttachedGod = SOPHOS; + WieldedBitmapPos = 160, 96; + EnchantmentPlusChance = 2; + DamageFlags = PIERCE; + GearStates = TELEPORT|TELEPORT_CONTROL; + + Config BROKEN; + { + Possibility = 0; + BitmapPos = 32, 224; + Price = 100; + WieldedBitmapPos = 176, 96; + EnchantmentPlusChance = 4; + GearStates = 0; + } +} + +turox /* meleeweapon-> */ +{ + StrengthModifier = 150; + FormModifier = 180; + DefaultSize = 90; + BitmapPos = 0, 32; + Possibility = 1; + DefaultMainVolume = 450; + DefaultSecondaryVolume = 375; + WeaponCategory = BLUNT_WEAPONS; + NameSingular = "mace"; + PostFix = "named Turox"; + MainMaterialConfig == METEORIC_STEEL; + SecondaryMaterialConfig == ARCANITE; + Roundness = 30; /* we count the handle, too */ + IsTwoHanded = true; + Alias == "Turox"; + ArticleMode = FORCE_THE; + CanBeCloned = false; + CanBeMirrored = true; + Price = 250; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 160, 192; + EnchantmentPlusChance = 2; + + Config BROKEN; + { + Possibility = 0; + BitmapPos = 32, 112; + Price = 50; + WieldedBitmapPos = 176, 192; + EnchantmentPlusChance = 4; + } +} + +eptyron /* meleeweapon-> */ +{ + DefaultSize = 100; + Possibility = 0; + WeaponCategory = AXES; + DefaultMainVolume = 150; + DefaultSecondaryVolume = 400; + BitmapPos = 16, 256; + FormModifier = 100; + StrengthModifier = 100; + Adjective = "blasphemous"; + ArticleMode = FORCE_THE; + NameSingular = "axe"; + PostFix = "named Eptyron"; + MainMaterialConfig = { 7, LEAD, TIN, COPPER, BRONZE, IRON, STEEL, MITHRIL; } + SecondaryMaterialConfig = { 7, BIRCH_WOOD, OAK_WOOD, TEAK_WOOD, EBONY_WOOD, BRONZE, IRON, STEEL; } + MaterialConfigChances = { 7, 500, 1000, 750, 100, 25, 25, 5; } + Roundness = 15; + IsTwoHanded = true; + Alias == "Eptyron"; + Price = 250; + AttachedGod = LORICATUS; + WieldedBitmapPos = 160, 176; + EnchantmentPlusChance = 2; + DamageFlags = SLASH; + CanBeBroken = false; + BaseEmitation = rgb24(100, 140, 100); + +/* + Config BROKEN; + { + Possibility = 10; + BitmapPos = 32, 176; + WieldedBitmapPos = 176, 176; + MainMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + SecondaryMaterialConfig = { 5, OAK_WOOD, TEAK_WOOD, EBONY_WOOD, MITHRIL, ADAMANT; } + MaterialConfigChances = { 5, 25, 100, 25, 25, 10; } + EnchantmentPlusChance = 10; + } */ +} + +taiaha /* meleeweapon-> */ +{ + /*Category = WAND;*/ + CanBePiled = false; + DefaultSize = 160; + Possibility = 10; + WeaponCategory = POLE_ARMS; + StrengthModifier = 80; + BitmapPos = 48, 448; + FormModifier = 85; + DefaultMainVolume = 100; + DefaultSecondaryVolume = 1500; + NameSingular = "taiaha"; + MainMaterialConfig = { 8, OAK_WOOD, TEAK_WOOD, EBONY_WOOD, RATA_WOOD, BONE, NEPHRITE, OMMEL_BONE, OMMEL_TOOTH; } + SecondaryMaterialConfig == KAURI_WOOD; + MaterialConfigChances = { 8, 250, 200, 100, 100, 400, 100, 25, 10; } + Roundness = 10; + IsTwoHanded = true; + AttachedGod = INFUSCOR; + WieldedBitmapPos = 160, 96; + EnchantmentPlusChance = 15; + DamageFlags = PIERCE; + Price = 500; + BeamRange = 5; + MinCharges = 2; + MaxCharges = 4; + IsKamikazeWeapon = true; + BreakMsg = "shatters to smithereens in an enormous explosion"; + TeleportPriority = 1000; + + Config BROKEN; + { + Possibility = 20; + BitmapPos = 64, 448; + WieldedBitmapPos = 176, 96; + MainMaterialConfig = { 7, BONE, EBONY_WOOD, RATA_WOOD, NEPHRITE, SAPPHIRE, RUBY, DIAMOND; } + MaterialConfigChances = {7, 150, 25, 25, 25, 5, 5, 5; } + EnchantmentPlusChance = 30; + } +} + +whipofthievery /* meleeweapon->whip-> */ +{ + DefaultSize = 250; + Possibility = 10; + DefaultMainVolume = 1000; + DefaultSecondaryVolume = 50; + StrengthModifier = 30; + NameSingular = "whip"; + PostFix = "of thievery"; + MainMaterialConfig == NYMPH_HAIR; + SecondaryMaterialConfig == SAPPHIRE; + Roundness = 2; + FormModifier = 20; /* this is multiplied by MainMaterial's flexibility */ + Price = 500; + AttachedGod = CLEPTIA; + EnchantmentPlusChance = 10; +} + +gleipnir /* meleeweapon->whip-> */ +{ + DefaultSize = 250; + Possibility = 0; + DefaultMainVolume = 1000; + DefaultSecondaryVolume = 50; + StrengthModifier = 30; + NameSingular = "whip of thievery"; + PostFix = "named Gleipnir"; + MainMaterialConfig == SPIDER_SILK; + SecondaryMaterialConfig == OBSIDIAN; + Roundness = 3; + FormModifier = 25; /* this is multiplied by MainMaterial's flexibility */ + Price = 1000; + AttachedGod = CLEPTIA; + EnchantmentPlusChance = 10; + CanBeWished = false; + Alias == "Gleipnir"; + IsQuestItem = true; +} + +scrollofenchantweapon /* scroll-> */ +{ + Possibility = 60; + Price = 250; + PostFix = "of enchant weapon"; + AttachedGod = SOPHOS; + Alias == "SoEW"; + ReadDifficulty = 500; +} + +scrollofenchantarmor /* scroll-> */ +{ + Possibility = 80; + Price = 250; + PostFix = "of enchant armor"; + AttachedGod = SOPHOS; + Alias == "SoEA"; + ReadDifficulty = 500; +} + +skull +{ + StrengthModifier = 100; + FormModifier = 15; + Roundness = 65; + DefaultSize = 20; + Possibility = 30; + DefaultMainVolume = 600; + BitmapPos = 48, 112; + Category = MISC; + NameSingular = "skull"; + MainMaterialConfig == BONE; + IsTwoHanded = true; + AttachedGod = MORTIFER; + WieldedBitmapPos = 160, 128; + IsValuable = false; +} + +gorovitsweapon +{ + Adjective = "Gorovits family"; + CanBeBroken = false; + CanBeWished = false; + ArticleMode = FORCE_THE; + IsQuestItem = true; + IsAbstract = true; + + Config GOROVITS_HAMMER; + { + DefaultSize = 90; + Possibility = 0; + WeaponCategory = BLUNT_WEAPONS; + DefaultMainVolume = 300; + DefaultSecondaryVolume = 600; + StrengthModifier = 125; + BitmapPos = 48, 160; + FormModifier = 125; + NameSingular = "hammer"; + MainMaterialConfig == MITHRIL; + SecondaryMaterialConfig == FIR_WOOD; + Roundness = 20; + IsTwoHanded = true; + CanBeUsedBySmith = true; + BaseEnchantment = 3; + Price = 250; + AttachedGod = LORICATUS; + WieldedBitmapPos = 176, 16; + } + + Config GOROVITS_SICKLE; + { + DefaultSize = 60; + Possibility = 0; + WeaponCategory = SMALL_SWORDS; + DefaultMainVolume = 125; + DefaultSecondaryVolume = 50; + BitmapPos = 48, 144; + FormModifier = 110; + StrengthModifier = 50; + NameSingular = "sickle"; + MainMaterialConfig == MITHRIL; + SecondaryMaterialConfig == FIR_WOOD; + Roundness = 50; + BaseEnchantment = 3; + Price = 250; + AttachedGod = SEGES; + WieldedBitmapPos = 176, 32; + DamageFlags = SLASH; + } + + Config GOROVITS_SCIMITAR; + { + DefaultSize = 150; + Possibility = 0; + WeaponCategory = LARGE_SWORDS; + DefaultMainVolume = 450; + DefaultSecondaryVolume = 150; + StrengthModifier = 180; + BitmapPos = 0, 16; + FormModifier = 170; + NameSingular = "scimitar"; + MainMaterialConfig == METEORIC_STEEL; + SecondaryMaterialConfig == METEORIC_STEEL; + Roundness = 15; + IsTwoHanded = true; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 176, 112; + DamageFlags = SLASH; + BaseEnchantment = 2; + Price = 300; + } +} + +scrollofrepair /* scroll-> */ +{ + Possibility = 55; + Price = 250; + PostFix = "of repair"; + Alias == "scroll of repairing"; + AttachedGod = LORICATUS; + ReadDifficulty = 500; +} + +encryptedscroll /* scroll-> */ +{ + UsesLongAdjectiveArticle = true; + Adjective = "encrypted"; + CanBeWished = false; + IsDestroyable = false; + IsMaterialChangeable = false; + IsPolymorphable = false; + CanBeCloned = false; + AttachedGod = MELLIS; + IsQuestItem = true; +} + +mondedrpass /* scroll-> */ +{ + Adjective = "mondedr"; + NameSingular = "pass"; + IsDestroyable = false; + IsMaterialChangeable = false; + IsPolymorphable = false; + CanBeCloned = false; + AttachedGod = CLEPTIA; + Price = 2000; + Possibility = 10; +} + +ringofthieves + { + Adjective = "ring"; + NameSingular = "of thieves"; + DefaultSize = 2; + DefaultMainVolume = 3; + StrengthModifier = 100; + BitmapPos = 16, 400; + FormModifier = 10; + Roundness = 10; /* it is considered here opened */ + WieldedBitmapPos = 160, 368; + TeleportPriority = 300; + Possibility = 0; + ElectricityResistance = 5; + FireResistance = 5; + AcidResistance = 1; + PoisonResistance = 1; + MainMaterialConfig == EMERALD; + GearStates = HASTE; + Price = 500; + AttachedGod = CLEPTIA; + CanBeWished = false; + } + +horn +{ + StrengthModifier = 70; + FormModifier = 30; + Roundness = 40; + DefaultSize = 30; + Possibility = 10; + WeaponCategory = BLUNT_WEAPONS; + DefaultMainVolume = 400; + BitmapPos = 48, 304; + Category = TOOL; + NameSingular = "horn"; + IsAbstract = true; + Price = 500; + CanBePiled = false; + AttachedGod = DULCIS; + WieldedBitmapPos = 160, 352; + TeleportPriority = 500; + + Config BRAVERY; + { + MainMaterialConfig == SILVER; + PostFix = "of bravery"; + } + + Config FEAR; + { + MainMaterialConfig == OBSIDIAN; + PostFix = "of fear"; + } + + Config CONFUSION; + { + MainMaterialConfig == JASPER; + PostFix = "of confusion"; + } +} + +thunderhammer /* meleeweapon-> */ +{ + BaseEmitation = rgb24(130, 130, 130); + IsAbstract = true; + + Config HAMMER; + { + DefaultSize = 40; + Possibility = 4; + WeaponCategory = BLUNT_WEAPONS; + DefaultMainVolume = 150; + DefaultSecondaryVolume = 300; + StrengthModifier = 90; + BitmapPos = 48, 160; + FormModifier = 50; + NameSingular = "thunder hammer"; + MainMaterialConfig = { 7, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OCTIRON, OMMEL_TOOTH; } + SecondaryMaterialConfig == FIR_WOOD; + MaterialConfigChances = { 7, 750, 500, 100, 50, 50, 20, 0; } + Roundness = 20; + CanBeUsedBySmith = true; + AttachedGod = LORICATUS; + WieldedBitmapPos = 176, 16; + EnchantmentPlusChance = 20; + } + + Config BROKEN|HAMMER; + { + Possibility = 4; + BitmapPos = 48, 192; + WieldedBitmapPos = 176, 224; + MainMaterialConfig = { 4, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + MaterialConfigChances = { 4, 100, 50, 50, 10; } + EnchantmentPlusChance = 40; + } + + Config WAR_HAMMER; + { + DefaultSize = 60; + Possibility = 2; + WeaponCategory = BLUNT_WEAPONS; + DefaultMainVolume = 250; + DefaultSecondaryVolume = 500; + StrengthModifier = 110; + BitmapPos = 80, 16; + FormModifier = 55; + NameSingular = "thunder war hammer"; + MainMaterialConfig = { 8, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OCTIRON, OMMEL_TOOTH; } + SecondaryMaterialConfig == FIR_WOOD; + MaterialConfigChances = { 8, 1000, 750, 500, 100, 50, 50, 50, 0; } + Roundness = 20; + IsTwoHanded = true; + CanBeUsedBySmith = true; + AttachedGod = LORICATUS; + WieldedBitmapPos = 176, 320; + EnchantmentPlusChance = 20; + } + + Config BROKEN|WAR_HAMMER; + { + Possibility = 1; + BitmapPos = 80, 32; + WieldedBitmapPos = 176, 336; + MainMaterialConfig = { 4, STEEL, METEORIC_STEEL, MITHRIL, ADAMANT; } + MaterialConfigChances = { 4, 250, 50, 50, 10; } + EnchantmentPlusChance = 30; + } + + Config SPEAR; + { + DefaultSize = 190; + Possibility = 4; + WeaponCategory = POLE_ARMS; + StrengthModifier = 70; + BitmapPos = 16, 144; + FormModifier = 95; + DefaultMainVolume = 100; + DefaultSecondaryVolume = 2500; + NameSingular = "thunder spear"; + MainMaterialConfig = { 8, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_BONE, OMMEL_TOOTH; } + SecondaryMaterialConfig == PINE_WOOD; + MaterialConfigChances = { 8, 650, 500, 350, 200, 50, 50, 0, 0; } + Roundness = 10; + IsTwoHanded = true; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 160, 96; + EnchantmentPlusChance = 15; + DamageFlags = PIERCE; + } + + Config BROKEN|SPEAR; + { + Possibility = 2; + BitmapPos = 32, 224; + WieldedBitmapPos = 176, 96; + MainMaterialConfig = { 7, STEEL, METEORIC_STEEL, MITHRIL, SAPPHIRE, RUBY, DIAMOND, ADAMANT; } + MaterialConfigChances = { 7, 150, 25, 25, 5, 5, 5, 5; } + EnchantmentPlusChance = 30; + } + + Config SPETUM; + { + DefaultSize = 180; + Possibility = 1; + WeaponCategory = POLE_ARMS; + StrengthModifier = 70; + BitmapPos = 110, 345; + FormModifier = 160; + DefaultMainVolume = 100; + DefaultSecondaryVolume = 1800; + NameSingular = "thunder spetum"; + MainMaterialConfig = { 10, TIN, COPPER, BRONZE, IRON, STEEL, METEORIC_STEEL, DAMASCUS_STEEL, MITHRIL, OMMEL_BONE, OMMEL_TOOTH; } + SecondaryMaterialConfig == TEAK_WOOD; + MaterialConfigChances = { 10, 50, 100, 250, 500, 1000, 250, 100, 50, 10, 5; } + Roundness = 25; + IsTwoHanded = true; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 149, 512; + EnchantmentMinusChance = 15; + EnchantmentPlusChance = 10; + DamageFlags = PIERCE; + } + + Config BROKEN|SPETUM; + { + Possibility = 2; + BitmapPos = 84, 349; + WieldedBitmapPos = 165, 512; + MainMaterialConfig = { 12, BRONZE, IRON, STEEL, METEORIC_STEEL, DAMASCUS_STEEL, MITHRIL, ILLITHIUM, RUBY, SAPPHIRE, EMERALD, DIAMOND, ADAMANT; } + MaterialConfigChances = { 12, 50, 100, 250, 500, 1000, 250, 100, 10, 10, 10, 5, 1; } + EnchantmentMinusChance = 10; + EnchantmentPlusChance = 5; + } + +} + +saalthul +{ + DefaultSize = 70; + Possibility = 1; + WeaponCategory = SMALL_SWORDS; + DefaultMainVolume = 60; + DefaultSecondaryVolume = 20; + BitmapPos = 48, 240; + FormModifier = 190; + StrengthModifier = 135; + Adjective = "short"; + NameSingular = "sword"; + PostFix = "named Saal'thul"; + MainMaterialConfig == MITHRIL; + SecondaryMaterialConfig == MITHRIL; + Roundness = 10; + Price = 500; + GearStates = INVISIBLE; + ArticleMode = FORCE_THE; + CanBeCloned = false; + CanBeMirrored = true; + Alias == "Saal'thul"; + AttachedGod = CLEPTIA; + WieldedBitmapPos = 160, 32; + EnchantmentPlusChance = 2; + DamageFlags = SLASH|PIERCE; + + Config BROKEN; + { + Possibility = 0; + BitmapPos = 48, 272; + Price = 100; + GearStates = 0; + WieldedBitmapPos = 176, 208; + EnchantmentPlusChance = 4; + } +} + +chameleonwhip /* meleeweapon->whip-> */ +{ + DefaultSize = 250; + Possibility = 10; + DefaultMainVolume = 1000; + DefaultSecondaryVolume = 50; + StrengthModifier = 30; + NameSingular = "chameleon whip"; + MainMaterialConfig == OMMEL_HAIR; + SecondaryMaterialConfig == RUBY; + Roundness = 2; + FormModifier = 20; /* this is multiplied by MainMaterial's flexibility */ + Price = 500; + AttachedGod = SCABIES; + WieldedBitmapPos = 160, 224; + EnchantmentMinusChance = 50; + EnchantmentPlusChance = 50; +} + +carrot +{ + Possibility = 150; + WeaponCategory = BLUNT_WEAPONS; + DefaultMainVolume = 60; + StrengthModifier = 75; + Category = FOOD; + BitmapPos = 64, 272; + FormModifier = 30; + DefaultSize = 20; + NameSingular = "carrot"; + MainMaterialConfig == CARROT_FLESH; + Alias == "bazooka"; + Roundness = 10; + CanBePiled = true; + AttachedGod = SEGES; + WieldedBitmapPos = 160, 352; + IsValuable = false; +} + +wondersmellstaff +{ + DefaultSize = 220; + Possibility = 5; + WeaponCategory = BLUNT_WEAPONS; + StrengthModifier = 150; + BitmapPos = 64, 240; + FormModifier = 125; + DefaultMainVolume = 2000; + DefaultSecondaryVolume = 200; + NameSingular = "staff"; + PostFix = "of wondrous smells"; + MainMaterialConfig = { 4, TEAK_WOOD, EBONY_WOOD, ARCANITE, OCTIRON; } + SecondaryMaterialConfig == RUBY; + MaterialConfigChances = { 4, 250, 100, 50, 25; } + Roundness = 10; + IsTwoHanded = true; + AttachedGod = INFUSCOR; + WieldedBitmapPos = 160, 304; + Price = 500; + EnchantmentPlusChance = 5; + + Config BROKEN; + { + Possibility = 5; + BitmapPos = 64, 256; + WieldedBitmapPos = 176, 304; + MainMaterialConfig = { 3, EBONY_WOOD, ARCANITE, OCTIRON; } + MaterialConfigChances = { 3, 100, 50, 25; } + EnchantmentPlusChance = 10; + } +} + +charmlyre +{ + DefaultMainVolume = 1500; + StrengthModifier = 50; + BitmapPos = 64, 368; + FormModifier = 20; + DefaultSize = 50; + Roundness = 50; + Possibility = 5; + Category = TOOL; + MainMaterialConfig == UNICORN_HORN; + NameSingular = "lyre"; + PostFix = "of charm"; + Price = 1000; + CanBePiled = false; + AttachedGod = DULCIS; + WieldedBitmapPos = 176, 352; +} + +ennerhead /* head-> */ +{ +} + +decosadshirt /* armor->bodyarmor-> */ +{ + DefaultSize = 60; + BitmapPos = 80, 64; + Possibility = 0; + DefaultMainVolume = 1000; + StrengthModifier = 75; + NameSingular = "shirt"; + PostFix = "with a Decos Bananas Co. ad"; + MainMaterialConfig == CLOTH; + CanBeBroken = false; + InElasticityPenaltyModifier = 30; + AttachedGod = MELLIS; + TorsoArmorBitmapPos = 32, 448; + ArmArmorBitmapPos = 80, 416; + AthleteArmArmorBitmapPos = 80, 448; + LegArmorBitmapPos = 16, 416; + Price = 0; +} + +scrollofdetectmaterial /* scroll-> */ +{ + Possibility = 50; + Price = 25; + PostFix = "of detect material"; + AttachedGod = LEGIFER; + ReadDifficulty = 200; +} + +stick +{ + DefaultSize = 50; + Possibility = 0; + WeaponCategory = BLUNT_WEAPONS; + Category = MISC; + DefaultMainVolume = 150; + StrengthModifier = 75; + BitmapPos = 80, 240; + FormModifier = 40; + NameSingular = "stick"; + MainMaterialConfig = { 7, BALSA_WOOD, PINE_WOOD, FIR_WOOD, BIRCH_WOOD, OAK_WOOD, TEAK_WOOD, EBONY_WOOD; } + MaterialConfigChances = { 7, 100, 350, 300, 250, 200, 150, 100; } + OKVisualEffects = MIRROR|FLIP; + Roundness = 10; + AttachedGod = NONE; + WieldedBitmapPos = 160, 352; + IsValuable = false; +} + +scrollofhardenmaterial /* scroll-> */ +{ + Possibility = 60; + Price = 500; + PostFix = "of harden material"; + Alias == "SoHM"; + AttachedGod = LORICATUS; + ReadDifficulty = 500; +} + +scrollofgolemcreation /* scroll-> */ +{ + Possibility = 10; + Price = 750; + PostFix = "of golem creation"; + AttachedGod = SOPHOS; + ReadDifficulty = 1000; +} + +gasgrenade +{ + DefaultSize = 15; + Category = MISC; + BitmapPos = 110, 32; + DefaultMainVolume = 50; + NameSingular = "gas grenade"; + FormModifier = 15; + AttachedGod = CRUENTUS; + Possibility = 75; + Price = 50; + MainMaterialConfig == IRON; + SecondaryMaterialConfig = { 4, MUSTARD_GAS, SKUNK_SMELL, FART, MAGIC_VAPOUR; } + MaterialConfigChances = { 4, 50, 10, 1, 10; } + Adjective = "dwarven"; + Alias == "gas grenade"; + IsThrowingWeapon = true; + ThrowItemTypes = THROW_GAS_GRENADE; +} + +holyhandgrenade +{ + DefaultSize = 15; + Category = MISC; + DefaultMainVolume = 200; + NameSingular = "handgrenade of Antioch"; + NamePlural = "handgrenades of Antioch"; + FormModifier = 15; + AttachedGod = VALPURUS; + Possibility = 5; + Price = 100; + MainMaterialConfig == VALPURIUM; + Adjective = "holy"; + Alias == "gas grenade"; +} + +mangoseedling +{ + DefaultSize = 15; + Category = MISC; + DefaultMainVolume = 20000; + NameSingular = "seedling of a holy mango tree"; + NamePlural = "seedlings of a holy mango tree"; + FormModifier = 15; + AttachedGod = VALPURUS; + Possibility = 0; + Price = 10000; + MainMaterialConfig == BALSA_WOOD; + /*Adjective = "holy";*/ + Alias == "seedling"; + CanBeWished = false; + IsPolymorphSpawnable = false; + BitmapPos = 64, 480; + BaseEmitation = 0; + Roundness = 10; + IsPolymorphable = false; + IsTwoHanded = true; + CanBeCloned = false; + CanBePiled = false; + AttachedGod = SILVA; + WieldedBitmapPos = 176, 0; + IsQuestItem = true; + HasSecondaryMaterial = false; + IsValuable = true; +} + +daggerofvenom +{ + DefaultSize = 40; + Possibility = 15; + WeaponCategory = SMALL_SWORDS; + DefaultMainVolume = 25; + DefaultSecondaryVolume = 25; + BitmapPos = 48, 256; + FormModifier = 75; + StrengthModifier = 60; + NameSingular = "dagger"; + PostFix = "of venom"; + MainMaterialConfig = {7, OMMEL_BONE, OMMEL_TOOTH, ILLITHIUM, BRONZE, STEEL, ARCANITE, MITHRIL; } + SecondaryMaterialConfig = {7, EMERALD, EMERALD, EMERALD, EMERALD, EMERALD, EMERALD, EMERALD; } + MaterialConfigChances = {7, 72, 48, 80, 225, 144, 100, 90; } + Roundness = 20; + Price = 150; + AttachedGod = CLEPTIA; + WieldedBitmapPos = 160, 288; + EnchantmentPlusChance = 2; + DamageFlags = SLASH|PIERCE; + + Config BROKEN; + { + Possibility = 10; + BitmapPos = 48, 288; + WieldedBitmapPos = 176, 288; + EnchantmentPlusChance = 4; + } +} + +weepblade +{ + DefaultSize = 70; + Possibility = 0; + WeaponCategory = SMALL_SWORDS; + DefaultMainVolume = 70; + DefaultSecondaryVolume = 30; + BitmapPos = 48, 240; + FormModifier = 80; + StrengthModifier = 90; + NameSingular = "blade"; + Adjective = "weeping"; + MainMaterialConfig = {6, MILKY_QUARTZ, QUARTZITE, ROSE_QUARTZ, PURPLE_CRYSTAL, BLUE_CRYSTAL, GREEN_CRYSTAL; } + SecondaryMaterialConfig = {6, MILKY_QUARTZ, QUARTZITE, ROSE_QUARTZ, PURPLE_CRYSTAL, BLUE_CRYSTAL, GREEN_CRYSTAL; } + MaterialConfigChances = {6, 420, 390, 364, 210, 195, 182; } + Roundness = 10; + Price = 250; + AttachedGod = SCABIES; + WieldedBitmapPos = 160, 16; + EnchantmentPlusChance = 2; + DamageFlags = SLASH|PIERCE; + + Config BROKEN; + { + Possibility = 0; + BitmapPos = 48, 272; + WieldedBitmapPos = 176, 208; + EnchantmentPlusChance = 4; + } +} + +acidshield /* armor ->*/ +{ + DefaultSize = 60; + Possibility = 5; + Category = SHIELD; + DefaultMainVolume = 350; + StrengthModifier = 150; + FormModifier = 15; + MainMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, ADAMANT, ARCANITE; } + MaterialConfigChances = { 5, 200, 300, 150, 30, 30; } + EnchantmentPlusChance = 0; + AcidResistance = 1; + NameSingular = "acid shield"; + BitmapPos = 32, 336; + WeaponCategory = SHIELDS; + CanBeBroken = true; + WieldedBitmapPos = 128, 32; + AttachedGod = SCABIES; + + Config BROKEN; + { + Possibility = 3; + DefaultSize = 45; + FormModifier = 20; + BitmapPos = 48, 224; + WieldedBitmapPos = 128, 48; + MainMaterialConfig = { 6, IRON, STEEL, METEORIC_STEEL, ADAMANT, ARCANITE, MITHRIL; } + MaterialConfigChances = { 6, 150, 250, 200, 60, 40, 20; } + EnchantmentPlusChance = 1; + } +} + +pantheonbook /* holybook-> */ +{ + DefaultSize = 30; + Possibility = 5; + Category = BOOK; + DefaultMainVolume = 2500; + StrengthModifier = 150; + BitmapPos = 32, 32; + FormModifier = 20; + Adjective = "large and mysterious"; + NameSingular = "saga of the pantheon"; + ArticleMode = FORCE_THE; + MainMaterialConfig == PARCHMENT; + Price = 500; + Roundness = 70; + IsTwoHanded = true; + CreateDivineConfigurations = false; + AttachedGod = VALPURUS; + WieldedBitmapPos = 176, 144; + ReadDifficulty = 1500; +} + +celestialmonograph /* holybook-> */ +{ + DefaultSize = 30; + Possibility = 0; + Category = BOOK; + DefaultMainVolume = 2500; + StrengthModifier = 150; + BitmapPos = 80, 432; + FormModifier = 20; + Adjective = "celestial"; + NameSingular = "monograph of Solicitus"; + ArticleMode = FORCE_THE; + MainMaterialConfig == PARCHMENT; + Price = 500; + Roundness = 70; + IsTwoHanded = true; + CreateDivineConfigurations = false; + AttachedGod = TERRA; + WieldedBitmapPos = 176, 144; + ReadDifficulty = 1500; + Alias == "monograph"; +} + +constitution /* holybook-> */ +{ + DefaultSize = 30; + Possibility = 0; + Category = BOOK; + DefaultMainVolume = 2500; + StrengthModifier = 150; + BitmapPos = 32, 32; + FormModifier = 20; + Adjective = ""; + PostFix = "of Independent Tweraif"; + NameSingular = "Constitution"; + ArticleMode = FORCE_THE; + MainMaterialConfig == PARCHMENT; + Price = 500; + Roundness = 70; + IsTwoHanded = true; + CreateDivineConfigurations = false; + AttachedGod = TERRA; + WieldedBitmapPos = 176, 144; + ReadDifficulty = 1500; + CanBeWished = false; + IsDestroyable = false; + IsMaterialChangeable = false; + IsPolymorphable = false; + CanBeCloned = false; + IsQuestItem = true; +} + +gorovitscopyoflenin /* item-> */ +{ + DefaultSize = 180; /* all details to height of Lenin appreciated */ + Category = MISC; + DefaultMainVolume = 80000; + DefaultSecondaryVolume = 100; /* clothes */ + MainMaterialConfig == LEAD; + SecondaryMaterialConfig == FABRIC; + Adjective = "Gorovits family life-size"; + NameSingular = "copy"; + NamePlural = "copies"; + PostFix = "of Lenin's mummy"; + FormModifier = 15; + AttachedGod = SOPHOS; + BitmapPos = 80, 48; + Possibility = 0; + Price = 50; + Alias == "Lenin"; +} + +chastitybelt +{ + DefaultSize = 40; + Possibility = 0; + WeaponCategory = UNCATEGORIZED; + DefaultMainVolume = 125; + StrengthModifier = 60; + BitmapPos = 80, 80; + Adjective = "chastity"; + MainMaterialConfig == IRON; + Roundness = 50; + WieldedBitmapPos = 160, 128; + BeltBitmapPos = 48, 432; + CanBeBroken = false; + DamageFlags = BLUNT; + CreateLockConfigurations = true; + /*IsAbstract = true;*/ +} + +firstbornchild +{ + DefaultSize = 60; + Possibility = 0; + WeaponCategory = UNCATEGORIZED; + DefaultMainVolume = 3000; + + DefaultSecondaryVolume = 75; /* cloth */ + SecondaryMaterialConfig == FABRIC; + + StrengthModifier = 40; + BitmapPos = 80, 144; + MainMaterialConfig == HUMAN_FLESH; + Roundness = 60; + CanBeBroken = false; + DamageFlags = BLUNT; + IsAbstract = true; + Adjective = "first-born"; + AttachedGod = SILVA; + + Config MALE; + { + NameSingular = "son"; + } + Config FEMALE; + { + NameSingular = "daughter"; + } +} + +rescuethepeasant +{ + DefaultSize = 70; + Possibility = 1; + WeaponCategory = SMALL_SWORDS; + DefaultMainVolume = 60; + DefaultSecondaryVolume = 20; + BitmapPos = 48, 240; + FormModifier = 190; + StrengthModifier = 135; + Adjective = "short"; + NameSingular = "sword"; + PostFix = "named Rescue the Peasant"; + MainMaterialConfig == ILLITHIUM; + SecondaryMaterialConfig == RUBY; + Roundness = 15; + Price = 500; + GearStates = SEARCHING; + ArticleMode = FORCE_THE; + CanBeCloned = false; + CanBeMirrored = true; + Alias == "Rescue the Peasant"; + AttachedGod = CLEPTIA; + WieldedBitmapPos = 160, 32; + EnchantmentPlusChance = 2; + DamageFlags = SLASH|PIERCE; + + Config BROKEN; + { + Possibility = 0; + BitmapPos = 48, 272; + Price = 100; + GearStates = 0; + WieldedBitmapPos = 176, 208; + EnchantmentPlusChance = 4; + } +} + +aegis /* shield-> */ +{ + DefaultSize = 60; + Possibility = 1; + Category = SHIELD; + DefaultMainVolume = 350; + StrengthModifier = 150; + BitmapPos = 32, 336; + FormModifier = 15; + Adjective = "holy"; + NameSingular = "shield"; + PostFix = "named Aegis"; + MainMaterialConfig == ARCANITE; + Roundness = 105; + Alias == "Aegis"; + WeaponCategory = SHIELDS; + CanBeBroken = false; + WieldedBitmapPos = 128, 32; + FireResistance = 15; + ElectricityResistance = 15; + AcidResistance = 15; + EnchantmentPlusChance = 15; + + Config BROKEN; + { + Possibility = 0; + DefaultSize = 40; + FormModifier = 20; + BitmapPos = 48, 224; + WieldedBitmapPos = 128, 48; + FireResistance = 15; + ElectricityResistance = 15; + AcidResistance = 15; + EnchantmentPlusChance = 30; + } +} + +phoenixshield /* shield-> */ +{ + DefaultSize = 60; + Possibility = 0; + Category = SHIELD; + DefaultMainVolume = 350; + StrengthModifier = 150; + BitmapPos = 32, 336; + FormModifier = 15; + Adjective = "blessed"; + NameSingular = "shield"; + PostFix = "of the Phoenix"; + MainMaterialConfig == GOLD; + Roundness = 85; + Alias == "Shield of the Phoenix"; + WeaponCategory = SHIELDS; + CanBeBroken = true; + WieldedBitmapPos = 128, 32; + EnchantmentPlusChance = 2; + GearStates = LIFE_SAVED; + + Config BROKEN; + { + Possibility = 0; + DefaultSize = 40; + FormModifier = 20; + BitmapPos = 48, 224; + WieldedBitmapPos = 128, 48; + EnchantmentPlusChance = 30; + GearStates = 0; + } +} + +defender /* meleeweapon-> */ +{ + DefaultSize = 120; + Possibility = 1; + WeaponCategory = LARGE_SWORDS; + StrengthModifier = 125; + BitmapPos = 110,257; + FormModifier = 145; + DefaultMainVolume = 225; + DefaultSecondaryVolume = 125; + Adjective = "knight"; + NameSingular = "sword"; + PostFix = "named Defender"; + MainMaterialConfig == ARCANITE; + SecondaryMaterialConfig == BRONZE; + Roundness = 65; + Alias == "Defender"; + IsTwoHanded = true; + ArticleMode = FORCE_THE; + CanBeCloned = false; + CanBeMirrored = true; + Price = 1000; + AttachedGod = LEGIFER; + WieldedBitmapPos = 150, 475; + EnchantmentPlusChance = 2; + DamageFlags = SLASH|PIERCE; + + + Config BROKEN; + { + Possibility = 0; + BitmapPos = 110, 279; + Price = 150; + WieldedBitmapPos = 176, 80; + EnchantmentPlusChance = 4; + } +} + +aethier /* meleeweapon-> */ +{ + DefaultSize = 150; + Possibility = 1; + WeaponCategory = LARGE_SWORDS; + StrengthModifier = 155; + BitmapPos = 0, 0; + FormModifier = 180; + DefaultMainVolume = 350; + DefaultSecondaryVolume = 150; + NameSingular = "two-handed sword"; + PostFix = "named Aethier"; + MainMaterialConfig == ILLITHIUM; + SecondaryMaterialConfig == SAPPHIRE; + Roundness = 20; + IsTwoHanded = true; + Alias == "Aethier"; + ArticleMode = FORCE_THE; + CanBeCloned = false; + CanBeMirrored = true; + Price = 750; + AttachedGod = LEGIFER; + WieldedBitmapPos = 160, 16; + EnchantmentPlusChance = 2; + DamageFlags = SLASH|PIERCE; + GearStates = INFRA_VISION; + + + Config BROKEN; + { + Possibility = 0; + BitmapPos = 32, 80; + WieldedBitmapPos = 176, 80; + Price = 150; + EnchantmentPlusChance = 4; + GearStates = 0; + } +} + +cronus +{ + DefaultSize = 60; + Possibility = 1; + WeaponCategory = SMALL_SWORDS; + DefaultMainVolume = 125; + DefaultSecondaryVolume = 50; + BitmapPos = 48, 144; + FormModifier = 120; + StrengthModifier = 50; + Adjective = "Cronus"; + NameSingular = "sickle"; + Alias == "Cronus"; + MainMaterialConfig == ADAMANT; + SecondaryMaterialConfig == OCTIRON; + Roundness = 30; + CanBeBroken = false; + CanBeWished = false; + BaseEnchantment = 2; + ArticleMode = FORCE_THE; + Price = 750; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 176, 32; + IsQuestItem = false; + DamageFlags = SLASH; + AffectsArmStrength = true; + + + Config BROKEN; + { + Possibility = 0; + BitmapPos = 48, 176; + Price = 150; + WieldedBitmapPos = 176, 240; + + } +} + +sunsword +{ + DefaultSize = 130; + Possibility = 1; + WeaponCategory = LARGE_SWORDS; + StrengthModifier = 150; + BitmapPos = 64, 160; + FormModifier = 200; + DefaultMainVolume = 225; + DefaultSecondaryVolume = 100; + Adjective = "shining"; + NameSingular = "bastard sword"; + PostFix = "named Sunsword"; + MainMaterialConfig == ARCANITE; + SecondaryMaterialConfig == SUN_CRYSTAL; + Roundness = 20; + IsTwoHanded = true; + Alias == "Sunsword"; + ArticleMode = FORCE_THE; + CanBeCloned = false; + CanBeMirrored = true; + Price = 500; + AttachedGod = SOPHOS; + WieldedBitmapPos = 160, 16; + EnchantmentPlusChance = 2; + DamageFlags = SLASH; + + Config BROKEN; + { + Possibility = 0; + BitmapPos = 64, 192; + WieldedBitmapPos = 176, 80; + Price = 100; + EnchantmentPlusChance = 4; + GearStates = 0; + } +} + +vormav + { + DefaultSize = 60; + Possibility = 0; + WeaponCategory = AXES; + DefaultMainVolume = 200; + DefaultSecondaryVolume = 500; + StrengthModifier = 80; + BitmapPos = 16, 256; + FormModifier = 140; + NameSingular = "axe"; + PostFix = "named Vormav"; + MainMaterialConfig == ILLITHIUM; + SecondaryMaterialConfig == RUBY; + Roundness = 20; + IsTwoHanded = true; + Alias == "Vormav"; + ArticleMode = FORCE_THE; + CanBeCloned = false; + CanBeMirrored = true; + Price = 500; + AttachedGod = LORICATUS; + WieldedBitmapPos = 160, 176; + EnchantmentPlusChance = 2; + DamageFlags = SLASH; + GearStates = HASTE|INFRA_VISION|ESP; + + + Config BROKEN; + { + Possibility = 0; + BitmapPos = 32, 176; + Price = 100; + WieldedBitmapPos = 176, 176; + EnchantmentPlusChance = 4; + GearStates = 0; + } +} + +belderiver /* meleeweapon-> */ +{ + DefaultSize = 80; + Possibility = 0; + WeaponCategory = LARGE_SWORDS; + DefaultMainVolume = 100; + DefaultSecondaryVolume = 40; + BitmapPos = 111, 191; + FormModifier = 500; + StrengthModifier = 100; + CanBeWished = false; + IsMaterialChangeable = false; + NameSingular = "dark sword"; + Adjective = "cursed"; + PostFix = "named Belderiver"; + ArticleMode = FORCE_THE; + MainMaterialConfig == PSYPHER; + SecondaryMaterialConfig == BLACK_DIAMOND; + IsPolymorphable = false; + Alias == "Belderiver"; + Roundness = 20; + CanBeCloned = false; + CanBeMirrored = true; + CanBeBroken = false; + CanBeEnchanted = false; + BaseEnchantment = 16; + AttachedGod = MORTIFER; + WieldedBitmapPos = 163, 398; + IsQuestItem = true; + DamageFlags = SLASH|PIERCE; +} + +demonhead + { + DefaultSize = 200; + Possibility = 1; + WeaponCategory = AXES; + DefaultMainVolume = 800; + DefaultSecondaryVolume = 2000; + StrengthModifier = 300; + BitmapPos = 64, 80; + FormModifier = 300; + Adjective = "extremely heavy"; + NameSingular = "battle-axe"; + PostFix = "named Demonhead"; + MainMaterialConfig == ILLITHIUM; + SecondaryMaterialConfig == EBONY_WOOD; + Roundness = 30; + IsTwoHanded = true; + Alias == "Demonhead"; + ArticleMode = FORCE_THE; + CanBeCloned = false; + CanBeMirrored = true; + Price = 750; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 160, 272; + EnchantmentPlusChance = 2; + DamageFlags = SLASH; + + + Config BROKEN; + { + Possibility = 0; + BitmapPos = 64, 96; + WieldedBitmapPos = 176, 272; + Price = 150; + EnchantmentPlusChance = 4; + } +} + +goldenjaguarshirt /* armor->bodyarmor-> */ +{ + DefaultSize = 60; + BitmapPos = 16, 112; + Possibility = 0; + CanBeWished = false; + IsDestroyable = false; + IsMaterialChangeable = true; + DefaultMainVolume = 1000; + StrengthModifier = 200; + NameSingular = "Shirt of the Golden Jaguar"; + MainMaterialConfig == ANGEL_HAIR; + ArticleMode = FORCE_THE; + IsPolymorphable = false; + CanBeBroken = false; + CanBeCloned = false; + CanBeMirrored = true; + InElasticityPenaltyModifier = 30; + CanBeEnchanted = true; + AttachedGod = LORICATUS; + GearStates = HASTE|POLYMORPH_CONTROL|TELEPORT_CONTROL; + TorsoArmorBitmapPos = 32, 416; + ArmArmorBitmapPos = 80, 416; + AthleteArmArmorBitmapPos = 80, 448; + LegArmorBitmapPos = 16, 416; +} + +loricatushammer +{ + DefaultSize = 90; + Possibility = 0; + WeaponCategory = BLUNT_WEAPONS; + DefaultMainVolume = 300; + DefaultSecondaryVolume = 600; + StrengthModifier = 125; + BitmapPos = 48, 160; + FormModifier = 180; + NameSingular = "hammer"; + PostFix = "of Loricatuses' Smithwork"; + Alias == "Loricatus Hammer"; + MainMaterialConfig == ILLITHIUM; + SecondaryMaterialConfig == SAPPHIRE; + Roundness = 25; + IsTwoHanded = true; + CanBeUsedBySmith = true; + CanBeBroken = false; + CanBeWished = false; + ArticleMode = FORCE_THE; + Price = 1000; + AttachedGod = LORICATUS; + WieldedBitmapPos = 176, 16; + IsQuestItem = true; +} + +muramasa /* meleeweapon-> */ +{ + StrengthModifier = 100; + FormModifier = 150; + DefaultSize = 150; + Possibility = 0; + CanBeWished = false; + BitmapPos = 109, 210; + WeaponCategory = LARGE_SWORDS; + IsMaterialChangeable = false; + DefaultMainVolume = 150; + DefaultSecondaryVolume = 60; + Adjective = "ancient"; + NameSingular = "katana"; + UsesLongAdjectiveArticle = true; + PostFix = "named Muramasa"; + ArticleMode = FORCE_THE; + MainMaterialConfig == ILLITHIUM; + SecondaryMaterialConfig == RUBY; + IsPolymorphable = false; + Alias == "Muramasa"; + Roundness = 30; + IsTwoHanded = false; + CanBeCloned = false; + CanBeMirrored = true; + CanBeBroken = false; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 176, 112; + IsQuestItem = true; +} + +masamune /* meleeweapon-> */ +{ + StrengthModifier = 100; + FormModifier = 115; + DefaultSize = 150; + Possibility = 0; + CanBeWished = false; + BitmapPos = 109, 210; + WeaponCategory = LARGE_SWORDS; + IsMaterialChangeable = false; + DefaultMainVolume = 150; + DefaultSecondaryVolume = 60; + Adjective = "ancient"; + NameSingular = "katana"; + UsesLongAdjectiveArticle = true; + PostFix = "named Masamune"; + ArticleMode = FORCE_THE; + MainMaterialConfig == ILLITHIUM; + SecondaryMaterialConfig == SAPPHIRE; + IsPolymorphable = false; + Alias == "Masamune"; + Roundness = 45; + IsTwoHanded = false; + CanBeCloned = false; + CanBeMirrored = true; + CanBeBroken = false; + AttachedGod = LEGIFER; + WieldedBitmapPos = 176, 112; + IsQuestItem = true; +} + +zulfiqar /* meleeweapon-> */ +{ + DefaultSize = 220; + Possibility = 1; + WeaponCategory = LARGE_SWORDS; + StrengthModifier = 175; + BitmapPos = 0, 16; + FormModifier = 160; + DefaultMainVolume = 400; + DefaultSecondaryVolume = 150; + Adjective = "two-handed"; + NameSingular = "scimitar"; + PostFix = "named Zulfiqar"; + MainMaterialConfig == METEORIC_STEEL; + SecondaryMaterialConfig == DAMASCUS_STEEL; + Roundness = 15; + IsTwoHanded = true; + Alias == "Zulfiqar"; + ArticleMode = FORCE_THE; + CanBeCloned = false; + CanBeMirrored = true; + Price = 750; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 176, 112; + EnchantmentPlusChance = 2; + DamageFlags = SLASH; + + + Config BROKEN; + { + Possibility = 0; + BitmapPos = 32, 96; + Price = 150; + WieldedBitmapPos = 176, 80; + EnchantmentPlusChance = 4; + } +} + +smite /* meleeweapon-> */ +{ + StrengthModifier = 150; + FormModifier = 175; + DefaultSize = 90; + BitmapPos = 0, 32; + Possibility = 0; + DefaultMainVolume = 450; + DefaultSecondaryVolume = 375; + WeaponCategory = BLUNT_WEAPONS; + NameSingular = "mace"; + PostFix = "named Smite"; + MainMaterialConfig == METEORIC_STEEL; + SecondaryMaterialConfig == EBONY_WOOD; + Roundness = 30; /* we count the handle, too */ + IsTwoHanded = true; + Alias == "Smite"; + ArticleMode = FORCE_THE; + CanBeCloned = false; + CanBeMirrored = true; + Price = 250; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 160, 192; + EnchantmentPlusChance = 2; + + Config BROKEN; + { + Possibility = 0; + BitmapPos = 32, 112; + Price = 50; + WieldedBitmapPos = 176, 192; + EnchantmentPlusChance = 4; + } +} + +maingauche + { + DefaultSize = 50; + Possibility = 25; + WeaponCategory = SMALL_SWORDS; + DefaultMainVolume = 30; + DefaultSecondaryVolume = 30; + BitmapPos = 110, 305; + FormModifier = 80; + StrengthModifier = 65; + NameSingular = "main gauche"; + MainMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + SecondaryMaterialConfig = { 5, IRON, STEEL, METEORIC_STEEL, MITHRIL, OMMEL_TOOTH; } + MaterialConfigChances = { 5, 1000, 100, 25, 25, 5; } + Roundness = 35; + AttachedGod = CLEPTIA; + WieldedBitmapPos = 160, 288; + EnchantmentPlusChance = 25; + DamageFlags = SLASH|PIERCE; + + Config BROKEN; + { + Possibility = 5; + BitmapPos = 110, 321; + WieldedBitmapPos = 176, 285; + MainMaterialConfig = { 7, STEEL, METEORIC_STEEL, MITHRIL, SAPPHIRE, RUBY, DIAMOND, ADAMANT; } + SecondaryMaterialConfig = { 7, STEEL, METEORIC_STEEL, MITHRIL, SAPPHIRE, RUBY, DIAMOND, ADAMANT; } + MaterialConfigChances = { 7, 100, 25, 25, 5, 5, 5, 5; } + EnchantmentPlusChance = 50; + } +} + +ullrbone +{ + Possibility = 5; + DefaultMainVolume = 500; + StrengthModifier = 150; + BitmapPos = 16, 240; + FormModifier = 250; + DefaultSize = 50; + Price = 1000; + NameSingular = "bone"; + PostFix = "of Ullr"; + ArticleMode = FORCE_THE; + IsPolymorphable = false; + Roundness = 20; + WieldedBitmapPos = 160, 336; + CanBePiled = false; + CanBeBroken = false; + MainMaterialConfig == BONE; + CanBeCloned = false; + CanBeMirrored = false; + AllowEquip = true; + IsValuable = true; +} + +vacuumblade /* meleeweapon-> */ +{ + DefaultSize = 120; + Possibility = 1; + WeaponCategory = LARGE_SWORDS; + DefaultMainVolume = 130; + DefaultSecondaryVolume = 45; + BitmapPos = 16, 336; + FormModifier = 110; + StrengthModifier = 100; + NameSingular = "blade"; + Adjective = "vacuum"; + MainMaterialConfig == VACUUM_BLADE_AIR; + SecondaryMaterialConfig == VACUUM_BLADE_AIR; + MaterialConfigChances = { 5, 1000, 750, 100, 25, 25; } + Roundness = 0; + BaseEmitation = rgb24(150, 120, 90); + Price = 250; + AttachedGod = SILVA; + WieldedBitmapPos = 160, 16; + EnchantmentPlusChance = 2; + DamageFlags = SLASH|PIERCE; + IsTwoHanded = false; + CanBeCloned = false; + CanBeMirrored = true; + CanBeBroken = false; + CanBeEnchanted = false; + IsMaterialChangeable = false; + } + +tipswordofpenetration /* meleeweapon-> */ +{ + DefaultSize = 120; + Possibility = 1; + WeaponCategory = LARGE_SWORDS; + StrengthModifier = 125; + BitmapPos = 86, 330; + FormModifier = 125; + DefaultMainVolume = 225; + DefaultSecondaryVolume = 125; + NameSingular = "tip sword"; + PostFix = "of Penetration"; + MainMaterialConfig == STEEL; + SecondaryMaterialConfig == SAPPHIRE; + Roundness = 30; + Alias == "Penetration"; + IsTwoHanded = true; + ArticleMode = FORCE_THE; + CanBeCloned = false; + CanBeMirrored = true; + Price = 1000; + AttachedGod = CRUENTUS; + WieldedBitmapPos = 140, 536; + EnchantmentPlusChance = 2; + DamageFlags = SLASH|PIERCE; + AffectsDexterity = true; + + + Config BROKEN; + { + Possibility = 0; + BitmapPos = 87, 267; + Price = 150; + WieldedBitmapPos = 160, 536; + EnchantmentPlusChance = 4; + } +} + +thievesgirdle /* armor-> */ +{ + DefaultSize = 150; + Possibility = 6; + WeaponCategory = WHIPS; + Category = BELT; + DefaultMainVolume = 250; + StrengthModifier = 100; + BitmapPos = 32, 384; + NameSingular = "thieves' girdle"; + Alias == "girdle"; + MainMaterialConfig = { 4, HARDENED_LEATHER, TROLL_HIDE, NYMPH_HAIR, OMMEL_HAIR; } + MaterialConfigChances = { 4, 1500, 500, 250, 100; } + Roundness = 10; + FormModifier = 10; /* this is multiplied by MainMaterial's flexibility */ + WieldedBitmapPos = 160, 224; + BeltBitmapPos = 48, 432; + CanBeBroken = true; + EnchantmentPlusChance = 10; + DamageFlags = SLASH; + IsSadistWeapon = true; + AffectsDexterity = true; + AffectsAgility = true; + BaseEnchantment = 2; + + Config BROKEN; + { + DefaultSize = 150; + Possibility = 2; + MainMaterialConfig = { 5, TROLL_HIDE, NYMPH_HAIR, OMMEL_HAIR, DRAGON_HIDE, SPIDER_SILK; } + MaterialConfigChances = { 5, 0, 0, 50, 25, 5; } + BitmapPos = 80, 384; + EnchantmentPlusChance = 20; + BeltBitmapPos = 48, 512; + } +} + +lynslag + { + DefaultSize = 120; + Possibility = 1; + WeaponCategory = SMALL_SWORDS; + DefaultMainVolume = 50; + DefaultSecondaryVolume = 45; + BitmapPos = 129, 63; + FormModifier = 145; + StrengthModifier = 75; + NameSingular = "rapier"; + PostFix = "named Lynslag"; + Alias == "Lynslag"; + MainMaterialConfig == MITHRIL; + SecondaryMaterialConfig == SAPPHIRE; + MaterialConfigChances = { 6, 100, 90, 80, 70, 25, 4; } + Roundness = 45; + AttachedGod = LEGIFER; + WieldedBitmapPos = 149, 449; + EnchantmentPlusChance = 2; + DamageFlags = PIERCE; + + Config BROKEN; + { + Possibility = 0; + BitmapPos = 132, 94; + WieldedBitmapPos = 166, 449; + EnchantmentPlusChance = 6; + } +} diff --git a/Script/material.dat b/Script/material.dat new file mode 100644 index 0000000..2680059 --- /dev/null +++ b/Script/material.dat @@ -0,0 +1,2722 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* + * NOTICE!!! + * + * This file contains SPOILERS, which might ruin your IVAN experience + * totally. Also, editing anything can DESTROY GAME BALANCE or CAUSE + * OBSCURE BUGS if you don't know what you're doing. So from here on, + * proceed at your own risk! + */ + +/* Numerical material data loaded during game startup */ + +/* Default values: */ + +material +{ + /* Obligatory: StrengthValue */ + /* Obligatory: ConsumeType */ + /* Obligatory: Density */ + /* Obligatory: Color */ + PriceModifier = 0; + Emitation = 0; + NutritionValue = 0; + ConsumeWisdomLimit = NO_LIMIT; + /* Obligatory: NameStem */ + /* NameStem by default: AdjectiveStem */ + Effect = EFFECT_NOTHING; + ConsumeEndMessage = CEM_NOTHING; + HitMessage = HM_NOTHING; + ExplosivePower = 0; + Alpha = 255; + Flexibility = 1; + EffectStrength = 100; + /* DigProductMaterial defaults to Config */ + /* Obligatory: AttachedGod */ + StepInWisdomLimit = NO_LIMIT; + Acidicity = 0; + NaturalForm = lump; + HardenedMaterial = NONE; + SoftenedMaterial = NONE; + IntelligenceRequirement = 0; + CommonFlags = IS_ABSTRACT|CAN_BE_WISHED|CAN_BE_MIRRORED; + NameFlags = 0; + CategoryFlags = 0; + BodyFlags = USE_MATERIAL_ATTRIBUTES; + InteractionFlags = CAN_DISSOLVE; + Stickiness = 0; + DisablesPanicWhenConsumed = false; +} + +solid +{ + Config VALPURIUM; + { + StrengthValue = 400; + ConsumeType = CT_METAL; + Density = 3000; + Color = rgb16(46, 224, 56); + Emitation = rgb24(150, 150, 150); + PriceModifier = 50000; + NameStem = "valpurium"; + Flexibility = 4; + AttachedGod = VALPURUS; + IntelligenceRequirement = 50; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_METAL|IS_SPARKLING; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config GRAVEL; + { + StrengthValue = 40; + ConsumeType = CT_MINERAL; + Density = 2500; + Color = rgb16(180, 180, 180); + NameStem = "gravel"; + AttachedGod = TERRA; + CommonFlags = Base&~CAN_BE_WISHED; + } + + Config MORAINE; + { + StrengthValue = 40; + ConsumeType = CT_MINERAL; + Density = 2500; + Color = rgb16(100, 60, 30); + NameStem = "moraine"; + DigProductMaterial = GRANITE; + AttachedGod = TERRA; + NaturalForm = stone; + CommonFlags = Base&~CAN_BE_WISHED; + } + + Config GLASS; + { + StrengthValue = 40; + ConsumeType = CT_MINERAL; + Density = 2500; + Color = rgb16(180, 180, 255); + NameStem = "glass"; + Alpha = 150; + AttachedGod = LEGIFER; + HardenedMaterial = ILLITHIUM; + IntelligenceRequirement = 5; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config PARCHMENT; + { + StrengthValue = 7; + ConsumeType = CT_MISC_ORGANIC; + Density = 800; + Color = rgb16(200, 200, 200); + NameStem = "parchment"; + Flexibility = 10; + AttachedGod = SOPHOS; + PriceModifier = 100; + HardenedMaterial = SPIDER_SILK; + IntelligenceRequirement = 5; + CategoryFlags = Base|CAN_BE_TAILORED|IS_GOLEM_MATERIAL; + InteractionFlags = CAN_BURN; + } + + Config CLOTH; + { + StrengthValue = 5; + ConsumeType = CT_MISC_ORGANIC; + Density = 200; + Color = rgb16(80, 80, 176); + NameStem = "cloth"; + Flexibility = 15; + AttachedGod = DULCIS; + HardenedMaterial = ANGEL_HAIR; + CategoryFlags = Base|CAN_BE_TAILORED|IS_GOLEM_MATERIAL; + InteractionFlags = CAN_BURN; + } + + Config HESSIAN_CLOTH; + { + StrengthValue = 10; + ConsumeType = CT_MISC_ORGANIC; + Density = 300; + Color = rgb16(134, 108, 58); + NameStem = "hessian cloth"; + Flexibility = 15; + AttachedGod = DULCIS; + HardenedMaterial = ANGEL_HAIR; + CategoryFlags = Base|CAN_BE_TAILORED|IS_GOLEM_MATERIAL; + InteractionFlags = CAN_BURN; + } + + Config MITHRIL; + { + StrengthValue = 200; + ConsumeType = CT_METAL; + Density = 5000; + Color = rgb16(224, 224, 224); + PriceModifier = 2000; + NameStem = "mithril"; + Flexibility = 2; + AttachedGod = SOPHOS; + HardenedMaterial = OCTIRON; + SoftenedMaterial = ARCANITE; + IntelligenceRequirement = 25; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_METAL|IS_SPARKLING|IS_GOLEM_MATERIAL; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config MARBLE; + { + StrengthValue = 50; + ConsumeType = CT_MINERAL; + Density = 3000; + Color = rgb16(210, 210, 210); + NameStem = "marble"; + AttachedGod = DULCIS; + NaturalForm = stone; + IntelligenceRequirement = 5; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config GOLD; + { + StrengthValue = 55; + ConsumeType = CT_METAL; + Density = 20000; + Color = rgb16(224, 224, 0); + PriceModifier = 1000; + NameStem = "gold"; + AdjectiveStem = "golden"; + Flexibility = 3; + AttachedGod = MELLIS; + HardenedMaterial = RUBY; + SoftenedMaterial = LEAD; + IntelligenceRequirement = 30; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_METAL|IS_SPARKLING|IS_GOLEM_MATERIAL; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config GRASS; + { + StrengthValue = 2; + ConsumeType = CT_MISC_ORGANIC; + Density = 100; + Color = rgb16(32, 110, 32); + NameStem = "grass"; + AdjectiveStem = "grassy"; + Flexibility = 15; + AttachedGod = SILVA; + CommonFlags = Base&~CAN_BE_WISHED; + BodyFlags = Base|CAN_REGENERATE|IS_ALIVE; + InteractionFlags = CAN_BURN; + } + +Config DEAD_GRASS; + { + StrengthValue = 1; + ConsumeType = CT_MISC_ORGANIC; + Density = 75; + Color = rgb16(148, 100, 5); + NameStem = "grass"; + AdjectiveStem = "grassy"; + Flexibility = 1; + AttachedGod = SILVA; + CommonFlags = Base&~CAN_BE_WISHED; + BodyFlags = Base; + InteractionFlags = CAN_BURN; + } + + Config LEATHER; + { + StrengthValue = 20; + ConsumeType = CT_MISC_ORGANIC; + Density = 800; + Color = rgb16(111, 64, 37); + NameStem = "leather"; + Flexibility = 10; + AttachedGod = SILVA; + HardenedMaterial = HARDENED_LEATHER; + CategoryFlags = Base|CAN_BE_TAILORED|IS_GOLEM_MATERIAL; + InteractionFlags = CAN_BURN; + } + + Config LEAF; + { + StrengthValue = 3; + ConsumeType = CT_MISC_ORGANIC; + Density = 500; + Color = rgb16(0, 160, 0); + NameStem = "leaf"; + Flexibility = 10; + AttachedGod = SILVA; + HardenedMaterial = PALM_LEAF; + CategoryFlags = Base|CAN_BE_TAILORED|IS_GOLEM_MATERIAL; + InteractionFlags = CAN_BURN; + } + + Config FABRIC; + { + StrengthValue = 5; + ConsumeType = CT_MISC_ORGANIC; + Density = 200; + Color = rgb16(176, 0, 0); + NameStem = "expensive fabric"; + Flexibility = 15; + AttachedGod = MELLIS; + CategoryFlags = Base|CAN_BE_TAILORED|IS_GOLEM_MATERIAL; + InteractionFlags = CAN_BURN; + } + + Config PALM_LEAF; + { + StrengthValue = 5; + ConsumeType = CT_MISC_ORGANIC; + Density = 500; + Color = rgb16(0, 160, 0); + NameStem = "palm leaf"; + Flexibility = 10; + AttachedGod = SILVA; + HardenedMaterial = NYMPH_HAIR; + SoftenedMaterial = LEAF; + CategoryFlags = Base|CAN_BE_TAILORED|IS_GOLEM_MATERIAL; + InteractionFlags = CAN_BURN; + } + + Config SULFUR; + { + StrengthValue = 50; + ConsumeType = CT_MINERAL; + Density = 2000; + Color = rgb16(128, 0, 0); + NameStem = "sulfur"; + Flexibility = 3; + AttachedGod = LORICATUS; + HardenedMaterial = TIN; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config UNICORN_HORN; + { + StrengthValue = 200; + ConsumeType = CT_MISC_ORGANIC; + Density = 2000; + Color = rgb16(200, 200, 200); + NameStem = "unicorn horn"; + AttachedGod = SEGES; + CommonFlags = Base|IS_VALUABLE; + IntelligenceRequirement = 20; + } + + Config DIAMOND; + { + StrengthValue = 300; + ConsumeType = CT_MINERAL; + Density = 3500; + Color = rgb16(240, 240, 240); + PriceModifier = 10000; + NameStem = "diamond"; + Alpha = 175; + AttachedGod = MELLIS; + NaturalForm = stone; + IntelligenceRequirement = 40; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_SPARKLING|IS_GOLEM_MATERIAL; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config FLAWLESS_DIAMOND; + { + StrengthValue = 350; + ConsumeType = CT_MINERAL; + Density = 3250; + Color = rgb16(200, 200, 255); + PriceModifier = 15000; + NameStem = "flawless diamond"; + Alpha = 175; + AttachedGod = MELLIS; + NaturalForm = stone; + IntelligenceRequirement = 44; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_SPARKLING|IS_GOLEM_MATERIAL; + InteractionFlags = Base; + } + + Config BLACK_DIAMOND; + { + StrengthValue = 375; + ConsumeType = CT_MINERAL; + Density = 3500; + Color = rgb16(0, 0, 0); + PriceModifier = 15000; + NameStem = "black diamond"; + Alpha = 175; + AttachedGod = MELLIS; + NaturalForm = stone; + IntelligenceRequirement = 50; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_SPARKLING|IS_GOLEM_MATERIAL; + InteractionFlags = Base; + } + + Config PSYPHER; + { + StrengthValue = 600; + ConsumeType = CT_MINERAL; + Density = 3500; + Color = rgb16(180, 50, 50); + PriceModifier = 15000; + NameStem = "psypher"; + Alpha = 200; + AttachedGod = MORTIFER; + NaturalForm = stone; + IntelligenceRequirement = 150; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base; + InteractionFlags = Base; + } + + Config SILVER; + { + StrengthValue = 60; + ConsumeType = CT_METAL; + Density = 10500; + Color = rgb16(220, 220, 220); + PriceModifier = 1000; + NameStem = "silver"; + AttachedGod = MELLIS; + HardenedMaterial = SAPPHIRE; + IntelligenceRequirement = 30; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_METAL|IS_SPARKLING|IS_GOLEM_MATERIAL; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config SAPPHIRE; + { + StrengthValue = 250; + ConsumeType = CT_MINERAL; + Density = 4000; + Color = rgb16(96, 96, 200); + PriceModifier = 6000; + NameStem = "sapphire"; + Alpha = 200; + AttachedGod = MELLIS; + NaturalForm = stone; + HardenedMaterial = DIAMOND; + IntelligenceRequirement = 35; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_SPARKLING|IS_GOLEM_MATERIAL; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config EMERALD; + { + StrengthValue = 250; + ConsumeType = CT_MINERAL; + Density = 4000; + Color = rgb16(0, 180, 0); + PriceModifier = 8000; + NameStem = "emerald"; + Alpha = 200; + AttachedGod = MELLIS; + NaturalForm = stone; + HardenedMaterial = DIAMOND; + IntelligenceRequirement = 35; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_SPARKLING|IS_GOLEM_MATERIAL; + InteractionFlags = Base; + } + + Config RUBY; + { + StrengthValue = 250; + ConsumeType = CT_MINERAL; + Density = 4000; + Color = rgb16(180, 0, 0); + PriceModifier = 8000; + NameStem = "ruby"; + Alpha = 200; + AttachedGod = MELLIS; + NaturalForm = stone; + HardenedMaterial = DIAMOND; + IntelligenceRequirement = 35; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_SPARKLING|IS_GOLEM_MATERIAL; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config BRONZE; + { + StrengthValue = 80; + ConsumeType = CT_METAL; + Density = 8300; + Color = rgb16(150, 100, 50); + PriceModifier = 30; + NameStem = "bronze"; + AttachedGod = LORICATUS; + HardenedMaterial = IRON; + SoftenedMaterial = COPPER; + CategoryFlags = Base|IS_METAL|IS_GOLEM_MATERIAL; + } + + Config COPPER; + { + StrengthValue = 70; + ConsumeType = CT_METAL; + Density = 9000; /* rounded from 8920 */ + Color = rgb16(96, 64, 32); + PriceModifier = 20; + NameStem = "copper"; + AttachedGod = LORICATUS; + HardenedMaterial = BRONZE; + SoftenedMaterial = TIN; + CategoryFlags = Base|IS_METAL|IS_GOLEM_MATERIAL; + } + + Config TIN; + { + StrengthValue = 60; + ConsumeType = CT_METAL; + Density = 7250; + Color = rgb16(100, 100, 100); + PriceModifier = 15; + NameStem = "tin"; + AttachedGod = LORICATUS; + HardenedMaterial = COPPER; + CategoryFlags = Base|IS_METAL|IS_GOLEM_MATERIAL; + } + + Config SPIDER_SILK; + { + StrengthValue = 180; + ConsumeType = CT_MISC_ORGANIC; + Density = 300; + Color = rgb16(160, 160, 160); + NameStem = "spider silk"; + Flexibility = 20; + AttachedGod = SOPHOS; + IntelligenceRequirement = 40; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|CAN_BE_TAILORED; + InteractionFlags = CAN_BURN; + } + + Config KEVLAR; + { + StrengthValue = 140; + ConsumeType = CT_PLASTIC; + Density = 1500; + Color = rgb16(144, 95, 32); + NameStem = "kevlar"; + Flexibility = 15; + AttachedGod = LORICATUS; + IntelligenceRequirement = 35; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|CAN_BE_TAILORED; + } + + Config OMMEL_HAIR; + { + StrengthValue = 60; + ConsumeType = CT_MISC_ORGANIC; + Density = 400; + Color = rgb16(160, 0, 0); + NameStem = "ommel hair"; + Flexibility = 15; + AttachedGod = SILVA; + HardenedMaterial = PHOENIX_FEATHER; + SoftenedMaterial = TROLL_HIDE; + IntelligenceRequirement = 10; + CommonFlags = Base|IS_VALUABLE; + NameFlags = Base|USE_AN; + CategoryFlags = Base|CAN_BE_TAILORED|IS_GOLEM_MATERIAL; + InteractionFlags = CAN_BURN; + } + + Config HARDENED_LEATHER; + { + StrengthValue = 35; + ConsumeType = CT_MISC_ORGANIC; + Density = 1200; + Color = rgb16(100, 50, 30); + NameStem = "hardened leather"; + Flexibility = 7; + AttachedGod = SOPHOS; + IntelligenceRequirement = 5; + HardenedMaterial = TROLL_HIDE; + SoftenedMaterial = LEATHER; + CategoryFlags = Base|CAN_BE_TAILORED|IS_GOLEM_MATERIAL; + InteractionFlags = CAN_BURN; + } + + Config TROLL_HIDE; + { + StrengthValue = 50; + ConsumeType = CT_MISC_ORGANIC; + Density = 1800; + Color = rgb16(128, 100, 32); + NameStem = "troll hide"; + Flexibility = 5; + AttachedGod = SILVA; + HardenedMaterial = OMMEL_HAIR; + SoftenedMaterial = HARDENED_LEATHER; + IntelligenceRequirement = 5; + CategoryFlags = Base|CAN_BE_TAILORED; + InteractionFlags = CAN_BURN; + } + + Config NYMPH_HAIR; + { + StrengthValue = 35; + ConsumeType = CT_MISC_ORGANIC; + Density = 300; + Color = rgb16(100, 100, 200); + NameStem = "nymph hair"; + Flexibility = 20; + AttachedGod = SILVA; + HardenedMaterial = TROLL_HIDE; + IntelligenceRequirement = 5; + CategoryFlags = Base|CAN_BE_TAILORED|IS_GOLEM_MATERIAL; + InteractionFlags = CAN_BURN; + } + + Config ANGEL_HAIR; + { + StrengthValue = 140; + ConsumeType = CT_MISC_ORGANIC; + Density = 300; + Color = rgb16(200, 200, 0); + NameStem = "angel hair"; + Flexibility = 25; + AttachedGod = DULCIS; + IntelligenceRequirement = 35; + CommonFlags = Base|IS_VALUABLE; + NameFlags = Base|USE_AN; + CategoryFlags = Base|CAN_BE_TAILORED; + InteractionFlags = CAN_BURN&~CAN_DISSOLVE; + } + + Config PHOENIX_FEATHER; + { + StrengthValue = 80; + ConsumeType = CT_MISC_ORGANIC; + Density = 200; + Color = rgb16(220, 220, 0); + NameStem = "phoenix feather"; + Flexibility = 30; + AttachedGod = SILVA; + HardenedMaterial = DRAGON_HIDE; + SoftenedMaterial = OMMEL_HAIR; + IntelligenceRequirement = 20; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|CAN_BE_TAILORED|IS_GOLEM_MATERIAL; + } + + Config GOLDEN_EAGLE_FEATHER; + { + StrengthValue = 300; + ConsumeType = CT_MISC_ORGANIC; + Density = 100; + Color = rgb16(80, 80, 176); + NameStem = "golden eagle feather"; + Flexibility = 25; + AttachedGod = VALPURUS; + IntelligenceRequirement = 50; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|CAN_BE_TAILORED; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config ICE; + { + StrengthValue = 30; + ConsumeType = CT_MINERAL;/* correct someday */ + Density = 750; + Color = rgb16(175, 175, 230); + NameStem = "ice"; + Alpha = 150; + AttachedGod = TERRA; + NaturalForm = stone; + NameFlags = Base|USE_AN; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config DRAGON_HIDE; + { + StrengthValue = 160; + ConsumeType = CT_MISC_ORGANIC; + Density = 2500; + Color = rgb16(160, 60, 32); + NameStem = "dragon hide"; + Flexibility = 5; + AttachedGod = SILVA; + IntelligenceRequirement = 35; + SoftenedMaterial = PHOENIX_FEATHER; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|CAN_BE_TAILORED|IS_GOLEM_MATERIAL; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config ARCANITE; + { + StrengthValue = 180; + ConsumeType = CT_METAL; + Density = 800; + Color = rgb16(224, 224, 120); + PriceModifier = 2000; + NameStem = "arcanite"; + AttachedGod = SOPHOS; + Flexibility = 3; + HardenedMaterial = MITHRIL; + IntelligenceRequirement = 20; + CommonFlags = Base|IS_VALUABLE; + NameFlags = Base|USE_AN; + CategoryFlags = Base|IS_METAL|IS_GOLEM_MATERIAL; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config ILLITHIUM; + { + StrengthValue = 225; + ConsumeType = CT_METAL; + Density = 6000; + Color = rgb16(200, 200, 240); + PriceModifier = 5000; + NameStem = "illithium"; + AttachedGod = LEGIFER; + IntelligenceRequirement = 25; + CommonFlags = Base|IS_VALUABLE; + NameFlags = Base|USE_AN; + CategoryFlags = Base|IS_METAL|IS_GOLEM_MATERIAL; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config BALSA_WOOD; + { + StrengthValue = 20; + ConsumeType = CT_MISC_ORGANIC; + Density = 250; + Color = rgb16(160, 108, 50); + NameStem = "balsa"; + AttachedGod = SILVA; + Flexibility = 2; + NaturalForm = stick; + HardenedMaterial = PINE_WOOD; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config PINE_WOOD; + { + StrengthValue = 25; + ConsumeType = CT_MISC_ORGANIC; + Density = 400; + Color = rgb16(208, 108, 42); + NameStem = "pine"; + AttachedGod = SILVA; + NaturalForm = stick; + HardenedMaterial = FIR_WOOD; + SoftenedMaterial = BALSA_WOOD; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config FIR_WOOD; + { + StrengthValue = 30; + ConsumeType = CT_MISC_ORGANIC; + Density = 500; + Color = rgb16(140, 96, 48); + NameStem = "fir"; + AttachedGod = SILVA; + NaturalForm = stick; + HardenedMaterial = BIRCH_WOOD; + SoftenedMaterial = PINE_WOOD; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config BIRCH_WOOD; + { + StrengthValue = 35; + ConsumeType = CT_MISC_ORGANIC; + Density = 600; + Color = rgb16(130, 70, 32); + NameStem = "birch"; + AttachedGod = SILVA; + NaturalForm = stick; + HardenedMaterial = OAK_WOOD; + SoftenedMaterial = FIR_WOOD; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config OAK_WOOD; + { + StrengthValue = 45; + ConsumeType = CT_MISC_ORGANIC; + Density = 700; + Color = rgb16(124, 50, 16); + NameStem = "oak"; + AttachedGod = SILVA; + NaturalForm = stick; + HardenedMaterial = TEAK_WOOD; + SoftenedMaterial = BIRCH_WOOD; + IntelligenceRequirement = 5; + NameFlags = Base|USE_AN; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config TEAK_WOOD; + { + StrengthValue = 50; + ConsumeType = CT_MISC_ORGANIC; + Density = 750; + Color = rgb16(80, 40, 30); + NameStem = "teak"; + AttachedGod = SILVA; + NaturalForm = stick; + HardenedMaterial = EBONY_WOOD; + SoftenedMaterial = OAK_WOOD; + IntelligenceRequirement = 5; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config EBONY_WOOD; + { + StrengthValue = 65; + ConsumeType = CT_MISC_ORGANIC; + Density = 1000; + Color = rgb16(48, 48, 48); + NameStem = "ebony"; + AttachedGod = SILVA; + NaturalForm = stick; + SoftenedMaterial = TEAK_WOOD; + IntelligenceRequirement = 10; + CommonFlags = Base|IS_VALUABLE; + NameFlags = Base|USE_AN; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config KAURI_WOOD; + { + StrengthValue = 60; + ConsumeType = CT_MISC_ORGANIC; + Density = 800; + Color = rgb16(140, 92, 42); + NameStem = "kauri"; + AttachedGod = SILVA; + NaturalForm = stick; + SoftenedMaterial = TEAK_WOOD; + IntelligenceRequirement = 10; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config RATA_WOOD; + { + StrengthValue = 70; + ConsumeType = CT_MISC_ORGANIC; + Density = 900; + Color = rgb16(166, 44, 24); + NameStem = "rata"; + AttachedGod = SILVA; + NaturalForm = stick; + SoftenedMaterial = KAURI_WOOD; + IntelligenceRequirement = 15; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config OCTIRON; + { + StrengthValue = 275; + ConsumeType = CT_METAL; + Density = 900; + Color = rgb16(36, 36, 36); + PriceModifier = 2500; + NameStem = "octiron"; + AttachedGod = SOPHOS; + Flexibility = 2; + SoftenedMaterial = MITHRIL; + IntelligenceRequirement = 40; + CommonFlags = Base|IS_VALUABLE; + NameFlags = Base|USE_AN; + CategoryFlags = Base|IS_METAL|IS_GOLEM_MATERIAL; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config BLUE_CRYSTAL; + { + StrengthValue = 140; + ConsumeType = CT_MINERAL; + Density = 2000; + Color = rgb16(150, 150, 170); + NameStem = "blue light crystal"; + AdjectiveStem = "crystal"; + Alpha = 150; + AttachedGod = TERRA; + Emitation = rgb24(135, 135, 160); + NaturalForm = stone; + PriceModifier = 8000; + HardenedMaterial = GREEN_CRYSTAL; + SoftenedMaterial = PURPLE_CRYSTAL; + IntelligenceRequirement = 20; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_SPARKLING|IS_GOLEM_MATERIAL; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config PURPLE_CRYSTAL; + { + StrengthValue = 130; + ConsumeType = CT_MINERAL; + Density = 2000; + Color = rgb16(175, 150, 175); + NameStem = "purple light crystal"; + AdjectiveStem = "crystal"; + Alpha = 150; + AttachedGod = TERRA; + Emitation = rgb24(175, 150, 175); + NaturalForm = stone; + PriceModifier = 8000; + HardenedMaterial = BLUE_CRYSTAL; + IntelligenceRequirement = 20; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_SPARKLING|IS_GOLEM_MATERIAL; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config GREEN_CRYSTAL; + { + StrengthValue = 150; + ConsumeType = CT_MINERAL; + Density = 2000; + Color = rgb16(150, 175, 150); + NameStem = "green light crystal"; + AdjectiveStem = "crystal"; + Alpha = 150; + AttachedGod = TERRA; + Emitation = rgb24(135, 155, 135); + NaturalForm = stone; + SoftenedMaterial = BLUE_CRYSTAL; + PriceModifier = 8000; + IntelligenceRequirement = 20; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_SPARKLING|IS_GOLEM_MATERIAL; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config SUN_CRYSTAL; + { + StrengthValue = 150; + ConsumeType = CT_MINERAL; + Density = 2000; + Color = rgb16(224, 224, 0); + NameStem = "sun light crystal"; + AdjectiveStem = "crystal"; + Alpha = 150; + AttachedGod = TERRA; + Emitation = rgb24(160, 160, 160); + NaturalForm = stone; + PriceModifier = 8000; + IntelligenceRequirement = 20; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_SPARKLING|; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config SAND_STONE; + { + StrengthValue = 25; + ConsumeType = CT_MINERAL; + Density = 2300; + Color = rgb16(150, 125, 80); + NameStem = "sandstone"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = LIME_STONE; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config LIME_STONE; + { + StrengthValue = 30; + ConsumeType = CT_MINERAL; + Density = 2500; + Color = rgb16(160, 170, 160); + NameStem = "limestone"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = CALCITE; + SoftenedMaterial = SAND_STONE; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config CALCITE; + { + StrengthValue = 35; + ConsumeType = CT_MINERAL; + Density = 2700; + Color = rgb16(180, 180, 200); + NameStem = "calcite"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = OBSIDIAN; + SoftenedMaterial = LIME_STONE; + Alpha = 140; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config OBSIDIAN; + { + StrengthValue = 40; + ConsumeType = CT_MINERAL; + Density = 2400; + Color = rgb16(40, 40, 40); + NameStem = "obsidian"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = GNEISS; + SoftenedMaterial = CALCITE; + NameFlags = Base|USE_AN; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config GNEISS; + { + StrengthValue = 45; + ConsumeType = CT_MINERAL; + Density = 2900; + Color = rgb16(108, 80, 75); + NameStem = "gneiss"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = SLATE; + SoftenedMaterial = OBSIDIAN; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config SLATE; + { + StrengthValue = 50; + ConsumeType = CT_MINERAL; + Density = 2700; + Color = rgb16(140, 80, 64); + NameStem = "slate"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = GRANITE; + SoftenedMaterial = GNEISS; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config GRANITE; + { + StrengthValue = 55; + ConsumeType = CT_MINERAL; + Density = 2700; + Color = rgb16(100, 100, 100); + NameStem = "granite"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = BASALT; + SoftenedMaterial = SLATE; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config BASALT; + { + StrengthValue = 60; + ConsumeType = CT_MINERAL; + Density = 2900; + Color = rgb16(60, 60, 60); + NameStem = "basalt"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = MILKY_QUARTZ; + SoftenedMaterial = GRANITE; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config EXTRA_HARD_BASALT; + { + StrengthValue = 120; + ConsumeType = CT_MINERAL; + Density = 3500; + Color = rgb16(60, 60, 60); + NameStem = "basalt"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = MILKY_QUARTZ; + CategoryFlags = Base; + } + + Config MILKY_QUARTZ; + { + StrengthValue = 65; + ConsumeType = CT_MINERAL; + Density = 2650; + Color = rgb16(200, 240, 200); + NameStem = "milky quartz"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = QUARTZITE; + SoftenedMaterial = BASALT; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config FLINT; + { + StrengthValue = 65; + ConsumeType = CT_MINERAL; + Density = 2650; + Color = rgb16(135, 150, 180); + NameStem = "flint"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = QUARTZITE; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config QUARTZITE; + { + StrengthValue = 70; + ConsumeType = CT_MINERAL; + Density = 2700; + Color = rgb16(175, 160, 135); + NameStem = "quartzite"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = PURPLE_CRYSTAL; + SoftenedMaterial = MILKY_QUARTZ; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config AMETHYST; + { + StrengthValue = 75; + ConsumeType = CT_MINERAL; + Density = 2650; + Color = rgb16(160, 35, 160); + NameStem = "amethyst"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = PURPLE_CRYSTAL; + PriceModifier = 1000; + IntelligenceRequirement = 5; + Alpha = 160; + NameFlags = Base|USE_AN; + CategoryFlags = Base|IS_SPARKLING|IS_GOLEM_MATERIAL; + } + + Config CITRINE; + { + StrengthValue = 75; + ConsumeType = CT_MINERAL; + Density = 2650; + Color = rgb16(230, 210, 40); + NameStem = "citrine"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = PURPLE_CRYSTAL; + PriceModifier = 1000; + IntelligenceRequirement = 5; + Alpha = 180; + CategoryFlags = Base|IS_SPARKLING|IS_GOLEM_MATERIAL; + } + + Config ROSE_QUARTZ; + { + StrengthValue = 75; + ConsumeType = CT_MINERAL; + Density = 2650; + Color = rgb16(160, 130, 130); + NameStem = "rose quartz"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = PURPLE_CRYSTAL; + PriceModifier = 1000; + IntelligenceRequirement = 5; + Alpha = 180; + CategoryFlags = Base|IS_SPARKLING|IS_GOLEM_MATERIAL; + } + + Config JASPER; + { + StrengthValue = 75; + ConsumeType = CT_MINERAL; + Density = 2650; + Color = rgb16(150, 40, 30); + NameStem = "jasper"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = PURPLE_CRYSTAL; + PriceModifier = 1000; + IntelligenceRequirement = 5; + CategoryFlags = Base|IS_SPARKLING|IS_GOLEM_MATERIAL; + } + + Config ROCK_CRYSTAL; + { + StrengthValue = 75; + ConsumeType = CT_MINERAL; + Density = 2650; + Color = rgb16(180, 180, 200); + NameStem = "rock crystal"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = PURPLE_CRYSTAL; + PriceModifier = 500; + IntelligenceRequirement = 5; + Alpha = 140; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config NEPHRITE; + { + StrengthValue = 75; + ConsumeType = CT_MINERAL; + Density = 2650; + Color = rgb16(24, 136, 64); + NameStem = "pounamu"; + /*Alias == "jade";*/ + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = PURPLE_CRYSTAL; + PriceModifier = 1000; + IntelligenceRequirement = 5; + Alpha = 180; + CategoryFlags = Base|IS_SPARKLING|IS_GOLEM_MATERIAL; + } + + Config DARK_GRASS; + { + StrengthValue = 2; + ConsumeType = CT_MISC_ORGANIC; + Density = 100; + Color = rgb16(75, 100, 75); + NameStem = "grass"; + AdjectiveStem = "grassy"; + Flexibility = 15; + AttachedGod = SILVA; + CommonFlags = Base&~CAN_BE_WISHED; + BodyFlags = Base|CAN_REGENERATE|IS_ALIVE; + InteractionFlags = CAN_BURN; + } + + Config LEAD; + { + StrengthValue = 110; + ConsumeType = CT_METAL; + Density = 11340; + Color = rgb16(230, 230, 230); + PriceModifier = 30; + NameStem = "lead"; + Flexibility = 2; + AttachedGod = LORICATUS; + HardenedMaterial = ADAMANT; + IntelligenceRequirement = 7; + CommonFlags = Base; + CategoryFlags = Base|IS_METAL|IS_GOLEM_MATERIAL; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config BLACK_GRANITE; + { + StrengthValue = 60; + ConsumeType = CT_MINERAL; + Density = 2700; + Color = rgb16(60, 60, 60); + NameStem = "black granite"; + AttachedGod = TERRA; + NaturalForm = stone; + HardenedMaterial = MILKY_QUARTZ; + SoftenedMaterial = GRANITE; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config BLACK_LEATHER; + { + StrengthValue = 40; + ConsumeType = CT_MISC_ORGANIC; + Density = 800; + Color = rgb16(40, 40, 40); + NameStem = "black leather"; + Flexibility = 10; + AttachedGod = SILVA; + HardenedMaterial = HARDENED_LEATHER; + CategoryFlags = Base|CAN_BE_TAILORED|IS_GOLEM_MATERIAL; + InteractionFlags = CAN_BURN; + } +} + +organic /* substances that spoil but are not flesh */ +{ + SpoilModifier = 10000; + BodyFlags = Base&~USE_MATERIAL_ATTRIBUTES; + + Config BANANA_FLESH; + { + StrengthValue = 3; + Density = 1200; + ConsumeType = CT_FRUIT; + NutritionValue = 1000; + Color = rgb16(224, 224, 0); + PriceModifier = 3000; + NameStem = "banana flesh"; + Flexibility = 5; + AttachedGod = SILVA; + } + + Config SCHOOL_FOOD; + { + StrengthValue = 3; + ConsumeType = CT_MEAT; + Density = 1500; + NutritionValue = 500; + Color = rgb16(111, 74, 37); + ConsumeWisdomLimit = 0; + NameStem = "school food"; + Effect = EFFECT_SCHOOL_FOOD; + ConsumeEndMessage = CEM_SCHOOL_FOOD; + HitMessage = HM_SCHOOL_FOOD; + Flexibility = 5; + AttachedGod = SCABIES; + PriceModifier = 1000; + SpoilModifier = 65000; + CommonFlags = Base&~(CAN_BE_WISHED|CAN_BE_MIRRORED); + } + + Config BANANA_PEEL; + { + StrengthValue = 5; + ConsumeType = CT_MISC_ORGANIC; + Density = 500; + Color = rgb16(224, 224, 0); + NameStem = "banana peel"; + Flexibility = 10; + AttachedGod = SILVA; + NaturalForm = bananapeels; + BodyFlags = Base|USE_MATERIAL_ATTRIBUTES; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config KIWI_FLESH; + { + StrengthValue = 3; + ConsumeType = CT_FRUIT; + Density = 1200; + NutritionValue = 1000; + Color = rgb16(160, 112, 32); + PriceModifier = 3000; + NameStem = "kiwi flesh"; + Flexibility = 5; + AttachedGod = SILVA; + } + + Config PINEAPPLE_FLESH; + { + StrengthValue = 5; + ConsumeType = CT_FRUIT; + Density = 1200; + NutritionValue = 1000; + Color = rgb16(192, 112, 96); + PriceModifier = 3000; + NameStem = "pineapple flesh"; + Flexibility = 5; + AttachedGod = SILVA; + } + + Config PLANT_FIBER; + { + StrengthValue = 7; + ConsumeType = CT_MISC_ORGANIC; + Density = 500; + Color = rgb16(111, 64, 37); + NameStem = "plant fiber"; + Flexibility = 10; + SpoilModifier = 20000; + AttachedGod = MORTIFER; + BodyFlags = Base|IS_ALIVE|CAN_REGENERATE; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + InteractionFlags = CAN_BURN; + } + + Config MUTANT_PLANT_FIBER; + { + StrengthValue = 7; + ConsumeType = CT_MISC_ORGANIC; + Density = 500; + Color = rgb16(111, 64, 37); + NameStem = "mutant plant fiber"; + Flexibility = 10; + SpoilModifier = 10000; + Emitation = rgb24(140, 100, 100); + AttachedGod = SCABIES; + BodyFlags = Base|IS_ALIVE|IS_WARM|CAN_REGENERATE; + InteractionFlags = CAN_BURN; + } + + Config BONE; + { + StrengthValue = 40; + Density = 2000; + ConsumeType = CT_BONE; + Color = rgb16(210, 210, 210); + NutritionValue = 500; + NameStem = "bone"; + ConsumeEndMessage = CEM_BONE; + SpoilModifier = 48000; + AttachedGod = MORTIFER; + HardenedMaterial = OMMEL_BONE; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + NaturalForm = bone; + } + + Config BREAD; + { + StrengthValue = 5; + ConsumeType = CT_PROCESSED; + Density = 250; + NutritionValue = 1000; + Color = rgb16(180, 96, 64); + PriceModifier = 3000; + NameStem = "bread"; + Flexibility = 5; + SpoilModifier = 20000; + AttachedGod = SEGES; + } + + Config HOLY_BANANA_FLESH; + { + StrengthValue = 3; + Density = 1200; + ConsumeType = CT_FRUIT; + NutritionValue = 20000; + Color = rgb16(224, 224, 0); + PriceModifier = 3000; + NameStem = "holy banana flesh"; + Flexibility = 10; + AttachedGod = SILVA; + Effect = EFFECT_HOLY_BANANA; + ConsumeEndMessage = CEM_HOLY_BANANA; + CommonFlags = Base|IS_VALUABLE&~(CAN_BE_WISHED|CAN_BE_MIRRORED); + BodyFlags = Base|IS_WARM; + } + + Config HOLY_MANGO_FLESH; + { + StrengthValue = 3; + Density = 1200; + ConsumeType = CT_FRUIT; + NutritionValue = 20000; + Color = rgb16(250, 158, 74); + PriceModifier = 10000; + NameStem = "holy mango flesh"; + Flexibility = 10; + AttachedGod = SILVA; + Effect = EFFECT_HOLY_MANGO; + ConsumeEndMessage = CEM_HOLY_MANGO; + CommonFlags = Base|IS_VALUABLE&~(CAN_BE_WISHED|CAN_BE_MIRRORED); + BodyFlags = Base|IS_WARM; + } + + Config MANGO_FLESH; + { + StrengthValue = 3; + Density = 1200; + ConsumeType = CT_FRUIT; + NutritionValue = 1000; + Color = rgb16(250, 158, 74); + PriceModifier = 3000; + NameStem = "mango flesh"; + Flexibility = 5; + AttachedGod = SILVA; + } + + Config CARROT_FLESH; + { + StrengthValue = 3; + ConsumeType = CT_FRUIT; + NameStem = "carrot flesh"; + Effect = EFFECT_TRAIN_PERCEPTION; + EffectStrength = 1000; + AttachedGod = SILVA; + Color = rgb16(220, 120, 0); + Density = 1200; + NutritionValue = 1000; + PriceModifier = 5000; + Flexibility = 5; + CommonFlags = Base|IS_VALUABLE&~CAN_BE_WISHED; + } + + Config OMMEL_CERUMEN; + { + StrengthValue = 1; + Color = rgb16(180, 150, 50); + NutritionValue = 500; + PriceModifier = 10000; + NameStem = "ommel cerumen"; + Effect = EFFECT_OMMEL_CERUMEN; + ConsumeEndMessage = CEM_OMMEL; + HitMessage = HM_OMMEL; + AttachedGod = SOPHOS; + Density = 500; + ConsumeType = CT_PROCESSED; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + CommonFlags = Base|IS_VALUABLE&~(CAN_BE_WISHED|CAN_BE_MIRRORED); + NameFlags = Base|USE_AN; + InteractionFlags = Base|EFFECT_IS_GOOD; + } + + Config OMMEL_BONE; + { + StrengthValue = 250; + Flexibility = 4; + Density = 750; + ConsumeType = CT_BONE; + Color = rgb16(210, 210, 210); + NutritionValue = 1000; + NameStem = "ommel bone"; + ConsumeEndMessage = CEM_BONE; + SpoilModifier = 48000; + AttachedGod = MORTIFER; + Effect = EFFECT_OMMEL_BONE; + ConsumeEndMessage = CEM_OMMEL_BONE; + NameFlags = Base|USE_AN; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + HardenedMaterial = OMMEL_TOOTH; + } + + Config OMMEL_TOOTH; + { + StrengthValue = 375; + Density = 5000; + ConsumeType = CT_BONE; + Color = rgb16(200, 200, 255); + NutritionValue = 1000; + NameStem = "ommel tooth"; + ConsumeEndMessage = CEM_BONE; + SpoilModifier = 48000; + AttachedGod = MORTIFER; + IntelligenceRequirement = 20; + Effect = EFFECT_OMMEL_BONE; + ConsumeEndMessage = CEM_OMMEL_BONE; + NameFlags = Base|USE_AN; + CategoryFlags = Base|IS_SPARKLING|IS_GOLEM_MATERIAL; + } +} + +gas +{ + StrengthValue = 1; + Density = 1; + Flexibility = 75; + ConsumeType = CT_GAS; + CommonFlags = Base&~CAN_BE_WISHED; + + Config AIR; + { + StrengthValue = 1; + Density = 1; + Color = rgb16(180, 180, 255); + NameStem = "air"; + Alpha = 150; + AttachedGod = TERRA; + NameFlags = Base|USE_AN; + } + + Config MAGICAL_AIR; + { + Color = rgb16(180, 180, 255); + NameStem = "magical air"; + Alpha = 150; + AttachedGod = SOPHOS; + BodyFlags = Base|CAN_REGENERATE&~USE_MATERIAL_ATTRIBUTES; + } + + Config SMOKE; + { + Color = rgb16(60, 60, 60); + NameStem = "smoke"; + Alpha = 255; + AttachedGod = LORICATUS; + Effect = EFFECT_NOTHING; + } + + Config SKUNK_SMELL; + { + Color = rgb16(20, 60, 20); + NameStem = "skunk smell"; + Alpha = 200; + AttachedGod = SCABIES; + EffectStrength = 4; + Effect = EFFECT_SKUNK_SMELL; + BreatheMessage = "It smells really bad here."; + StepInWisdomLimit = 3; + } + + Config GHOST; + { + Color = rgb16(255, 255, 255); + NameStem = "ghost"; + Alpha = 150; + AttachedGod = MORTIFER; + BodyFlags = Base|CAN_REGENERATE&~USE_MATERIAL_ATTRIBUTES; + } + + Config MAGIC_VAPOUR; + { + Color = rgb16(200, 60, 200); + NameStem = "raw vapourized magic"; + Alpha = 200; + AttachedGod = SOPHOS; + Effect = EFFECT_MAGIC_MUSHROOM; + BreatheMessage = "The air is filled with tiny magical bubbles."; + StepInWisdomLimit = 10; + } + + Config EVIL_WONDER_STAFF_VAPOUR; + { + Color = rgb16(200, 0, 0); + NameStem = "mysterious red gas"; + Alpha = 200; + AttachedGod = INFUSCOR; + Effect = EFFECT_EVIL_WONDER_STAFF_VAPOUR; + BreatheMessage = "A sinister stench surrounds you."; + StepInWisdomLimit = 5; + } + + Config GOOD_WONDER_STAFF_VAPOUR; + { + Color = rgb16(100, 100, 200); + NameStem = "mysterious blue gas"; + Alpha = 200; + AttachedGod = SOPHOS; + Effect = EFFECT_GOOD_WONDER_STAFF_VAPOUR; + BreatheMessage = "It smells oddly intelligent here."; + } + + Config FART; + { + Color = rgb16(120, 70, 40); + NameStem = "brown gas"; + Alpha = 150; + AttachedGod = SILVA; + BreatheMessage = "Ugh. It smells horrible here."; + CategoryFlags = Base|IS_SCARY; + } + + Config MUSTARD_GAS; + { + Color = rgb16(120, 70, 40); + NameStem = "mustard gas"; + Alpha = 200; + AttachedGod = CRUENTUS; + BreatheMessage = "It smells like mustard here."; + Effect = EFFECT_MUSTARD_GAS; + StepInWisdomLimit = 8; + } + + Config VACUUM_BLADE_AIR; + { + StrengthValue = 1; + Density = 10; + Color = rgb16(180, 180, 255); + NameStem = "vacuum blade air"; + Alpha = 150; + AttachedGod = TERRA; + NameFlags = Base; + } + +} + +liquid +{ + ConsumeType = CT_LIQUID; + Density = 1000; + Flexibility = 50; + RustModifier = 25; + CommonFlags = Base&~CAN_BE_WISHED; + + Config OMMEL_URINE; + { + StrengthValue = 1; + Color = rgb16(160, 160, 0); + Emitation = rgb24(140, 140, 100); + NutritionValue = 500; + PriceModifier = 2500; + NameStem = "ommel urine"; + Effect = EFFECT_OMMEL_URINE; + ConsumeEndMessage = CEM_OMMEL; + HitMessage = HM_OMMEL; + Alpha = 150; + AttachedGod = NEFAS; + CommonFlags = Base|IS_VALUABLE&~CAN_BE_MIRRORED; + NameFlags = Base|USE_AN; + BodyFlags = Base|IS_WARM; + InteractionFlags = Base|EFFECT_IS_GOOD; + } + + Config PEPSI; + { + StrengthValue = 200; + Density = 1500; + NutritionValue = 500; + Color = rgb16(48, 48, 48); + ConsumeWisdomLimit = 2; + NameStem = "pepsi"; + Effect = EFFECT_PEPSI; + ConsumeEndMessage = CEM_PEPSI; + HitMessage = HM_PEPSI; + AttachedGod = MELLIS; + RustModifier = 200; + Acidicity = 10; + PriceModifier = 1000; + CommonFlags = Base|IS_VALUABLE; + StepInWisdomLimit = 20; + } + + Config WATER; + { + StrengthValue = 1; + NutritionValue = 10; + Color = rgb16(144, 144, 240); + NameStem = "water"; + Alpha = 150; + AttachedGod = TERRA; + } + + Config HEALING_LIQUID; + { + StrengthValue = 1; + NutritionValue = 250; + Color = rgb16(180, 0, 0); + PriceModifier = 1500; + NameStem = "healing liquid"; + Effect = EFFECT_HEAL; + ConsumeEndMessage = CEM_HEALING_LIQUID; + HitMessage = HM_HEALING_LIQUID; + Alpha = 150; + AttachedGod = SEGES; + CommonFlags = Base|IS_VALUABLE; + InteractionFlags = Base|EFFECT_IS_GOOD; + } + + Config BLOOD; + { + StrengthValue = 1; + NutritionValue = 50; + Color = rgb16(100, 0, 0); + NameStem = "blood"; + Alpha = 175; + Flexibility = 40; + AttachedGod = CRUENTUS; + RustModifier = 1; + CategoryFlags = IS_BLOOD; + } + + Config GLOWING_BLOOD; + { + StrengthValue = 1; + NutritionValue = 75; + Color = rgb16(100, 0, 0); + Emitation = rgb24(140, 100, 100); + NameStem = "glowing blood"; + Alpha = 150; + Flexibility = 40; + AttachedGod = SCABIES; + RustModifier = 1; + CategoryFlags = IS_BLOOD; + } + + Config BROWN_SLIME; + { + StrengthValue = 5; + Density = 600; + NutritionValue = 10; + Color = rgb16(130, 90, 40); + NameStem = "brown slime"; + Alpha = 200; + Flexibility = 25; + AttachedGod = SCABIES; + RustModifier = 50; + Acidicity = 25; + ConsumeWisdomLimit = 3; + BodyFlags = Base&~USE_MATERIAL_ATTRIBUTES; + InteractionFlags = Base|AFFECT_INSIDE&~CAN_DISSOLVE; + Stickiness = 100; + StepInWisdomLimit = 4; + } + + Config YELLOW_SLIME; + { + StrengthValue = 5; + Density = 400; + NutritionValue = 5; + Color = rgb16(0, 230, 0); + NameStem = "green slime"; + Alpha = 200; + Flexibility = 35; + AttachedGod = SCABIES; + RustModifier = 200; + Acidicity = 100; + ConsumeWisdomLimit = 4; + BodyFlags = Base&~USE_MATERIAL_ATTRIBUTES; + InteractionFlags = Base|AFFECT_INSIDE&~CAN_DISSOLVE; + Stickiness = 25; + StepInWisdomLimit = 5; + } + + Config POISON_LIQUID; + { + StrengthValue = 1; + NutritionValue = 100; + Color = rgb16(0, 128, 0); + ConsumeWisdomLimit = 5; + NameStem = "poison"; + Effect = EFFECT_POISON; + Alpha = 150; + PriceModifier = 250; + AttachedGod = SCABIES; + } + + Config VALDEMAR; + { + StrengthValue = 1; + NutritionValue = 80; + Color = rgb16(50, 0, 0); + ConsumeWisdomLimit = 10; + NameStem = "Valdemar"; + Effect = EFFECT_CONFUSE; + Alpha = 200; + ExplosivePower = 4000; + PriceModifier = 500; + AttachedGod = NEFAS; + EffectStrength = 150; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_BEVERAGE; + InteractionFlags = CAN_EXPLODE|EFFECT_IS_GOOD; + } + + Config ANTIDOTE_LIQUID; + { + StrengthValue = 1; + NutritionValue = 100; + Color = rgb16(128, 0, 128); + NameStem = "antidote liquid"; + Effect = EFFECT_ANTIDOTE; + ConsumeEndMessage = CEM_ANTIDOTE; + HitMessage = HM_ANTIDOTE; + Alpha = 170; + EffectStrength = 100; + PriceModifier = 1000; + AttachedGod = SEGES; + CommonFlags = Base|IS_VALUABLE; + InteractionFlags = Base|EFFECT_IS_GOOD; + } + + Config VODKA; + { + StrengthValue = 1; + NutritionValue = 80; + Color = rgb16(144, 144, 144); + ConsumeWisdomLimit = 10; + NameStem = "vodka"; + Effect = EFFECT_CONFUSE; + Alpha = 150; + EffectStrength = 300; + ExplosivePower = 20000; + PriceModifier = 500; + AttachedGod = NEFAS; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = Base|IS_BEVERAGE; + InteractionFlags = CAN_EXPLODE|EFFECT_IS_GOOD; + DisablesPanicWhenConsumed = true; + } + + Config TROLL_BLOOD; + { + StrengthValue = 1; + NutritionValue = 1000; + Color = rgb16(160, 0, 0); + NameStem = "troll blood"; + Alpha = 175; + Flexibility = 40; + Effect = EFFECT_HEAL; + ConsumeEndMessage = CEM_HEALING_LIQUID; + HitMessage = HM_HEALING_LIQUID; + EffectStrength = 50; + PriceModifier = 750; + AttachedGod = CRUENTUS; + CommonFlags = Base|IS_VALUABLE; + CategoryFlags = IS_BLOOD; + BodyFlags = Base|IS_WARM; + InteractionFlags = Base|EFFECT_IS_GOOD; + } + + Config DARK_FROG_BLOOD; + { + StrengthValue = 1; + NutritionValue = 40; + Color = rgb16(50, 0, 0); + NameStem = "dark frog blood"; + Alpha = 175; + Flexibility = 40; + AttachedGod = CRUENTUS; + RustModifier = 200; + Acidicity = 100; + ConsumeWisdomLimit = 0; + CategoryFlags = IS_BLOOD; + InteractionFlags = Base|AFFECT_INSIDE; + StepInWisdomLimit = 0; + } + + Config SPIDER_BLOOD; + { + StrengthValue = 1; + NutritionValue = 40; + Color = rgb16(50, 0, 0); + NameStem = "spider blood"; + Alpha = 175; + Flexibility = 40; + AttachedGod = CRUENTUS; + CategoryFlags = IS_BLOOD; + Effect = EFFECT_POISON; + EffectStrength = 1000; + } + + Config VOMIT; + { + StrengthValue = 1; + NutritionValue = 200; + Color = rgb16(10, 230, 10); + NameStem = "vomit"; + Alpha = 175; + Flexibility = 35; + AttachedGod = SCABIES; + RustModifier = 200; + Acidicity = 10; + InteractionFlags = Base|AFFECT_INSIDE; + StepInWisdomLimit = 5; + } + + Config ACIDOUS_BLOOD; + { + StrengthValue = 150; + NutritionValue = 50; + Color = rgb16(100, 0, 0); + NameStem = "acidous blood"; + Alpha = 175; + Flexibility = 40; + AttachedGod = CRUENTUS; + Acidicity = 500: + RustModifier = 500; + ConsumeWisdomLimit = 4; + CategoryFlags = IS_BLOOD; + InteractionFlags = Base|AFFECT_INSIDE; + StepInWisdomLimit = 10; + NameFlags = Base|USE_AN; + } + + Config SULPHURIC_ACID; + { + StrengthValue = 1; + NutritionValue = 5; + Color = rgb16(160, 160, 0); + NameStem = "sulphuric acid"; + Alpha = 150; + Flexibility = 45; + AttachedGod = SCABIES; + RustModifier = 400; + Acidicity = 400; + ConsumeWisdomLimit = 4; + PriceModifier = 100; + InteractionFlags = Base|AFFECT_INSIDE; + StepInWisdomLimit = 8; + } + + Config VINEGAR; + { + StrengthValue = 1; + NutritionValue = 1000; + Color = rgb16(100, 100, 0); + NameStem = "vinegar"; + Alpha = 150; + Flexibility = 45; + AttachedGod = SCABIES; + RustModifier = 50; + Acidicity = 50; + ConsumeWisdomLimit = 6; + PriceModifier = 100; + InteractionFlags = Base|AFFECT_INSIDE; + StepInWisdomLimit = 10; + } + + Config DOG_DROOL; + { + StrengthValue = 2; + NutritionValue = 50; + Color = rgb16(144, 144, 240); + NameStem = "dog drool"; + Alpha = 175; + AttachedGod = SILVA; + RustModifier = 5; + } + + Config PEA_SOUP; + { + StrengthValue = 5; + NutritionValue = 500; + Color = rgb16(0, 200, 0); + NameStem = "pea soup"; + Effect = EFFECT_PEA_SOUP; + ConsumeEndMessage = CEM_PEA_SOUP; + AttachedGod = SEGES; + PriceModifier = 100; + } + + Config OMMEL_SWEAT; + { + StrengthValue = 1; + Color = rgb16(144, 144, 240); + NutritionValue = 250; + PriceModifier = 2500; + NameStem = "ommel sweat"; + Effect = EFFECT_OMMEL_SWEAT; + ConsumeEndMessage = CEM_OMMEL; + HitMessage = HM_OMMEL; + AttachedGod = SEGES; + CommonFlags = Base|IS_VALUABLE&~CAN_BE_MIRRORED; + NameFlags = Base|USE_AN; + InteractionFlags = Base|EFFECT_IS_GOOD; + } + + Config OMMEL_TEARS; + { + StrengthValue = 1; + Color = rgb16(144, 144, 240); + NutritionValue = 250; + PriceModifier = 2500; + NameStem = "ommel tears"; + Effect = EFFECT_OMMEL_TEARS; + ConsumeEndMessage = CEM_OMMEL; + HitMessage = HM_OMMEL; + AttachedGod = MELLIS; + CommonFlags = Base|IS_VALUABLE&~CAN_BE_MIRRORED; + NameFlags = Base|USE_AN; + InteractionFlags = Base|EFFECT_IS_GOOD; + } + + Config OMMEL_SNOT; + { + StrengthValue = 1; + Color = rgb16(145, 145, 16); + NutritionValue = 750; + PriceModifier = 7500; + NameStem = "ommel snot"; + Effect = EFFECT_OMMEL_SNOT; + ConsumeEndMessage = CEM_OMMEL; + HitMessage = HM_OMMEL; + AttachedGod = SEGES; + Density = 800; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + CommonFlags = Base|IS_VALUABLE&~CAN_BE_MIRRORED; + NameFlags = Base|USE_AN; + InteractionFlags = Base|EFFECT_IS_GOOD; + Stickiness = 100; + } + + Config SWEAT; + { + StrengthValue = 2; + NutritionValue = 50; + Color = rgb16(144, 144, 240); + NameStem = "sweat"; + Alpha = 175; + AttachedGod = SILVA; + RustModifier = 5; + } + + Config LIQUID_HORROR; + { + StrengthValue = 1; + Density = 2500; + NutritionValue = 50; + Color = rgb16(40, 80, 40); + ConsumeWisdomLimit = 2; + NameStem = "liquified fear"; + Effect = EFFECT_PANIC; + ConsumeEndMessage = CEM_LIQUID_HORROR; + /*HitMessage = NONE;*/ + AttachedGod = SOLICITU; + RustModifier = 0; + Acidicity = 0; + PriceModifier = 50; + CommonFlags = Base|IS_VALUABLE&~CAN_BE_MIRRORED; + StepInWisdomLimit = 20; + } + + Config SICK_BLOOD; + { + StrengthValue = 1; + NutritionValue = 40; + Color = rgb16(50, 0, 0); + NameStem = "sick blood"; + Alpha = 175; + Flexibility = 40; + AttachedGod = SCABIES; + CategoryFlags = IS_BLOOD; + } + + Config MUSTARD_GAS_LIQUID; + { + StrengthValue = 1; + NutritionValue = 10; + Color = rgb16(144, 144, 240); + NameStem = "mustard gas in liquid form"; + Alpha = 150; + AttachedGod = CRUENTUS; + Effect = EFFECT_MUSTARD_GAS_LIQUID; + } + + Config OMMEL_BLOOD; + { + StrengthValue = 1; + NutritionValue = 250; + Color = rgb16(180, 20, 20); + NameStem = "ommel blood"; + Alpha = 175; + Flexibility = 40; + AttachedGod = CRUENTUS; + Acidicity = 5000: + RustModifier = 5000; + ConsumeWisdomLimit = 1; + CategoryFlags = IS_BLOOD; + InteractionFlags = Base|AFFECT_INSIDE; + StepInWisdomLimit = 1; + NameFlags = Base|USE_AN; + PriceModifier = 5000; + CommonFlags = Base|IS_VALUABLE&~CAN_BE_MIRRORED; + ConsumeEndMessage = CEM_OMMEL; + } + + Config CURDLED_OMMEL_BLOOD; + { + StrengthValue = 1; + Color = rgb16(180, 60, 60); + NutritionValue = 250; + PriceModifier = 10000; + NameStem = "curdled ommel blood"; + Effect = EFFECT_OMMEL_SWEAT; + ConsumeEndMessage = CEM_OMMEL; + HitMessage = HM_OMMEL; + AttachedGod = SCABIES; + CommonFlags = Base|IS_VALUABLE&~CAN_BE_MIRRORED; + InteractionFlags = Base|EFFECT_IS_GOOD; + } +} + +flesh +{ + StrengthValue = 5; + ConsumeType = CT_MEAT; + Density = 1200; + NutritionValue = 20; + Color = rgb16(160, 32, 16); + NameStem = "flesh"; + Flexibility = 5; + SpoilModifier = 10000; + PriceModifier = 60; + BodyFlags = Base|IS_ALIVE|IS_WARM|CAN_HAVE_PARASITE|CAN_REGENERATE&~USE_MATERIAL_ATTRIBUTES; + InteractionFlags = Base|IS_AFFECTED_BY_MUSTARD_GAS; + + Config GOBLINOID_FLESH; + { + Color = rgb16(0, 128, 0); + NameStem = "green flesh"; + NutritionValue = 15; + PriceModifier = 40; + AttachedGod = MORTIFER; + } + + Config PORK; + { + NutritionValue = 2500; + NameStem = "pork"; + SpoilModifier = 20000; + PriceModifier = 8000; + AttachedGod = SEGES; + BodyFlags = Base&~CAN_HAVE_PARASITE; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config BEEF; + { + NutritionValue = 2500; + NameStem = "beef"; + SpoilModifier = 20000; + PriceModifier = 8000; + AttachedGod = SEGES; + BodyFlags = Base&~CAN_HAVE_PARASITE; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config FROG_FLESH; + { + ConsumeWisdomLimit = 0; + Effect = EFFECT_DARKNESS; + ConsumeEndMessage = CEM_FROG_FLESH; + HitMessage = HM_FROG_FLESH; + NameStem = "frog flesh"; + AttachedGod = MORTIFER; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config ELPURI_FLESH; + { + ConsumeWisdomLimit = 0; + Effect = EFFECT_DARKNESS; + ConsumeEndMessage = CEM_FROG_FLESH; + HitMessage = HM_FROG_FLESH; + StrengthValue = 10; + Density = 2400; + NameStem = "Elpuri's flesh"; + Color = rgb16(60, 60, 60); + AttachedGod = MORTIFER; + NameFlags = Base|USE_AN; + BodyFlags = Base&~IS_WARM; + InteractionFlags = Base&~CAN_DISSOLVE; + } + + Config HUMAN_FLESH; + { + NameStem = "human flesh"; + AttachedGod = MORTIFER; + CategoryFlags = Base|IS_GOLEM_MATERIAL; + } + + Config DOLPHIN_FLESH; + { + NameStem = "dolphin flesh"; + AttachedGod = MORTIFER; + } + + Config BEAR_FLESH; + { + NameStem = "raw bear flesh"; + AttachedGod = MORTIFER; + } + + Config WOLF_FLESH; + { + NameStem = "wolf flesh"; + AttachedGod = MORTIFER; + } + + Config DOG_FLESH; + { + NameStem = "dog flesh"; + AttachedGod = MORTIFER; + } + + Config FOX_FLESH; + { + NameStem = "fox flesh"; + AttachedGod = MORTIFER; + } + + Config ENNER_BEAST_FLESH; + { + StrengthValue = 10; + Density = 1800; + ConsumeWisdomLimit = 0; + NameStem = "enner beast flesh"; + AttachedGod = DULCIS; + NameFlags = Base|USE_AN; + } + + Config SPIDER_FLESH; + { + NameStem = "spider flesh"; + AttachedGod = MORTIFER; + Effect = EFFECT_POISON; + EffectStrength = 100; + ConsumeWisdomLimit = 3; + } + + Config JACKAL_FLESH; + { + NameStem = "jackal flesh"; + AttachedGod = MORTIFER; + } + + Config MUTANT_ASS_FLESH; + { + NameStem = "mutant ass flesh"; + Effect = EFFECT_POLYMORPH; + EffectStrength = 6; + Emitation = rgb24(140, 100, 100); + ConsumeWisdomLimit = 8; + AttachedGod = SCABIES; + } + + Config BAT_FLESH; + { + NameStem = "bat flesh"; + AttachedGod = MORTIFER; + } + + Config WERE_WOLF_FLESH; + { + NameStem = "werewolf flesh"; + Effect = EFFECT_LYCANTHROPY; + EffectStrength = 10; + ConsumeWisdomLimit = 10; + AttachedGod = MORTIFER; + } + + Config KOBOLD_FLESH; + { + NutritionValue = 10; + ConsumeWisdomLimit = 6; + NameStem = "kobold flesh"; + Effect = EFFECT_KOBOLD_FLESH; + ConsumeEndMessage = CEM_KOBOLD_FLESH; + HitMessage = HM_KOBOLD_FLESH; + PriceModifier = 0; + AttachedGod = MORTIFER; + } + + Config GIBBERLING_FLESH; + { + NameStem = "gibberling flesh"; + AttachedGod = MORTIFER; + } + + Config KABOUTER_FLESH; + { + NameStem = "kabouter flesh"; + AttachedGod = MORTIFER; + } + + Config ULDRA_FLESH; + { + NameStem = "uldra flesh"; + AttachedGod = MORTIFER; + } + + Config VAMPIRE_FLESH; + { + NameStem = "vampire flesh"; + Effect = EFFECT_VAMPIRISM; + EffectStrength = 10; + ConsumeWisdomLimit = 10; + AttachedGod = MORTIFER; + } + + Config CAT_FLESH; + { + NameStem = "cat flesh"; + AttachedGod = MORTIFER; + } + + Config RAT_FLESH; + { + NameStem = "rat flesh"; + AttachedGod = MORTIFER; + } + + Config MOUSE_FLESH; + { + NameStem = "mouse flesh"; + AttachedGod = MORTIFER; + } + + Config ANGEL_FLESH; + { + NameStem = "angel flesh"; + AttachedGod = MORTIFER; + } + + Config DWARF_FLESH; + { + NameStem = "dwarf flesh"; + AttachedGod = MORTIFER; + } + + Config DAEMON_FLESH; + { + Color = rgb16(180, 0, 0); + NameStem = "daemon flesh"; + Effect = EFFECT_POISON; + EffectStrength = 10; + ConsumeWisdomLimit = 8; + AttachedGod = MORTIFER; + InteractionFlags = Base&~CAN_DISSOLVE; + SpoilModifier = 100000; + StrengthValue = 50; + Density = 600; + Flexibility = 30; + } + + Config MAMMOTH_FLESH; + { + NameStem = "mammoth flesh"; + AttachedGod = MORTIFER; + } + + Config BLACK_UNICORN_FLESH; + { + Color = rgb16(48, 48, 48); + NameStem = "black unicorn flesh"; + PriceModifier = 100; + AttachedGod = DULCIS; + Effect = EFFECT_BLACK_UNICORN_FLESH; + ConsumeEndMessage = CEM_BLACK_UNICORN_FLESH; + ConsumeWisdomLimit = 10; + CommonFlags = Base|IS_VALUABLE; + BodyFlags = Base&~IS_WARM; + } + + Config GRAY_UNICORN_FLESH; + { + Color = rgb16(112, 112, 112); + NameStem = "gray unicorn flesh"; + PriceModifier = 100; + AttachedGod = LORICATUS; + Effect = EFFECT_GRAY_UNICORN_FLESH; + ConsumeEndMessage = CEM_GRAY_UNICORN_FLESH; + CommonFlags = Base|IS_VALUABLE; + } + + Config WHITE_UNICORN_FLESH; + { + Color = rgb16(160, 160, 160); + NameStem = "white unicorn flesh"; + PriceModifier = 100; + AttachedGod = MORTIFER; + Effect = EFFECT_WHITE_UNICORN_FLESH; + ConsumeEndMessage = CEM_WHITE_UNICORN_FLESH; + CommonFlags = Base|IS_VALUABLE; + } + + Config LION_FLESH; + { + NameStem = "lion flesh"; + AttachedGod = MORTIFER; + } + + Config BUFFALO_FLESH; + { + NameStem = "buffalo flesh"; + AttachedGod = MORTIFER; + } + + Config SNAKE_FLESH; + { + NameStem = "snake flesh"; + Effect = EFFECT_POISON; + EffectStrength = 15; + ConsumeWisdomLimit = 3; + AttachedGod = MORTIFER; + } + + Config ORC_FLESH; + { + NameStem = "orc flesh"; + AttachedGod = MORTIFER; + NutritionValue = 15; + ConsumeWisdomLimit = 15; + } + + Config OSTRICH_FLESH; + { + NameStem = "ostrich flesh"; + AttachedGod = MORTIFER; + } + + Config CHAMELEON_FLESH; + { + NameStem = "chameleon flesh"; + Effect = EFFECT_POLYMORPH; + PriceModifier = 50; + ConsumeWisdomLimit = 7; + AttachedGod = SCABIES; + } + + Config FLOATING_EYE_FLESH; + { + NameStem = "floating eye flesh"; + Density = 1000; + Effect = EFFECT_ESP; + AttachedGod = LEGIFER; + PriceModifier = 120; + CommonFlags = Base|IS_VALUABLE; + InteractionFlags = Base|EFFECT_IS_GOOD; + } + + Config MUSHROOM_FLESH; + { + NameStem = "mushroom"; + Density = 600; + Effect = EFFECT_MUSHROOM; + ConsumeWisdomLimit = 10; + AttachedGod = NEFAS; + EffectStrength = 20; + PriceModifier = 100; + BodyFlags = Base&~IS_WARM; + } + + Config MOOSE_FLESH; + { + NameStem = "moose flesh"; + AttachedGod = MORTIFER; + Effect = EFFECT_DETECTING; + EffectStrength = 1; + ConsumeWisdomLimit = 1; + } + + Config MAGPIE_FLESH; + { + NameStem = "magpie flesh"; + AttachedGod = MORTIFER; + } + + Config EAGLE_FLESH; + { + NameStem = "eagle flesh"; + AttachedGod = MORTIFER; + } + + Config SKUNK_FLESH; + { + NameStem = "skunk flesh"; + AttachedGod = MORTIFER; + Effect = EFFECT_POISON; + EffectStrength = 5; + ConsumeWisdomLimit = 4; + } + + Config HEDGEHOG_FLESH; + { + NameStem = "hedgehog flesh"; + AttachedGod = MORTIFER; + } + + Config MUTANT_BUNNY_FLESH; + { + NameStem = "mutant bunny flesh"; + Effect = EFFECT_POLYMORPH; + EffectStrength = 6; + Emitation = rgb24(140, 100, 100); + ConsumeWisdomLimit = 8; + AttachedGod = SCABIES; + } + +Config MUTANT_HEDGEHOG_FLESH; + { + NameStem = "mutant hedgehog flesh"; + Effect = EFFECT_POLYMORPH; + EffectStrength = 6; + Emitation = rgb24(100, 100, 140); + ConsumeWisdomLimit = 8; + AttachedGod = SCABIES; + } + + Config HATTIFATTENER_FLESH; + { + NameStem = "hattifattener flesh"; + AttachedGod = MORTIFER; + } + + Config BLINK_DOG_FLESH; + { + NameStem = "blink dog flesh"; + AttachedGod = MORTIFER; + Effect = EFFECT_TELEPORT_CONTROL; + PriceModifier = 120; + CommonFlags = Base|IS_VALUABLE; + } + + Config MAGIC_MUSHROOM_FLESH; + { + NameStem = "magical mushroom"; + Density = 600; + Effect = EFFECT_MAGIC_MUSHROOM; + ConsumeWisdomLimit = 10; + AttachedGod = NEFAS; + EffectStrength = 20; + PriceModifier = 150; + BodyFlags = Base&~IS_WARM; + } + + Config SICK_SPIDER_FLESH; + { + NameStem = "sick spider flesh"; + AttachedGod = SCABIES; + } + + Config MIND_WORM_FLESH; + { + NameStem = "sick spider flesh"; + AttachedGod = SCABIES; + } + + Config OKAPI_FLESH; + { + NameStem = "raw okapi flesh"; + AttachedGod = MORTIFER; + } + + Config THUNDER_BIRD_FLESH; + { + NameStem = "thunderbird flesh"; + AttachedGod = MORTIFER; + } +} + +powder +{ + CommonFlags = Base&~CAN_BE_WISHED; + + Config GUN_POWDER; + { + StrengthValue = 2; + ConsumeType = CT_MINERAL; + Density = 250; + Color = rgb16(30, 30, 30); + PriceModifier = 1000; + NameStem = "gunpowder"; + ExplosivePower = 15000; + Flexibility = 35; + AttachedGod = CRUENTUS; + CommonFlags = Base|IS_VALUABLE; + InteractionFlags = CAN_EXPLODE; + } + + Config SNOW; + { + StrengthValue = 2; + ConsumeType = CT_MINERAL;/* add something appropriate for this */ + Density = 500; + Color = rgb16(200, 200, 200); + NameStem = "snow"; + AdjectiveStem = "snowy"; + Flexibility = 40; + AttachedGod = TERRA; + RainColor = rgb16(255, 255, 255); + } + + Config SAND; + { + StrengthValue = 2; + ConsumeType = CT_MINERAL;/* add something appropriate for this */ + Density = 1600; + Color = rgb16(200, 200, 125); + NameStem = "sand"; + AdjectiveStem = "sandy"; + Flexibility = 40; + AttachedGod = TERRA; + } +} + +ironalloy /* metals that rust */ +{ + ConsumeType = CT_METAL; + AttachedGod = LORICATUS; + CategoryFlags = Base|IS_METAL|IS_GOLEM_MATERIAL; + + Config IRON; + { + StrengthValue = 100; + Density = 8000; + Color = rgb16(96, 96, 96); + PriceModifier = 40; + NameStem = "iron"; + RustModifier = 100; + HardenedMaterial = STEEL; + SoftenedMaterial = BRONZE; + IntelligenceRequirement = 5; + NameFlags = Base|USE_AN; + } + + Config STEEL; + { + StrengthValue = 125; + ConsumeType = CT_METAL; + Density = 7500; + Color = rgb16(128, 128, 128); + PriceModifier = 40; + NameStem = "steel"; + AttachedGod = LORICATUS; + RustModifier = 75; + HardenedMaterial = METEORIC_STEEL; + SoftenedMaterial = IRON; + IntelligenceRequirement = 10; + CommonFlags = Base|IS_VALUABLE; + } + + Config METEORIC_STEEL; + { + StrengthValue = 150; + ConsumeType = CT_METAL; + Density = 7000; + Color = rgb16(160, 160, 160); + PriceModifier = 250; + NameStem = "meteoric steel"; + AttachedGod = LORICATUS; + RustModifier = 50; + HardenedMaterial = DAMASCUS_STEEL; + SoftenedMaterial = STEEL; + IntelligenceRequirement = 15; + CommonFlags = Base|IS_VALUABLE; + } + + Config DAMASCUS_STEEL; + { + StrengthValue = 175; + ConsumeType = CT_METAL; + Density = 10000; + Color = rgb16(80, 80, 80); + PriceModifier = 500; + NameStem = "damascus steel"; + AttachedGod = LORICATUS; + RustModifier = 25; + HardenedMaterial = ADAMANT; + SoftenedMaterial = METEORIC_STEEL; + IntelligenceRequirement = 20; + CommonFlags = Base|IS_VALUABLE; + } + + Config ADAMANT; + { + StrengthValue = 350; + ConsumeType = CT_METAL; + Density = 5000; + Color = rgb16(220, 220, 240); + PriceModifier = 10000; + NameStem = "adamant"; + AdjectiveStem = "adamantine"; + AttachedGod = LORICATUS; + RustModifier = 15; + IntelligenceRequirement = 40; + HardenedMaterial = VALPURIUM; + CommonFlags = Base|IS_VALUABLE; + NameFlags = Base|USE_AN; + CategoryFlags = Base|IS_SPARKLING; + } + +} diff --git a/Script/olterra.dat b/Script/olterra.dat new file mode 100644 index 0000000..c5bc1ae --- /dev/null +++ b/Script/olterra.dat @@ -0,0 +1,763 @@ +/* + * + * Iter Vehemens ad Necem (IVAN) + * Copyright (C) Timo Kiviluoto + * Released under the GNU General + * Public License + * + * See LICENSING which should be included + * along with this file for more details + * + */ + +/* + * NOTICE!!! + * + * This file contains SPOILERS, which might ruin your IVAN experience + * totally. Also, editing anything can DESTROY GAME BALANCE or CAUSE + * OBSCURE BUGS if you don't know what you're doing. So from here on, + * proceed at your own risk! + */ + +/* Numerical olterrain data loaded during game startup */ + +/* Default values: */ + +olterrain +{ + /* Obligatory: BitmapPos */ + UsesLongArticle = false; + Adjective = ""; + UsesLongAdjectiveArticle = false; + /* Obligatory: NameSingular */ + /* NameSingular + "s" by default: NamePlural */ + PostFix = ""; + ArticleMode = 0; + /* Obligatory: MainMaterialConfig */ + /* Obligatory: SecondaryMaterialConfig */ + /* Obligatory if multiple material configurations defined: MaterialConfigChances */ + IsAbstract = true; /* This is false by default and does not inherit! */ + OKVisualEffects = NONE; + MaterialColorB = 0; + MaterialColorC = 0; + MaterialColorD = 0; + SitMessage = "You sit for some time. Nothing happens."; + /* Obligatory: DigMessage */ + CanBeDestroyed = false; + RestModifier = 1; + RestMessage = ""; + ShowMaterial = true; + IsUpLink = false; + CreateDivineConfigurations = false; + StorageVolume = 0; + HPModifier = 100; + IsSafeToCreateDoor = false; + AttachedGod = NONE; + CreateLockConfigurations = false; /* Can't be overridden by Configs */ + Walkability = ANY_MOVE; + IsAlwaysTransparent = true; + UseBorderTiles = false; + BorderTilePriority = 0; + HasSecondaryMaterial = false; + CreateWindowConfigurations = false; + ShowThingsUnder = true; + IsWall = false; +} + +wall +{ + IsAbstract = true; + CanBeDestroyed = true; + IsSafeToCreateDoor = true; + Walkability = ETHEREAL; + IsAlwaysTransparent = false; + BorderTilePriority = 100; + CreateWindowConfigurations = true; + MaterialColorC = rgb16(140, 96, 48); + MaterialColorD = rgb16(180, 180, 255); + ShowThingsUnder = false; + LeftOverItems == NaturalMaterialForm { MainMaterial = 0 { Volume = 500:1500; } Times = 1:4; } + IsWall = true; + + Config BRICK_FINE; + { + DigMessage = "The wall is pretty hard, but you still manage to go through it."; + MainMaterialConfig == GRANITE; + NameSingular = "wall"; + BitmapPos = 64, 272; + HPModifier = 150; + WindowBitmapPos = 96, 256; + } + + Config BRICK_PROPAGANDA; + { + DigMessage = "You break the wall down despite the uncomfortable feeling of being constantly watched."; + MainMaterialConfig == GRANITE; + NameSingular = "wall"; + PostFix = "and a poster with a picture of high priest Petrus, the ruler of the Attnamese Empire"; + BitmapPos = 80, 272; + HPModifier = 150; + } + + Config BRICK_OLD; + { + DigMessage = "The piece of wall is very old and shabby. You dig it with ease."; + MainMaterialConfig == GRANITE; + UsesLongAdjectiveArticle = true; + Adjective = "old"; + NameSingular = "wall"; + BitmapPos = 16, 256; + HPModifier = 75; + } + + Config BRICK_PRIMITIVE; + { + DigMessage = "The piece of wall is very old and shabby. You dig it with ease."; + MainMaterialConfig == BALSA_WOOD; + Adjective = "primitive"; + NameSingular = "wall"; + BitmapPos = 48, 272; + HPModifier = 75; + WindowBitmapPos = 80, 256; + } + + Config BRICK_PRIMITIVE_PROPAGANDA; + { + DigMessage = "You break the wall down despite the uncomfortable feeling of being constantly watched."; + MainMaterialConfig == BALSA_WOOD; + Adjective = "primitive"; + NameSingular = "wall"; + PostFix = "and a poster with a picture of high priest Petrus, the ruler of the Attnamese Empire"; + BitmapPos = 96, 272; + HPModifier = 75; + } + + Config STONE_WALL; + { + OKVisualEffects = MIRROR|FLIP|ROTATE; + DigMessage = "The ground is hard to dig."; + MainMaterialConfig == GRANITE; + MaterialColorB = rgb16(56, 56, 56); + NameSingular = "wall"; + BitmapPos = 0, 32; + HPModifier = 100; + } + + Config ICE_WALL; + { + OKVisualEffects = MIRROR|FLIP|ROTATE; + DigMessage = "The ice is pretty easy to dig."; + MainMaterialConfig == ICE; + NameSingular = "wall"; + BitmapPos = 0, 32; + HPModifier = 40; + } + Config BROKEN_WALL; + { + OKVisualEffects = MIRROR|FLIP; + DigMessage = "The ground is easy to dig."; + MainMaterialConfig == GRANITE; + MaterialColorB = rgb16(56, 56, 56); + Adjective = "broken"; + NameSingular = "wall"; + BitmapPos = 48, 288; + HPModifier = 25; + } +} + +decoration +{ + IsAbstract = true; + LeftOverItems == NaturalMaterialForm { MainMaterial = 0 { Volume = 500:1500; } Times = 1:3; } + + Config PINE; + { + OKVisualEffects = MIRROR; + CanBeDestroyed = true; + DigMessage = "You chop the tree down."; + MainMaterialConfig == PINE_WOOD; + ShowMaterial = false; + MaterialColorB = rgb16(32, 100, 32); + NameSingular = "pine"; + BitmapPos = 16, 320; + HPModifier = 25; + } + + Config FIR; + { + OKVisualEffects = MIRROR; + CanBeDestroyed = true; + DigMessage = "You chop the tree down."; + MainMaterialConfig == FIR_WOOD; + ShowMaterial = false; + MaterialColorB = rgb16(32, 100, 32); + NameSingular = "fir"; + BitmapPos = 16, 352; + HPModifier = 25; + IsAlwaysTransparent = false; + } + + Config PALM; + { + OKVisualEffects = MIRROR; + CanBeDestroyed = true; + DigMessage = "You chop the tree down."; + MainMaterialConfig == BALSA_WOOD; + ShowMaterial = false; + MaterialColorB = rgb16(32, 80, 32); + NameSingular = "palm"; + BitmapPos = 16, 368; + HPModifier = 25;/* + IsAlwaysTransparent = false;*/ + } + + Config HOLY_TREE; + { + CanBeDestroyed = true; + DigMessage = "You ruthlessly chop the beautiful tree down."; + MainMaterialConfig == GOLD; + ShowMaterial = false; + MaterialColorB = rgb16(224, 224, 0); + Adjective = "holy"; + NameSingular = "tree"; + BitmapPos = 32, 336; + ShowMaterial = true; + HPModifier = 25; + AttachedGod = SILVA; + } + + Config CARPET; + { + DigMessage = "You can't force yourself to ruin this wonderful carpet."; + MainMaterialConfig == FABRIC; + NameSingular = "carpet"; + BitmapPos = 0, 272; + } + + Config COUCH; + { + CanBeDestroyed = true; + DigMessage = "You destroy the couch and find some valuables dropped by its owners."; + MainMaterialConfig == FABRIC; + NameSingular = "couch"; + BitmapPos = 0, 400; + RestModifier = 2; + MaterialColorB = rgb16(200, 200, 0); + SitMessage = "The couch is extremely soft and confortable. You relax well."; + RestMessage = "You rest well on the soft sofa."; + HPModifier = 25; + LeftOverItems == stone { Times = 1:2; } + } + + Config DOUBLE_BED; + { + CanBeDestroyed = true; + DigMessage = "You smash the bed into pieces."; + MainMaterialConfig == FABRIC; + Adjective = "luxurious"; + NameSingular = "double bed"; + BitmapPos = 48, 304; + MaterialColorB = rgb16(200, 200, 0); + ShowMaterial = false; + RestModifier = 5; + SitMessage = "The beautiful bed is very soft. You get a feeling it's not meant for your kind of people."; + RestMessage = "You lay yourself on the confortable bed."; + HPModifier = 25; + LeftOverItems == 0; + } + + Config POOL_BORDER; + { + DigMessage = "The water splashes a bit."; /* Only correct if over a water pool */ + MainMaterialConfig == MARBLE; + NameSingular = ""; + BitmapPos = 32, 320; + } + + Config POOL_CORNER; + { + DigMessage = "The water splashes a bit."; /* Only correct if over a water pool */ + MainMaterialConfig == MARBLE; + NameSingular = ""; + BitmapPos = 48, 320; + } + + Config SNOW_PINE; + { + OKVisualEffects = MIRROR; + CanBeDestroyed = true; + DigMessage = "You chop the tree down."; + MainMaterialConfig == PINE_WOOD; + ShowMaterial = false; + MaterialColorB = rgb16(220, 220, 220); + NameSingular = "pine"; + PostFix = "covered with snow"; + BitmapPos = 16, 320; + HPModifier = 25; + } + + Config SNOW_FIR; + { + OKVisualEffects = MIRROR; + CanBeDestroyed = true; + DigMessage = "You chop the tree down."; + MainMaterialConfig == FIR_WOOD; + ShowMaterial = false; + MaterialColorB = rgb16(220, 220, 220); + NameSingular = "fir"; + PostFix = "covered with snow"; + BitmapPos = 16, 352; + HPModifier = 25; + IsAlwaysTransparent = false; + } + + Config ANVIL; + { + OKVisualEffects = MIRROR; + CanBeDestroyed = true; + DigMessage = "You break the anvil."; + MainMaterialConfig == IRON; + NameSingular = "anvil"; + BitmapPos = 16, 432; + HPModifier = 25; + LeftOverItems == NaturalMaterialForm { MainMaterial = 0 { Volume = 250:750; } Times = 1:3; } + } + + Config SHARD; + { + OKVisualEffects = MIRROR; + CanBeDestroyed = true; + DigMessage = "You shatter the crystal into pieces."; + NameSingular = "shard"; + BitmapPos = 16, 416; + HPModifier = 5; + MainMaterialConfig = { 3, BLUE_CRYSTAL, PURPLE_CRYSTAL, GREEN_CRYSTAL; } + MaterialConfigChances = { 3, 100, 100, 100; } + LeftOverItems == NaturalMaterialForm { MainMaterial = 0 { Volume = 100:200; } Times = 1:3; } + } + + Config CACTUS; + { + OKVisualEffects = MIRROR; + CanBeDestroyed = true; + DigMessage = "You chop the cactus down."; + MainMaterialConfig == PLANT_FIBER; + ShowMaterial = false; + MaterialColorB = rgb16(32, 100, 32); + NameSingular = "cactus"; + NamePlural = "cactuses"; + BitmapPos = 16, 352; + HPModifier = 20; + LeftOverItems == 0; + } + + Config OAK; + { + CanBeDestroyed = true; + DigMessage = "You ruthlessly chop the tree down."; + MainMaterialConfig == OAK_WOOD; + ShowMaterial = false; + MaterialColorB = rgb16(0, 150, 0); + NameSingular = "oak tree"; + BitmapPos = 32, 336; + ShowMaterial = true; + HPModifier = 25; + IsAlwaysTransparent = false; + } + + Config BIRCH; + { + CanBeDestroyed = true; + DigMessage = "You chop the tree down."; + MainMaterialConfig == BIRCH_WOOD; + ShowMaterial = false; + MaterialColorB = rgb16(125, 160, 80); + MaterialColorC = rgb16(220, 220, 220); + NameSingular = "birch tree"; + BitmapPos = 80, 368; + HPModifier = 25; + } + + Config TEAK; + { + CanBeDestroyed = true; + DigMessage = "You ruthlessly chop the tree down."; + MainMaterialConfig == TEAK_WOOD; + ShowMaterial = false; + MaterialColorB = rgb16(0, 220, 0); + NameSingular = "teak tree"; + BitmapPos = 32, 336; + HPModifier = 25; + } + + Config DWARF_BIRCH; + { + CanBeDestroyed = true; + DigMessage = "You chop the dwarf birch down."; + MainMaterialConfig == BIRCH_WOOD; + ShowMaterial = false; + MaterialColorB = rgb16(125, 160, 80); + MaterialColorC = rgb16(220, 220, 220); + NameSingular = "dwarf birch"; + BitmapPos = 32, 368; + HPModifier = 10; + } + +} + +door /* olterrain-> */ +{ + DigMessage = "You break the door."; + CanBeDestroyed = true; + MainMaterialConfig == FIR_WOOD; + NameSingular = "door"; + BitmapPos = 0, 176; + OpenBitmapPos = 0, 48; + /* PostFix depends on LockType */ + HPModifier = 20; + CreateLockConfigurations = true; + LeftOverItems == NaturalMaterialForm { MainMaterial = 0 { Volume = 250:750; } Times = 1:3; } + IsAlwaysTransparent = false; + Config BARDOOR; + { + BitmapPos = 16, 96; + IsAlwaysTransparent = true; + } + Config SECRET_DOOR; + { + BitmapPos = 16, 144; + NameSingular = "door"; + Adjective = "very secret"; + } +} + +stairs /* olterrain-> */ +{ + IsAbstract = true; + DigMessage = "The stairs are too hard to dig."; + MainMaterialConfig == GRANITE; + MaterialColorB = rgb16(160, 64, 0); + NameSingular = "stairway"; + + Config STAIRS_UP; + { + PostFix = "upwards"; + BitmapPos = 0, 192; + IsUpLink = true; + } + + Config STAIRS_DOWN; + { + PostFix = "downwards"; + BitmapPos = 0, 208; + } + + Config SUMO_ARENA_ENTRY; + { + PostFix = "downwards"; + BitmapPos = 0, 208; + } + + Config SUMO_ARENA_EXIT; + { + PostFix = "upwards"; + BitmapPos = 0, 192; + IsUpLink = true; + } + + Config KHARAZ_ARAD_ENTRY; + { + PostFix = "downwards"; + BitmapPos = 0, 208; + } + + Config KHARAZ_ARAD_EXIT; + { + PostFix = "upwards"; + BitmapPos = 0, 192; + IsUpLink = true; + } + + Config WAYPOINT_DEEPER; /*stairs_down*/ + { + PostFix = "leading onward"; + BitmapPos = 48, 112; + DigMessage = "The milestone is too hard to dig."; + MaterialColorB = rgb16(160, 120, 0); + NameSingular = "milestone"; + } + + Config WAYPOINT_SHALLOWER; /*stairs_up*/ + { + PostFix = "leading back"; + BitmapPos = 48, 96; + IsUpLink = true; + DigMessage = "The milestone is too hard to dig."; + MaterialColorB = rgb16(160, 120, 0); + NameSingular = "milestone"; + } + +} + +portal /* stairs -> */ +{ + DigMessage = "The portal is magically protected from your feeble attack."; + MainMaterialConfig == GOLD; + MaterialColorB = rgb16(200, 0, 0); + NameSingular = "magic portal"; + AttachedGod = MORTIFER; + /* BitmapPos overridden */ + + /* Config specialities hard-coded */ + + Config OREE_LAIR_ENTRY; { } + Config OREE_LAIR_EXIT; { } + Config DARK_LEVEL; { } +} + +altar /* olterrain-> */ +{ + DigMessage = "An invisible wall stops your feeble attempt."; + MainMaterialConfig == GRANITE; + UsesLongArticle = true; + NameSingular = "altar"; + BitmapPos = 0, 368; + /* PostFix depends on Config */ + CreateDivineConfigurations = true; + IsAbstract = true; +} + +throne /* olterrain-> */ +{ + DigMessage = "The throne resists."; + MainMaterialConfig == GOLD; + MaterialColorB = rgb16(200, 200, 0); + NameSingular = "throne"; + BitmapPos = 0, 416; + AttachedGod = VALPURUS; +} + +olterraincontainer /* olterrain->decoration-> */ +{ + IsAbstract = true; + Walkability = ETHEREAL; + DigMessage = "You smash this piece of furniture into pieces."; + + Config BOOK_CASE; + { + CanBeDestroyed = true; + MainMaterialConfig == FIR_WOOD; + NameSingular = "bookcase"; + BitmapPos = 16, 272; + SitMessage = "The bookcase is very unconfortable to sit on."; + StorageVolume = 100000; + HPModifier = 5; + AttachedGod = SOPHOS; + LeftOverItems == NaturalMaterialForm { MainMaterial = 0 { Volume = 250:750; } Times = 1:2; } + } + + Config CHEST_OF_DRAWERS; + { + CanBeDestroyed = true; + MainMaterialConfig == FIR_WOOD; + NameSingular = "chest of drawers"; + BitmapPos = 0, 224; + SitMessage = "The chest of drawers is very unconfortable to sit on."; + StorageVolume = 50000; + HPModifier = 40; + AttachedGod = SOPHOS; + LeftOverItems == NaturalMaterialForm { MainMaterial = 0 { Volume = 500:1000; } Times = 1:2; } + } + Config SHELF; + { + CanBeDestroyed = true; + MainMaterialConfig == FIR_WOOD; + NameSingular = ""; + BitmapPos = 16, 128; + SitMessage = "The shelf is very unconfortable to sit on."; + StorageVolume = 80000; + HPModifier = 20; + AttachedGod = ATAVUS; + LeftOverItems == NaturalMaterialForm { MainMaterial = 0 { Volume = 250:750; } Times = 1:2; } + MaterialColorB = rgb16(200,200,200); + } +} + +fountain /* olterrain-> */ +{ + DigMessage = "The water splashes a bit."; /* Wrong if SecondaryMaterial != WATER. */ + MainMaterialConfig == MARBLE; + SecondaryMaterialConfig == WATER; + /* Uses ContainerPostFix() */ + /* Adjective depends on being dried out or not */ + NameSingular = "fountain"; + /* BitmapPos depends on being dried out or not */ + HasSecondaryMaterial = true; +} + +brokendoor /* olterrain->door-> */ +{ + DigMessage = "You destroy the broken door."; + Adjective = "broken"; + BitmapPos = 0, 160; + CreateLockConfigurations = true; + Config BARDOOR; + { + IsAlwaysTransparent = true; + BitmapPos = 32,96; + } + Config SECRET_DOOR; + { + BitmapPos = 16, 160; + NameSingular = "door"; + Adjective = "broken very secret"; + } +} + +boulder /* olterrain-> */ +{ + DigMessage = "You smash the boulder to tiny bits."; + MainMaterialConfig == GRANITE; + NameSingular = "boulder"; + SitMessage = "The boulder is rather hard to sit on."; + CanBeDestroyed = true; + IsAbstract = true; + HPModifier = 50; + LeftOverItems == NaturalMaterialForm { MainMaterial = 0 { Volume = 500:1500; } Times = 1:3; } + + Config 1; + { + BitmapPos = 64, 288; + } + + Config 2; + { + BitmapPos = 64, 304; + } + + Config 3; + { + BitmapPos = 64, 304; /* Should be checked */ + HPModifier = 70; + Adjective = "large"; + IsAlwaysTransparent = false; + Walkability = ETHEREAL; + } + + Config SNOW_BOULDER; + { + MaterialColorB = rgb16(220, 220, 220); + BitmapPos = 80, 306; + Adjective = "snow-covered"; + } +} + +sign +{ + DigMessage = "You smash the sign to tiny bits."; + MainMaterialConfig == BALSA_WOOD; + MaterialColorB = rgb16(200,200,200); + /* Uses own postfix */ + NameSingular = "sign"; + BitmapPos = 16, 336; + SitMessage = "You feel very weird sitting on the sign."; + CanBeDestroyed = true; + MaterialColorB = rgb16(48, 48, 48); + HPModifier = 10; + LeftOverItems == NaturalMaterialForm { MainMaterial = 0 { Volume = 250:500; } } +} + +ward +{ + DigMessage = "You smash the ward to smithereens."; + MainMaterialConfig == MILKY_QUARTZ; + NameSingular = "ward"; + BitmapPos = 64, 128; + SitMessage = "You feel a mysterious warmth radiating from the ward."; + CanBeDestroyed = true; + MaterialColorB = rgb16(100, 100, 24); + HPModifier = 25; + LeftOverItems == NaturalMaterialForm { MainMaterial = 0 { Volume = 250:500; } } +} + +earth +{ + CanBeDestroyed = true; + IsSafeToCreateDoor = true; + Walkability = ETHEREAL; + IsAlwaysTransparent = false; + OKVisualEffects = MIRROR|FLIP|ROTATE; + DigMessage = "The ground is fairly easy to dig."; + MainMaterialConfig == MORAINE; + UsesLongArticle = true; + NameSingular = "earth"; + BitmapPos = 96, 416; + HPModifier = 50; + BorderTilePriority = 10; + UseBorderTiles = true; + ShowThingsUnder = false; + LeftOverItems == NaturalMaterialForm { MainMaterial = 0 { Volume = 500:1500; } Times = 1:3; } + IsWall = true; +} + +monsterportal +{ + DigMessage = "The portal is magically protected from your feeble attack."; + MainMaterialConfig == SILVER; + MaterialColorB = rgb16(0, 0, 90); + NameSingular = "magic portal"; + AttachedGod = MORTIFER; +} + +coffin /* olterrain->decoration->olterraincontainer */ +{ + Walkability = ETHEREAL; + DigMessage = "You smash the coffin into pieces. The spirits are not happy."; + CanBeDestroyed = true; + MainMaterialConfig == FIR_WOOD; + NameSingular = "coffin"; + BitmapPos = 16, 272; + SitMessage = "The coffin is cold to sit on."; + StorageVolume = 100000; + AttachedGod = MORTIFER; + LeftOverItems == NaturalMaterialForm { MainMaterial = 0 { Volume = 250:750; } Times = 1:2; } +} + +barwall +{ + CanBeDestroyed = true; + IsSafeToCreateDoor = true; + Walkability = ETHEREAL; + IsAlwaysTransparent = true; + BorderTilePriority = 100; + CreateWindowConfigurations = false; + MaterialColorC = rgb16(140, 96, 48); + MaterialColorD = rgb16(180, 180, 255); + ShowThingsUnder = true; + LeftOverItems == NaturalMaterialForm { MainMaterial = 0 { Volume = 500:1500; } Times = 1:4; } + IsWall = true; + + DigMessage = "You break the bars."; + MainMaterialConfig == IRON; + NameSingular = "bars"; + BitmapPos = 16, 112; + HPModifier = 25; + ShowThingsUnder = true; + AttachedGod = LEGIFER; + Config BROKEN_BARWALL; + { + BitmapPos = 32, 112; + Adjective = "broken"; + Walkability = ANY_MOVE; + } +} + +ironmaiden +{ + LeftOverItems == NaturalMaterialForm { MainMaterial = 0 { Volume = 500:1500; } Times = 1:3; } CanBeDestroyed = true; + DigMessage = "The iron maiden falls into pieces."; + MainMaterialConfig == IRON; + ShowMaterial = false; + NameSingular = "iron maiden"; + HPModifier = 250; +} diff --git a/Thanks to these people.txt b/Thanks to these people.txt new file mode 100644 index 0000000..afba995 --- /dev/null +++ b/Thanks to these people.txt @@ -0,0 +1,11 @@ +J_Kahvi - Menatrix Fusanga graphic, little dragon graphic, some ideas. + +chaostrom - Menatrix Fusanga idea. + +herself - Maintaining #attnam @ irc.freenode.com + +Z[eno] - Helping me solve some problems, errors, etc. + +planplan - This guy created the plant levels, made it so thieves' girdle gives dexterity, and made a neato graphic to ASCII converter + +And most of all... the developers who created IVAN. \ No newline at end of file diff --git a/igor/AUTHORS b/igor/AUTHORS new file mode 100644 index 0000000..e69de29 diff --git a/igor/COPYING b/igor/COPYING new file mode 100644 index 0000000..e69de29 diff --git a/igor/CVS/Entries b/igor/CVS/Entries new file mode 100644 index 0000000..5efa4d1 --- /dev/null +++ b/igor/CVS/Entries @@ -0,0 +1,10 @@ +/AUTHORS/1.1/Mon Jul 25 11:17:27 2005// +/COPYING/1.1/Mon Jul 25 11:17:27 2005// +/ChangeLog/1.1/Mon Jul 25 11:17:27 2005// +/INSTALL/1.1/Mon Jul 25 11:17:27 2005// +/Makefile.am/1.1/Sat Feb 8 22:55:58 2003// +/NEWS/1.1/Mon Jul 25 11:17:27 2005// +/README/1.1/Mon Jul 25 11:17:27 2005// +/aclocal.m4/1.2/Mon Feb 17 10:54:07 2003// +/configure.in/1.3/Mon Apr 7 17:10:35 2003// +D diff --git a/igor/CVS/Entries.Log b/igor/CVS/Entries.Log new file mode 100644 index 0000000..b7481e5 --- /dev/null +++ b/igor/CVS/Entries.Log @@ -0,0 +1,2 @@ +A D/Include//// +A D/Source//// diff --git a/igor/CVS/Repository b/igor/CVS/Repository new file mode 100644 index 0000000..46402ae --- /dev/null +++ b/igor/CVS/Repository @@ -0,0 +1 @@ +ivan/igor diff --git a/igor/CVS/Root b/igor/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/igor/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/igor/ChangeLog b/igor/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/igor/INSTALL b/igor/INSTALL new file mode 100644 index 0000000..e69de29 diff --git a/igor/Include/CVS/Entries b/igor/Include/CVS/Entries new file mode 100644 index 0000000..33f4507 --- /dev/null +++ b/igor/Include/CVS/Entries @@ -0,0 +1,2 @@ +/Makefile.am/1.1/Sat Feb 8 22:55:58 2003// +D diff --git a/igor/Include/CVS/Repository b/igor/Include/CVS/Repository new file mode 100644 index 0000000..274cebc --- /dev/null +++ b/igor/Include/CVS/Repository @@ -0,0 +1 @@ +ivan/igor/Include diff --git a/igor/Include/CVS/Root b/igor/Include/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/igor/Include/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/igor/Include/Makefile.am b/igor/Include/Makefile.am new file mode 100644 index 0000000..e69de29 diff --git a/igor/Makefile.am b/igor/Makefile.am new file mode 100644 index 0000000..30b7568 --- /dev/null +++ b/igor/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = Source Include diff --git a/igor/NEWS b/igor/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/igor/README b/igor/README new file mode 100644 index 0000000..e69de29 diff --git a/igor/Source/CVS/Entries b/igor/Source/CVS/Entries new file mode 100644 index 0000000..26b409c --- /dev/null +++ b/igor/Source/CVS/Entries @@ -0,0 +1,3 @@ +/Makefile.am/1.1/Sat Feb 8 22:55:58 2003// +/igor.cpp/1.18/Tue May 10 20:33:09 2005// +D diff --git a/igor/Source/CVS/Repository b/igor/Source/CVS/Repository new file mode 100644 index 0000000..375f029 --- /dev/null +++ b/igor/Source/CVS/Repository @@ -0,0 +1 @@ +ivan/igor/Source diff --git a/igor/Source/CVS/Root b/igor/Source/CVS/Root new file mode 100644 index 0000000..8d60cee --- /dev/null +++ b/igor/Source/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ivan.cvs.sourceforge.net:/cvsroot/ivan diff --git a/igor/Source/Makefile.am b/igor/Source/Makefile.am new file mode 100644 index 0000000..fd997b7 --- /dev/null +++ b/igor/Source/Makefile.am @@ -0,0 +1,4 @@ +bin_PROGRAMS = igor +INCLUDES=-I@top_srcdir@/../FeLib/Include -I@top_srcdir@/../Main/Include -I../Include +igor_SOURCES = igor.cpp +igor_LDADD = @top_builddir@/../FeLib/Source/libFeLib.a \ No newline at end of file diff --git a/igor/Source/igor.cpp b/igor/Source/igor.cpp new file mode 100644 index 0000000..685029c --- /dev/null +++ b/igor/Source/igor.cpp @@ -0,0 +1,240 @@ +#include +#include +#include +#include + +#include "graphics.h" +#include "bitmap.h" +#include "whandler.h" +#include "feio.h" +#include "rawbit.h" +#include "felist.h" + +#define STRETCH 5 +#define TILE_SIZE 16 +cv2 TILE_V2(TILE_SIZE, TILE_SIZE); + +int Main(int, char**) +{ + festring OldDirectory; + std::ifstream IConfigFile("igor.cfg"); + + if(IConfigFile.is_open()) + { + char ch; + + while(IConfigFile.get(ch)) + OldDirectory << ch; + } + + IConfigFile.close(); + std::cout << "Where is the graphics directory? "; + + if(OldDirectory.GetSize()) + std::cout << '[' << OldDirectory.CStr() << "] "; + + festring Directory; + char ch; + + while((ch = getchar()) != '\n') + Directory << ch; + + if(Directory.IsEmpty()) + Directory = OldDirectory; + + if(!Directory.IsEmpty() && Directory[Directory.GetSize() - 1] != '/') + Directory << '/'; + + std::ofstream OConfigFile("igor.cfg"); + OConfigFile << Directory.CStr(); + OConfigFile.close(); + + graphics::Init(); + graphics::SetMode("IGOR 1.203", 0, v2(800, 600), false); + graphics::LoadDefaultFont(Directory + "Font.pcx"); + DOUBLE_BUFFER->ClearToColor(0); + + rawbitmap* CBitmap; + felist List(CONST_S("Choose file to edit:")); + List.AddEntry(CONST_S("Char.pcx"), LIGHT_GRAY); + List.AddEntry(CONST_S("Humanoid.pcx"), LIGHT_GRAY); + List.AddEntry(CONST_S("Item.pcx"), LIGHT_GRAY); + List.AddEntry(CONST_S("GLTerra.pcx"), LIGHT_GRAY); + List.AddEntry(CONST_S("OLTerra.pcx"), LIGHT_GRAY); + ushort Selected; + festring FileName; + List.SetPos(v2(300, 250)); + List.SetWidth(200); + + while((Selected = List.Draw()) & FELIST_ERROR_BIT) + if(Selected == ESCAPED) + return 0; + + switch(Selected) + { + case 0: FileName = CONST_S("Char.pcx"); break; + case 1: FileName = CONST_S("Humanoid.pcx"); break; + case 2: FileName = CONST_S("Item.pcx"); break; + case 3: FileName = CONST_S("GLTerra.pcx"); break; + case 4: FileName = CONST_S("OLTerra.pcx"); break; + } + + CBitmap = new rawbitmap(Directory + FileName); + bitmap CursorBitmap(Directory + "Cursor.pcx"); + CursorBitmap.ActivateFastFlag(); + v2 Cursor(0, 0); + int k = 0; + Selected = 0; + packcol16 Color[4] = { MakeRGB16(47, 131, 95), MakeRGB16(123, 0, 127), MakeRGB16(0, 131, 131), MakeRGB16(175, 131, 0) }; + std::vector DrawQueue; + uchar TempBuffer[256]; + blitdata B1 = { DOUBLE_BUFFER, + { 0, 0 }, + { RES.X - STRETCH * 16 - 10, RES.Y - STRETCH * 16 - 10 }, + { TILE_SIZE, TILE_SIZE }, + { STRETCH } }; + blitdata B2 = { DOUBLE_BUFFER, + { 0, 0 }, + { RES.X - STRETCH * 32 - 20, RES.Y - STRETCH * 16 - 10 }, + { TILE_SIZE, TILE_SIZE }, + { STRETCH } }; + blitdata B3 = { DOUBLE_BUFFER, + { 0, 0 }, + { 0, 0 }, + { TILE_SIZE, TILE_SIZE }, + { 0 }, + TRANSPARENT_COLOR }; + + for(;;) + { + static v2 MoveVector[] = { v2(0, -16), v2(-16, 0), v2(0, 16), v2(16, 0) }; + static int Key[] = { 'w', 'a', 's', 'd' }; + + int c; + + for(c = 0; c < 4; ++c) + { + if(Key[c] == k) + { + v2 NewPos = Cursor + MoveVector[c]; + + if(NewPos.X >= 0 && NewPos.X <= CBitmap->GetSize().X - 16 && NewPos.Y >= 0 && NewPos.Y <= CBitmap->GetSize().Y - 16) + Cursor = NewPos; + + break; + } + + if((Key[c]&~0x20) == k) + { + v2 NewPos = Cursor + (MoveVector[c] << 2); + + if(NewPos.X >= 0 && NewPos.X <= CBitmap->GetSize().X - 16 && NewPos.Y >= 0 && NewPos.Y <= CBitmap->GetSize().Y - 16) + Cursor = NewPos; + + break; + } + } + + if(k >= 0x31 && k <= 0x34) + Selected = k - 0x31; + else if(k == '+') + CBitmap->AlterGradient(Cursor, TILE_V2, Selected, 1, false); + else if(k == '-') + CBitmap->AlterGradient(Cursor, TILE_V2, Selected, -1, false); + else if(k == '>') + CBitmap->AlterGradient(Cursor, TILE_V2, Selected, 1, true); + else if(k == '<') + CBitmap->AlterGradient(Cursor, TILE_V2, Selected, -1, true); + else if(k == KEY_UP) + CBitmap->Roll(Cursor, TILE_V2, v2(0, -1), TempBuffer); + else if(k == KEY_DOWN) + CBitmap->Roll(Cursor, TILE_V2, v2(0, 1), TempBuffer); + else if(k == KEY_RIGHT) + CBitmap->Roll(Cursor, TILE_V2, v2(1, 0), TempBuffer); + else if(k == KEY_LEFT) + CBitmap->Roll(Cursor, TILE_V2, v2(-1, 0), TempBuffer); + else if(k == '=') + { + FONT->Printf(DOUBLE_BUFFER, v2(10, 460), RED, "Select col to swap with [1-4/ESC]"); + graphics::BlitDBToScreen(); + + for(k = GET_KEY(); k != 0x1B; k = GET_KEY()) + if(k >= 0x31 && k <= 0x34) + { + CBitmap->SwapColors(Cursor, TILE_V2, Selected, k - 0x31); + break; + } + } + else if(k == 0x1B) + { + FONT->Printf(DOUBLE_BUFFER, v2(10, 460), RED, "Save? [y/n/c]"); + graphics::BlitDBToScreen(); + + for(;;) + { + k = GET_KEY(); + + if(k == 'y' || k == 'Y') + { + CBitmap->Save(Directory + FileName); + delete CBitmap; + return 0; + } + + if(k == 'n' || k == 'N') + { + delete CBitmap; + return 0; + } + + if(k == 'c' || k == 'C') + break; + } + } + else if(k == 'p') + { + std::vector::iterator i = std::find(DrawQueue.begin(), DrawQueue.end(), Cursor); + + if(i == DrawQueue.end()) + DrawQueue.push_back(Cursor); + else + DrawQueue.erase(i); + } + else if(k == 'c') + DrawQueue.clear(); + + DOUBLE_BUFFER->ClearToColor(0); + DOUBLE_BUFFER->Fill(0, 0, CBitmap->GetSize(), 0xF81F); + CBitmap->MaskedBlit(DOUBLE_BUFFER, v2(0, 0), v2(0, 0), CBitmap->GetSize(), Color); + DOUBLE_BUFFER->DrawRectangle(RES.X - STRETCH * 16 - 12, RES.Y - STRETCH * 16 - 12, RES.X - 9, RES.Y - 9, DARK_GRAY, true); + DOUBLE_BUFFER->DrawRectangle(RES.X - STRETCH * 32 - 22, RES.Y - STRETCH * 16 - 12, RES.X - STRETCH * 16 - 19, RES.Y - 9, DARK_GRAY, true); + B1.Src = Cursor; + DOUBLE_BUFFER->StretchBlit(B1); + FONT->Printf(DOUBLE_BUFFER, v2(10, 480), WHITE, "Control cursor: wasd and WASD"); + FONT->Printf(DOUBLE_BUFFER, v2(10, 490), WHITE, "Select m-col: 1-4"); + FONT->Printf(DOUBLE_BUFFER, v2(10, 500), WHITE, "Safely alter gradient: ±"); + FONT->Printf(DOUBLE_BUFFER, v2(10, 510), WHITE, "Power alter gradient: <>"); + FONT->Printf(DOUBLE_BUFFER, v2(10, 520), WHITE, "Swap m-cols: ="); + FONT->Printf(DOUBLE_BUFFER, v2(10, 530), WHITE, "Push to / pop from draw queue: p"); + FONT->Printf(DOUBLE_BUFFER, v2(10, 540), WHITE, "Clear draw queue: c"); + FONT->Printf(DOUBLE_BUFFER, v2(10, 550), WHITE, "Roll picture: arrow keys"); + FONT->Printf(DOUBLE_BUFFER, v2(10, 570), WHITE, "MColor selected: %d", Selected + 1); + FONT->Printf(DOUBLE_BUFFER, v2(10, 580), WHITE, "Current position: (%d, %d)", Cursor.X, Cursor.Y); + + for(c = 0; c < DrawQueue.size(); ++c) + { + B2.Src = DrawQueue[c]; + DOUBLE_BUFFER->StretchBlit(B2); + B3.Dest = DrawQueue[c]; + CursorBitmap.NormalMaskedBlit(B3); + } + + B3.Dest = Cursor; + CursorBitmap.NormalMaskedBlit(B3); + graphics::BlitDBToScreen(); + k = GET_KEY(); + } + + return 1; +} + diff --git a/igor/aclocal.m4 b/igor/aclocal.m4 new file mode 100644 index 0000000..95a287f --- /dev/null +++ b/igor/aclocal.m4 @@ -0,0 +1,966 @@ +# aclocal.m4 generated automatically by aclocal 1.6.3 -*- Autoconf -*- + +# Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# Do all the work for Automake. -*- Autoconf -*- + +# This macro actually does too much some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +# Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 8 + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +AC_PREREQ([2.52]) + +# Autoconf 2.50 wants to disallow AM_ names. We explicitly allow +# the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl + AC_REQUIRE([AC_PROG_INSTALL])dnl +# test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) +fi + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl + AC_SUBST([PACKAGE], [AC_PACKAGE_TARNAME])dnl + AC_SUBST([VERSION], [AC_PACKAGE_VERSION])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) + AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) +AM_MISSING_PROG(AMTAR, tar) +AM_PROG_INSTALL_SH +AM_PROG_INSTALL_STRIP +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl + +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_][CC], + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_][CC], + defn([AC_PROG_][CC])[_AM_DEPENDENCIES(CC)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_][CXX], + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_][CXX], + defn([AC_PROG_][CXX])[_AM_DEPENDENCIES(CXX)])])dnl +]) +]) + +# Copyright 2002 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +AC_DEFUN([AM_AUTOMAKE_VERSION],[am__api_version="1.6"]) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION so it can be traced. +# This function is AC_REQUIREd by AC_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], + [AM_AUTOMAKE_VERSION([1.6.3])]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright 2001, 2002 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 2 + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# ------------------------------ +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) + +# _AM_SET_OPTIONS(OPTIONS) +# ---------------------------------- +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# +# Check to make sure that the build environment is sane. +# + +# Copyright 1996, 1997, 2000, 2001 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 3 + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT(yes)]) + +# -*- Autoconf -*- + + +# Copyright 1997, 1999, 2000, 2001 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 3 + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + AC_MSG_WARN([`missing' script is too old or missing]) +fi +]) + +# AM_AUX_DIR_EXPAND + +# Copyright 2001 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to +# `$srcdir', `$srcdir/..', or `$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is `.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +# Rely on autoconf to set up CDPATH properly. +AC_PREREQ([2.50]) + +AC_DEFUN([AM_AUX_DIR_EXPAND], [ +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` +]) + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. + +# Copyright 2001 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +install_sh=${install_sh-"$am_aux_dir/install-sh"} +AC_SUBST(install_sh)]) + +# AM_PROG_INSTALL_STRIP + +# Copyright 2001 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# One issue with vendor `install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in `make install-strip', and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be `maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# serial 4 -*- Autoconf -*- + +# Copyright 1999, 2000, 2001 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "GCJ", or "OBJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +ifelse([$1], CC, [depcc="$CC" am_compiler_list=], + [$1], CXX, [depcc="$CXX" am_compiler_list=], + [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + for depmode in $am_compiler_list; do + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + echo '#include "conftest.h"' > conftest.c + echo 'int i;' > conftest.h + echo "${am__include} ${am__quote}conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=conftest.c object=conftest.o \ + depfile=conftest.Po tmpdepfile=conftest.TPo \ + $SHELL ./depcomp $depcc -c conftest.c -o conftest.o >/dev/null 2>&1 && + grep conftest.h conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES +AC_DEFUN([AM_SET_DEPDIR], +[rm -f .deps 2>/dev/null +mkdir .deps 2>/dev/null +if test -d .deps; then + DEPDIR=.deps +else + # MS-DOS does not allow filenames that begin with a dot. + DEPDIR=_deps +fi +rmdir .deps 2>/dev/null +AC_SUBST([DEPDIR]) +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking Speeds up one-time builds + --enable-dependency-tracking Do not reject slow dependency extractors]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH]) +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +#serial 2 + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[for mf in $CONFIG_FILES; do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + grep '^DEP_FILES *= *[[^ @%:@]]' < "$mf" > /dev/null || continue + # Extract the definition of DEP_FILES from the Makefile without + # running `make'. + DEPDIR=`sed -n -e '/^DEPDIR = / s///p' < "$mf"` + test -z "$DEPDIR" && continue + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n -e '/^U = / s///p' < "$mf"` + test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR" + # We invoke sed twice because it is the simplest approach to + # changing $(DEPDIR) to its actual value in the expansion. + for file in `sed -n -e ' + /^DEP_FILES = .*\\\\$/ { + s/^DEP_FILES = // + :loop + s/\\\\$// + p + n + /\\\\$/ b loop + p + } + /^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each `.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Copyright 2001 Free Software Foundation, Inc. -*- Autoconf -*- + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 2 + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +doit: + @echo done +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | fgrep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi +AC_SUBST(am__include) +AC_SUBST(am__quote) +AC_MSG_RESULT($_am_result) +rm -f confinc confmf +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright 1997, 2000, 2001 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 5 + +AC_PREREQ(2.52) + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE]) +AC_SUBST([$1_FALSE]) +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([conditional \"$1\" was never defined. +Usually this means the macro was only invoked conditionally.]) +fi])]) + +# Figure out how to run the assembler. -*- Autoconf -*- + +# serial 2 + +# Copyright 2001 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# AM_PROG_AS +# ---------- +AC_DEFUN([AM_PROG_AS], +[# By default we simply use the C compiler to build assembly code. +AC_REQUIRE([AC_PROG_CC]) +: ${CCAS='$(CC)'} +# Set ASFLAGS if not already set. +: ${CCASFLAGS='$(CFLAGS)'} +AC_SUBST(CCAS) +AC_SUBST(CCASFLAGS)]) + +# Configure paths for SDL +# Sam Lantinga 9/21/99 +# stolen from Manish Singh +# stolen back from Frank Belew +# stolen from Manish Singh +# Shamelessly stolen from Owen Taylor + +dnl AM_PATH_SDL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS +dnl +AC_DEFUN(AM_PATH_SDL, +[dnl +dnl Get the cflags and libraries from the sdl-config script +dnl +AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], + sdl_prefix="$withval", sdl_prefix="") +AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], + sdl_exec_prefix="$withval", sdl_exec_prefix="") +AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program], + , enable_sdltest=yes) + + if test x$sdl_exec_prefix != x ; then + sdl_args="$sdl_args --exec-prefix=$sdl_exec_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_exec_prefix/bin/sdl-config + fi + fi + if test x$sdl_prefix != x ; then + sdl_args="$sdl_args --prefix=$sdl_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_prefix/bin/sdl-config + fi + fi + + AC_REQUIRE([AC_CANONICAL_TARGET]) + PATH="$prefix/bin:$prefix/usr/bin:$PATH" + AC_PATH_PROG(SDL_CONFIG, sdl-config, no, [$PATH]) + min_sdl_version=ifelse([$1], ,0.11.0,$1) + AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) + no_sdl="" + if test "$SDL_CONFIG" = "no" ; then + no_sdl=yes + else + SDL_CFLAGS=`$SDL_CONFIG $sdlconf_args --cflags` + SDL_LIBS=`$SDL_CONFIG $sdlconf_args --libs` + + sdl_major_version=`$SDL_CONFIG $sdl_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + sdl_minor_version=`$SDL_CONFIG $sdl_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x$enable_sdltest" = "xyes" ; then + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" +dnl +dnl Now check if the installed SDL is sufficiently new. (Also sanity +dnl checks the results of sdl-config to some extent +dnl + rm -f conf.sdltest + AC_TRY_RUN([ +#include +#include +#include +#include "SDL.h" + +char* +my_strdup (char *str) +{ + char *new_str; + + if (str) + { + new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char)); + strcpy (new_str, str); + } + else + new_str = NULL; + + return new_str; +} + +int main (int argc, char *argv[]) +{ + int major, minor, micro; + char *tmp_version; + + /* This hangs on some systems (?) + system ("touch conf.sdltest"); + */ + { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); } + + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = my_strdup("$min_sdl_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_sdl_version"); + exit(1); + } + + if (($sdl_major_version > major) || + (($sdl_major_version == major) && ($sdl_minor_version > minor)) || + (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** 'sdl-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version); + printf("*** of SDL required is %d.%d.%d. If sdl-config is correct, then it is\n", major, minor, micro); + printf("*** best to upgrade to the required version.\n"); + printf("*** If sdl-config was wrong, set the environment variable SDL_CONFIG\n"); + printf("*** to point to the correct copy of sdl-config, and remove the file\n"); + printf("*** config.cache before re-running configure\n"); + return 1; + } +} + +],, no_sdl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + if test "x$no_sdl" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test "$SDL_CONFIG" = "no" ; then + echo "*** The sdl-config script installed by SDL could not be found" + echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the SDL_CONFIG environment variable to the" + echo "*** full path to sdl-config." + else + if test -f conf.sdltest ; then + : + else + echo "*** Could not run SDL test program, checking why..." + CFLAGS="$CFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" + AC_TRY_LINK([ +#include +#include "SDL.h" + +int main(int argc, char *argv[]) +{ return 0; } +#undef main +#define main K_and_R_C_main +], [ return 0; ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding SDL or finding the wrong" + echo "*** version of SDL. If it is not finding SDL, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means SDL was incorrectly installed" + echo "*** or that you have moved SDL since it was installed. In the latter case, you" + echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + SDL_CFLAGS="" + SDL_LIBS="" + ifelse([$3], , :, [$3]) + fi + AC_SUBST(SDL_CFLAGS) + AC_SUBST(SDL_LIBS) + rm -f conf.sdltest +]) + diff --git a/igor/configure.in b/igor/configure.in new file mode 100644 index 0000000..1b9885b --- /dev/null +++ b/igor/configure.in @@ -0,0 +1,28 @@ +AC_INIT(igor,1.203) +AC_CANONICAL_TARGET + +AC_CONFIG_SRCDIR(Source/igor.cpp) +AM_INIT_AUTOMAKE([foreign]) +AM_PROG_AS +AM_INIT_AUTOMAKE([foreign],igor,1.203) +AC_PROG_CC + +AC_PROG_CXX + +dnl Check for SDL +SDL_VERSION=1.2.0 +AM_PATH_SDL($SDL_VERSION, + :, + AC_MSG_ERROR([*** SDL version $SDL_VERSION not found!]) +) + +CFLAGS="$CFLAGS $SDL_CFLAGS " +LIBS="$LIBS $SDL_LIBS" + +CPPFLAGS="$SDL_CFLAGS -DLINUX -DUSE_SDL -DGCC" + +AC_PROG_INSTALL + +AC_PROG_RANLIB + +AC_OUTPUT(Makefile Source/Makefile Include/Makefile) diff --git a/ivan.cfg b/ivan.cfg new file mode 100644 index 0000000..843a385 --- /dev/null +++ b/ivan.cfg @@ -0,0 +1,10 @@ +DefaultName = ""; +DefaultPetName = "Lampshade"; +AutoSaveInterval = 5; +Contrast = 100; +WarnAboutVeryDangerousMonsters = 1; +AutoDropLeftOvers = 1; +LookZoom = 1; +UseAlternativeKeys = 0; +BeNice = 1; +FullScreenMode = 0; diff --git a/ivanmgw.mak b/ivanmgw.mak new file mode 100644 index 0000000..87fddf7 --- /dev/null +++ b/ivanmgw.mak @@ -0,0 +1,40 @@ +# Iter Vehemens ad Necem makefile for MinGW environment + +# Copyrights (C) Timo Kiviluoto / IvanDev 2002-2004 + +CC = g++ -o +FeLibDIR = C:\CLIVAN\FeLib +FeLibGCH = +#FeLibGCH = $(FeLibDIR)/Include/allocate.h.gch $(FeLibDIR)/Include/bitmap.h.gch $(FeLibDIR)/Include/config.h.gch $(FeLibDIR)/Include/error.h.gch $(FeLibDIR)/Include/fearray.h.gch $(FeLibDIR)/Include/feio.h.gch $(FeLibDIR)/Include/felibdef.h.gch $(FeLibDIR)/Include/felist.h.gch $(FeLibDIR)/Include/feloops.h.gch $(FeLibDIR)/Include/femath.h.gch $(FeLibDIR)/Include/festring.h.gch $(FeLibDIR)/Include/fetime.h.gch $(FeLibDIR)/Include/graphics.h.gch $(FeLibDIR)/Include/hscore.h.gch $(FeLibDIR)/Include/rawbit.h.gch $(FeLibDIR)/Include/rect.h.gch $(FeLibDIR)/Include/save.h.gch $(FeLibDIR)/Include/v2.h.gch $(FeLibDIR)/Include/whandler.h.gch +FeLibOBJ = $(FeLibDIR)/Source/bitmap.o $(FeLibDIR)/Source/config.o $(FeLibDIR)/Source/error.o $(FeLibDIR)/Source/feio.o $(FeLibDIR)/Source/felist.o $(FeLibDIR)/Source/femain.o $(FeLibDIR)/Source/femath.o $(FeLibDIR)/Source/festring.o $(FeLibDIR)/Source/fetime.o $(FeLibDIR)/Source/graphics.o $(FeLibDIR)/Source/hscore.o $(FeLibDIR)/Source/rawbit.o $(FeLibDIR)/Source/save.o $(FeLibDIR)/Source/whandler.o +IVANDIR = C:\CLIVAN\Main +IVANBIN = IVAN.exe +IVANGCH = +#IVANGCH = $(IVANDIR)/Include/action.h.gch $(IVANDIR)/Include/actions.h.gch $(IVANDIR)/Include/area.h.gch $(IVANDIR)/Include/bodypart.h.gch $(IVANDIR)/Include/char.h.gch $(IVANDIR)/Include/command.h.gch $(IVANDIR)/Include/cont.h.gch $(IVANDIR)/Include/database.h.gch $(IVANDIR)/Include/dungeon.h.gch $(IVANDIR)/Include/entity.h.gch $(IVANDIR)/Include/fluid.h.gch $(IVANDIR)/Include/game.h.gch $(IVANDIR)/Include/gear.h.gch $(IVANDIR)/Include/god.h.gch $(IVANDIR)/Include/gods.h.gch $(IVANDIR)/Include/human.h.gch $(IVANDIR)/Include/iconf.h.gch $(IVANDIR)/Include/id.h.gch $(IVANDIR)/Include/igraph.h.gch $(IVANDIR)/Include/iloops.h.gch $(IVANDIR)/Include/item.h.gch $(IVANDIR)/Include/level.h.gch $(IVANDIR)/Include/lsquare.h.gch $(IVANDIR)/Include/lterra.h.gch $(IVANDIR)/Include/lterras.h.gch $(IVANDIR)/Include/materia.h.gch $(IVANDIR)/Include/materias.h.gch $(IVANDIR)/Include/message.h.gch $(IVANDIR)/Include/miscitem.h.gch $(IVANDIR)/Include/nonhuman.h.gch $(IVANDIR)/Include/object.h.gch $(IVANDIR)/Include/pool.h.gch $(IVANDIR)/Include/proto.h.gch $(IVANDIR)/Include/rain.h.gch $(IVANDIR)/Include/room.h.gch $(IVANDIR)/Include/rooms.h.gch $(IVANDIR)/Include/script.h.gch $(IVANDIR)/Include/smoke.h.gch $(IVANDIR)/Include/square.h.gch $(IVANDIR)/Include/stack.h.gch $(IVANDIR)/Include/team.h.gch $(IVANDIR)/Include/terra.h.gch $(IVANDIR)/Include/trap.h.gch $(IVANDIR)/Include/traps.h.gch $(IVANDIR)/Include/worldmap.h.gch $(IVANDIR)/Include/wskill.h.gch $(IVANDIR)/Include/wterra.h.gch $(IVANDIR)/Include/wterras.h.gch +IVANOBJ = $(IVANDIR)/Source/actset.o $(IVANDIR)/Source/areaset.o $(IVANDIR)/Source/charset.o $(IVANDIR)/Source/charsset.o $(IVANDIR)/Source/command.o $(IVANDIR)/Source/coreset.o $(IVANDIR)/Source/dataset.o $(IVANDIR)/Source/dungeon.o $(IVANDIR)/Source/game.o $(IVANDIR)/Source/godset.o $(IVANDIR)/Source/iconf.o $(IVANDIR)/Source/id.o $(IVANDIR)/Source/igraph.o $(IVANDIR)/Source/itemset.o $(IVANDIR)/Source/levelset.o $(IVANDIR)/Source/main.o $(IVANDIR)/Source/materset.o $(IVANDIR)/Source/message.o $(IVANDIR)/Source/object.o $(IVANDIR)/Source/roomset.o $(IVANDIR)/Source/script.o $(IVANDIR)/Source/slotset.o $(IVANDIR)/Source/trapset.o $(IVANDIR)/Source/wmapset.o $(IVANDIR)/Source/wskill.o +#FLAGS = -DGCC -DUSE_SDL -DWIZARD -IInclude -I$(FeLibDIR)/Include -pg -ffast-math -W -Wall -pedantic -march=athlon-xp -mrtd -maccumulate-outgoing-args -mwindows +#FLAGS = -DGCC -DUSE_SDL -DWIZARD -IInclude -I$(FeLibDIR)/Include -pg -O3 -ffast-math -march=athlon-xp -mwindows +FLAGS = -DGCC -DUSE_SDL -DWIZARD -IInclude -I$(FeLibDIR)/Include -O3 -ffast-math -s -W -Wall -pedantic -mwindows +LIBS = -lmingw32 -lSDLmain -lSDL + +all: $(IVANBIN) + +$(FeLibGCH) : %.h.gch : %.h + @echo Compiling $@... + @$(CC) $@ -c $< $(FLAGS) + +$(FeLibOBJ) : %.o : %.cpp + @echo Compiling $@... + @$(CC) $@ -c $< $(FLAGS) + +$(IVANGCH) : %.h.gch : %.h + @echo Compiling $@... + @$(CC) $@ -c $< $(FLAGS) -I$(IVANDIR)/Include + +$(IVANOBJ) : %.o : %.cpp + @echo Compiling $@... + @$(CC) $@ -c $< $(FLAGS) -I$(IVANDIR)/Include + +$(IVANBIN) : $(FeLibGCH) $(FeLibOBJ) $(IVANGCH) $(IVANOBJ) + @echo Compiling $(IVANBIN)... + @$(CC) $(IVANBIN) $(FeLibOBJ) $(IVANOBJ) $(FLAGS) $(LIBS) diff --git a/msvcp50.dll b/msvcp50.dll new file mode 100644 index 0000000..678a699 Binary files /dev/null and b/msvcp50.dll differ diff --git a/msvcrt.dll b/msvcrt.dll new file mode 100644 index 0000000..65f05a8 Binary files /dev/null and b/msvcrt.dll differ diff --git a/sourceforge_icon.png b/sourceforge_icon.png new file mode 100644 index 0000000..78d76ed Binary files /dev/null and b/sourceforge_icon.png differ