Merge branch 'master' into releases 1.2.0
authorHolger Schemel <info@artsoft.org>
Sat, 30 Aug 2014 08:32:16 +0000 (10:32 +0200)
committerHolger Schemel <info@artsoft.org>
Sat, 30 Aug 2014 08:32:16 +0000 (10:32 +0200)
54 files changed:
CHANGES
COPYING [new file with mode: 0644]
COPYRIGHT [deleted file]
CREDITS [new file with mode: 0644]
DISCLAIMER [deleted file]
HARDWARE [new file with mode: 0644]
INSTALL [new file with mode: 0644]
INSTALLATION [deleted file]
Makefile [new file with mode: 0644]
README
RECOMMENDATIONS [deleted file]
REGISTRATION [deleted symlink]
THIS_IS_NOW_FREEWARE [deleted file]
src/Makefile
src/buttons.c
src/buttons.h
src/cartoons.c [new file with mode: 0644]
src/cartoons.h [new file with mode: 0644]
src/editor.c
src/editor.h
src/events.c
src/events.h
src/files.c
src/files.h
src/game.c
src/game.h
src/image.c [new file with mode: 0644]
src/image.h [new file with mode: 0644]
src/init.c
src/init.h
src/joystick.c [new file with mode: 0644]
src/joystick.h [new file with mode: 0644]
src/main.c
src/main.h
src/misc.c
src/misc.h
src/msdos.c [new file with mode: 0644]
src/msdos.h [new file with mode: 0644]
src/netserv.c [new file with mode: 0644]
src/netserv.h [new file with mode: 0644]
src/network.c [new file with mode: 0644]
src/network.h [new file with mode: 0644]
src/pcx.c [new file with mode: 0644]
src/pcx.h [new file with mode: 0644]
src/random.c [new file with mode: 0644]
src/random.h [new file with mode: 0644]
src/screens.c
src/screens.h
src/sound.c
src/sound.h
src/tape.c [new file with mode: 0644]
src/tape.h [new file with mode: 0644]
src/tools.c
src/tools.h

diff --git a/CHANGES b/CHANGES
index 5ad2b64da8e931abf1d3576d0f9a984de33c2ade..4a42a215c46cc0f2514dc53c1242eb324e18d9e9 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,70 @@
 
-Prerelease Version 0.9b [4 NOV 95]
-------------------------------------
+Release Version 1.2.0 [5 DEC 1998]
+--------------------------------------------------------
+       - DOS/Windows version
+       - new WAV sound loader
+       - new PCX graphics loader
+       - network multiplayer games with upto four players
+       - no separate network server needed; each client can
+         fork a network server at startup if there's no server
+         running at this moment
+       - possibility to invoke the game to act as a standalone
+         network server (on a separate machine, for example)
+       - local multiplayer games with upto four players
+       - support for upto four joysticks
+       - completely free customizable keyboard and joystick
+         for all four players individually
+       - new joystick calibration screen which can be left
+         (with Escape key) if no joystick is connected... ;-)
+       - new (working) GIF graphics loader (but still support
+         for the old XPM method)
+       - supports private colormap with extremely less flashing
+         on 8-bit (256 colors) displays
+       - soft-scrolling with 50 frames per second (which raises
+         the system requirements and makes it completely
+         unplayable on my "old reference" 486/33 (where 0.9b runs
+         smoothly) and running at 90% speed on my K6-200.
+       - completely new file format for personal setup data
+         in ASCII format which is human readable and easily
+         customizable even with a texteditor; stored in the
+         user's home directory and no longer somewhere in the
+         game's installation directory
+       - high score lists changed: now one file per level and
+         no longer one file per level series; now using readable
+         ASCII format
+       - useful command line options to specify the X11 display,
+         the game's base (installation) directory, an alternate
+         level directory, standalone server execution and verbose
+         execution
+
+Release Version 1.1 [???] [NOT RELEASED]
+----------------------------------
+       - new (but broken) GIF graphics loader to be independent
+         from the XPM library and replace all graphics by GIFs.
+
+Release Version 1.0 [9 APR 1997] [NOT RELEASED]
+---------------------------------------------
+       - the game now contains many really playable levels,
+         not only a few levels for testing
+       - the game is now even better playable by keyboard
+         (now you have the same gameplay functionality
+         compared to playing with a joystick. Especially
+         there are diagonal directions with keyboard playing
+         and the fire buttons are mapped to the shift keys)
+       - a lot of new elements for better emulation of levels
+         from the games "Boulderdash", "Emerald Mine" and
+         "Sokoban". New elements to build "Dynablaster" style
+         levels.
+       - enhanced functionality of the level tape recorder
+         to make it possible to go on with a game at any tape
+         position
+
+Prerelease Version 0.9b2 [21 NOV 1995] [NOT RELEASED]
+---------------------------------------------------
+       - new game elements
+
+Prerelease Version 0.9b [4 NOV 1995]
+----------------------------------
        - the game is now completely Freeware
        - the game is now better playable by keyboard
          (in the last version, the player was making more than
@@ -15,6 +79,6 @@ Prerelease Version 0.9b [4 NOV 95]
        - FreeBSD sound and joystick support (thanks to Jean-Marc
          Zucconi)
 
-Prerelease Version 0.9 [23 OCT 95]
+Prerelease Version 0.9 [23 OCT 1995]
 ----------------------------------
        - first (pre)release version
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+       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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    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.
+
+  <signature of Ty Coon>, 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/COPYRIGHT b/COPYRIGHT
deleted file mode 100644 (file)
index b2b7fc7..0000000
--- a/COPYRIGHT
+++ /dev/null
@@ -1,25 +0,0 @@
-This program is copyrighted (c) 1995 by Holger Schemel.
-
-It is freely distributable as long as no profit is made with it
-and as long as the package is left complete and unmodified.
-
-The fact that this program comes with full source does not mean
-that you can do what you want with it - it is only intended to
-give you the chance to build it on your own machine.
-
-If you want to distribute this program on CDROM, you have to ask for
-permission first -- just send me E-Mail and tell me what kind of
-CDROM it is. Normally I will give my permission if
- - the CDROM itself contains only freely distributable software
- - the author of this program (me!) gets a free copy of the CDROM
- - the program is distributed in its original, unmodified package
-
-If the three conditions above are fulfilled, this permission will
-automatically given to the following CDROM distributions:
- - InfoMagic "LINUX Developer's Resource"
- - The Debian Linux Distribution CDROM
- - any CDROM with a mirror of sunsite.unc.edu or a mirror of another
-   FTP server that contains this program
-
-If you are in doubt (e.g., want to distribute a pre-installed version),
-feel free to mail me about it (see the file 'README' in this archive).
diff --git a/CREDITS b/CREDITS
new file mode 100644 (file)
index 0000000..bb18741
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,3 @@
+
+[Credits file yet to be written.]
+
diff --git a/DISCLAIMER b/DISCLAIMER
deleted file mode 100644 (file)
index b6e06a6..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-
-THIS GAME IS PROVIDED "AS IS" -- USE IT AT YOUR OWN RISK!
-IF THIS GAME BLOWS UP YOUR COMPUTER OR OPERATING SYSTEM
-OR ANYTHING ELSE, IT'S NOT MY FAULT! I'M NOT RESPONSIBLE
-FOR ANY DAMAGE THAT MIGHT BE CAUSED BY MY PROGRAM!
diff --git a/HARDWARE b/HARDWARE
new file mode 100644 (file)
index 0000000..49de985
--- /dev/null
+++ b/HARDWARE
@@ -0,0 +1,92 @@
+
+[Very outdated -- should be rewritten...]
+
+
+Some recommendations for more fun playing Rocks'n'Diamonds:
+===========================================================
+
+About sound
+-----------
+It is highly recommended to have a decent sound interface for playing
+this game (although it only comes with bad-quality 8000-Hz-samples to
+save memory and to be compatible with /dev/audio).
+
+The best sound platform is an actual Linux system with (at least) kernel
+1.2.x, because it offers some nice real-time sound features which are
+needed to have background music loops in the game.
+
+On all other systems you don't have music loops, but you still have all
+the other sounds.
+
+
+About game speed
+----------------
+You should have a relatively fast hardware.
+
+The game was developed on a i486/33 (which is three years old now),
+and you should probably better not try to run it on a slower system.
+
+You should have an accelerated graphic card; I have found out that it
+starts being playable on my 486/33 with an old ISA cirrus logic 5426
+graphic card, which has a blitter that is supported by XFree86[tm],
+but that it was nearly unplayable on a 486/66 with old, unaccelerated
+ET4000.
+
+If all works fine, you should have something around 30 frames per second.
+
+If you think that the game is to slow to play, you should try out the
+following things:
+
+- Set "Buffered Gfx" to "off" in the setup menu. Normally (on a fast
+  enough system) you should use buffered graphics, which means that
+  all graphics is drawn into an invisible Pixmap and is then copied
+  to the X11 window to avoid flickering. If you disable this double
+  buffering, the graphic is directly drawn into the window. This can
+  cause some slight flickering, but makes graphic operations roughly
+  twice as fast compared to double buffering.
+
+- Set "Game Music" to "off" in the setup menu (and maybe "sound loops"
+  too). Where disabling buffered graphics may be required with slow
+  graphics hardware, disabling sound is especially recommended on slow
+  CPU systems (486/33 and slower), because the sound server eats up a
+  significant amount of CPU time when playing long sounds.
+
+You might also notice that bigger levels tend to be slower on slow
+systems.
+
+About the option "Fading" in the setup menu: It gives a nice looking
+fading when switching to a new screen, but you should really only try
+this out if you think that you have a very fast graphics hardware.
+
+
+About music
+-----------
+The background music loops are ripped out from several nice music albums.
+Just have a look at the info screen to find out from which album each
+sound loop came from -- they are all very well done pieces of music, but
+unfortunately they don't sound better after converting them to 8 kHz
+samples (to conform to standard SUN /dev/audio). Maybe I change this to
+a better quality in the future, but at the moment, where the sounds
+directory has a size of nearly a megabyte, I'll stay with 8 kHz samples.
+
+So, if you have a non-Linux system (which cannot play sound loops) or
+don't like the "telephone quality" of the music loops, just get some of
+these CDs and play them while your playing the game! ;-)
+
+
+About game-play
+---------------
+It is *strongly recommended* to play this game with a high-quality joystick.
+That means, throw your $10 joystick out of the window and buy a decent
+Competition-Pro Digital PC joystick or a high-quality CH Products Analog
+joystick. Believe me, it doubles the fun of playing the game.
+
+If you only have a normal Unix system (and no fine Linux system), you
+are forced to play with the keyboard. It works, but is not only less fun,
+but also more difficult with some levels, because you cannot move in
+diagonal directions with keyboard control at the moment. Another bad thing
+is that you will have some problems when pressing many keys at the same
+time. This might change in the future, when I implement a better keyboard
+handling which not only solves these problems but allows diagonal directions,
+too.
+
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..697475a
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,85 @@
+
+Installation instructions for Rocks'n'Diamonds 1.2.0
+====================================================
+
+Compilation
+-----------
+
+If your system supports Linux/i386/ELF/libc5 executables (Linux package)
+or if you run DOS/Windows (DOS package), you can directly use the included
+precompiled binary.
+
+If you use a different system, just recompile the game:
+
+If you use Linux with gcc or DOS/Windows with djgpp, just try 'make'
+which should work without problems.
+
+If you use SUN/Solaris with gcc and GNU make, try 'make solaris'.
+
+If the above doesn't work, edit the Makefile in the top level directory.
+If it doesn't work either, edit the Makefile in the 'src' subdirectory.
+
+If you had to modify any Makefile to compile it, please drop me a note
+about it to 'aeglos@valinor.owl.de'. Thanks!
+
+
+Customization
+-------------
+
+The following configuration options in the top level Makefile help
+you to customize the game to your system and your environment:
+
+CC             Choose your favorite ANSI C compiler.
+               Default is 'gcc'.
+
+PLATFORM       Choose your platform, if auto detection fails.
+               Auto detection should work for Linux and DOS
+               (just type 'make'), on SUN/Solaris systems 'make solaris'
+               should work. Other systems may need additional editing
+               of 'src/Makefile' to set some additional compiler options.
+
+X11_PATH       Specify the path to your X11 installation. Include files
+               and libraries are searched for at $(X11_PATH)/include and
+               $(X11_PATH)/lib, if you set this variables. If you don't
+               set this variable, your compiler must be able to find X11
+               by itself, which works fine for Linux, for example.
+
+GAME_DIR       Specify the directory where the program looks for all the
+               graphics, sounds, levels and high scores. If you leave this
+               commented out, the current directory ('.') is used, which
+               lets you use the game without installing it somewhere in
+               your system (although you have to 'cd' to the game directory
+               each time you want to play it).
+
+JOYSTICK       Uncomment this line if your system does not support game
+               port hardware like joysticks (and has therefore no joystick
+               include file). Currently the game should work with joysticks
+               on Linux and FreeBSD.
+
+SOUNDS         Uncomment this line if your system does not support audio
+               hardware. Currently the game should work with sound support
+               on Linux, FreeBSD, SUN/Solaris, HP-UX and most Unix system
+               that support '/dev/dsp' or '/dev/audio' devices.
+
+SCORE_ENTRIES  Choose if you want to allow many entries in the high score
+               table for one player or exactly one entry. Systems with a
+               lot of users maybe want to limit high score entries to only
+               one entry for each player (and therefore set 'SCORE_ENTRIES'
+               to 'ONE_PER_NAME') where systems used by only one person
+               maybe want to use the default of 'MANY_PER_NAME' which is
+               automatically choosen if you leave everything commented out.
+
+Modifications of the Makefile in the directory 'src' are normally not needed.
+If the game fails to compile out of the box, you may have to modify some
+of the system variables there to get it to compile.
+
+SYSTEM         May need to be set to some system dependend values.
+
+INCL, LIBS     Maybe some more directories for include files and libraries
+               have to be added to this variables depending on your system.
+
+
+If you have any comments, additions or modifications to the Makefile(s),
+please send me mail: 'aeglos@valinor.owl.de'. Thanks!
+
+05-DEC-1998, Holger Schemel
diff --git a/INSTALLATION b/INSTALLATION
deleted file mode 100644 (file)
index 298ff6d..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-
-WHAT TO DO TO INSTALL "ROCKS_N_DIAMONDS-0.9" ON YOUR SYSTEM
-===========================================================
-
-If you have a Linux system (Intel based, a.out, kernel 1.2.x), you
-can use the precompiled binary.
-
-If you have another Unix system, or an ELF system or just want to
-compile the program by yourself, do the following:
-
-Edit the file "Makefile" and set the following options:
-
-XPM_INCLUDE_FILE       -       change this to the location where you
-                               have your XPM-Library include file.
-
-                               You must have the XPM-Library "libXpm"
-                               installed on your system to run this game.
-                               If you don't have it, look at your local
-                               FTP site with X11 archive to get it! :)
-
-GAME_DIR               -       You can leave these values untouched
-                               if you use the default location for
-                               the game directory.
-                               If you want to move it to other places in
-                               your directory tree, just change them
-                               accordingly. (You can use symlinks instead,
-                               of course.)
-                               You might want to set GAME_DIR to the full
-                               path of the game in your file system, so
-                               you don't have to go to the game directory
-                               when you want to play it.
-
-SOUNDS                 -       If you never want to hear any sounds,
-                               set this to "-DNO_SOUNDS", but be warned:
-                               It's much less fun playing without sound! :)
-                               (The program detects by itself if it can
-                               play sounds or not, anyway.)
-
-SCORE_ENTRIES          -       Set this to "-DONE_PER_NAME" if you want
-                               to allow only one entry per player in the
-                               score file for each level. This is useful
-                               if there are many players to prevent one
-                               single player to "flood" the Hall Of Fame
-                               of every level completely with his name...
-                               On single user systems, "-DMANY_PER_NAME"
-                               might be useful to have your name more
-                               than once in the list.
-
-SYSTEM                 -       If you have problems with the default
-                               settings, it might be useful to uncomment
-                               one of the defines for "SYSTEM" in the
-                               Makefile, which defines values like
-                               "-DSYSV" and something like that.
-
-CC, INCL, LIBS         -       Set these ones to the appropriate values
-                               you usually use on your system to compile.
-
-
-Now you can 'make clean' and 'make' and the game binary should be
-compiled right out of the box. I have tested it with "-Wall" on my
-system, so I hope that it compiles fine on other systems, too.
-
-The program should compile and run on Linux (of course),
-HP-UX, AIX, Net-BSD, SUN and IRIX.
-
-Have fun!
-
-Holger Schemel, 22. Oktober 1995
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..3e80faf
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,59 @@
+#=============================================================================#
+# Makefile for Rocks'n'Diamonds 1.2                                           #
+# (c) 1995-98 Holger Schemel, aeglos@valinor.owl.de                           #
+#=============================================================================#
+
+#-----------------------------------------------------------------------------#
+# configuration section                                                       #
+#-----------------------------------------------------------------------------#
+
+# specify your favorite ANSI C compiler
+CC = gcc
+
+# explicitely choose your platform, if defaults doesn't work right
+# needed for SUN/Solaris; Linux and DOS work fine with auto detection
+# PLATFORM = solaris
+# PLATFORM = unix
+# PLATFORM = dos
+
+# specify path to X11 on your system
+# if undefined, use system defaults (works fine with Linux/gcc)
+# X11_PATH = /usr/X11
+
+# specify path to install game data (graphics, sounds, levels, scores)
+# default is '.', so you can play without installing game data somewhere
+# GAME_DIR = /usr/local/games
+
+# uncomment this if your system has no joystick include file
+# JOYSTICK = -DNO_JOYSTICK
+
+# uncomment this if your system has no sound
+# SOUNDS = -DNO_SOUNDS
+
+# choose if you want to allow many global score file entries for one player
+# default is 'MANY_PER_NAME'
+# when installing the game in a multi user environment, choose this
+# SCORE_ENTRIES = ONE_PER_NAME
+# when installing the game in a single user environment, choose this
+# SCORE_ENTRIES = MANY_PER_NAME
+
+#-----------------------------------------------------------------------------#
+# you should not need to change anything below                                #
+#-----------------------------------------------------------------------------#
+
+.EXPORT_ALL_VARIABLES:
+
+MAKE = make
+
+SRC_DIR = src
+MAKE_CMD = @$(MAKE) -C $(SRC_DIR)
+
+
+all:
+       $(MAKE_CMD)
+
+solaris:
+       $(MAKE_CMD) PLATFORM=solaris
+
+clean:
+       $(MAKE_CMD) clean
diff --git a/README b/README
index e8933d0dec4a8cb4b24a8277cf7fe4da68a896d2..a16fd5edc33c3ff16b07d066222cc4a6ca383497 100644 (file)
--- a/README
+++ b/README
@@ -1,16 +1,15 @@
 Welcome to
 
-   R O C K S   ' N '   D I A M O N D S
-   -----------------------------------
+   R O C K S   ' N '   D I A M O N D S   ' 9 8
+   -------------------------------------------
 
-A game for Unix/X11 by Holger Schemel, (c) 1995 by Holger Schemel.
+A game for Unix/X11 by Holger Schemel, (c) 1995-98 by Holger Schemel.
 
 Introduction
 ============
 This is a nice little game with color graphics and sound for your
 Unix system with color X11. You need an 8-Bit color display or better.
-It is not recommended on black&white systems, and maybe not on gray
-scale systems.
+It will not work on black&white systems, and maybe not on gray scale systems.
 
 If you know the game "Boulderdash" (Commodore C64) or "Emerald Mine"
 (Amiga), you know what "ROCKS'N'DIAMONDS" is about.
@@ -18,48 +17,53 @@ If you know the game "Boulderdash" (Commodore C64) or "Emerald Mine"
 
 Getting started
 ===============
-Just 'cd' to the 'rocks_n_diamonds' directory and type 'rocksndiamonds'!
-This works only on Linux boxes, because the included binary was
-compiled for Linux systems. If you have another Unix system like
-HPUX, NetBSD or SUN, you first have to type 'make' to compile it.
-This may be needed on Linux systems, too, if you have an older
-system (kernel, libraries, ...) or if you have only ELF libraries.
+Just 'cd' to the directory 'rocksndiamonds-*' (Unix) or 'rocks-*' (DOS)
+and type 'rocksndiamonds' (Linux) or 'rocks' (DOS/Windows)!
+This works only on Linux and DOS systems, because the included binary was
+compiled for Linux (i386/libc5) (if you have the Unix package) and DOS
+(if you have the DOS/Windows package).
+If you have another Unix system like HPUX, NetBSD or SUN, you first have to
+type 'make' to compile it.
+This may be needed on Linux systems, too, depending on your kernel version,
+your libc version, your binary format, your processor, ...
 
 (The included binary was compiled on the following system:
-Kernel 1.2.13, libc 4.5.26, GCC 2.5.8, 'a.out' format)
+AMD K6, kernel 2.0.35, libc5, gcc 2.7.2.1, ELF format)
+
 
 The Menues
 ==========
 You can see eight blue circles on the left side of the eight green menu
 texts; these are buttons to activate the menu commands by simply clicking
-on them with the left mouse button. The button will then change to red.
+on them with a mouse. The button will then change to red.
 (You can control the menues over the keyboard or joystick, too. Just use
 the arrow keys and the 'Return' or 'Enter' key or, if you use a joystick,
 the appropriate direction and the fire button.)
 
-The menu 'name'
----------------
+The menu 'name' / 'team'
+------------------------
 When you start the game the first time, your login name will appear in
 the 'NAME:' field. If you want to use a different name for playing, for
 example a funny player name or a name for cheating, you can click on the
 button and enter a new name.
 
-If you choose a certain special name, you will be in a cheat mode where
-you can choose all levels without playing the lower levels before... :)
+This menu will show the text 'team' instead of 'name' if you activated
+the team (local multiplayer) mode in the setup menu. See below.
 
 The menue 'level'
 -----------------
-If you have played some levels of this game, you can choose the already
-played levels at any time, but you cannot choose the higher levels. This
-means, you can choose levels from level 0 to the highest level that you
-have ever won. This is known as your 'handicap'.
+Choose any level from the current level series you want. The former
+'handicap' limitation in choosing levels has been removed because of
+beeing annoying.
 
-If the level number is red, you have choosen a 'ready' level, if it is
-yellow, you have choosen a 'user' level, which is blank and can be
-edited by yourself with the built-in level editor (see below).
+If the level number is red, you have choosen a 'ready' level series (which
+is read-only and cannot be modified by the level editor); if it is yellow
+you have choosen a 'user' level series (which is writable and can be changed
+by the builf-in level editor). See below for using the level editor.
 
 To choose new level series, click on the button on the left and choose
-the new level serie.
+the new level series. Scroll the page up and down with the blue arrow
+buttons if there are more level series than would fit on the screen.
 
 Hall of fame
 ------------
@@ -69,46 +73,81 @@ Click again to go back to the main menu.
 Level creator
 -------------
 This brings you to the level editor, if you have switched to a 'yellow'
-level, which are empty and can be filled by yourself. See below.
+(writable) level series.
 
 Info screen
 -----------
 This screen shows you all elements which appear in the game and presents
 you the background music loops which you can listen to while playing the
-levels (only available on Linux systems).
+levels (not available on all systems).
 
 Start game
 ----------
-This will start the game.
+This will start the game. The game will be automatically recorded 'on tape'
+if you have choosen this from the setup menu (see below). If you haven't
+choosen auto-recording level, but want to record this certain game, press
+the 'record' button on the level tape recorder to start game and recording.
 
 Setup
 -----
 To change some things in the game, use the setup menu.
 You can enable/disable "Sound" (enables/disables _all_ sounds in
-the game), "Sound loops" (only allowed on Linux systems with
-VoxWare[tm] sound driver; don't worry if you never heard of it --
-it's the name of the standard Linux sound driver), "Game music"
+the game), "Sound loops" (only useful on Linux systems), "Game music"
 (can always be enabled on very fast systems [exception: you don't
 like it], on slower systems it will take some percent of CPU time
 which will slow things down a bit) and "Toons", which will forbid/
 permit the little animated toons.
 
-"Buffered Gfx" can be set to "off" on slower systems, "Fading" gives
-a nice fading effect when displaying new screens, but unfortunately
-I haven't found a system which is fast enough to display it so far.
-(Maybe this works better on highly accelerated X servers.) Better set
-this to "off" if you have a normal system...
+Forget about the setup options "Buffered Gfx" and "Fading" -- they
+have no effect at the moment and will probably disappear in future
+versions of the game. (Double-buffering is now always activated, because
+systems are fast enough now compared to 1995, when the last version of the
+game was released. Especially soft-scrolling needs the double-buffering.
+About fading from one screen to another with silly effects: Most players
+will probably deactivate it after a few minutes (see also "quick doors").)
+
+Enable "scroll delay" to avoid scrolling at each step, giving you an area
+where you can walk without scrolling until you get near enough to the screen
+border.
+
+Enable "soft scroll" for soft-scrolling. Looks nice, but may need a
+relatively fast graphics card. Systems from 1998 and later should be fast
+enough for enabling soft-scrolling.
+
+Enable "quick doors" if you are unpatient when switching between the
+several game screens where the doors have to be opened or closed. You will
+almost surely enable this if you design your own levels and therefore
+often switch between game and editor screens.
 
 Set "auto-record" to "on" if you want to automatically record each game
-to tape.
+by the built-in level tape recorder.
 
-If you have a Linux system with a joystick, you can choose the "1st" or
-the "2nd" joystick port and use "Cal. Joystick" to calibrate it. Use
-"Save and exit" after calibration to save it for later playing sessions.
+For configuration of input devices like keyboard and joysticks, choose
+the sub-menu "input devices".
 
 "Exit" quits the setup menu without saving the changes, "Save and exit"
 will save and then return to the main menu.
 
+
+Input Devices (sub-menu of the setup menu)
+------------------------------------------
+
+"Player" lets you choose one of the four players.
+
+"Device" lets you choose the input device you want for this player;
+you can choose between the keyboard and one of the maximal four supported
+joysticks. (Normally you won't have more than two joysticks, but the Linux
+joystick driver supports more than two joysticks.)
+
+"Customize" / "Calibrate" lets you calibrate a joystick or customize the
+keyboard keys used for moving left, right, up and down, "snapping" fields
+without moving towards them and placing bombs. Just press the key you want
+to use for the specified action or press Return or Enter to stay with the
+already configured values.
+
+"Exit" brings you back to the setup screen.
+
+
 Quit
 ----
 Exit the game.
@@ -141,55 +180,39 @@ Stop/Pause/Play           Game controls to stop the game, pause it and go on
 Music buttons          The three music buttons can be used to control the
                        background music loop, the 'looping' sounds and
                        all other sounds. The little red light shows you
-                       if it is enabled or disabled. On slower systems
-                       (and a 486DX33 with Soundblaster _is_ a slower
-                       system) it increases the game speed to turn off
-                       background music. You can completely turn off all
-                       sound effects in the setup menu, although it is
-                       much more fun to have them enabled when it
-                       doesn't eats up to much speed.
-
-                       (A little note: The sound server currently needs
-                       about 10% CPU time on my 486DX/33/SBPro system
-                       when playing background music. I wonder if this
-                       would get better with a better soundcard, like
-                       Gravis Ultrasound, or if only pure CPU power
-                       helps in this case...)
+                       if it is enabled or disabled.
 
 About the game itself: Of course you know Boulderdash, so you will know
 how to play the game. :)
-If not: You can move your playing figure (the smiley) with the arrow
-keys or with the joystick (if you have no joystick and even no arrow
-keys on your keyboard, you can use the keys 'i', 'j', 'k' and 'm' for
-the directions. To 'snap' a field near you without moving to it, you
+If not: You can move your playing figure with the configured keys (which
+will normally and by default be the arrow keys) or with a joystick.
+To 'snap' a field near you without moving to it, you
 can use the left fire button on your joystick (hold it down, move the
-stick to 'snap' the field, release the button) or the keys 'e', 's',
-'d' and 'x'. To place a piece of dynamite, use the right fire button
-on your joystick or use the 'b' key (and, after placing the dynamite,
-better see to move away from this field...).
+stick to 'snap' the field, release the button) or the key you have
+configured for this action (by default one of the left modifier keys like
+'Shift' or 'Control').
+To place a piece of dynamite, use the right fire button on your joystick or
+use the key you have configured for this (by default one of the right modifier
+keys, but you can change all this to what you like).
+After placing the dynamite, better see to move away from this field...).
 
 Just try the levels from the 'tutorial' level serie to see what most
 of the elements do or have a look at the info screen!
 
-Note: It is *highly recommended* to use a joystick for playing this
-game! It is possible to play it with the keyboard, but it is *much
-more fun* to play with a joystick, and some levels are very difficult
-to solve with the keyboard. So, the best platform for this game is a
-Linux system (which gives you background music, too).
 
 The Level Editor
 ================
-To build your own levels, just choose a 'yellow', empty level. If you
-cannot find any 'yellow' levels, choose a different level serie or
-choose the higher level numbers (if you have a small 'handicap' number,
-the higher levels will be skipped to reach the 'empty' levels.
-
-Another way is to create your own level series. Just add a line to the
-file 'levels/ROCKS.levelinfo' with the following entries:
-- the name of the level directory (create this directory under 'levels')
-- the name of the level serie (don't use any whitespaces within the name)
-- the 'ready' (red) levels (start with zero)
-- the 'empty' (yellow) levels (set this to some number of blank levels)
+To create your own levels, it's a good idea to start with your personal
+level series, which has been created automatically the first time you
+started the game (together with some configuration files). These personal
+files are stored in '~/.rocksndiamonds' on Unix systems and in 'userdata'
+in the current playing directory (which normally is the game directory)
+on DOS/Windows systems.
+
+The levels that come with the game are normally read-only, to avoid
+problems on multi user systems, but you can set them to 'writable' in
+the file 'levelinfo.conf' ('lvlinfo.cnf' on DOS systems) in each level
+directory.
 
 To edit a level, you can use all three mouse buttons to draw in the
 level window. Click into the elements field with one of the three buttons
@@ -217,12 +240,9 @@ played games. Just use them like a normal video recorder.
 
 Recording a game on tape:
 -------------------------
-Just press the 'record' button (the one with the red point on it) and
-either press 'Start Game' or press on 'record' or 'pause' to end the
-pause mode and start playing and recording.
-
-If you have set "auto record" in the setup menu to "on", you just have
-to press 'Start Game' as usual.
+If you have enabled "auto-record", every game will automatically be recorded,
+so you just have to press "start game". Pressing the 'record' button on the
+tape recorder will start game and record it in any case.
 
 Saving a game tape:
 -------------------
@@ -240,23 +260,38 @@ or the playing of the tape and continue by pressing 'pause' again.
 You can use either the tape recorder buttons or the game control buttons
 for this purpose.
 
+If you want to continue a previously recorded game, press 'pause' while
+playing, then 'record' to switch from 'paused playing' to 'paused recording'
+and then continue the game by pressing 'record' or 'pause'. If you want
+to fast-forward the tape to get faster to the point where you want to
+continue playing, press 'play' again while already playing, therefore
+activating 'fast forward' playing mode. Press again the 'play' button
+to enter a special playing mode: 'pause before end' will stop a few seconds
+before the end of the tape (which will in most cases stop a few seconds
+before you get killed in the previous playing recorded on that tape) --
+after the automatic stop (which enters the 'pause' mode) you can continue
+the game as described above.
+
 
 And Now Have Fun!
 =================
 Have fun playing the game, building new levels and breaking all high
-scores! ;)
+scores! :-)
+
+If you have designed new levels, mail them to me to include them in the
+next version (which will be released much earlier than again after three
+years like this version... ;-)
 
 If you have any comments, problems, suggestions, donations, flames,
 send them to
 
        aeglos@valinor.owl.de
-or     aeglos@uni-paderborn.de
 
-or Snail Mail
+or Snail Mail to
 
        Holger Schemel
-       Sennehof 28
-       33659 Bielefeld
+       Oststrasse 11a
+       33604 Bielefeld
        GERMANY
 
 Have fun,
diff --git a/RECOMMENDATIONS b/RECOMMENDATIONS
deleted file mode 100644 (file)
index a08795b..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-
-Some recommendations for more fun playing Rocks'n'Diamonds:
-===========================================================
-
-About sound
------------
-It is highly recommended to have a decent sound interface for playing
-this game (although it only comes with bad-quality 8000-Hz-samples to
-save memory and to be compatible with /dev/audio).
-
-The best sound platform is an actual Linux system with (at least) kernel
-1.2.x, because it offers some nice real-time sound features which are
-needed to have background music loops in the game.
-
-On all other systems you don't have music loops, but you still have all
-the other sounds.
-
-
-About game speed
-----------------
-You should have a relatively fast hardware.
-
-The game was developed on a i486/33 (which is three years old now),
-and you should probably better not try to run it on a slower system.
-
-You should have an accelerated graphic card; I have found out that it
-starts being playable on my 486/33 with an old ISA cirrus logic 5426
-graphic card, which has a blitter that is supported by XFree86[tm],
-but that it was nearly unplayable on a 486/66 with old, unaccelerated
-ET4000.
-
-If all works fine, you should have something around 30 frames per second.
-
-If you think that the game is to slow to play, you should try out the
-following things:
-
-- Set "Buffered Gfx" to "off" in the setup menu. Normally (on a fast
-  enough system) you should use buffered graphics, which means that
-  all graphics is drawn into an invisible Pixmap and is then copied
-  to the X11 window to avoid flickering. If you disable this double
-  buffering, the graphic is directly drawn into the window. This can
-  cause some slight flickering, but makes graphic operations roughly
-  twice as fast compared to double buffering.
-
-- Set "Game Music" to "off" in the setup menu (and maybe "sound loops"
-  too). Where disabling buffered graphics may be required with slow
-  graphics hardware, disabling sound is especially recommended on slow
-  CPU systems (486/33 and slower), because the sound server eats up a
-  significant amount of CPU time when playing long sounds.
-
-You might also notice that bigger levels tend to be slower on slow
-systems.
-
-About the option "Fading" in the setup menu: It gives a nice looking
-fading when switching to a new screen, but you should really only try
-this out if you think that you have a very fast graphics hardware.
-
-
-About music
------------
-The background music loops are ripped out from several nice music albums.
-Just have a look at the info screen to find out from which album each
-sound loop came from -- they are all very well done pieces of music, but
-unfortunately they don't sound better after converting them to 8 kHz
-samples (to conform to standard SUN /dev/audio). Maybe I change this to
-a better quality in the future, but at the moment, where the sounds
-directory has a size of nearly a megabyte, I'll stay with 8 kHz samples.
-
-So, if you have a non-Linux system (which cannot play sound loops) or
-don't like the "telephone quality" of the music loops, just get some of
-these CDs and play them while your playing the game! ;-)
-
-
-About game-play
----------------
-It is *strongly recommended* to play this game with a high-quality joystick.
-That means, throw your $10 joystick out of the window and buy a decent
-Competition-Pro Digital PC joystick or a high-quality CH Products Analog
-joystick. Believe me, it doubles the fun of playing the game.
-
-If you only have a normal Unix system (and no fine Linux system), you
-are forced to play with the keyboard. It works, but is not only less fun,
-but also more difficult with some levels, because you cannot move in
-diagonal directions with keyboard control at the moment. Another bad thing
-is that you will have some problems when pressing many keys at the same
-time. This might change in the future, when I implement a better keyboard
-handling which not only solves these problems but allows diagonal directions,
-too.
-
diff --git a/REGISTRATION b/REGISTRATION
deleted file mode 120000 (symlink)
index 5f4a260..0000000
+++ /dev/null
@@ -1 +0,0 @@
-THIS_IS_NOW_FREEWARE
\ No newline at end of file
diff --git a/THIS_IS_NOW_FREEWARE b/THIS_IS_NOW_FREEWARE
deleted file mode 100644 (file)
index c0e5098..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-
-THIS GAME IS NOW COMPLETELY FREEWARE!
-=====================================
-
-In version 0.9, I planned to offer the possibility to become a
-registered user and get special level series for registered users
-only and some more things.
-
-Now I have decided that it is better to distribute it as Freeware,
-that means: Freely distributable, but still copyrighted software.
-
-If you like this game, please send me mail about it!
-
-       aeglos@valinor.owl.de
-
-or SnailMail:
-
-       Holger Schemel
-       Sennehof 28
-       33659 Bielefeld
-       GERMANY
-
-Have fun!
index 7333fb2803193b88aeb9f734a75b5b5ab2f152c9..965c05884d96e86601f8718dd28242b0c809cc75 100644 (file)
@@ -1,40 +1,74 @@
-#
-# Makefile fuer "Rocks'n'Diamonds -- McDuffin Strikes Back"
-#
+#=============================================================================#
+# Makefile for Rocks'n'Diamonds 1.2                                           #
+# (c) 1995-98 Holger Schemel, aeglos@valinor.owl.de                           #
+#=============================================================================#
+
+ifndef PLATFORM                        # platform not defined -- try auto detection
+ifdef COMSPEC
+PLATFORM = dos
+else
+PLATFORM = unix
+endif
+endif
+
+ifdef X11_PATH                 # path to X11 specified by top level Makefile
+XINC_PATH = $(X11_PATH)/include
+XLIB_PATH = $(X11_PATH)/lib
+X11_INCL = -I$(XINC_PATH)
+X11_LIBS = -L$(XLIB_PATH)
+endif
+
+ifndef GAME_DIR                        # path to game data not defined -- try '.'
+GAME_DIR = .
+endif
+
+ifndef SCORE_ENTRIES           # number of score entries per player undefined
+SCORE_ENTRIES = MANY_PER_NAME
+endif
+
+
+# The Xpm library is no longer needed to build this program,
+# but is used to load graphics if XPM_INCLUDE_FILE is defined.
+# If you want to use the Xpm library, convert the PCX files to XPM
+# files (and you need corresponding mask files in X11 Bitmap format).
+
+# XPM_INCLUDE_FILE = -DXPM_INCLUDE_FILE="<X11/xpm.h>"
+# EXTRA_X11_LIBS = -lXpm
 
-PROGNAME = rocksndiamonds
+
+ifeq ($(PLATFORM),dos)         # DOS / Windows
+
+RM = del
+PROGNAME = ..\rocks.exe
+LIBS = -lm -lalleg
+
+else                           # Unix
 
 RM = rm -f
-CC = gcc
-CPP = $(CC) -E
-# CC = cc                              # for HP-UX and others
+PROGNAME = ../rocksndiamonds
+
+ifeq ($(PLATFORM),solaris)
+EXTRA_LIBS = -lnsl -lsocket -R$(XLIB_PATH)
+endif
+
+INCL = $(X11_INCL)
+LIBS = $(X11_LIBS) $(EXTRA_X11_LIBS) -lX11 -lm $(EXTRA_LIBS)
+
+endif
 
-GAME_DIR = -DGAME_DIR=\".\"            # path of the game and its data
-# JOYSTICK = -DNO_JOYSTICK             # no joystick
-# SOUNDS = -DNO_SOUNDS                 # no sounds
-# SCORE_ENTRIES = -DONE_PER_NAME       # only one score entry per name
-SCORE_ENTRIES = -DMANY_PER_NAME                # many score entries per name
 
-# the XPM-Library is needed to build this program:
-XPM_INCLUDE_FILE = -DXPM_INCLUDE_FILE="<X11/xpm.h>"
+CONFIG_GAME_DIR = -DGAME_DIR="\"$(GAME_DIR)\""
+CONFIG_SCORE_ENTRIES = -D$(SCORE_ENTRIES)
 
-CONFIG = $(GAME_DIR) $(SOUNDS) $(JOYSTICK)     \
-        $(SCORE_ENTRIES) $(XPM_INCLUDE_FILE)
+CONFIG = $(CONFIG_GAME_DIR) $(SOUNDS) $(JOYSTICK)      \
+        $(CONFIG_SCORE_ENTRIES) $(XPM_INCLUDE_FILE)
 
-# DEBUG = -DDEBUG -g -ansi -pedantic -Wall
-# DEBUG = -DDEBUG -g -Wall
-DEBUG = -O6
+# OPTIONS = -DDEBUG -g -Wall   # only for debugging purposes
+OPTIONS = -O3
 
-# SYSTEM = -Aa -D_HPUX_SOURCE -Dhpux   # for HP-UX (obsolete)
-# SYSTEM = -DSYSV -Ae                  # for HP-UX
-# SYSTEM = -DSYSV                      # for systems without 'usleep()'
-# INCL = -I/usr/include/X11R5          # for HP-UX and others
-# LIBS = -lXpm -lX11 -lm
-# LIBS = -L/usr/lib/X11R5 -lXpm -lX11 -lm # for HP-UX and others
-LIBS = -lXpm -lXpm -lXpm -lX11 -lm     # triple -lXpm; else I got an error...
+# SYSTEM = -DSYSV -Ae          # maybe needed for HP-UX
 
-# CFLAGS = -O2 $(CONFIG) $(SYSTEM)
-CFLAGS = $(DEBUG) $(CONFIG) $(SYSTEM) $(INCL)
+CFLAGS = $(OPTIONS) $(SYSTEM) $(INCL) $(CONFIG)
 
 SRCS = main.c          \
        init.c          \
@@ -46,7 +80,16 @@ SRCS =       main.c          \
        editor.c        \
        buttons.c       \
        files.c         \
-       sound.c
+       tape.c          \
+       sound.c         \
+       joystick.c      \
+       cartoons.c      \
+       random.c        \
+       pcx.c           \
+       image.c         \
+       network.c       \
+       netserv.c       \
+       msdos.c
 
 OBJS = main.o          \
        init.o          \
@@ -58,20 +101,25 @@ OBJS =     main.o          \
        editor.o        \
        buttons.o       \
        files.o         \
-       sound.o
+       tape.o          \
+       sound.o         \
+       joystick.o      \
+       cartoons.o      \
+       random.o        \
+       pcx.o           \
+       image.o         \
+       network.o       \
+       netserv.o       \
+       msdos.o
+
+all:   $(PROGNAME)
 
-all:   $(OBJS)
+$(PROGNAME):   $(OBJS)
        $(CC) $(CFLAGS) $(OBJS) $(LIBS) -o $(PROGNAME)
 
 .c.o:
        $(CC) $(CFLAGS) -c $*.c
 
 clean:
-       $(RM) $(OBJS)
-
-depend:
-       for i in $(SRCS); do $(CPP) $(CFLAGS) -M $$i; done > .depend
-
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
+       $(RM) *.o
+       $(RM) $(PROGNAME)
index 8b825451584020ed9110305eaf1481da869dce89..62c5a1c1332a2f794f146cba92d05f5def4a5114 100644 (file)
@@ -1,13 +1,12 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  buttons.c                                               *
 ***********************************************************/
 #include "tools.h"
 #include "misc.h"
 #include "editor.h"
+#include "tape.h"
+
+/* some positions in the video tape control window */
+#define VIDEO_BUTTON_EJECT_XPOS        (VIDEO_CONTROL_XPOS + 0 * VIDEO_BUTTON_XSIZE)
+#define VIDEO_BUTTON_STOP_XPOS (VIDEO_CONTROL_XPOS + 1 * VIDEO_BUTTON_XSIZE)
+#define VIDEO_BUTTON_PAUSE_XPOS        (VIDEO_CONTROL_XPOS + 2 * VIDEO_BUTTON_XSIZE)
+#define VIDEO_BUTTON_REC_XPOS  (VIDEO_CONTROL_XPOS + 3 * VIDEO_BUTTON_XSIZE)
+#define VIDEO_BUTTON_PLAY_XPOS (VIDEO_CONTROL_XPOS + 4 * VIDEO_BUTTON_XSIZE)
+#define VIDEO_BUTTON_ANY_YPOS  (VIDEO_CONTROL_YPOS)
+#define VIDEO_DATE_LABEL_XPOS  (VIDEO_DISPLAY1_XPOS)
+#define VIDEO_DATE_LABEL_YPOS  (VIDEO_DISPLAY1_YPOS)
+#define VIDEO_DATE_LABEL_XSIZE (VIDEO_DISPLAY_XSIZE)
+#define VIDEO_DATE_LABEL_YSIZE (VIDEO_DISPLAY_YSIZE)
+#define VIDEO_DATE_XPOS                (VIDEO_DISPLAY1_XPOS+1)
+#define VIDEO_DATE_YPOS                (VIDEO_DISPLAY1_YPOS+14)
+#define VIDEO_DATE_XSIZE       (VIDEO_DISPLAY_XSIZE)
+#define VIDEO_DATE_YSIZE       16
+#define VIDEO_REC_LABEL_XPOS   (VIDEO_DISPLAY2_XPOS)
+#define VIDEO_REC_LABEL_YPOS   (VIDEO_DISPLAY2_YPOS)
+#define VIDEO_REC_LABEL_XSIZE  20
+#define VIDEO_REC_LABEL_YSIZE  12
+#define VIDEO_REC_SYMBOL_XPOS  (VIDEO_DISPLAY2_XPOS+20)
+#define VIDEO_REC_SYMBOL_YPOS  (VIDEO_DISPLAY2_YPOS)
+#define VIDEO_REC_SYMBOL_XSIZE 16
+#define VIDEO_REC_SYMBOL_YSIZE 16
+#define VIDEO_PLAY_LABEL_XPOS  (VIDEO_DISPLAY2_XPOS+65)
+#define VIDEO_PLAY_LABEL_YPOS  (VIDEO_DISPLAY2_YPOS)
+#define VIDEO_PLAY_LABEL_XSIZE 22
+#define VIDEO_PLAY_LABEL_YSIZE 12
+#define VIDEO_PLAY_SYMBOL_XPOS (VIDEO_DISPLAY2_XPOS+52)
+#define VIDEO_PLAY_SYMBOL_YPOS (VIDEO_DISPLAY2_YPOS)
+#define VIDEO_PLAY_SYMBOL_XSIZE        11
+#define VIDEO_PLAY_SYMBOL_YSIZE        13
+#define VIDEO_PAUSE_LABEL_XPOS (VIDEO_DISPLAY2_XPOS)
+#define VIDEO_PAUSE_LABEL_YPOS (VIDEO_DISPLAY2_YPOS+20)
+#define VIDEO_PAUSE_LABEL_XSIZE        35
+#define VIDEO_PAUSE_LABEL_YSIZE        8
+#define VIDEO_PAUSE_SYMBOL_XPOS        (VIDEO_DISPLAY2_XPOS+35)
+#define VIDEO_PAUSE_SYMBOL_YPOS        (VIDEO_DISPLAY2_YPOS)
+#define VIDEO_PAUSE_SYMBOL_XSIZE 17
+#define VIDEO_PAUSE_SYMBOL_YSIZE 13
+#define VIDEO_TIME_XPOS                (VIDEO_DISPLAY2_XPOS+38)
+#define VIDEO_TIME_YPOS                (VIDEO_DISPLAY2_YPOS+14)
+#define VIDEO_TIME_XSIZE       50
+#define VIDEO_TIME_YSIZE       16
+
+/* special */
+#define VIDEO_PBEND_LABEL_XPOS 6
+#define VIDEO_PBEND_LABEL_YPOS 220
+#define VIDEO_PBEND_LABEL_XSIZE        35
+#define VIDEO_PBEND_LABEL_YSIZE        30
+
+#define ON_VIDEO_BUTTON(x,y)   ((x)>=(VX+VIDEO_CONTROL_XPOS) &&        \
+                                (x)< (VX+VIDEO_CONTROL_XPOS +          \
+                                      VIDEO_CONTROL_XSIZE) &&          \
+                                (y)>=(VY+VIDEO_CONTROL_YPOS) &&        \
+                                (y)< (VY+VIDEO_CONTROL_YPOS +          \
+                                      VIDEO_CONTROL_YSIZE))
+#define VIDEO_BUTTON(x)                (((x)-(VX+VIDEO_CONTROL_XPOS))/VIDEO_BUTTON_XSIZE)
+
+#define VIDEO_STATE_OFF                (VIDEO_STATE_PLAY_OFF   |       \
+                                VIDEO_STATE_REC_OFF    |       \
+                                VIDEO_STATE_PAUSE_OFF  |       \
+                                VIDEO_STATE_FFWD_OFF   |       \
+                                VIDEO_STATE_PBEND_OFF  |       \
+                                VIDEO_STATE_DATE_OFF   |       \
+                                VIDEO_STATE_TIME_OFF)
+#define VIDEO_PRESS_OFF                (VIDEO_PRESS_PLAY_OFF   |       \
+                                VIDEO_PRESS_REC_OFF    |       \
+                                VIDEO_PRESS_PAUSE_OFF  |       \
+                                VIDEO_PRESS_STOP_OFF   |       \
+                                VIDEO_PRESS_EJECT_OFF)
+#define VIDEO_ALL_OFF          (VIDEO_STATE_OFF | VIDEO_PRESS_OFF)
+
+#define VIDEO_STATE_ON         (VIDEO_STATE_PLAY_ON    |       \
+                                VIDEO_STATE_REC_ON     |       \
+                                VIDEO_STATE_PAUSE_ON   |       \
+                                VIDEO_STATE_FFWD_ON    |       \
+                                VIDEO_STATE_PBEND_ON   |       \
+                                VIDEO_STATE_DATE_ON    |       \
+                                VIDEO_STATE_TIME_ON)
+#define VIDEO_PRESS_ON         (VIDEO_PRESS_PLAY_ON    |       \
+                                VIDEO_PRESS_REC_ON     |       \
+                                VIDEO_PRESS_PAUSE_ON   |       \
+                                VIDEO_PRESS_STOP_ON    |       \
+                                VIDEO_PRESS_EJECT_ON)
+#define VIDEO_ALL_ON           (VIDEO_STATE_ON | VIDEO_PRESS_ON)
+
+#define VIDEO_STATE            (VIDEO_STATE_ON | VIDEO_STATE_OFF)
+#define VIDEO_PRESS            (VIDEO_PRESS_ON | VIDEO_PRESS_OFF)
+#define VIDEO_ALL              (VIDEO_ALL_ON | VIDEO_ALL_OFF)
+
+
+/* some positions in the sound control window */
+#define SOUND_BUTTON_XSIZE     30
+#define SOUND_BUTTON_YSIZE     30
+#define SOUND_CONTROL_XPOS     5
+#define SOUND_CONTROL_YPOS     245
+#define SOUND_CONTROL_XSIZE     (3*SOUND_BUTTON_XSIZE)
+#define SOUND_CONTROL_YSIZE     (1*SOUND_BUTTON_YSIZE)
+#define SOUND_BUTTON_MUSIC_XPOS         (SOUND_CONTROL_XPOS + 0 * SOUND_BUTTON_XSIZE)
+#define SOUND_BUTTON_LOOPS_XPOS         (SOUND_CONTROL_XPOS + 1 * SOUND_BUTTON_XSIZE)
+#define SOUND_BUTTON_SIMPLE_XPOS (SOUND_CONTROL_XPOS + 2 * SOUND_BUTTON_XSIZE)
+#define SOUND_BUTTON_ANY_YPOS   (SOUND_CONTROL_YPOS)
+
+#define ON_SOUND_BUTTON(x,y)   ((x)>=(DX+SOUND_CONTROL_XPOS) &&        \
+                                (x)< (DX+SOUND_CONTROL_XPOS +          \
+                                      SOUND_CONTROL_XSIZE) &&          \
+                                (y)>=(DY+SOUND_CONTROL_YPOS) &&        \
+                                (y)< (DY+SOUND_CONTROL_YPOS +          \
+                                      SOUND_CONTROL_YSIZE))
+#define SOUND_BUTTON(x)                (((x)-(DX+SOUND_CONTROL_XPOS))/SOUND_BUTTON_XSIZE)
+
+/* some positions in the game control window */
+#define GAME_BUTTON_STOP_XPOS  (GAME_CONTROL_XPOS + 0 * GAME_BUTTON_XSIZE)
+#define GAME_BUTTON_PAUSE_XPOS (GAME_CONTROL_XPOS + 1 * GAME_BUTTON_XSIZE)
+#define GAME_BUTTON_PLAY_XPOS  (GAME_CONTROL_XPOS + 2 * GAME_BUTTON_XSIZE)
+#define GAME_BUTTON_ANY_YPOS   (GAME_CONTROL_YPOS)
+
+#define ON_GAME_BUTTON(x,y)    ((x)>=(DX+GAME_CONTROL_XPOS) && \
+                                (x)< (DX+GAME_CONTROL_XPOS +           \
+                                      GAME_CONTROL_XSIZE) &&           \
+                                (y)>=(DY+GAME_CONTROL_YPOS) && \
+                                (y)< (DY+GAME_CONTROL_YPOS +           \
+                                      GAME_CONTROL_YSIZE))
+#define GAME_BUTTON(x)         (((x)-(DX+GAME_CONTROL_XPOS))/GAME_BUTTON_XSIZE)
+
+/* some positions in the asking window */
+#define OK_BUTTON_XPOS         2
+#define OK_BUTTON_YPOS         250
+#define OK_BUTTON_GFX_YPOS     0
+#define OK_BUTTON_XSIZE                46
+#define OK_BUTTON_YSIZE                28
+#define NO_BUTTON_XPOS         52
+#define NO_BUTTON_YPOS         OK_BUTTON_YPOS
+#define NO_BUTTON_XSIZE                OK_BUTTON_XSIZE
+#define NO_BUTTON_YSIZE                OK_BUTTON_YSIZE
+#define CONFIRM_BUTTON_XPOS    2
+#define CONFIRM_BUTTON_GFX_YPOS        30
+#define CONFIRM_BUTTON_YPOS    OK_BUTTON_YPOS
+#define CONFIRM_BUTTON_XSIZE   96
+#define CONFIRM_BUTTON_YSIZE   OK_BUTTON_YSIZE
+
+#define ON_YESNO_BUTTON(x,y)   (((x)>=(DX+OK_BUTTON_XPOS) &&           \
+                                 (x)< (DX+OK_BUTTON_XPOS +             \
+                                       OK_BUTTON_XSIZE) &&             \
+                                 (y)>=(DY+OK_BUTTON_YPOS) &&           \
+                                 (y)< (DY+OK_BUTTON_YPOS +             \
+                                       OK_BUTTON_YSIZE)) ||            \
+                                ((x)>=(DX+NO_BUTTON_XPOS) &&           \
+                                 (x)< (DX+NO_BUTTON_XPOS +             \
+                                       NO_BUTTON_XSIZE) &&             \
+                                 (y)>=(DY+NO_BUTTON_YPOS) &&           \
+                                 (y)< (DY+NO_BUTTON_YPOS +             \
+                                       NO_BUTTON_YSIZE)))
+#define ON_CONFIRM_BUTTON(x,y) (((x)>=(DX+CONFIRM_BUTTON_XPOS) &&      \
+                                 (x)< (DX+CONFIRM_BUTTON_XPOS +        \
+                                       CONFIRM_BUTTON_XSIZE) &&        \
+                                 (y)>=(DY+CONFIRM_BUTTON_YPOS) &&      \
+                                 (y)< (DY+CONFIRM_BUTTON_YPOS +        \
+                                       CONFIRM_BUTTON_YSIZE)))
+#define YESNO_BUTTON(x)                (((x)-(DX+OK_BUTTON_XPOS))/OK_BUTTON_XSIZE)
+
+/* some positions in the choose player window */
+#define PLAYER_BUTTON_XSIZE    30
+#define PLAYER_BUTTON_YSIZE    30
+#define PLAYER_BUTTON_GFX_XPOS 5
+#define PLAYER_BUTTON_GFX_YPOS (215-30)
+#define PLAYER_CONTROL_XPOS    (5 + PLAYER_BUTTON_XSIZE/2)
+#define PLAYER_CONTROL_YPOS    (215 - PLAYER_BUTTON_YSIZE/2)
+#define PLAYER_CONTROL_XSIZE   (2*PLAYER_BUTTON_XSIZE)
+#define PLAYER_CONTROL_YSIZE   (2*PLAYER_BUTTON_YSIZE)
+#define PLAYER_BUTTON_1_XPOS   (PLAYER_CONTROL_XPOS + 0 * PLAYER_BUTTON_XSIZE)
+#define PLAYER_BUTTON_2_XPOS   (PLAYER_CONTROL_XPOS + 1 * PLAYER_BUTTON_XSIZE)
+#define PLAYER_BUTTON_3_XPOS   (PLAYER_CONTROL_XPOS + 0 * PLAYER_BUTTON_XSIZE)
+#define PLAYER_BUTTON_4_XPOS   (PLAYER_CONTROL_XPOS + 1 * PLAYER_BUTTON_XSIZE)
+#define PLAYER_BUTTON_1_YPOS   (PLAYER_CONTROL_YPOS + 0 * PLAYER_BUTTON_YSIZE)
+#define PLAYER_BUTTON_2_YPOS   (PLAYER_CONTROL_YPOS + 0 * PLAYER_BUTTON_YSIZE)
+#define PLAYER_BUTTON_3_YPOS   (PLAYER_CONTROL_YPOS + 1 * PLAYER_BUTTON_YSIZE)
+#define PLAYER_BUTTON_4_YPOS   (PLAYER_CONTROL_YPOS + 1 * PLAYER_BUTTON_YSIZE)
+
+#define ON_PLAYER_BUTTON(x,y)  ((x)>=(DX+PLAYER_CONTROL_XPOS) &&       \
+                                (x)< (DX+PLAYER_CONTROL_XPOS +         \
+                                      PLAYER_CONTROL_XSIZE) &&         \
+                                (y)>=(DY+PLAYER_CONTROL_YPOS) &&       \
+                                (y)< (DY+PLAYER_CONTROL_YPOS +         \
+                                      PLAYER_CONTROL_YSIZE))
+#define PLAYER_BUTTON(x,y)     ((((x)-(DX+PLAYER_CONTROL_XPOS)) /      \
+                                 PLAYER_BUTTON_XSIZE) + 2 *            \
+                                (((y)-(DY+PLAYER_CONTROL_YPOS)) /      \
+                                 PLAYER_BUTTON_YSIZE))
+
+
+/* some definitions for the editor control window */
+
+#define ON_EDIT_BUTTON(x,y)    (((x)>=(VX+ED_BUTTON_CTRL_XPOS) &&      \
+                                 (x)< (VX+ED_BUTTON_CTRL_XPOS +        \
+                                       ED_BUTTON_CTRL_XSIZE) &&        \
+                                 (y)>=(VY+ED_BUTTON_CTRL_YPOS) &&      \
+                                 (y)< (VY+ED_BUTTON_CTRL_YPOS +        \
+                                       ED_BUTTON_CTRL_YSIZE +          \
+                                       ED_BUTTON_FILL_YSIZE)) ||       \
+                                ((x)>=(VX+ED_BUTTON_LEFT_XPOS) &&      \
+                                 (x)< (VX+ED_BUTTON_LEFT_XPOS +        \
+                                       ED_BUTTON_LEFT_XSIZE +          \
+                                       ED_BUTTON_UP_XSIZE +            \
+                                       ED_BUTTON_RIGHT_XSIZE) &&       \
+                                 (y)>=(VY+ED_BUTTON_LEFT_YPOS) &&      \
+                                 (y)< (VY+ED_BUTTON_LEFT_YPOS +        \
+                                       ED_BUTTON_LEFT_YSIZE)) ||       \
+                                ((x)>=(VX+ED_BUTTON_UP_XPOS) &&        \
+                                 (x)< (VX+ED_BUTTON_UP_XPOS +          \
+                                       ED_BUTTON_UP_XSIZE) &&          \
+                                 (y)>=(VY+ED_BUTTON_UP_YPOS) &&        \
+                                 (y)< (VY+ED_BUTTON_UP_YPOS +          \
+                                       ED_BUTTON_UP_YSIZE +            \
+                                       ED_BUTTON_DOWN_YSIZE)))
+
+#define ON_CTRL_BUTTON(x,y)    ((x)>=(VX+ED_BUTTON_EDIT_XPOS) &&       \
+                                (x)< (VX+ED_BUTTON_EDIT_XPOS +         \
+                                      ED_BUTTON_EDIT_XSIZE) &&         \
+                                (y)>=(VY+ED_BUTTON_EDIT_YPOS) &&       \
+                                (y)< (VY+ED_BUTTON_EDIT_YPOS +         \
+                                      ED_BUTTON_EDIT_YSIZE +           \
+                                      ED_BUTTON_CLEAR_YSIZE +          \
+                                      ED_BUTTON_UNDO_YSIZE +           \
+                                      ED_BUTTON_EXIT_YSIZE))
+
+#define ON_ELEM_BUTTON(x,y)    (((x)>=(DX+ED_BUTTON_EUP_XPOS) &&       \
+                                 (x)< (DX+ED_BUTTON_EUP_XPOS +         \
+                                       ED_BUTTON_EUP_XSIZE) &&         \
+                                 (y)>=(DY+ED_BUTTON_EUP_YPOS) &&       \
+                                 (y)< (DY+ED_BUTTON_EUP_YPOS +         \
+                                       ED_BUTTON_EUP_YSIZE)) ||        \
+                                ((x)>=(DX+ED_BUTTON_EDOWN_XPOS) &&     \
+                                 (x)< (DX+ED_BUTTON_EDOWN_XPOS +       \
+                                       ED_BUTTON_EDOWN_XSIZE) &&       \
+                                 (y)>=(DY+ED_BUTTON_EDOWN_YPOS) &&     \
+                                 (y)< (DY+ED_BUTTON_EDOWN_YPOS +       \
+                                       ED_BUTTON_EDOWN_YSIZE)) ||      \
+                                ((x)>=(DX+ED_BUTTON_ELEM_XPOS) &&      \
+                                 (x)< (DX+ED_BUTTON_ELEM_XPOS +        \
+                                       MAX_ELEM_X*ED_BUTTON_ELEM_XSIZE) && \
+                                 (y)>=(DY+ED_BUTTON_ELEM_YPOS) &&      \
+                                 (y)< (DY+ED_BUTTON_ELEM_YPOS +        \
+                                       MAX_ELEM_Y*ED_BUTTON_ELEM_YSIZE)))
+
+#define ON_COUNT_BUTTON(x,y)   (((((x)>=ED_COUNT_GADGET_XPOS &&        \
+                                   (x)<(ED_COUNT_GADGET_XPOS +         \
+                                        ED_BUTTON_MINUS_XSIZE)) ||     \
+                                  ((x)>=(ED_COUNT_GADGET_XPOS +        \
+                                         (ED_BUTTON_PLUS_XPOS -        \
+                                          ED_BUTTON_MINUS_XPOS)) &&    \
+                                   (x)<(ED_COUNT_GADGET_XPOS +         \
+                                        (ED_BUTTON_PLUS_XPOS -         \
+                                         ED_BUTTON_MINUS_XPOS) +       \
+                                        ED_BUTTON_PLUS_XSIZE))) &&     \
+                                 ((y)>=ED_COUNT_GADGET_YPOS &&         \
+                                  (y)<(ED_COUNT_GADGET_YPOS +          \
+                                       16*ED_COUNT_GADGET_YSIZE)) &&   \
+                                 (((y)-ED_COUNT_GADGET_YPOS) %         \
+                                  ED_COUNT_GADGET_YSIZE) <             \
+                                 ED_BUTTON_MINUS_YSIZE) ||             \
+                                ((((x)>=ED_SIZE_GADGET_XPOS &&         \
+                                   (x)<(ED_SIZE_GADGET_XPOS +          \
+                                        ED_BUTTON_MINUS_XSIZE)) ||     \
+                                  ((x)>=(ED_SIZE_GADGET_XPOS +         \
+                                         (ED_BUTTON_PLUS_XPOS -        \
+                                          ED_BUTTON_MINUS_XPOS)) &&    \
+                                   (x)<(ED_SIZE_GADGET_XPOS +          \
+                                        (ED_BUTTON_PLUS_XPOS -         \
+                                         ED_BUTTON_MINUS_XPOS) +       \
+                                        ED_BUTTON_PLUS_XSIZE))) &&     \
+                                 ((y)>=ED_SIZE_GADGET_YPOS &&          \
+                                  (y)<(ED_SIZE_GADGET_YPOS +           \
+                                       2*ED_SIZE_GADGET_YSIZE)) &&     \
+                                 (((y)-ED_SIZE_GADGET_YPOS) %          \
+                                  ED_SIZE_GADGET_YSIZE) <              \
+                                 ED_BUTTON_MINUS_YSIZE))
+
+#define EDIT_BUTTON(x,y)       (((y) < (VY + ED_BUTTON_CTRL_YPOS +     \
+                                        ED_BUTTON_CTRL_YSIZE)) ? 0 :   \
+                                ((y) < (VY + ED_BUTTON_CTRL_YPOS +     \
+                                        ED_BUTTON_CTRL_YSIZE +         \
+                                        ED_BUTTON_FILL_YSIZE)) ? 1 :   \
+                                ((x) < (VX + ED_BUTTON_LEFT_XPOS +     \
+                                        ED_BUTTON_LEFT_XSIZE) ? 2 :    \
+                                 (x) > (VX + ED_BUTTON_LEFT_XPOS +     \
+                                        ED_BUTTON_LEFT_XSIZE +         \
+                                        ED_BUTTON_UP_XSIZE) ? 5 :      \
+                                 3+(((y)-(VY + ED_BUTTON_CTRL_YPOS +   \
+                                          ED_BUTTON_CTRL_YSIZE +       \
+                                          ED_BUTTON_FILL_YSIZE)) /     \
+                                    ED_BUTTON_UP_YSIZE)))
+
+#define CTRL_BUTTON(x,y)       (((y) < (VY + ED_BUTTON_EDIT_YPOS +     \
+                                        ED_BUTTON_EDIT_YSIZE)) ? 0 :   \
+                                1+(((y)-(VY + ED_BUTTON_EDIT_YPOS +    \
+                                        ED_BUTTON_EDIT_YSIZE)) /       \
+                                   ED_BUTTON_CLEAR_YSIZE))
+
+#define ELEM_BUTTON(x,y)       (((y) < (DY + ED_BUTTON_EUP_YPOS +      \
+                                        ED_BUTTON_EUP_YSIZE)) ? 0 :    \
+                                ((y) > (DY + ED_BUTTON_EDOWN_YPOS)) ? 1 : \
+                                2+(((y) - (DY + ED_BUTTON_ELEM_YPOS)) /   \
+                                ED_BUTTON_ELEM_YSIZE)*MAX_ELEM_X +     \
+                                ((x) - (DX + ED_BUTTON_ELEM_XPOS)) /   \
+                                ED_BUTTON_ELEM_XSIZE)
+
+#define COUNT_BUTTON(x,y)      ((x) < ED_SIZE_GADGET_XPOS ?            \
+                                ((((y) - ED_COUNT_GADGET_YPOS) /       \
+                                  ED_COUNT_GADGET_YSIZE)*2 +           \
+                                 ((x) < (ED_COUNT_GADGET_XPOS +        \
+                                         ED_BUTTON_MINUS_XSIZE) ? 0 : 1)) : \
+                                32+((((y) - ED_SIZE_GADGET_YPOS) /     \
+                                     ED_SIZE_GADGET_YSIZE)*2 +         \
+                                    ((x) < (ED_SIZE_GADGET_XPOS +      \
+                                            ED_BUTTON_MINUS_XSIZE) ? 0 : 1)))
 
 /****************************************************************/
 /********** drawing buttons and corresponding displays **********/
 void DrawVideoDisplay(unsigned long state, unsigned long value)
 {
   int i;
-  int part1 = 0, part2 = 1;
+  int part_label = 0, part_symbol = 1;
   int xpos = 0, ypos = 1, xsize = 2, ysize = 3;
   static char *monatsname[12] =
   {
@@ -33,57 +350,70 @@ void DrawVideoDisplay(unsigned long state, unsigned long value)
   };
   static int video_pos[10][2][4] =
   {
-    VIDEO_PLAY_LABEL_XPOS, VIDEO_PLAY_LABEL_YPOS,
-    VIDEO_PLAY_LABEL_XSIZE,VIDEO_PLAY_LABEL_YSIZE,
-    VIDEO_PLAY_SYMBOL_XPOS, VIDEO_PLAY_SYMBOL_YPOS,
-    VIDEO_PLAY_SYMBOL_XSIZE,VIDEO_PLAY_SYMBOL_YSIZE,
-
-    VIDEO_REC_LABEL_XPOS, VIDEO_REC_LABEL_YPOS,
-    VIDEO_REC_LABEL_XSIZE,VIDEO_REC_LABEL_YSIZE,
-    VIDEO_REC_SYMBOL_XPOS, VIDEO_REC_SYMBOL_YPOS,
-    VIDEO_REC_SYMBOL_XSIZE,VIDEO_REC_SYMBOL_YSIZE,
-
-    VIDEO_PAUSE_LABEL_XPOS, VIDEO_PAUSE_LABEL_YPOS,
-    VIDEO_PAUSE_LABEL_XSIZE,VIDEO_PAUSE_LABEL_YSIZE,
-    VIDEO_PAUSE_SYMBOL_XPOS, VIDEO_PAUSE_SYMBOL_YPOS,
-    VIDEO_PAUSE_SYMBOL_XSIZE,VIDEO_PAUSE_SYMBOL_YSIZE,
-
-    VIDEO_DATE_LABEL_XPOS, VIDEO_DATE_LABEL_YPOS,
-    VIDEO_DATE_LABEL_XSIZE,VIDEO_DATE_LABEL_YSIZE,
-    VIDEO_DATE_XPOS, VIDEO_DATE_YPOS,
-    VIDEO_DATE_XSIZE,VIDEO_DATE_YSIZE,
-
-    0,0,
-    0,0,
-    VIDEO_TIME_XPOS, VIDEO_TIME_YPOS,
-    VIDEO_TIME_XSIZE,VIDEO_TIME_YSIZE,
-
-    VIDEO_BUTTON_PLAY_XPOS, VIDEO_BUTTON_ANY_YPOS,
-    VIDEO_BUTTON_XSIZE,VIDEO_BUTTON_YSIZE,
-    0,0,
-    0,0,
-
-    VIDEO_BUTTON_REC_XPOS, VIDEO_BUTTON_ANY_YPOS,
-    VIDEO_BUTTON_XSIZE,VIDEO_BUTTON_YSIZE,
-    0,0,
-    0,0,
-
-    VIDEO_BUTTON_PAUSE_XPOS, VIDEO_BUTTON_ANY_YPOS,
-    VIDEO_BUTTON_XSIZE,VIDEO_BUTTON_YSIZE,
-    0,0,
-    0,0,
-
-    VIDEO_BUTTON_STOP_XPOS, VIDEO_BUTTON_ANY_YPOS,
-    VIDEO_BUTTON_XSIZE,VIDEO_BUTTON_YSIZE,
-    0,0,
-    0,0,
-
-    VIDEO_BUTTON_EJECT_XPOS, VIDEO_BUTTON_ANY_YPOS,
-    VIDEO_BUTTON_XSIZE,VIDEO_BUTTON_YSIZE,
-    0,0,
-    0,0
+    {{ VIDEO_PLAY_LABEL_XPOS, VIDEO_PLAY_LABEL_YPOS,
+       VIDEO_PLAY_LABEL_XSIZE,VIDEO_PLAY_LABEL_YSIZE },
+     { VIDEO_PLAY_SYMBOL_XPOS, VIDEO_PLAY_SYMBOL_YPOS,
+       VIDEO_PLAY_SYMBOL_XSIZE,VIDEO_PLAY_SYMBOL_YSIZE }},
+
+    {{ VIDEO_REC_LABEL_XPOS, VIDEO_REC_LABEL_YPOS,
+       VIDEO_REC_LABEL_XSIZE,VIDEO_REC_LABEL_YSIZE },
+     { VIDEO_REC_SYMBOL_XPOS, VIDEO_REC_SYMBOL_YPOS,
+       VIDEO_REC_SYMBOL_XSIZE,VIDEO_REC_SYMBOL_YSIZE }},
+
+    {{ VIDEO_PAUSE_LABEL_XPOS, VIDEO_PAUSE_LABEL_YPOS,
+       VIDEO_PAUSE_LABEL_XSIZE,VIDEO_PAUSE_LABEL_YSIZE },
+     { VIDEO_PAUSE_SYMBOL_XPOS, VIDEO_PAUSE_SYMBOL_YPOS,
+       VIDEO_PAUSE_SYMBOL_XSIZE,VIDEO_PAUSE_SYMBOL_YSIZE }},
+
+    {{ VIDEO_DATE_LABEL_XPOS, VIDEO_DATE_LABEL_YPOS,
+       VIDEO_DATE_LABEL_XSIZE,VIDEO_DATE_LABEL_YSIZE },
+     { VIDEO_DATE_XPOS, VIDEO_DATE_YPOS,
+       VIDEO_DATE_XSIZE,VIDEO_DATE_YSIZE }},
+
+    {{ 0,0,
+       0,0 },
+     { VIDEO_TIME_XPOS, VIDEO_TIME_YPOS,
+       VIDEO_TIME_XSIZE,VIDEO_TIME_YSIZE }},
+
+    {{ VIDEO_BUTTON_PLAY_XPOS, VIDEO_BUTTON_ANY_YPOS,
+       VIDEO_BUTTON_XSIZE,VIDEO_BUTTON_YSIZE },
+     { 0,0,
+       0,0 }},
+
+    {{ VIDEO_BUTTON_REC_XPOS, VIDEO_BUTTON_ANY_YPOS,
+       VIDEO_BUTTON_XSIZE,VIDEO_BUTTON_YSIZE },
+     { 0,0,
+       0,0 }},
+
+    {{ VIDEO_BUTTON_PAUSE_XPOS, VIDEO_BUTTON_ANY_YPOS,
+       VIDEO_BUTTON_XSIZE,VIDEO_BUTTON_YSIZE },
+     { 0,0,
+       0,0 }},
+
+    {{ VIDEO_BUTTON_STOP_XPOS, VIDEO_BUTTON_ANY_YPOS,
+       VIDEO_BUTTON_XSIZE,VIDEO_BUTTON_YSIZE },
+     { 0,0,
+       0,0 }},
+
+    {{ VIDEO_BUTTON_EJECT_XPOS, VIDEO_BUTTON_ANY_YPOS,
+       VIDEO_BUTTON_XSIZE,VIDEO_BUTTON_YSIZE },
+     { 0,0,
+       0,0 }}
   };
 
+  if (state & VIDEO_STATE_PBEND_OFF)
+  {
+    int cx = DOOR_GFX_PAGEX3, cy = DOOR_GFX_PAGEY2;
+
+    XCopyArea(display,pix[PIX_DOOR],drawto,gc,
+             cx + VIDEO_REC_LABEL_XPOS,
+             cy + VIDEO_REC_LABEL_YPOS,
+             VIDEO_PBEND_LABEL_XSIZE,
+             VIDEO_PBEND_LABEL_YSIZE,
+             VX + VIDEO_REC_LABEL_XPOS,
+             VY + VIDEO_REC_LABEL_YPOS);
+  }
+
   for(i=0;i<20;i++)
   {
     if (state & (1<<i))
@@ -95,25 +425,51 @@ void DrawVideoDisplay(unsigned long state, unsigned long value)
       else
        cx = DOOR_GFX_PAGEX3;   /* i gerade => STATE_OFF / PRESS_ON */
 
-      if (video_pos[pos][part1][0])
+      if (video_pos[pos][part_label][0] && value != VIDEO_DISPLAY_SYMBOL_ONLY)
        XCopyArea(display,pix[PIX_DOOR],drawto,gc,
-                 cx + video_pos[pos][part1][xpos],
-                 cy + video_pos[pos][part1][ypos],
-                 video_pos[pos][part1][xsize],
-                 video_pos[pos][part1][ysize],
-                 VX + video_pos[pos][part1][xpos],
-                 VY + video_pos[pos][part1][ypos]);
-      if (video_pos[pos][part2][0])
+                 cx + video_pos[pos][part_label][xpos],
+                 cy + video_pos[pos][part_label][ypos],
+                 video_pos[pos][part_label][xsize],
+                 video_pos[pos][part_label][ysize],
+                 VX + video_pos[pos][part_label][xpos],
+                 VY + video_pos[pos][part_label][ypos]);
+      if (video_pos[pos][part_symbol][0] && value != VIDEO_DISPLAY_LABEL_ONLY)
        XCopyArea(display,pix[PIX_DOOR],drawto,gc,
-                 cx + video_pos[pos][part2][xpos],
-                 cy + video_pos[pos][part2][ypos],
-                 video_pos[pos][part2][xsize],
-                 video_pos[pos][part2][ysize],
-                 VX + video_pos[pos][part2][xpos],
-                 VY + video_pos[pos][part2][ypos]);
+                 cx + video_pos[pos][part_symbol][xpos],
+                 cy + video_pos[pos][part_symbol][ypos],
+                 video_pos[pos][part_symbol][xsize],
+                 video_pos[pos][part_symbol][ysize],
+                 VX + video_pos[pos][part_symbol][xpos],
+                 VY + video_pos[pos][part_symbol][ypos]);
     }
   }
 
+  if (state & VIDEO_STATE_FFWD_ON)
+  {
+    int cx = DOOR_GFX_PAGEX4, cy = DOOR_GFX_PAGEY2;
+
+    XCopyArea(display,pix[PIX_DOOR],drawto,gc,
+             cx + VIDEO_PLAY_SYMBOL_XPOS,
+             cy + VIDEO_PLAY_SYMBOL_YPOS,
+             VIDEO_PLAY_SYMBOL_XSIZE - 2,
+             VIDEO_PLAY_SYMBOL_YSIZE,
+             VX + VIDEO_PLAY_SYMBOL_XPOS - 9,
+             VY + VIDEO_PLAY_SYMBOL_YPOS);
+  }
+
+  if (state & VIDEO_STATE_PBEND_ON)
+  {
+    int cx = DOOR_GFX_PAGEX6, cy = DOOR_GFX_PAGEY1;
+
+    XCopyArea(display,pix[PIX_DOOR],drawto,gc,
+             cx + VIDEO_PBEND_LABEL_XPOS,
+             cy + VIDEO_PBEND_LABEL_YPOS,
+             VIDEO_PBEND_LABEL_XSIZE,
+             VIDEO_PBEND_LABEL_YSIZE,
+             VX + VIDEO_REC_LABEL_XPOS,
+             VY + VIDEO_REC_LABEL_YPOS);
+  }
+
   if (state & VIDEO_STATE_DATE_ON)
   {
     int tag = value % 100;
@@ -161,7 +517,7 @@ void DrawCompleteVideoDisplay()
   if (tape.date && tape.length)
   {
     DrawVideoDisplay(VIDEO_STATE_DATE_ON,tape.date);
-    DrawVideoDisplay(VIDEO_STATE_TIME_ON,0);
+    DrawVideoDisplay(VIDEO_STATE_TIME_ON,tape.length_seconds);
   }
 
   XCopyArea(display,drawto,pix[PIX_DB_DOOR],gc,
@@ -174,7 +530,7 @@ void DrawSoundDisplay(unsigned long state)
 
   pos = (state & BUTTON_SOUND_MUSIC ? SOUND_BUTTON_MUSIC_XPOS :
         state & BUTTON_SOUND_LOOPS ? SOUND_BUTTON_LOOPS_XPOS :
-        SOUND_BUTTON_SOUND_XPOS);
+        SOUND_BUTTON_SIMPLE_XPOS);
 
   if (state & BUTTON_ON)
     cy -= SOUND_BUTTON_YSIZE;
@@ -209,34 +565,119 @@ void DrawGameButton(unsigned long state)
   redraw_mask |= REDRAW_DOOR_1;
 }
 
-void DrawChooseButton(unsigned long state)
+void DrawYesNoButton(unsigned long state, int mode)
 {
-  int pos, cx = DOOR_GFX_PAGEX4, cy = 0;
+  Drawable dest_drawto;
+  int dest_xoffset, dest_yoffset;
+  int xpos, cx = DOOR_GFX_PAGEX4;
 
-  pos = (state & BUTTON_OK ? OK_BUTTON_XPOS : NO_BUTTON_XPOS);
+  if (mode == DB_INIT)
+  {
+    dest_drawto = pix[PIX_DB_DOOR];
+    dest_xoffset = DOOR_GFX_PAGEX1;
+    dest_yoffset = 0;
+  }
+  else
+  {
+    dest_drawto = drawto;
+    dest_xoffset = DX;
+    dest_yoffset = DY;
+  }
+
+  xpos = (state & BUTTON_OK ? OK_BUTTON_XPOS : NO_BUTTON_XPOS);
 
   if (state & BUTTON_PRESSED)
     cx = DOOR_GFX_PAGEX3;
 
-  XCopyArea(display,pix[PIX_DOOR],drawto,gc,
-           cx + pos,cy + OK_BUTTON_GFX_YPOS,
-           OK_BUTTON_XSIZE,OK_BUTTON_YSIZE,
-           DX + pos,DY + OK_BUTTON_YPOS);
+  XCopyArea(display, pix[PIX_DOOR], dest_drawto, gc,
+           cx + xpos, OK_BUTTON_GFX_YPOS,
+           OK_BUTTON_XSIZE, OK_BUTTON_YSIZE,
+           dest_xoffset + xpos, dest_yoffset + OK_BUTTON_YPOS);
 
   redraw_mask |= REDRAW_DOOR_1;
 }
 
-void DrawConfirmButton(unsigned long state)
+void DrawConfirmButton(unsigned long state, int mode)
 {
+  Drawable dest_drawto;
+  int dest_xoffset, dest_yoffset;
+  int cx = DOOR_GFX_PAGEX4;
+
+  if (mode == DB_INIT)
+  {
+    dest_drawto = pix[PIX_DB_DOOR];
+    dest_xoffset = DOOR_GFX_PAGEX1;
+    dest_yoffset = 0;
+  }
+  else
+  {
+    dest_drawto = drawto;
+    dest_xoffset = DX;
+    dest_yoffset = DY;
+  }
+
+  if (state & BUTTON_PRESSED)
+    cx = DOOR_GFX_PAGEX3;
+
+  XCopyArea(display, pix[PIX_DOOR], dest_drawto, gc,
+           cx + CONFIRM_BUTTON_XPOS, CONFIRM_BUTTON_GFX_YPOS,
+           CONFIRM_BUTTON_XSIZE, CONFIRM_BUTTON_YSIZE,
+           dest_xoffset + CONFIRM_BUTTON_XPOS,
+           dest_yoffset + CONFIRM_BUTTON_YPOS);
+
+  redraw_mask |= REDRAW_DOOR_1;
+}
+
+void DrawPlayerButton(unsigned long state, int mode)
+{
+  Drawable dest_drawto;
+  int dest_xoffset, dest_yoffset;
+  int graphic = GFX_SPIELER1;  /* default */
+  int graphic_offset = (PLAYER_BUTTON_XSIZE - TILEX/2)/2;
+  int xpos, ypos;
   int cx = DOOR_GFX_PAGEX4, cy = 0;
 
+  if (mode == DB_INIT)
+  {
+    dest_drawto = pix[PIX_DB_DOOR];
+    dest_xoffset = DOOR_GFX_PAGEX1;
+    dest_yoffset = 0;
+  }
+  else
+  {
+    dest_drawto = drawto;
+    dest_xoffset = DX;
+    dest_yoffset = DY;
+  }
+
+  if (state & BUTTON_PLAYER_1)
+    graphic = GFX_SPIELER1;
+  else if (state & BUTTON_PLAYER_2)
+    graphic = GFX_SPIELER2;
+  else if (state & BUTTON_PLAYER_3)
+    graphic = GFX_SPIELER3;
+  else if (state & BUTTON_PLAYER_4)
+    graphic = GFX_SPIELER4;
+
+  xpos = (state & BUTTON_PLAYER_1 || state & BUTTON_PLAYER_3 ?
+         PLAYER_BUTTON_1_XPOS : PLAYER_BUTTON_2_XPOS);
+  ypos = (state & BUTTON_PLAYER_1 || state & BUTTON_PLAYER_2 ?
+         PLAYER_BUTTON_1_YPOS : PLAYER_BUTTON_3_YPOS);
+
   if (state & BUTTON_PRESSED)
+  {
     cx = DOOR_GFX_PAGEX3;
+    graphic_offset += 1;
+  }
 
-  XCopyArea(display,pix[PIX_DOOR],drawto,gc,
-           cx + CONFIRM_BUTTON_XPOS,cy + CONFIRM_BUTTON_GFX_YPOS,
-           CONFIRM_BUTTON_XSIZE,CONFIRM_BUTTON_YSIZE,
-           DX + CONFIRM_BUTTON_XPOS,DY + CONFIRM_BUTTON_YPOS);
+  XCopyArea(display, pix[PIX_DOOR], dest_drawto, gc,
+           cx + PLAYER_BUTTON_GFX_XPOS, cy + PLAYER_BUTTON_GFX_YPOS,
+           PLAYER_BUTTON_XSIZE, PLAYER_BUTTON_YSIZE,
+           dest_xoffset + xpos, dest_yoffset + ypos);
+  DrawMiniGraphicExt(dest_drawto,gc,
+                    dest_xoffset + xpos + graphic_offset,
+                    dest_yoffset + ypos + graphic_offset,
+                    graphic);
 
   redraw_mask |= REDRAW_DOOR_1;
 }
@@ -250,23 +691,23 @@ void DrawEditButton(unsigned long state)
   int cx = DOOR_GFX_PAGEX6, cy = DOOR_GFX_PAGEY2;
   static int edit_pos[6][4] =
   {
-    ED_BUTTON_CTRL_XPOS,ED_BUTTON_CTRL_YPOS,
-    ED_BUTTON_CTRL_XSIZE,ED_BUTTON_CTRL_YSIZE,
+   {ED_BUTTON_CTRL_XPOS,ED_BUTTON_CTRL_YPOS,
+    ED_BUTTON_CTRL_XSIZE,ED_BUTTON_CTRL_YSIZE},
 
-    ED_BUTTON_FILL_XPOS,ED_BUTTON_FILL_YPOS,
-    ED_BUTTON_FILL_XSIZE,ED_BUTTON_FILL_YSIZE,
+   {ED_BUTTON_FILL_XPOS,ED_BUTTON_FILL_YPOS,
+    ED_BUTTON_FILL_XSIZE,ED_BUTTON_FILL_YSIZE},
 
-    ED_BUTTON_LEFT_XPOS,ED_BUTTON_LEFT_YPOS,
-    ED_BUTTON_LEFT_XSIZE,ED_BUTTON_LEFT_YSIZE,
+   {ED_BUTTON_LEFT_XPOS,ED_BUTTON_LEFT_YPOS,
+    ED_BUTTON_LEFT_XSIZE,ED_BUTTON_LEFT_YSIZE},
 
-    ED_BUTTON_UP_XPOS,ED_BUTTON_UP_YPOS,
-    ED_BUTTON_UP_XSIZE,ED_BUTTON_UP_YSIZE,
+   {ED_BUTTON_UP_XPOS,ED_BUTTON_UP_YPOS,
+    ED_BUTTON_UP_XSIZE,ED_BUTTON_UP_YSIZE},
 
-    ED_BUTTON_DOWN_XPOS,ED_BUTTON_DOWN_YPOS,
-    ED_BUTTON_DOWN_XSIZE,ED_BUTTON_DOWN_YSIZE,
+   {ED_BUTTON_DOWN_XPOS,ED_BUTTON_DOWN_YPOS,
+    ED_BUTTON_DOWN_XSIZE,ED_BUTTON_DOWN_YSIZE},
 
-    ED_BUTTON_RIGHT_XPOS,ED_BUTTON_RIGHT_YPOS,
-    ED_BUTTON_RIGHT_XSIZE,ED_BUTTON_RIGHT_YSIZE
+   {ED_BUTTON_RIGHT_XPOS,ED_BUTTON_RIGHT_YPOS,
+    ED_BUTTON_RIGHT_XSIZE,ED_BUTTON_RIGHT_YSIZE}
   };
 
   if (state & ED_BUTTON_PRESSED)
@@ -294,17 +735,17 @@ void DrawCtrlButton(unsigned long state)
   int cx = DOOR_GFX_PAGEX4, cy = DOOR_GFX_PAGEY1+80;
   static int edit_pos[4][4] =
   {
-    ED_BUTTON_EDIT_XPOS,ED_BUTTON_EDIT_YPOS,
-    ED_BUTTON_EDIT_XSIZE,ED_BUTTON_EDIT_YSIZE,
+   {ED_BUTTON_EDIT_XPOS,ED_BUTTON_EDIT_YPOS,
+    ED_BUTTON_EDIT_XSIZE,ED_BUTTON_EDIT_YSIZE},
 
-    ED_BUTTON_CLEAR_XPOS,ED_BUTTON_CLEAR_YPOS,
-    ED_BUTTON_CLEAR_XSIZE,ED_BUTTON_CLEAR_YSIZE,
+   {ED_BUTTON_CLEAR_XPOS,ED_BUTTON_CLEAR_YPOS,
+    ED_BUTTON_CLEAR_XSIZE,ED_BUTTON_CLEAR_YSIZE},
 
-    ED_BUTTON_UNDO_XPOS,ED_BUTTON_UNDO_YPOS,
-    ED_BUTTON_UNDO_XSIZE,ED_BUTTON_UNDO_YSIZE,
+   {ED_BUTTON_UNDO_XPOS,ED_BUTTON_UNDO_YPOS,
+    ED_BUTTON_UNDO_XSIZE,ED_BUTTON_UNDO_YSIZE},
 
-    ED_BUTTON_EXIT_XPOS,ED_BUTTON_EXIT_YPOS,
-    ED_BUTTON_EXIT_XSIZE,ED_BUTTON_EXIT_YSIZE
+   {ED_BUTTON_EXIT_XPOS,ED_BUTTON_EXIT_YPOS,
+    ED_BUTTON_EXIT_XSIZE,ED_BUTTON_EXIT_YSIZE}
   };
 
   if (state & ED_BUTTON_PRESSED)
@@ -332,14 +773,14 @@ void DrawElemButton(int button_nr, int button_state)
   int from_x, from_y, to_x,to_y, size_x, size_y;
   static int edit_pos[3][4] =
   {
-    ED_BUTTON_EUP_XPOS,ED_BUTTON_EUP_YPOS,
-    ED_BUTTON_EUP_XSIZE,ED_BUTTON_EUP_YSIZE,
+   {ED_BUTTON_EUP_XPOS,ED_BUTTON_EUP_YPOS,
+    ED_BUTTON_EUP_XSIZE,ED_BUTTON_EUP_YSIZE},
 
-    ED_BUTTON_EDOWN_XPOS,ED_BUTTON_EDOWN_YPOS,
-    ED_BUTTON_EDOWN_XSIZE,ED_BUTTON_EDOWN_YSIZE,
+   {ED_BUTTON_EDOWN_XPOS,ED_BUTTON_EDOWN_YPOS,
+    ED_BUTTON_EDOWN_XSIZE,ED_BUTTON_EDOWN_YSIZE},
 
-    ED_BUTTON_ELEM_XPOS,ED_BUTTON_ELEM_YPOS,
-    ED_BUTTON_ELEM_XSIZE,ED_BUTTON_ELEM_YSIZE
+   {ED_BUTTON_ELEM_XPOS,ED_BUTTON_ELEM_YPOS,
+    ED_BUTTON_ELEM_XSIZE,ED_BUTTON_ELEM_YSIZE}
   };
 
   if (button_nr<ED_BUTTON_ELEM)
@@ -394,12 +835,12 @@ void DrawElemButton(int button_nr, int button_state)
     XCopyArea(display,pix[PIX_DOOR],drawto,gc,
              from_x,from_y, size_x,size_y, to_x,to_y);
 
-    DrawMiniGraphicExtHiRes(drawto,gc,
-                           DX+ED_BUTTON_ELEM_XPOS+3+shift + 
-                           (elem_pos % MAX_ELEM_X)*ED_BUTTON_ELEM_XSIZE,
-                           DY+ED_BUTTON_ELEM_YPOS+3-shift +
-                           (elem_pos / MAX_ELEM_X)*ED_BUTTON_ELEM_YSIZE,
-                           graphic);
+    DrawMiniGraphicExt(drawto,gc,
+                      DX+ED_BUTTON_ELEM_XPOS+3+shift + 
+                      (elem_pos % MAX_ELEM_X)*ED_BUTTON_ELEM_XSIZE,
+                      DY+ED_BUTTON_ELEM_YPOS+3-shift +
+                      (elem_pos / MAX_ELEM_X)*ED_BUTTON_ELEM_YSIZE,
+                      graphic);
   }
 
   redraw_mask |= REDRAW_DOOR_1;
@@ -437,7 +878,7 @@ int CheckVideoButtons(int mx, int my, int button)
 {
   int return_code = 0;
   static int choice = -1;
-  static BOOL pressed = FALSE;
+  static boolean pressed = FALSE;
   static int video_button[5] =
   {
     VIDEO_PRESS_EJECT_ON,
@@ -497,12 +938,12 @@ int CheckSoundButtons(int mx, int my, int button)
 {
   int return_code = 0;
   static int choice = -1;
-  static BOOL pressed = FALSE;
+  static boolean pressed = FALSE;
   int sound_state[3];
 
-  sound_state[0] = BUTTON_SOUND_MUSIC | (BUTTON_ON * sound_music_on);
-  sound_state[1] = BUTTON_SOUND_LOOPS | (BUTTON_ON * sound_loops_on);
-  sound_state[2] = BUTTON_SOUND_SOUND | (BUTTON_ON * sound_on);
+  sound_state[0] = BUTTON_SOUND_MUSIC  | (BUTTON_ON * setup.sound_music);
+  sound_state[1] = BUTTON_SOUND_LOOPS  | (BUTTON_ON * setup.sound_loops);
+  sound_state[2] = BUTTON_SOUND_SIMPLE | (BUTTON_ON * setup.sound_simple);
 
   if (button)
   {
@@ -554,7 +995,7 @@ int CheckGameButtons(int mx, int my, int button)
 {
   int return_code = 0;
   static int choice = -1;
-  static BOOL pressed = FALSE;
+  static boolean pressed = FALSE;
   int game_state[3] =
   {
     BUTTON_GAME_STOP,
@@ -608,12 +1049,12 @@ int CheckGameButtons(int mx, int my, int button)
   return(return_code);
 }
 
-int CheckChooseButtons(int mx, int my, int button)
+int CheckYesNoButtons(int mx, int my, int button)
 {
   int return_code = 0;
   static int choice = -1;
-  static BOOL pressed = FALSE;
-  static int choose_button[5] =
+  static boolean pressed = FALSE;
+  static int yesno_button[5] =
   {
     BUTTON_OK,
     BUTTON_NO
@@ -623,33 +1064,33 @@ int CheckChooseButtons(int mx, int my, int button)
   {
     if (!motion_status)                /* Maustaste neu gedrückt */
     {
-      if (ON_CHOOSE_BUTTON(mx,my))
+      if (ON_YESNO_BUTTON(mx,my))
       {
-       choice = CHOOSE_BUTTON(mx);
+       choice = YESNO_BUTTON(mx);
        pressed = TRUE;
-       DrawChooseButton(choose_button[choice] | BUTTON_PRESSED);
+       DrawYesNoButton(yesno_button[choice] | BUTTON_PRESSED, DB_NORMAL);
       }
     }
     else                       /* Mausbewegung bei gedrückter Maustaste */
     {
-      if ((!ON_CHOOSE_BUTTON(mx,my) || CHOOSE_BUTTON(mx)!=choice) &&
+      if ((!ON_YESNO_BUTTON(mx,my) || YESNO_BUTTON(mx)!=choice) &&
          choice>=0 && pressed)
       {
        pressed = FALSE;
-       DrawChooseButton(choose_button[choice] | BUTTON_RELEASED);
+       DrawYesNoButton(yesno_button[choice] | BUTTON_RELEASED, DB_NORMAL);
       }
-      else if (ON_CHOOSE_BUTTON(mx,my) &&CHOOSE_BUTTON(mx)==choice && !pressed)
+      else if (ON_YESNO_BUTTON(mx,my) && YESNO_BUTTON(mx)==choice && !pressed)
       {
        pressed = TRUE;
-       DrawChooseButton(choose_button[choice] | BUTTON_PRESSED);
+       DrawYesNoButton(yesno_button[choice] | BUTTON_PRESSED, DB_NORMAL);
       }
     }
   }
   else                         /* Maustaste wieder losgelassen */
   {
-    if (ON_CHOOSE_BUTTON(mx,my) && CHOOSE_BUTTON(mx)==choice && pressed)
+    if (ON_YESNO_BUTTON(mx,my) && YESNO_BUTTON(mx)==choice && pressed)
     {
-      DrawChooseButton(choose_button[choice] | BUTTON_RELEASED);
+      DrawYesNoButton(yesno_button[choice] | BUTTON_RELEASED, DB_NORMAL);
       return_code = choice+1;
       choice = -1;
       pressed = FALSE;
@@ -669,7 +1110,7 @@ int CheckConfirmButton(int mx, int my, int button)
 {
   int return_code = 0;
   static int choice = -1;
-  static BOOL pressed = FALSE;
+  static boolean pressed = FALSE;
 
   if (button)
   {
@@ -679,7 +1120,7 @@ int CheckConfirmButton(int mx, int my, int button)
       {
        choice = 0;
        pressed = TRUE;
-       DrawConfirmButton(BUTTON_PRESSED);
+       DrawConfirmButton(BUTTON_PRESSED, DB_NORMAL);
       }
     }
     else                       /* Mausbewegung bei gedrückter Maustaste */
@@ -687,12 +1128,12 @@ int CheckConfirmButton(int mx, int my, int button)
       if (!ON_CONFIRM_BUTTON(mx,my) && choice>=0 && pressed)
       {
        pressed = FALSE;
-       DrawConfirmButton(BUTTON_RELEASED);
+       DrawConfirmButton(BUTTON_RELEASED, DB_NORMAL);
       }
       else if (ON_CONFIRM_BUTTON(mx,my) && !pressed)
       {
        pressed = TRUE;
-       DrawConfirmButton(BUTTON_PRESSED);
+       DrawConfirmButton(BUTTON_PRESSED, DB_NORMAL);
       }
     }
   }
@@ -700,7 +1141,7 @@ int CheckConfirmButton(int mx, int my, int button)
   {
     if (ON_CONFIRM_BUTTON(mx,my) && pressed)
     {
-      DrawConfirmButton(BUTTON_RELEASED);
+      DrawConfirmButton(BUTTON_RELEASED, DB_NORMAL);
       return_code = BUTTON_CONFIRM;
       choice = -1;
       pressed = FALSE;
@@ -716,13 +1157,72 @@ int CheckConfirmButton(int mx, int my, int button)
   return(return_code);
 }
 
+int CheckPlayerButtons(int mx, int my, int button)
+{
+  int return_code = 0;
+  static int choice = -1;
+  static boolean pressed = FALSE;
+  int player_state[4] =
+  {
+    BUTTON_PLAYER_1,
+    BUTTON_PLAYER_2,
+    BUTTON_PLAYER_3,
+    BUTTON_PLAYER_4
+  };
+
+  if (button)
+  {
+    if (!motion_status)                /* Maustaste neu gedrückt */
+    {
+      if (ON_PLAYER_BUTTON(mx,my))
+      {
+       choice = PLAYER_BUTTON(mx,my);
+       pressed = TRUE;
+       DrawPlayerButton(player_state[choice] | BUTTON_PRESSED, DB_NORMAL);
+      }
+    }
+    else                       /* Mausbewegung bei gedrückter Maustaste */
+    {
+      if ((!ON_PLAYER_BUTTON(mx,my) || PLAYER_BUTTON(mx,my)!=choice) &&
+         choice>=0 && pressed)
+      {
+       pressed = FALSE;
+       DrawPlayerButton(player_state[choice] | BUTTON_RELEASED, DB_NORMAL);
+      }
+      else if (ON_PLAYER_BUTTON(mx,my) && PLAYER_BUTTON(mx,my)==choice && !pressed)
+      {
+       pressed = TRUE;
+       DrawPlayerButton(player_state[choice] | BUTTON_PRESSED, DB_NORMAL);
+      }
+    }
+  }
+  else                         /* Maustaste wieder losgelassen */
+  {
+    if (ON_PLAYER_BUTTON(mx,my) && PLAYER_BUTTON(mx,my)==choice && pressed)
+    {
+      DrawPlayerButton(player_state[choice] | BUTTON_RELEASED, DB_NORMAL);
+      return_code = player_state[choice];
+      choice = -1;
+      pressed = FALSE;
+    }
+    else
+    {
+      choice = -1;
+      pressed = FALSE;
+    }
+  }
+
+  BackToFront();
+  return(return_code);
+}
+
 /* several buttons in the level editor */
 
 int CheckEditButtons(int mx, int my, int button)
 {
   int return_code = 0;
   static int choice = -1;
-  static BOOL pressed = FALSE;
+  static boolean pressed = FALSE;
   static int edit_button[6] =
   {
     ED_BUTTON_CTRL,
@@ -792,7 +1292,7 @@ int CheckCtrlButtons(int mx, int my, int button)
 {
   int return_code = 0;
   static int choice = -1;
-  static BOOL pressed = FALSE;
+  static boolean pressed = FALSE;
   static int ctrl_button[4] =
   {
     ED_BUTTON_EDIT,
@@ -851,7 +1351,7 @@ int CheckElemButtons(int mx, int my, int button)
 {
   int return_code = -1;
   static int choice = -1;
-  static BOOL pressed = FALSE;
+  static boolean pressed = FALSE;
 
   if (button)
   {
@@ -912,7 +1412,7 @@ int CheckCountButtons(int mx, int my, int button)
 {
   int return_code = -1;
   static int choice = -1;
-  static BOOL pressed = FALSE;
+  static boolean pressed = FALSE;
 
   if (button)
   {
index 98ac5cafe6d8222d5eea9a5996e5ed3c49c12876..8111aaebcb543040e24b1ace624abf134b094667 100644 (file)
@@ -1,13 +1,12 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  buttons.h                                               *
 ***********************************************************/
@@ -17,6 +16,8 @@
 
 #include "main.h"
 
+/* the following definitions are also used by tools.c */
+
 /* some positions in the video tape control window */
 #define VIDEO_DISPLAY1_XPOS    5
 #define VIDEO_DISPLAY1_YPOS    5
 #define VIDEO_CONTROL_YPOS     77
 #define VIDEO_CONTROL_XSIZE    (VIDEO_DISPLAY_XSIZE)
 #define VIDEO_CONTROL_YSIZE    (VIDEO_BUTTON_YSIZE)
-#define VIDEO_BUTTON_EJECT_XPOS        (VIDEO_CONTROL_XPOS + 0 * VIDEO_BUTTON_XSIZE)
-#define VIDEO_BUTTON_STOP_XPOS (VIDEO_CONTROL_XPOS + 1 * VIDEO_BUTTON_XSIZE)
-#define VIDEO_BUTTON_PAUSE_XPOS        (VIDEO_CONTROL_XPOS + 2 * VIDEO_BUTTON_XSIZE)
-#define VIDEO_BUTTON_REC_XPOS  (VIDEO_CONTROL_XPOS + 3 * VIDEO_BUTTON_XSIZE)
-#define VIDEO_BUTTON_PLAY_XPOS (VIDEO_CONTROL_XPOS + 4 * VIDEO_BUTTON_XSIZE)
-#define VIDEO_BUTTON_ANY_YPOS  (VIDEO_CONTROL_YPOS)
-#define VIDEO_DATE_LABEL_XPOS  (VIDEO_DISPLAY1_XPOS)
-#define VIDEO_DATE_LABEL_YPOS  (VIDEO_DISPLAY1_YPOS)
-#define VIDEO_DATE_LABEL_XSIZE (VIDEO_DISPLAY_XSIZE)
-#define VIDEO_DATE_LABEL_YSIZE (VIDEO_DISPLAY_YSIZE)
-#define VIDEO_DATE_XPOS                (VIDEO_DISPLAY1_XPOS+1)
-#define VIDEO_DATE_YPOS                (VIDEO_DISPLAY1_YPOS+14)
-#define VIDEO_DATE_XSIZE       (VIDEO_DISPLAY_XSIZE)
-#define VIDEO_DATE_YSIZE       16
-#define VIDEO_REC_LABEL_XPOS   (VIDEO_DISPLAY2_XPOS)
-#define VIDEO_REC_LABEL_YPOS   (VIDEO_DISPLAY2_YPOS)
-#define VIDEO_REC_LABEL_XSIZE  20
-#define VIDEO_REC_LABEL_YSIZE  12
-#define VIDEO_REC_SYMBOL_XPOS  (VIDEO_DISPLAY2_XPOS+20)
-#define VIDEO_REC_SYMBOL_YPOS  (VIDEO_DISPLAY2_YPOS)
-#define VIDEO_REC_SYMBOL_XSIZE 16
-#define VIDEO_REC_SYMBOL_YSIZE 16
-#define VIDEO_PLAY_LABEL_XPOS  (VIDEO_DISPLAY2_XPOS+65)
-#define VIDEO_PLAY_LABEL_YPOS  (VIDEO_DISPLAY2_YPOS)
-#define VIDEO_PLAY_LABEL_XSIZE 22
-#define VIDEO_PLAY_LABEL_YSIZE 12
-#define VIDEO_PLAY_SYMBOL_XPOS (VIDEO_DISPLAY2_XPOS+50)
-#define VIDEO_PLAY_SYMBOL_YPOS (VIDEO_DISPLAY2_YPOS)
-#define VIDEO_PLAY_SYMBOL_XSIZE        13
-#define VIDEO_PLAY_SYMBOL_YSIZE        13
-#define VIDEO_PAUSE_LABEL_XPOS (VIDEO_DISPLAY2_XPOS)
-#define VIDEO_PAUSE_LABEL_YPOS (VIDEO_DISPLAY2_YPOS+20)
-#define VIDEO_PAUSE_LABEL_XSIZE        35
-#define VIDEO_PAUSE_LABEL_YSIZE        8
-#define VIDEO_PAUSE_SYMBOL_XPOS        (VIDEO_DISPLAY2_XPOS+35)
-#define VIDEO_PAUSE_SYMBOL_YPOS        (VIDEO_DISPLAY2_YPOS)
-#define VIDEO_PAUSE_SYMBOL_XSIZE 13
-#define VIDEO_PAUSE_SYMBOL_YSIZE 13
-#define VIDEO_TIME_XPOS                (VIDEO_DISPLAY2_XPOS+38)
-#define VIDEO_TIME_YPOS                (VIDEO_DISPLAY2_YPOS+14)
-#define VIDEO_TIME_XSIZE       50
-#define VIDEO_TIME_YSIZE       16
 
-#define ON_VIDEO_BUTTON(x,y)   ((x)>=(VX+VIDEO_CONTROL_XPOS) &&        \
-                                (x)< (VX+VIDEO_CONTROL_XPOS +          \
-                                      VIDEO_CONTROL_XSIZE) &&          \
-                                (y)>=(VY+VIDEO_CONTROL_YPOS) &&        \
-                                (y)< (VY+VIDEO_CONTROL_YPOS +          \
-                                      VIDEO_CONTROL_YSIZE))
-#define VIDEO_BUTTON(x)                (((x)-(VX+VIDEO_CONTROL_XPOS))/VIDEO_BUTTON_XSIZE)
+/* values for requests */
+#define BUTTON_OK              (1L<<0)
+#define BUTTON_NO              (1L<<1)
+#define BUTTON_CONFIRM         (1L<<2)
+
+/* values for choosing network player */
+#define BUTTON_PLAYER_1                (1L<<10)
+#define BUTTON_PLAYER_2                (1L<<11)
+#define BUTTON_PLAYER_3                (1L<<12)
+#define BUTTON_PLAYER_4                (1L<<13)
+
+/* for DrawPlayerButton() */
+#define DB_INIT                        0
+#define DB_NORMAL              1
+
+/* the following definitions are also used by screens.c */
+
+/* buttons of the video tape player */
+#define BUTTON_VIDEO_EJECT     1
+#define BUTTON_VIDEO_STOP      2
+#define BUTTON_VIDEO_PAUSE     3
+#define BUTTON_VIDEO_REC       4
+#define BUTTON_VIDEO_PLAY      5
 
 /* values for video tape control */
 #define VIDEO_STATE_PLAY_OFF   (1L<<0)
 #define VIDEO_PRESS_EJECT_OFF  (1L<<19)
 #define VIDEO_PRESS_EJECT      (VIDEO_PRESS_EJECT_OFF  | VIDEO_PRESS_EJECT_ON)
 
-#define BUTTON_VIDEO_EJECT     1
-#define BUTTON_VIDEO_STOP      2
-#define BUTTON_VIDEO_PAUSE     3
-#define BUTTON_VIDEO_REC       4
-#define BUTTON_VIDEO_PLAY      5
-
-#define VIDEO_STATE_OFF                (VIDEO_STATE_PLAY_OFF   |       \
-                                VIDEO_STATE_REC_OFF    |       \
-                                VIDEO_STATE_PAUSE_OFF  |       \
-                                VIDEO_STATE_DATE_OFF   |       \
-                                VIDEO_STATE_TIME_OFF)
-#define VIDEO_PRESS_OFF                (VIDEO_PRESS_PLAY_OFF   |       \
-                                VIDEO_PRESS_REC_OFF    |       \
-                                VIDEO_PRESS_PAUSE_OFF  |       \
-                                VIDEO_PRESS_STOP_OFF   |       \
-                                VIDEO_PRESS_EJECT_OFF)
-#define VIDEO_ALL_OFF          (VIDEO_STATE_OFF | VIDEO_PRESS_OFF)
-
-#define VIDEO_STATE_ON         (VIDEO_STATE_PLAY_ON    |       \
-                                VIDEO_STATE_REC_ON     |       \
-                                VIDEO_STATE_PAUSE_ON   |       \
-                                VIDEO_STATE_DATE_ON    |       \
-                                VIDEO_STATE_TIME_ON)
-#define VIDEO_PRESS_ON         (VIDEO_PRESS_PLAY_ON    |       \
-                                VIDEO_PRESS_REC_ON     |       \
-                                VIDEO_PRESS_PAUSE_ON   |       \
-                                VIDEO_PRESS_STOP_ON    |       \
-                                VIDEO_PRESS_EJECT_ON)
-#define VIDEO_ALL_ON           (VIDEO_STATE_ON | VIDEO_PRESS_ON)
+/* special */
+#define VIDEO_STATE_FFWD_OFF   ((1L<<20) | VIDEO_STATE_PAUSE_OFF)
+#define VIDEO_STATE_FFWD_ON    (1L<<21)
+#define VIDEO_STATE_FFWD       (VIDEO_STATE_FFWD_OFF   | VIDEO_STATE_FFWD_ON)
+#define VIDEO_STATE_PBEND_OFF  (1L<<22)
+#define VIDEO_STATE_PBEND_ON   (1L<<23)
+#define VIDEO_STATE_PBEND      (VIDEO_STATE_PBEND_OFF  | VIDEO_STATE_PBEND_ON)
 
-#define VIDEO_STATE            (VIDEO_STATE_ON | VIDEO_STATE_OFF)
-#define VIDEO_PRESS            (VIDEO_PRESS_ON | VIDEO_PRESS_OFF)
-#define VIDEO_ALL              (VIDEO_ALL_ON | VIDEO_ALL_OFF)
-
-
-/* some positions in the sound control window */
-#define SOUND_BUTTON_XSIZE     30
-#define SOUND_BUTTON_YSIZE     30
-#define SOUND_CONTROL_XPOS     5
-#define SOUND_CONTROL_YPOS     245
-#define SOUND_CONTROL_XSIZE    90
-#define SOUND_CONTROL_YSIZE    (SOUND_BUTTON_YSIZE)
-#define SOUND_BUTTON_MUSIC_XPOS        (SOUND_CONTROL_XPOS + 0 * SOUND_BUTTON_XSIZE)
-#define SOUND_BUTTON_LOOPS_XPOS        (SOUND_CONTROL_XPOS + 1 * SOUND_BUTTON_XSIZE)
-#define SOUND_BUTTON_SOUND_XPOS        (SOUND_CONTROL_XPOS + 2 * SOUND_BUTTON_XSIZE)
-#define SOUND_BUTTON_ANY_YPOS  (SOUND_CONTROL_YPOS)
-
-#define ON_SOUND_BUTTON(x,y)   ((x)>=(DX+SOUND_CONTROL_XPOS) &&        \
-                                (x)< (DX+SOUND_CONTROL_XPOS +          \
-                                      SOUND_CONTROL_XSIZE) &&          \
-                                (y)>=(DY+SOUND_CONTROL_YPOS) &&        \
-                                (y)< (DY+SOUND_CONTROL_YPOS +          \
-                                      SOUND_CONTROL_YSIZE))
-#define SOUND_BUTTON(x)                (((x)-(DX+SOUND_CONTROL_XPOS))/SOUND_BUTTON_XSIZE)
+/* tags to draw video display labels or symbols only */
+#define VIDEO_DISPLAY_DEFAULT          0
+#define VIDEO_DISPLAY_LABEL_ONLY       1
+#define VIDEO_DISPLAY_SYMBOL_ONLY      2
 
 /* values for sound control */
 #define BUTTON_SOUND_MUSIC     (1L<<0)
 #define BUTTON_SOUND_LOOPS     (1L<<1)
-#define BUTTON_SOUND_SOUND     (1L<<2)
+#define BUTTON_SOUND_SIMPLE    (1L<<2)
 #define BUTTON_RELEASED                0
 #define BUTTON_PRESSED         (1L<<3)
 #define BUTTON_OFF             0
 #define BUTTON_ON              (1L<<4)
-#define BUTTON_SOUND_MUSIC_OFF (BUTTON_SOUND_MUSIC | BUTTON_OFF)
-#define BUTTON_SOUND_LOOPS_OFF (BUTTON_SOUND_LOOPS | BUTTON_OFF)
-#define BUTTON_SOUND_SOUND_OFF (BUTTON_SOUND_SOUND | BUTTON_OFF)
-#define BUTTON_SOUND_MUSIC_ON  (BUTTON_SOUND_MUSIC | BUTTON_ON)
-#define BUTTON_SOUND_LOOPS_ON  (BUTTON_SOUND_LOOPS | BUTTON_ON)
-#define BUTTON_SOUND_SOUND_ON  (BUTTON_SOUND_SOUND | BUTTON_ON)
-
-
-/* some positions in the game control window */
-#define GAME_BUTTON_XSIZE      30
-#define GAME_BUTTON_YSIZE      30
-#define GAME_CONTROL_XPOS      5
-#define GAME_CONTROL_YPOS      215
-#define GAME_CONTROL_XSIZE     90
-#define GAME_CONTROL_YSIZE     (GAME_BUTTON_YSIZE)
-#define GAME_BUTTON_STOP_XPOS  (GAME_CONTROL_XPOS + 0 * GAME_BUTTON_XSIZE)
-#define GAME_BUTTON_PAUSE_XPOS (GAME_CONTROL_XPOS + 1 * GAME_BUTTON_XSIZE)
-#define GAME_BUTTON_PLAY_XPOS  (GAME_CONTROL_XPOS + 2 * GAME_BUTTON_XSIZE)
-#define GAME_BUTTON_ANY_YPOS   (GAME_CONTROL_YPOS)
-
-#define ON_GAME_BUTTON(x,y)    ((x)>=(DX+GAME_CONTROL_XPOS) && \
-                                (x)< (DX+GAME_CONTROL_XPOS +           \
-                                      GAME_CONTROL_XSIZE) &&           \
-                                (y)>=(DY+GAME_CONTROL_YPOS) && \
-                                (y)< (DY+GAME_CONTROL_YPOS +           \
-                                      GAME_CONTROL_YSIZE))
-#define GAME_BUTTON(x)         (((x)-(DX+GAME_CONTROL_XPOS))/GAME_BUTTON_XSIZE)
+#define BUTTON_SOUND_MUSIC_OFF (BUTTON_SOUND_MUSIC  | BUTTON_OFF)
+#define BUTTON_SOUND_LOOPS_OFF (BUTTON_SOUND_LOOPS  | BUTTON_OFF)
+#define BUTTON_SOUND_SIMPLE_OFF        (BUTTON_SOUND_SIMPLE | BUTTON_OFF)
+#define BUTTON_SOUND_MUSIC_ON  (BUTTON_SOUND_MUSIC  | BUTTON_ON)
+#define BUTTON_SOUND_LOOPS_ON  (BUTTON_SOUND_LOOPS  | BUTTON_ON)
+#define BUTTON_SOUND_SIMPLE_ON (BUTTON_SOUND_SIMPLE | BUTTON_ON)
 
 /* values for game control */
 #define BUTTON_GAME_STOP       (1L<<0)
 #define BUTTON_GAME_PAUSE      (1L<<1)
 #define BUTTON_GAME_PLAY       (1L<<2)
 
+/* the following definitions are also used by game.c */
 
-/* some positions in the asking window */
-#define OK_BUTTON_XPOS         2
-#define OK_BUTTON_YPOS         250
-#define OK_BUTTON_GFX_YPOS     0
-#define OK_BUTTON_XSIZE                46
-#define OK_BUTTON_YSIZE                28
-#define NO_BUTTON_XPOS         52
-#define NO_BUTTON_YPOS         OK_BUTTON_YPOS
-#define NO_BUTTON_XSIZE                OK_BUTTON_XSIZE
-#define NO_BUTTON_YSIZE                OK_BUTTON_YSIZE
-#define CONFIRM_BUTTON_XPOS    2
-#define CONFIRM_BUTTON_GFX_YPOS        30
-#define CONFIRM_BUTTON_YPOS    OK_BUTTON_YPOS
-#define CONFIRM_BUTTON_XSIZE   96
-#define CONFIRM_BUTTON_YSIZE   OK_BUTTON_YSIZE
-
-#define ON_CHOOSE_BUTTON(x,y)  (((x)>=(DX+OK_BUTTON_XPOS) &&           \
-                                 (x)< (DX+OK_BUTTON_XPOS +             \
-                                       OK_BUTTON_XSIZE) &&             \
-                                 (y)>=(DY+OK_BUTTON_YPOS) &&           \
-                                 (y)< (DY+OK_BUTTON_YPOS +             \
-                                       OK_BUTTON_YSIZE)) ||            \
-                                ((x)>=(DX+NO_BUTTON_XPOS) &&           \
-                                 (x)< (DX+NO_BUTTON_XPOS +             \
-                                       NO_BUTTON_XSIZE) &&             \
-                                 (y)>=(DY+NO_BUTTON_YPOS) &&           \
-                                 (y)< (DY+NO_BUTTON_YPOS +             \
-                                       NO_BUTTON_YSIZE)))
-#define ON_CONFIRM_BUTTON(x,y) (((x)>=(DX+CONFIRM_BUTTON_XPOS) &&      \
-                                 (x)< (DX+CONFIRM_BUTTON_XPOS +        \
-                                       CONFIRM_BUTTON_XSIZE) &&        \
-                                 (y)>=(DY+CONFIRM_BUTTON_YPOS) &&      \
-                                 (y)< (DY+CONFIRM_BUTTON_YPOS +        \
-                                       CONFIRM_BUTTON_YSIZE)))
-#define CHOOSE_BUTTON(x)       (((x)-(DX+OK_BUTTON_XPOS))/OK_BUTTON_XSIZE)
-
-/* values for asking control */
-#define BUTTON_OK              (1L<<0)
-#define BUTTON_NO              (1L<<1)
-#define BUTTON_CONFIRM         (1L<<2)
+/* some positions in the game control window */
+#define GAME_BUTTON_XSIZE      30
+#define GAME_BUTTON_YSIZE      30
+#define GAME_CONTROL_XPOS      5
+#define GAME_CONTROL_YPOS      215
+#define GAME_CONTROL_XSIZE     (3*GAME_BUTTON_XSIZE)
+#define GAME_CONTROL_YSIZE     (1*GAME_BUTTON_YSIZE)
 
+/* the following definitions are also used by editor.c */
 
 /* some positions in the editor control window */
 #define ED_BUTTON_EUP_XPOS     35
 #define ED_SIZE_VALUE_XPOS     (ED_SIZE_GADGET_XPOS+ED_BUTTON_MINUS_XSIZE+7)
 #define ED_SIZE_VALUE_YPOS     ED_SIZE_TEXT_YPOS
 
-#define ON_EDIT_BUTTON(x,y)    (((x)>=(VX+ED_BUTTON_CTRL_XPOS) &&      \
-                                 (x)< (VX+ED_BUTTON_CTRL_XPOS +        \
-                                       ED_BUTTON_CTRL_XSIZE) &&        \
-                                 (y)>=(VY+ED_BUTTON_CTRL_YPOS) &&      \
-                                 (y)< (VY+ED_BUTTON_CTRL_YPOS +        \
-                                       ED_BUTTON_CTRL_YSIZE +          \
-                                       ED_BUTTON_FILL_YSIZE)) ||       \
-                                ((x)>=(VX+ED_BUTTON_LEFT_XPOS) &&      \
-                                 (x)< (VX+ED_BUTTON_LEFT_XPOS +        \
-                                       ED_BUTTON_LEFT_XSIZE +          \
-                                       ED_BUTTON_UP_XSIZE +            \
-                                       ED_BUTTON_RIGHT_XSIZE) &&       \
-                                 (y)>=(VY+ED_BUTTON_LEFT_YPOS) &&      \
-                                 (y)< (VY+ED_BUTTON_LEFT_YPOS +        \
-                                       ED_BUTTON_LEFT_YSIZE)) ||       \
-                                ((x)>=(VX+ED_BUTTON_UP_XPOS) &&        \
-                                 (x)< (VX+ED_BUTTON_UP_XPOS +          \
-                                       ED_BUTTON_UP_XSIZE) &&          \
-                                 (y)>=(VY+ED_BUTTON_UP_YPOS) &&        \
-                                 (y)< (VY+ED_BUTTON_UP_YPOS +          \
-                                       ED_BUTTON_UP_YSIZE +            \
-                                       ED_BUTTON_DOWN_YSIZE)))
-
-#define ON_CTRL_BUTTON(x,y)    ((x)>=(VX+ED_BUTTON_EDIT_XPOS) &&       \
-                                (x)< (VX+ED_BUTTON_EDIT_XPOS +         \
-                                      ED_BUTTON_EDIT_XSIZE) &&         \
-                                (y)>=(VY+ED_BUTTON_EDIT_YPOS) &&       \
-                                (y)< (VY+ED_BUTTON_EDIT_YPOS +         \
-                                      ED_BUTTON_EDIT_YSIZE +           \
-                                      ED_BUTTON_CLEAR_YSIZE +          \
-                                      ED_BUTTON_UNDO_YSIZE +           \
-                                      ED_BUTTON_EXIT_YSIZE))
-
-#define ON_ELEM_BUTTON(x,y)    (((x)>=(DX+ED_BUTTON_EUP_XPOS) &&       \
-                                 (x)< (DX+ED_BUTTON_EUP_XPOS +         \
-                                       ED_BUTTON_EUP_XSIZE) &&         \
-                                 (y)>=(DY+ED_BUTTON_EUP_YPOS) &&       \
-                                 (y)< (DY+ED_BUTTON_EUP_YPOS +         \
-                                       ED_BUTTON_EUP_YSIZE)) ||        \
-                                ((x)>=(DX+ED_BUTTON_EDOWN_XPOS) &&     \
-                                 (x)< (DX+ED_BUTTON_EDOWN_XPOS +       \
-                                       ED_BUTTON_EDOWN_XSIZE) &&       \
-                                 (y)>=(DY+ED_BUTTON_EDOWN_YPOS) &&     \
-                                 (y)< (DY+ED_BUTTON_EDOWN_YPOS +       \
-                                       ED_BUTTON_EDOWN_YSIZE)) ||      \
-                                ((x)>=(DX+ED_BUTTON_ELEM_XPOS) &&      \
-                                 (x)< (DX+ED_BUTTON_ELEM_XPOS +        \
-                                       MAX_ELEM_X*ED_BUTTON_ELEM_XSIZE) && \
-                                 (y)>=(DY+ED_BUTTON_ELEM_YPOS) &&      \
-                                 (y)< (DY+ED_BUTTON_ELEM_YPOS +        \
-                                       MAX_ELEM_Y*ED_BUTTON_ELEM_YSIZE)))
-
-#define ON_COUNT_BUTTON(x,y)   (((((x)>=ED_COUNT_GADGET_XPOS &&        \
-                                   (x)<(ED_COUNT_GADGET_XPOS +         \
-                                        ED_BUTTON_MINUS_XSIZE)) ||     \
-                                  ((x)>=(ED_COUNT_GADGET_XPOS +        \
-                                         (ED_BUTTON_PLUS_XPOS -        \
-                                          ED_BUTTON_MINUS_XPOS)) &&    \
-                                   (x)<(ED_COUNT_GADGET_XPOS +         \
-                                        (ED_BUTTON_PLUS_XPOS -         \
-                                         ED_BUTTON_MINUS_XPOS) +       \
-                                        ED_BUTTON_PLUS_XSIZE))) &&     \
-                                 ((y)>=ED_COUNT_GADGET_YPOS &&         \
-                                  (y)<(ED_COUNT_GADGET_YPOS +          \
-                                       16*ED_COUNT_GADGET_YSIZE)) &&   \
-                                 (((y)-ED_COUNT_GADGET_YPOS) %         \
-                                  ED_COUNT_GADGET_YSIZE) <             \
-                                 ED_BUTTON_MINUS_YSIZE) ||             \
-                                ((((x)>=ED_SIZE_GADGET_XPOS &&         \
-                                   (x)<(ED_SIZE_GADGET_XPOS +          \
-                                        ED_BUTTON_MINUS_XSIZE)) ||     \
-                                  ((x)>=(ED_SIZE_GADGET_XPOS +         \
-                                         (ED_BUTTON_PLUS_XPOS -        \
-                                          ED_BUTTON_MINUS_XPOS)) &&    \
-                                   (x)<(ED_SIZE_GADGET_XPOS +          \
-                                        (ED_BUTTON_PLUS_XPOS -         \
-                                         ED_BUTTON_MINUS_XPOS) +       \
-                                        ED_BUTTON_PLUS_XSIZE))) &&     \
-                                 ((y)>=ED_SIZE_GADGET_YPOS &&          \
-                                  (y)<(ED_SIZE_GADGET_YPOS +           \
-                                       2*ED_SIZE_GADGET_YSIZE)) &&     \
-                                 (((y)-ED_SIZE_GADGET_YPOS) %          \
-                                  ED_SIZE_GADGET_YSIZE) <              \
-                                 ED_BUTTON_MINUS_YSIZE))
-
-#define EDIT_BUTTON(x,y)       (((y) < (VY + ED_BUTTON_CTRL_YPOS +     \
-                                        ED_BUTTON_CTRL_YSIZE)) ? 0 :   \
-                                ((y) < (VY + ED_BUTTON_CTRL_YPOS +     \
-                                        ED_BUTTON_CTRL_YSIZE +         \
-                                        ED_BUTTON_FILL_YSIZE)) ? 1 :   \
-                                ((x) < (VX + ED_BUTTON_LEFT_XPOS +     \
-                                        ED_BUTTON_LEFT_XSIZE) ? 2 :    \
-                                 (x) > (VX + ED_BUTTON_LEFT_XPOS +     \
-                                        ED_BUTTON_LEFT_XSIZE +         \
-                                        ED_BUTTON_UP_XSIZE) ? 5 :      \
-                                 3+(((y)-(VY + ED_BUTTON_CTRL_YPOS +   \
-                                          ED_BUTTON_CTRL_YSIZE +       \
-                                          ED_BUTTON_FILL_YSIZE)) /     \
-                                    ED_BUTTON_UP_YSIZE)))
-
-#define CTRL_BUTTON(x,y)       (((y) < (VY + ED_BUTTON_EDIT_YPOS +     \
-                                        ED_BUTTON_EDIT_YSIZE)) ? 0 :   \
-                                1+(((y)-(VY + ED_BUTTON_EDIT_YPOS +    \
-                                        ED_BUTTON_EDIT_YSIZE)) /       \
-                                   ED_BUTTON_CLEAR_YSIZE))
-
-#define ELEM_BUTTON(x,y)       (((y) < (DY + ED_BUTTON_EUP_YPOS +      \
-                                        ED_BUTTON_EUP_YSIZE)) ? 0 :    \
-                                ((y) > (DY + ED_BUTTON_EDOWN_YPOS)) ? 1 : \
-                                2+(((y) - (DY + ED_BUTTON_ELEM_YPOS)) /   \
-                                ED_BUTTON_ELEM_YSIZE)*MAX_ELEM_X +     \
-                                ((x) - (DX + ED_BUTTON_ELEM_XPOS)) /   \
-                                ED_BUTTON_ELEM_XSIZE)
-
-#define COUNT_BUTTON(x,y)      ((x) < ED_SIZE_GADGET_XPOS ?            \
-                                ((((y) - ED_COUNT_GADGET_YPOS) /       \
-                                  ED_COUNT_GADGET_YSIZE)*2 +           \
-                                 ((x) < (ED_COUNT_GADGET_XPOS +        \
-                                         ED_BUTTON_MINUS_XSIZE) ? 0 : 1)) : \
-                                32+((((y) - ED_SIZE_GADGET_YPOS) /     \
-                                     ED_SIZE_GADGET_YSIZE)*2 +         \
-                                    ((x) < (ED_SIZE_GADGET_XPOS +      \
-                                            ED_BUTTON_MINUS_XSIZE) ? 0 : 1)))
-
 /* values for asking control */
 #define ED_BUTTON_CTRL         (1L<<0)
 #define ED_BUTTON_FILL         (1L<<1)
 #define ED_BUTTON_EDOWN                1
 #define ED_BUTTON_ELEM         2
 
-
 void DrawVideoDisplay(unsigned long, unsigned long);
 void DrawCompleteVideoDisplay(void);
 void DrawSoundDisplay(unsigned long);
 void DrawGameButton(unsigned long);
-void DrawChooseButton(unsigned long);
-void DrawConfirmButton(unsigned long);
+void DrawYesNoButton(unsigned long, int);
+void DrawConfirmButton(unsigned long, int);
+void DrawPlayerButton(unsigned long, int);
 void DrawEditButton(unsigned long state);
 void DrawCtrlButton(unsigned long state);
 void DrawElemButton(int, int);
@@ -499,8 +256,9 @@ void DrawCountButton(int, int);
 int CheckVideoButtons(int, int, int);
 int CheckSoundButtons(int, int, int);
 int CheckGameButtons(int, int, int);
-int CheckChooseButtons(int, int, int);
+int CheckYesNoButtons(int, int, int);
 int CheckConfirmButton(int, int, int);
+int CheckPlayerButtons(int, int, int);
 int CheckEditButtons(int, int, int);
 int CheckCtrlButtons(int, int, int);
 int CheckElemButtons(int, int, int);
diff --git a/src/cartoons.c b/src/cartoons.c
new file mode 100644 (file)
index 0000000..834996b
--- /dev/null
@@ -0,0 +1,563 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
+*----------------------------------------------------------*
+*  cartoons.c                                              *
+***********************************************************/
+
+#include "cartoons.h"
+#include "main.h"
+#include "misc.h"
+#include "tools.h"
+
+static void HandleAnimation(int);
+static boolean AnimateToon(int, boolean);
+static void DrawAnim(Pixmap, GC, int, int, int, int, int, int, int, int);
+
+struct AnimInfo
+{
+  int width, height;
+  int src_x, src_y;
+  int frames;
+  int frames_per_second;
+  int stepsize;
+  boolean pingpong;
+  int direction;
+  int position;
+};
+
+/* values for cartoon figures */
+#define NUM_TOONS      18
+
+#define DWARF_XSIZE    40
+#define DWARF_YSIZE    48
+#define DWARF_X                2
+#define DWARF_Y                72
+#define DWARF2_Y       186
+#define DWARF_FRAMES   8
+#define DWARF_FPS      10
+#define DWARF_STEPSIZE 4
+#define JUMPER_XSIZE   48
+#define JUMPER_YSIZE   56
+#define JUMPER_X       2
+#define JUMPER_Y       125
+#define JUMPER_FRAMES  8
+#define JUMPER_FPS     10
+#define JUMPER_STEPSIZE        4
+#define CLOWN_XSIZE    80
+#define CLOWN_YSIZE    110
+#define CLOWN_X                327
+#define CLOWN_Y                10
+#define CLOWN_FRAMES   1
+#define CLOWN_FPS      10
+#define CLOWN_STEPSIZE 4
+#define BIRD_XSIZE     32
+#define BIRD_YSIZE     30
+#define BIRD1_X                2
+#define BIRD1_Y                2
+#define BIRD2_X                2
+#define BIRD2_Y                37
+#define BIRD_FRAMES    8
+#define BIRD_FPS       20
+#define BIRD_STEPSIZE  4
+
+#define GAMETOON_XSIZE         TILEX
+#define GAMETOON_YSIZE         TILEY
+#define GAMETOON_FRAMES_4      4
+#define GAMETOON_FRAMES_8      8
+#define GAMETOON_FPS           20
+#define GAMETOON_STEPSIZE      4
+
+#define ANIMDIR_LEFT   1
+#define ANIMDIR_RIGHT  2
+#define ANIMDIR_UP     4
+#define ANIMDIR_DOWN   8
+
+#define ANIMPOS_ANY    0
+#define ANIMPOS_LEFT   1
+#define ANIMPOS_RIGHT  2
+#define ANIMPOS_UP     4
+#define ANIMPOS_DOWN   8
+#define ANIMPOS_UPPER  16
+
+#define ANIM_START     0
+#define ANIM_CONTINUE  1
+#define ANIM_STOP      2
+
+void InitAnimation()
+{
+  HandleAnimation(ANIM_START);
+}
+
+void StopAnimation()
+{
+  HandleAnimation(ANIM_STOP);
+}
+
+void DoAnimation()
+{
+  HandleAnimation(ANIM_CONTINUE);
+}
+
+void HandleAnimation(int mode)
+{
+  static unsigned long animstart_delay = -1;
+  static unsigned long animstart_delay_value = 0;
+  static boolean anim_restart = TRUE;
+  static boolean reset_delay = TRUE;
+  static int toon_nr = 0;
+  int draw_mode;
+
+  if (!setup.toons)
+    return;
+
+  switch(mode)
+  {
+    case ANIM_START:
+      anim_restart = TRUE;
+      reset_delay = TRUE;
+
+      /* Fill empty backbuffer for animation functions */
+      if (setup.direct_draw && game_status == PLAYING)
+      {
+       int xx,yy;
+
+       SetDrawtoField(DRAW_BACKBUFFER);
+
+       for(xx=0; xx<SCR_FIELDX; xx++)
+         for(yy=0; yy<SCR_FIELDY; yy++)
+           DrawScreenField(xx,yy);
+       DrawAllPlayers();
+
+       SetDrawtoField(DRAW_DIRECT);
+      }
+
+      if (setup.soft_scrolling && game_status == PLAYING)
+      {
+       int fx = FX, fy = FY;
+
+        fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
+        fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
+
+       XCopyArea(display,fieldbuffer,backbuffer,gc,
+                 fx,fy, SXSIZE,SYSIZE,
+                 SX,SY);
+      }
+
+      return;
+      break;
+    case ANIM_CONTINUE:
+      break;
+    case ANIM_STOP:
+      redraw_mask |= (REDRAW_FIELD | REDRAW_FROM_BACKBUFFER);
+
+      /* Redraw background even when in direct drawing mode */
+      draw_mode = setup.direct_draw;
+      setup.direct_draw = FALSE;
+
+      BackToFront();
+
+      setup.direct_draw = draw_mode;
+
+      return;
+      break;
+    default:
+      break;
+  }
+
+  if (reset_delay)
+  {
+    animstart_delay = Counter();
+    animstart_delay_value = SimpleRND(3000);
+    reset_delay = FALSE;
+  }
+
+  if (anim_restart)
+  {
+    if (!DelayReached(&animstart_delay, animstart_delay_value))
+      return;
+
+    toon_nr = SimpleRND(NUM_TOONS);
+  }
+
+  anim_restart = reset_delay = AnimateToon(toon_nr,anim_restart);
+}
+
+boolean AnimateToon(int toon_nr, boolean restart)
+{
+  static int pos_x = 0, pos_y = 0;
+  static int delta_x = 0, delta_y = 0;
+  static int frame = 0, frame_step = 1;
+  static boolean horiz_move, vert_move;
+  static unsigned long anim_delay = 0;
+  static unsigned long anim_delay_value = 0;
+  static int width,height;
+  static int pad_x,pad_y;
+  static int cut_x,cut_y;
+  static int src_x, src_y;
+  static int dest_x, dest_y;
+  static struct AnimInfo toon[NUM_TOONS] =
+  {
+    {
+      DWARF_XSIZE, DWARF_YSIZE,
+      DWARF_X, DWARF_Y,
+      DWARF_FRAMES,
+      DWARF_FPS,
+      DWARF_STEPSIZE,
+      ANIM_NORMAL,
+      ANIMDIR_RIGHT,
+      ANIMPOS_DOWN
+    },
+    {
+      DWARF_XSIZE, DWARF_YSIZE,
+      DWARF_X, DWARF2_Y,
+      DWARF_FRAMES,
+      DWARF_FPS,
+      DWARF_STEPSIZE,
+      ANIM_NORMAL,
+      ANIMDIR_LEFT,
+      ANIMPOS_DOWN
+    },
+    {
+      JUMPER_XSIZE, JUMPER_YSIZE,
+      JUMPER_X, JUMPER_Y,
+      JUMPER_FRAMES,
+      JUMPER_FPS,
+      JUMPER_STEPSIZE,
+      ANIM_NORMAL,
+      ANIMDIR_RIGHT,
+      ANIMPOS_DOWN
+    },
+    {
+      CLOWN_XSIZE, CLOWN_YSIZE,
+      CLOWN_X, CLOWN_Y,
+      CLOWN_FRAMES,
+      CLOWN_FPS,
+      CLOWN_STEPSIZE,
+      ANIM_NORMAL,
+      ANIMDIR_UP,
+      ANIMPOS_ANY
+    },
+    {
+      BIRD_XSIZE, BIRD_YSIZE,
+      BIRD1_X, BIRD1_Y,
+      BIRD_FRAMES,
+      BIRD_FPS,
+      BIRD_STEPSIZE,
+      ANIM_OSCILLATE,
+      ANIMDIR_RIGHT,
+      ANIMPOS_UPPER
+    },
+    {
+      BIRD_XSIZE, BIRD_YSIZE,
+      BIRD2_X, BIRD2_Y,
+      BIRD_FRAMES,
+      BIRD_FPS,
+      BIRD_STEPSIZE,
+      ANIM_OSCILLATE,
+      ANIMDIR_LEFT,
+      ANIMPOS_UPPER
+    },
+    {
+      GAMETOON_XSIZE, GAMETOON_YSIZE,
+      ((GFX_SPIELER1_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+      ((GFX_SPIELER1_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+      GAMETOON_FRAMES_4,
+      GAMETOON_FPS,
+      GAMETOON_STEPSIZE,
+      ANIM_NORMAL,
+      ANIMDIR_LEFT,
+      ANIMPOS_DOWN
+    },
+    {
+      GAMETOON_XSIZE, GAMETOON_YSIZE,
+      ((GFX_SPIELER1_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+      ((GFX_SPIELER1_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+      GAMETOON_FRAMES_4,
+      GAMETOON_FPS,
+      GAMETOON_STEPSIZE,
+      ANIM_NORMAL,
+      ANIMDIR_RIGHT,
+      ANIMPOS_DOWN
+    },
+    {
+      GAMETOON_XSIZE, GAMETOON_YSIZE,
+      ((GFX_PINGUIN_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+      ((GFX_PINGUIN_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+      GAMETOON_FRAMES_4,
+      GAMETOON_FPS,
+      GAMETOON_STEPSIZE,
+      ANIM_NORMAL,
+      ANIMDIR_LEFT,
+      ANIMPOS_DOWN
+    },
+    {
+      GAMETOON_XSIZE, GAMETOON_YSIZE,
+      ((GFX_PINGUIN_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+      ((GFX_PINGUIN_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+      GAMETOON_FRAMES_4,
+      GAMETOON_FPS,
+      GAMETOON_STEPSIZE,
+      ANIM_NORMAL,
+      ANIMDIR_RIGHT,
+      ANIMPOS_DOWN
+    },
+    {
+      GAMETOON_XSIZE, GAMETOON_YSIZE,
+      ((GFX_MAULWURF_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+      ((GFX_MAULWURF_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+      GAMETOON_FRAMES_4,
+      GAMETOON_FPS,
+      GAMETOON_STEPSIZE,
+      ANIM_NORMAL,
+      ANIMDIR_LEFT,
+      ANIMPOS_DOWN
+    },
+    {
+      GAMETOON_XSIZE, GAMETOON_YSIZE,
+      ((GFX_MAULWURF_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+      ((GFX_MAULWURF_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+      GAMETOON_FRAMES_4,
+      GAMETOON_FPS,
+      GAMETOON_STEPSIZE,
+      ANIM_NORMAL,
+      ANIMDIR_RIGHT,
+      ANIMPOS_DOWN
+    },
+    {
+      GAMETOON_XSIZE, GAMETOON_YSIZE,
+      ((GFX_SCHWEIN_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+      ((GFX_SCHWEIN_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+      GAMETOON_FRAMES_4,
+      GAMETOON_FPS,
+      GAMETOON_STEPSIZE,
+      ANIM_NORMAL,
+      ANIMDIR_LEFT,
+      ANIMPOS_DOWN
+    },
+    {
+      GAMETOON_XSIZE, GAMETOON_YSIZE,
+      ((GFX_SCHWEIN_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+      ((GFX_SCHWEIN_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+      GAMETOON_FRAMES_4,
+      GAMETOON_FPS,
+      GAMETOON_STEPSIZE,
+      ANIM_NORMAL,
+      ANIMDIR_RIGHT,
+      ANIMPOS_DOWN
+    },
+    {
+      GAMETOON_XSIZE, GAMETOON_YSIZE,
+      ((GFX_DRACHE_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+      ((GFX_DRACHE_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+      GAMETOON_FRAMES_4,
+      GAMETOON_FPS,
+      GAMETOON_STEPSIZE,
+      ANIM_NORMAL,
+      ANIMDIR_LEFT,
+      ANIMPOS_DOWN
+    },
+    {
+      GAMETOON_XSIZE, GAMETOON_YSIZE,
+      ((GFX_DRACHE_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+      ((GFX_DRACHE_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+      GAMETOON_FRAMES_4,
+      GAMETOON_FPS,
+      GAMETOON_STEPSIZE,
+      ANIM_NORMAL,
+      ANIMDIR_RIGHT,
+      ANIMPOS_DOWN
+    },
+    {
+      GAMETOON_XSIZE, GAMETOON_YSIZE,
+      ((GFX_SONDE - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+      ((GFX_SONDE - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+      GAMETOON_FRAMES_8,
+      GAMETOON_FPS,
+      GAMETOON_STEPSIZE,
+      ANIM_NORMAL,
+      ANIMDIR_LEFT,
+      ANIMPOS_ANY
+    },
+    {
+      GAMETOON_XSIZE, GAMETOON_YSIZE,
+      ((GFX_SONDE - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+      ((GFX_SONDE - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+      GAMETOON_FRAMES_8,
+      GAMETOON_FPS,
+      GAMETOON_STEPSIZE,
+      ANIM_NORMAL,
+      ANIMDIR_RIGHT,
+      ANIMPOS_ANY
+    },
+  };
+  struct AnimInfo *anim = &toon[toon_nr];
+  Pixmap anim_pixmap = (toon_nr < 6 ? pix[PIX_TOONS] : pix[PIX_HEROES]);
+  GC anim_clip_gc = (toon_nr < 6 ? clip_gc[PIX_TOONS] : clip_gc[PIX_HEROES]);
+
+  if (restart)
+  {
+    horiz_move = (anim->direction & (ANIMDIR_LEFT | ANIMDIR_RIGHT));
+    vert_move = (anim->direction & (ANIMDIR_UP | ANIMDIR_DOWN));
+    anim_delay_value = 1000/anim->frames_per_second;
+    frame = 0;
+
+    if (horiz_move)
+    {
+      if (anim->position==ANIMPOS_UP)
+       pos_y = 0;
+      else if (anim->position==ANIMPOS_DOWN)
+       pos_y = FULL_SYSIZE-anim->height;
+      else if (anim->position==ANIMPOS_UPPER)
+       pos_y = SimpleRND((FULL_SYSIZE-anim->height)/2);
+      else
+       pos_y = SimpleRND(FULL_SYSIZE-anim->height);
+
+      if (anim->direction==ANIMDIR_RIGHT)
+      {
+       delta_x = anim->stepsize;
+       pos_x = -anim->width+delta_x;
+      }
+      else
+      {
+       delta_x = -anim->stepsize;
+       pos_x = FULL_SXSIZE+delta_x;
+      }
+      delta_y = 0;
+    }
+    else
+    {
+      if (anim->position==ANIMPOS_LEFT)
+       pos_x = 0;
+      else if (anim->position==ANIMPOS_RIGHT)
+       pos_x = FULL_SXSIZE-anim->width;
+      else
+       pos_x = SimpleRND(FULL_SXSIZE-anim->width);
+
+      if (anim->direction==ANIMDIR_DOWN)
+      {
+       delta_y = anim->stepsize;
+       pos_y = -anim->height+delta_y;
+      }
+      else
+      {
+       delta_y = -anim->stepsize;
+       pos_y = FULL_SYSIZE+delta_y;
+      }
+      delta_x = 0;
+    }
+  }
+
+  if (pos_x <= -anim->width  - anim->stepsize ||
+      pos_x >=  FULL_SXSIZE  + anim->stepsize ||
+      pos_y <= -anim->height - anim->stepsize ||
+      pos_y >=  FULL_SYSIZE  + anim->stepsize)
+    return(TRUE);
+
+  if (!DelayReached(&anim_delay, anim_delay_value))
+  {
+    if (game_status==HELPSCREEN && !restart)
+      DrawAnim(anim_pixmap,anim_clip_gc,
+              src_x+cut_x,src_y+cut_y, width,height,
+              REAL_SX+dest_x,REAL_SY+dest_y, pad_x,pad_y);
+
+    return(FALSE);
+  }
+
+  if (pos_x<-anim->width)
+    pos_x = -anim->width;
+  else if (pos_x>FULL_SXSIZE)
+    pos_x = FULL_SXSIZE;
+  if (pos_y<-anim->height)
+    pos_y = -anim->height;
+  else if (pos_y>FULL_SYSIZE)
+    pos_y = FULL_SYSIZE;
+
+  pad_x = (horiz_move ? anim->stepsize : 0);
+  pad_y = (vert_move  ? anim->stepsize : 0);
+  src_x = anim->src_x + frame * anim->width;
+  src_y = anim->src_y;
+  dest_x = pos_x;
+  dest_y = pos_y;
+  cut_x = cut_y = 0;
+  width  = anim->width;
+  height = anim->height;
+
+  if (pos_x<0)
+  {
+    dest_x = 0;
+    width += pos_x;
+    cut_x = -pos_x;
+  }
+  else if (pos_x>FULL_SXSIZE-anim->width)
+    width -= (pos_x - (FULL_SXSIZE-anim->width));
+
+  if (pos_y<0)
+  {
+    dest_y = 0;
+    height += pos_y;
+    cut_y = -pos_y;
+  }
+  else if (pos_y>FULL_SYSIZE-anim->height)
+    height -= (pos_y - (FULL_SYSIZE-anim->height));
+
+  DrawAnim(anim_pixmap,anim_clip_gc,
+          src_x+cut_x,src_y+cut_y, width,height,
+          REAL_SX+dest_x,REAL_SY+dest_y, pad_x,pad_y);
+
+  pos_x += delta_x;
+  pos_y += delta_y;
+  frame += frame_step;
+
+  if (frame<0 || frame>=anim->frames)
+  {
+    if (anim->pingpong)
+    {
+      frame_step *= -1;
+      frame = (frame<0 ? 1 : anim->frames-2);
+    }
+    else
+      frame = (frame<0 ? anim->frames-1 : 0);
+  }
+
+  return(FALSE);
+}
+
+void DrawAnim(Pixmap toon_pixmap, GC toon_clip_gc,
+             int src_x, int src_y, int width, int height,
+             int dest_x, int dest_y, int pad_x, int pad_y)
+{
+  int buf_x = DOOR_GFX_PAGEX3, buf_y = DOOR_GFX_PAGEY1;
+
+#if 1
+  /* special method to avoid flickering interference with BackToFront() */
+  XCopyArea(display,backbuffer,pix[PIX_DB_DOOR],gc,dest_x-pad_x,dest_y-pad_y,
+           width+2*pad_x,height+2*pad_y, buf_x,buf_y);
+  XSetClipOrigin(display,toon_clip_gc,dest_x-src_x,dest_y-src_y);
+  XCopyArea(display,toon_pixmap,backbuffer,toon_clip_gc,
+           src_x,src_y, width,height, dest_x,dest_y);
+  XCopyArea(display,backbuffer,window,gc, dest_x-pad_x,dest_y-pad_y,
+           width+2*pad_x,height+2*pad_y, dest_x-pad_x,dest_y-pad_y);
+  BackToFront();
+  XCopyArea(display,pix[PIX_DB_DOOR],backbuffer,gc, buf_x,buf_y,
+           width+2*pad_x,height+2*pad_y, dest_x-pad_x,dest_y-pad_y);
+#else
+  /* normal method, causing flickering interference with BackToFront() */
+  XCopyArea(display,backbuffer,pix[PIX_DB_DOOR],gc,dest_x-pad_x,dest_y-pad_y,
+           width+2*pad_x,height+2*pad_y, buf_x,buf_y);
+  XSetClipOrigin(display,toon_clip_gc,
+                buf_x-src_x+pad_x,buf_y-src_y+pad_y);
+  XCopyArea(display,toon_pixmap,pix[PIX_DB_DOOR],toon_clip_gc,
+           src_x,src_y, width,height, buf_x+pad_x,buf_y+pad_y);
+  XCopyArea(display,pix[PIX_DB_DOOR],window,gc, buf_x,buf_y,
+           width+2*pad_x,height+2*pad_y, dest_x-pad_x,dest_y-pad_y);
+#endif
+
+  XFlush(display);
+}
diff --git a/src/cartoons.h b/src/cartoons.h
new file mode 100644 (file)
index 0000000..fdde0cb
--- /dev/null
@@ -0,0 +1,21 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
+*----------------------------------------------------------*
+*  cartoons.h                                              *
+***********************************************************/
+
+#ifndef CARTOONS_H
+#define CARTOONS_H
+
+void InitAnimation(void);
+void StopAnimation(void);
+void DoAnimation(void);
+
+#endif
index 454fb74e88981448c75eabbd3181e0fa25bbf3ca..2955faecfba6787249536d83bcab1ccae933c9d3 100644 (file)
@@ -1,13 +1,12 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  editor.c                                                *
 ***********************************************************/
 #include "buttons.h"
 #include "files.h"
 
+/* positions in the level editor */
+#define ED_WIN_MB_LEFT_XPOS    7
+#define ED_WIN_MB_LEFT_YPOS    6
+#define ED_WIN_LEVELNR_XPOS    77
+#define ED_WIN_LEVELNR_YPOS    7
+#define ED_WIN_MB_MIDDLE_XPOS  7
+#define ED_WIN_MB_MIDDLE_YPOS  258
+#define ED_WIN_MB_RIGHT_XPOS   77
+#define ED_WIN_MB_RIGHT_YPOS   258
+
+/* other constants for the editor */
+#define ED_SCROLL_NO           0
+#define ED_SCROLL_LEFT         1
+#define ED_SCROLL_RIGHT                2
+#define ED_SCROLL_UP           4
+#define ED_SCROLL_DOWN         8
+
+/* delay value to avoid too fast scrolling etc. */
+#define CHOICE_DELAY_VALUE     100
+
 static int level_xpos,level_ypos;
-static BOOL edit_mode;
-static BOOL name_typing;
+static boolean edit_mode;
+static boolean name_typing;
 static int new_element1 = EL_MAUERWERK;
 static int new_element2 = EL_LEERRAUM;
 static int new_element3 = EL_ERDREICH;
@@ -29,6 +48,66 @@ static int new_element3 = EL_ERDREICH;
 int element_shift;
 int editor_element[] =
 {
+  EL_CHAR_A + ('B' - 'A'),
+  EL_CHAR_A + ('O' - 'A'),
+  EL_CHAR_A + ('U' - 'A'),
+  EL_CHAR_A + ('L' - 'A'),
+
+  EL_CHAR_MINUS,
+  EL_CHAR_A + ('D' - 'A'),
+  EL_CHAR_A + ('E' - 'A'),
+  EL_CHAR_A + ('R' - 'A'),
+
+  EL_CHAR_A + ('D' - 'A'),
+  EL_CHAR_A + ('A' - 'A'),
+  EL_CHAR_A + ('S' - 'A'),
+  EL_CHAR_A + ('H' - 'A'),
+
+  EL_SPIELFIGUR,
+  EL_LEERRAUM,
+  EL_ERDREICH,
+  EL_BETON,
+
+  EL_FELSBODEN,
+  EL_SIEB2_LEER,
+  EL_AUSGANG_ZU,
+  EL_AUSGANG_AUF,
+
+  EL_EDELSTEIN_BD,
+  EL_BUTTERFLY_O,
+  EL_FIREFLY_O,
+  EL_FELSBROCKEN,
+
+  EL_BUTTERFLY_L,
+  EL_FIREFLY_L,
+  EL_BUTTERFLY_R,
+  EL_FIREFLY_R,
+
+  EL_AMOEBE_BD,
+  EL_BUTTERFLY_U,
+  EL_FIREFLY_U,
+  EL_LEERRAUM,
+
+  EL_CHAR_A + ('E' - 'A'),
+  EL_CHAR_A + ('M' - 'A'),
+  EL_CHAR_A + ('E' - 'A'),
+  EL_CHAR_MINUS,
+
+  EL_CHAR_A + ('R' - 'A'),
+  EL_CHAR_A + ('A' - 'A'),
+  EL_CHAR_A + ('L' - 'A'),
+  EL_CHAR_A + ('D' - 'A'),
+
+  EL_CHAR_A + ('M' - 'A'),
+  EL_CHAR_A + ('I' - 'A'),
+  EL_CHAR_A + ('N' - 'A'),
+  EL_CHAR_A + ('E' - 'A'),
+
+  EL_SPIELER1,
+  EL_SPIELER2,
+  EL_SPIELER3,
+  EL_SPIELER4,
+
   EL_SPIELFIGUR,
   EL_LEERRAUM,
   EL_ERDREICH,
@@ -44,51 +123,46 @@ int editor_element[] =
   EL_KOKOSNUSS,
   EL_BOMBE,
 
+  EL_ERZ_EDEL,
+  EL_ERZ_DIAM,
   EL_MORAST_LEER,
   EL_MORAST_VOLL,
+
+  EL_DYNAMIT_AUS,
+  EL_DYNAMIT,
   EL_AUSGANG_ZU,
   EL_AUSGANG_AUF,
 
-  EL_KAEFER,
-  EL_FLIEGER,
   EL_MAMPFER,
-  EL_ZOMBIE,
+  EL_KAEFER_O,
+  EL_FLIEGER_O,
+  EL_ROBOT,
+
+  EL_KAEFER_L,
+  EL_FLIEGER_L,
+  EL_KAEFER_R,
+  EL_FLIEGER_R,
 
-  EL_PACMAN,
-  EL_DYNAMIT_AUS,
-  EL_DYNAMIT,
   EL_ABLENK_AUS,
+  EL_KAEFER_U,
+  EL_FLIEGER_U,
+  EL_UNSICHTBAR,
 
   EL_BADEWANNE1,
   EL_SALZSAEURE,
   EL_BADEWANNE2,
-  EL_BADEWANNE,
+  EL_LEERRAUM,
 
   EL_BADEWANNE3,
   EL_BADEWANNE4,
   EL_BADEWANNE5,
-  EL_UNSICHTBAR,
+  EL_LEERRAUM,
 
   EL_TROPFEN,
   EL_AMOEBE_TOT,
   EL_AMOEBE_NASS,
   EL_AMOEBE_NORM,
 
-  EL_AMOEBE_VOLL,
-
-/*
-  EL_LIFE,
-*/
-  EL_LIFE_ASYNC,
-
-  EL_ERZ_EDEL,
-  EL_ERZ_DIAM,
-
-  EL_ZEIT_VOLL,
-  EL_ZEIT_LEER,
-  EL_BIRNE_AUS,
-  EL_BIRNE_EIN,
-
   EL_SCHLUESSEL1,
   EL_SCHLUESSEL2,
   EL_SCHLUESSEL3,
@@ -104,20 +178,92 @@ int editor_element[] =
   EL_PFORTE3X,
   EL_PFORTE4X,
 
-  EL_KAEFER_R,
-  EL_KAEFER_O,
-  EL_KAEFER_L,
-  EL_KAEFER_U,
+  EL_CHAR_A + ('M' - 'A'),
+  EL_CHAR_A + ('O' - 'A'),
+  EL_CHAR_A + ('R' - 'A'),
+  EL_CHAR_A + ('E' - 'A'),
 
-  EL_FLIEGER_R,
-  EL_FLIEGER_O,
-  EL_FLIEGER_L,
-  EL_FLIEGER_U,
+  EL_PFEIL_L,
+  EL_PFEIL_R,
+  EL_PFEIL_O,
+  EL_PFEIL_U,
 
-  EL_PACMAN_R,
+  EL_AMOEBE_VOLL,
+  EL_EDELSTEIN_GELB,
+  EL_EDELSTEIN_ROT,
+  EL_EDELSTEIN_LILA,
+
+  EL_ERZ_EDEL_BD,
+  EL_ERZ_EDEL_GELB,
+  EL_ERZ_EDEL_ROT,
+  EL_ERZ_EDEL_LILA,
+
+  EL_LIFE,
   EL_PACMAN_O,
+  EL_ZEIT_VOLL,
+  EL_ZEIT_LEER,
+
   EL_PACMAN_L,
+  EL_MAMPFER2,
+  EL_PACMAN_R,
+  EL_MAUER_LEBT,
+
+  EL_LIFE_ASYNC,
   EL_PACMAN_U,
+  EL_BIRNE_AUS,
+  EL_BIRNE_EIN,
+
+  EL_DYNABOMB_NR,
+  EL_DYNABOMB_SZ,
+  EL_DYNABOMB_XL,
+  EL_BADEWANNE,
+
+  EL_MAULWURF,
+  EL_PINGUIN,
+  EL_SCHWEIN,
+  EL_DRACHE,
+
+  EL_SONDE,
+  EL_MAUER_X,
+  EL_MAUER_Y,
+  EL_MAUER_XY,
+
+  EL_CHAR_A + ('S' - 'A'),
+  EL_CHAR_A + ('O' - 'A'),
+  EL_CHAR_A + ('K' - 'A'),
+  EL_CHAR_A + ('O' - 'A'),
+
+  EL_CHAR_MINUS,
+  EL_CHAR_A + ('B' - 'A'),
+  EL_CHAR_A + ('A' - 'A'),
+  EL_CHAR_A + ('N' - 'A'),
+
+  EL_SOKOBAN_OBJEKT,
+  EL_SOKOBAN_FELD_LEER,
+  EL_SOKOBAN_FELD_VOLL,
+  EL_BETON,
+
+/*
+  EL_CHAR_A + ('D' - 'A'),
+  EL_CHAR_A + ('Y' - 'A'),
+  EL_CHAR_A + ('N' - 'A'),
+  EL_CHAR_A + ('A' - 'A'),
+
+  EL_CHAR_A + ('B' - 'A'),
+  EL_CHAR_A + ('L' - 'A'),
+  EL_CHAR_A + ('A' - 'A'),
+  EL_CHAR_A + ('S' - 'A'),
+
+  EL_CHAR_MINUS,
+  EL_CHAR_A + ('T' - 'A'),
+  EL_CHAR_A + ('E' - 'A'),
+  EL_CHAR_A + ('R' - 'A'),
+*/
+
+  EL_LEERRAUM,
+  EL_LEERRAUM,
+  EL_LEERRAUM,
+  EL_LEERRAUM,
 
   EL_CHAR_AUSRUF,
   EL_CHAR_ZOLL,
@@ -228,26 +374,26 @@ void DrawLevelEd()
     else
       graphic = GFX_LEERRAUM;
 
-    DrawMiniGraphicExtHiRes(pix[PIX_DB_DOOR],gc,
-                           DOOR_GFX_PAGEX1+ED_BUTTON_ELEM_XPOS+3 + 
-                           (i%MAX_ELEM_X)*ED_BUTTON_ELEM_XSIZE,
-                           DOOR_GFX_PAGEY1+ED_BUTTON_ELEM_YPOS+3 +
-                           (i/MAX_ELEM_X)*ED_BUTTON_ELEM_YSIZE,
-                           graphic);
+    DrawMiniGraphicExt(pix[PIX_DB_DOOR],gc,
+                      DOOR_GFX_PAGEX1+ED_BUTTON_ELEM_XPOS+3 + 
+                      (i%MAX_ELEM_X)*ED_BUTTON_ELEM_XSIZE,
+                      DOOR_GFX_PAGEY1+ED_BUTTON_ELEM_YPOS+3 +
+                      (i/MAX_ELEM_X)*ED_BUTTON_ELEM_YSIZE,
+                      graphic);
   }
 
-  DrawMiniGraphicExtHiRes(pix[PIX_DB_DOOR],gc,
-                         DOOR_GFX_PAGEX1+ED_WIN_MB_LEFT_XPOS,
-                         DOOR_GFX_PAGEY1+ED_WIN_MB_LEFT_YPOS,
-                         el2gfx(new_element1));
-  DrawMiniGraphicExtHiRes(pix[PIX_DB_DOOR],gc,
-                         DOOR_GFX_PAGEX1+ED_WIN_MB_MIDDLE_XPOS,
-                         DOOR_GFX_PAGEY1+ED_WIN_MB_MIDDLE_YPOS,
-                         el2gfx(new_element2));
-  DrawMiniGraphicExtHiRes(pix[PIX_DB_DOOR],gc,
-                         DOOR_GFX_PAGEX1+ED_WIN_MB_RIGHT_XPOS,
-                         DOOR_GFX_PAGEY1+ED_WIN_MB_RIGHT_YPOS,
-                         el2gfx(new_element3));
+  DrawMiniGraphicExt(pix[PIX_DB_DOOR],gc,
+                    DOOR_GFX_PAGEX1+ED_WIN_MB_LEFT_XPOS,
+                    DOOR_GFX_PAGEY1+ED_WIN_MB_LEFT_YPOS,
+                    el2gfx(new_element1));
+  DrawMiniGraphicExt(pix[PIX_DB_DOOR],gc,
+                    DOOR_GFX_PAGEX1+ED_WIN_MB_MIDDLE_XPOS,
+                    DOOR_GFX_PAGEY1+ED_WIN_MB_MIDDLE_YPOS,
+                    el2gfx(new_element2));
+  DrawMiniGraphicExt(pix[PIX_DB_DOOR],gc,
+                    DOOR_GFX_PAGEX1+ED_WIN_MB_RIGHT_XPOS,
+                    DOOR_GFX_PAGEY1+ED_WIN_MB_RIGHT_YPOS,
+                    el2gfx(new_element3));
   DrawTextExt(pix[PIX_DB_DOOR],gc,
              DOOR_GFX_PAGEX2+ED_WIN_LEVELNR_XPOS,
              DOOR_GFX_PAGEY1+ED_WIN_LEVELNR_YPOS,
@@ -479,27 +625,28 @@ void FloodFill(int from_x, int from_y, int fill_element)
 {
   int i,x,y;
   int old_element;
-  static int check[4][2] = { -1,0, 0,-1, 1,0, 0,1 };
+  static int check[4][2] = { {-1,0}, {0,-1}, {1,0}, {0,1} };
   static int safety = 0;
 
+  /* check if starting field still has the desired content */
+  if (Feld[from_x][from_y] == fill_element)
+    return;
+
   safety++;
 
-  if (safety>lev_fieldx*lev_fieldy)
-  {
-    fprintf(stderr,"Something went wrong in 'FloodFill()'. Please debug.\n");
-    exit(-1);
-  }
+  if (safety > lev_fieldx*lev_fieldy)
+    Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
 
   old_element = Feld[from_x][from_y];
   Feld[from_x][from_y] = fill_element;
 
   for(i=0;i<4;i++)
   {
-    x = from_x+check[i][0];
-    y = from_y+check[i][1];
+    x = from_x + check[i][0];
+    y = from_y + check[i][1];
 
-    if (IN_LEV_FIELD(x,y) && Feld[x][y]==old_element)
-      FloodFill(x,y,fill_element);
+    if (IN_LEV_FIELD(x,y) && Feld[x][y] == old_element)
+      FloodFill(x, y, fill_element);
   }
 
   safety--;
@@ -509,7 +656,7 @@ void LevelEd(int mx, int my, int button)
 {
   static int last_button = 0;
   static int in_field_pressed = FALSE;
-  static BOOL use_floodfill = FALSE;
+  static boolean use_floodfill = FALSE;
   int x = (mx-SX)/MINI_TILEX; 
   int y = (my-SY)/MINI_TILEY; 
 
@@ -547,69 +694,61 @@ void LevelEd(int mx, int my, int button)
   }
   else                         /********** EDIT/CTRL-FENSTER **********/
   {
+    static unsigned long choice_delay = 0;
     int choice = CheckElemButtons(mx,my,button);
     int elem_pos = choice-ED_BUTTON_ELEM;
 
-    switch(choice)
+    if (((choice == ED_BUTTON_EUP && element_shift>0) ||
+        (choice == ED_BUTTON_EDOWN &&
+         element_shift<elements_in_list-MAX_ELEM_X*MAX_ELEM_Y)) &&
+       DelayReached(&choice_delay, CHOICE_DELAY_VALUE))
     {
-      case ED_BUTTON_EUP:
-      case ED_BUTTON_EDOWN:
-        if ((choice==ED_BUTTON_EUP && element_shift>0) ||
-           (choice==ED_BUTTON_EDOWN &&
-            element_shift<elements_in_list-MAX_ELEM_X*MAX_ELEM_Y))
-       {
-         int i, step;
-
-         step = (button==1 ? MAX_ELEM_X : button==2 ? 5*MAX_ELEM_X :
-                 elements_in_list);
-         element_shift += (choice==ED_BUTTON_EUP ? -step : step);
-         if (element_shift<0)
-           element_shift = 0;
-         if (element_shift>elements_in_list-MAX_ELEM_X*MAX_ELEM_Y)
-           element_shift = elements_in_list-MAX_ELEM_X*MAX_ELEM_Y;
-         if (element_shift % MAX_ELEM_X)
-           element_shift += MAX_ELEM_X-(element_shift % MAX_ELEM_X);
-
-         for(i=0;i<MAX_ELEM_X*MAX_ELEM_Y;i++)
-           DrawElemButton(i+2,ED_BUTTON_RELEASED);
-         BackToFront();
-         Delay(100000);
-       }
-       break;
-      default:
-       if (elem_pos>=0 && elem_pos<MAX_ELEM_X*MAX_ELEM_Y)
-       {
-         int new_element;
-
-         if (elem_pos+element_shift < elements_in_list)
-           new_element = editor_element[elem_pos+element_shift];
-         else
-           new_element = EL_LEERRAUM;
-
-         if (last_button==1)
-           new_element1 = new_element;
-         else if (last_button==2)
-           new_element2 = new_element;
-         else if (last_button==3)
-           new_element3 = new_element;
-
-         DrawMiniGraphicExtHiRes(drawto,gc,
-                                 DX+ED_WIN_MB_LEFT_XPOS,
-                                 DY+ED_WIN_MB_LEFT_YPOS,
-                                 el2gfx(new_element1));
-         DrawMiniGraphicExtHiRes(drawto,gc,
-                                 DX+ED_WIN_MB_MIDDLE_XPOS,
-                                 DY+ED_WIN_MB_MIDDLE_YPOS,
-                                 el2gfx(new_element2));
-         DrawMiniGraphicExtHiRes(drawto,gc,
-                                 DX+ED_WIN_MB_RIGHT_XPOS,
-                                 DY+ED_WIN_MB_RIGHT_YPOS,
-                                 el2gfx(new_element3));
-         redraw_mask |= REDRAW_DOOR_1;
-       }
-       break;
+      int i, step;
+
+      step = (button==1 ? MAX_ELEM_X : button==2 ? 5*MAX_ELEM_X :
+             elements_in_list);
+      element_shift += (choice==ED_BUTTON_EUP ? -step : step);
+      if (element_shift<0)
+       element_shift = 0;
+      if (element_shift>elements_in_list-MAX_ELEM_X*MAX_ELEM_Y)
+       element_shift = elements_in_list-MAX_ELEM_X*MAX_ELEM_Y;
+      if (element_shift % MAX_ELEM_X)
+       element_shift += MAX_ELEM_X-(element_shift % MAX_ELEM_X);
+
+      for(i=0;i<MAX_ELEM_X*MAX_ELEM_Y;i++)
+       DrawElemButton(i+2,ED_BUTTON_RELEASED);
     }
+    else if (elem_pos>=0 && elem_pos<MAX_ELEM_X*MAX_ELEM_Y)
+    {
+      int new_element;
 
+      if (elem_pos+element_shift < elements_in_list)
+       new_element = editor_element[elem_pos+element_shift];
+      else
+       new_element = EL_LEERRAUM;
+
+      if (last_button==1)
+       new_element1 = new_element;
+      else if (last_button==2)
+       new_element2 = new_element;
+      else if (last_button==3)
+       new_element3 = new_element;
+
+      DrawMiniGraphicExt(drawto,gc,
+                        DX+ED_WIN_MB_LEFT_XPOS,
+                        DY+ED_WIN_MB_LEFT_YPOS,
+                        el2gfx(new_element1));
+      DrawMiniGraphicExt(drawto,gc,
+                        DX+ED_WIN_MB_MIDDLE_XPOS,
+                        DY+ED_WIN_MB_MIDDLE_YPOS,
+                        el2gfx(new_element2));
+      DrawMiniGraphicExt(drawto,gc,
+                        DX+ED_WIN_MB_RIGHT_XPOS,
+                        DY+ED_WIN_MB_RIGHT_YPOS,
+                        el2gfx(new_element3));
+      redraw_mask |= REDRAW_DOOR_1;
+    }
+  
     if (edit_mode)             /********** EDIT-FENSTER **********/
     {
       switch(CheckEditButtons(mx,my,button))
@@ -625,13 +764,15 @@ void LevelEd(int mx, int my, int button)
          edit_mode = FALSE;
          break;
        case ED_BUTTON_FILL:
-         AreYouSure("Caution ! Flood fill mode ! Choose area !",AYS_OPEN);
+         Request("Caution ! Flood fill mode ! Choose area !",REQ_OPEN);
          use_floodfill = TRUE;
          return;
          break;
        case ED_BUTTON_LEFT:
          if (level_xpos>=0)
          {
+           if (!DelayReached(&choice_delay, CHOICE_DELAY_VALUE))
+             break;
            if (lev_fieldx<2*SCR_FIELDX-2)
              break;
 
@@ -642,13 +783,13 @@ void LevelEd(int mx, int my, int button)
              ScrollMiniLevel(level_xpos,level_ypos,ED_SCROLL_RIGHT);
            else
              DrawMiniLevel(level_xpos,level_ypos);
-           BackToFront();
-           Delay(100000);
          }
          break;
        case ED_BUTTON_RIGHT:
          if (level_xpos<=lev_fieldx-2*SCR_FIELDX)
          {
+           if (!DelayReached(&choice_delay, CHOICE_DELAY_VALUE))
+             break;
            if (lev_fieldx<2*SCR_FIELDX-2)
              break;
 
@@ -659,13 +800,13 @@ void LevelEd(int mx, int my, int button)
              ScrollMiniLevel(level_xpos,level_ypos,ED_SCROLL_LEFT);
            else
              DrawMiniLevel(level_xpos,level_ypos);
-           BackToFront();
-           Delay(100000);
          }
          break;
        case ED_BUTTON_UP:
          if (level_ypos>=0)
          {
+           if (!DelayReached(&choice_delay, CHOICE_DELAY_VALUE))
+             break;
            if (lev_fieldy<2*SCR_FIELDY-2)
              break;
 
@@ -676,13 +817,13 @@ void LevelEd(int mx, int my, int button)
              ScrollMiniLevel(level_xpos,level_ypos,ED_SCROLL_DOWN);
            else
              DrawMiniLevel(level_xpos,level_ypos);
-           BackToFront();
-           Delay(100000);
          }
          break;
        case ED_BUTTON_DOWN:
          if (level_ypos<=lev_fieldy-2*SCR_FIELDY)
          {
+           if (!DelayReached(&choice_delay, CHOICE_DELAY_VALUE))
+             break;
            if (lev_fieldy<2*SCR_FIELDY-2)
              break;
 
@@ -693,8 +834,6 @@ void LevelEd(int mx, int my, int button)
              ScrollMiniLevel(level_xpos,level_ypos,ED_SCROLL_UP);
            else
              DrawMiniLevel(level_xpos,level_ypos);
-           BackToFront();
-           Delay(100000);
          }
          break;
        default:
@@ -747,11 +886,11 @@ void LevelEd(int mx, int my, int button)
     }
     else                       /********** KONTROLL-FENSTER **********/
     {
-      static long choice_delay = 0;
       int choice = CheckCountButtons(mx,my,button);
       int step = (button==1 ? 1 : button==2 ? 5 : button==3 ? 10 : 0);
 
-      if (choice>=0 && choice<36 && DelayReached(&choice_delay,10))
+      if (choice >= 0 && choice < 36 &&
+         DelayReached(&choice_delay, CHOICE_DELAY_VALUE))
       {
        if (!(choice % 2))
          step = -step;
@@ -890,7 +1029,7 @@ void LevelEd(int mx, int my, int button)
          edit_mode = TRUE;
          break;
        case ED_BUTTON_CLEAR:
-         if (AreYouSure("Are you sure to clear this level ?",AYS_ASK))
+         if (Request("Are you sure to clear this level ?",REQ_ASK))
          {
            for(x=0;x<MAX_LEV_FIELDX;x++) 
              for(y=0;y<MAX_LEV_FIELDY;y++) 
@@ -899,7 +1038,8 @@ void LevelEd(int mx, int my, int button)
          }
          break;
        case ED_BUTTON_UNDO:
-         if (AreYouSure("Exit without saving ?",AYS_ASK | AYS_STAY_OPEN))
+         if (leveldir[leveldir_nr].readonly ||
+             Request("Exit without saving ?",REQ_ASK | REQ_STAY_OPEN))
          {
            CloseDoor(DOOR_CLOSE_BOTH);
            game_status=MAINMENU;
@@ -914,18 +1054,25 @@ void LevelEd(int mx, int my, int button)
        case ED_BUTTON_EXIT:
          {
            int figur_vorhanden = FALSE;
+
+           if (leveldir[leveldir_nr].readonly)
+           {
+             Request("This level is read only !",REQ_CONFIRM);
+             break;
+           }
+
            for(y=0;y<lev_fieldy;y++) 
              for(x=0;x<lev_fieldx;x++)
                if (Feld[x][y]==EL_SPIELFIGUR || Feld[x][y]==EL_SPIELER1) 
                  figur_vorhanden = TRUE;
 
            if (!figur_vorhanden)
-             AreYouSure("No Level without Gregor Mc Duffin please !",
-                        AYS_CONFIRM);
+             Request("No Level without Gregor Mc Duffin please !",
+                        REQ_CONFIRM);
            else
            {
-             if (AreYouSure("Save this level and kill the old ?",
-                            AYS_ASK | AYS_STAY_OPEN))
+             if (Request("Save this level and kill the old ?",
+                            REQ_ASK | REQ_STAY_OPEN))
              {
                for(x=0;x<lev_fieldx;x++)
                  for(y=0;y<lev_fieldy;y++) 
@@ -1087,7 +1234,7 @@ void LevelNameTyping(KeySym key)
                ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
                "<",FS_SMALL,FC_RED);
   }
-  else if (key==XK_Delete && len>0)
+  else if ((key==XK_Delete || key==XK_BackSpace) && len>0)
   {
     level.name[len-1] = 0;
     len--;
index b4ebfed2e03d6324d2e11226eebb45c50a3392c3..dae87c12e6d4a72a1f115a6acabc9e993142c1fe 100644 (file)
@@ -1,13 +1,12 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  editor.h                                                *
 ***********************************************************/
 
 #include "main.h"
 
-/* positions in the level editor */
-#define ED_WIN_MB_LEFT_XPOS    7
-#define ED_WIN_MB_LEFT_YPOS    6
-#define ED_WIN_LEVELNR_XPOS    77
-#define ED_WIN_LEVELNR_YPOS    7
-#define ED_WIN_MB_MIDDLE_XPOS  7
-#define ED_WIN_MB_MIDDLE_YPOS  258
-#define ED_WIN_MB_RIGHT_XPOS   77
-#define ED_WIN_MB_RIGHT_YPOS   258
-
+/* number of element button columns and rows in the edit window */
 #define MAX_ELEM_X             4
 #define MAX_ELEM_Y             10
 
-/* other constants for the editor */
-#define ED_SCROLL_NO           0
-#define ED_SCROLL_LEFT         1
-#define ED_SCROLL_RIGHT                2
-#define ED_SCROLL_UP           4
-#define ED_SCROLL_DOWN         8
-
 extern int element_shift;
 extern int editor_element[];
 extern int elements_in_list;
 
 void DrawLevelEd(void);
-void ScrollMiniLevel(int, int, int);
 void LevelEd(int, int, int);
 void LevelNameTyping(KeySym);
 
index 9eb8d62312979a1b418dd4d95edfbd563130bb63..2d8eca3e861657b6442169036ac88d5395f08934 100644 (file)
@@ -1,29 +1,37 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  events.c                                                *
 ***********************************************************/
 
 #include "events.h"
+#include "init.h"
 #include "screens.h"
 #include "tools.h"
 #include "game.h"
 #include "editor.h"
 #include "misc.h"
+#include "tape.h"
+#include "joystick.h"
+#include "network.h"
+
+/* values for key_status */
+#define KEY_NOT_PRESSED                FALSE
+#define KEY_RELEASED           FALSE
+#define KEY_PRESSED            TRUE
 
 void EventLoop(void)
 {
   while(1)
   {
-    if (XPending(display))     /* got an event */
+    if (XPending(display))     /* got event from X server */
     {
       XEvent event;
 
@@ -31,52 +39,68 @@ void EventLoop(void)
 
       switch(event.type)
       {
-       case Expose:
-         HandleExposeEvent((XExposeEvent *) &event);
-         break;
-       case UnmapNotify:
-         SleepWhileUnmapped();
-         break;
        case ButtonPress:
-         HandleButtonEvent((XButtonEvent *) &event);
-         break;
        case ButtonRelease:
          HandleButtonEvent((XButtonEvent *) &event);
          break;
+
        case MotionNotify:
          HandleMotionEvent((XMotionEvent *) &event);
          break;
+
        case KeyPress:
-         HandleKeyEvent((XKeyEvent *) &event);
-         break;
        case KeyRelease:
          HandleKeyEvent((XKeyEvent *) &event);
          break;
-       case FocusIn:
-         HandleFocusEvent(FOCUS_IN);
-         break;
-       case FocusOut:
-         HandleFocusEvent(FOCUS_OUT);
-         break;
+
        default:
+         HandleOtherEvents(&event);
          break;
       }
     }
-    else                       /* got no event, but don't be lazy... */
-    {
-      HandleNoXEvent();
 
-      XSync(display,FALSE);
+    HandleNoXEvent();
+
+    /* don't use all CPU time when idle; the main loop while playing
+       has its own synchronization and is CPU friendly, too */
 
-      if (game_status!=PLAYING)
-       Delay(10000);           /* don't use all CPU time when idle */
+    if (game_status != PLAYING)
+    {
+      XSync(display, FALSE);
+      Delay(10);
     }
 
-    if (game_status==EXITGAME)
+    if (game_status == EXITGAME)
       return;
   }
 }
 
+void HandleOtherEvents(XEvent *event)
+{
+  switch(event->type)
+  {
+    case Expose:
+      HandleExposeEvent((XExposeEvent *) event);
+      break;
+
+    case UnmapNotify:
+      SleepWhileUnmapped();
+      break;
+
+    case FocusIn:
+    case FocusOut:
+      HandleFocusEvent((XFocusChangeEvent *) event);
+      break;
+
+    case ClientMessage:
+      HandleClientMessageEvent((XClientMessageEvent *) event);
+      break;
+
+    default:
+      break;
+  }
+}
+
 void ClearEventQueue()
 {
   while(XPending(display))
@@ -87,25 +111,16 @@ void ClearEventQueue()
 
     switch(event.type)
     {
-      case Expose:
-        HandleExposeEvent((XExposeEvent *) &event);
-       break;
-      case UnmapNotify:
-       SleepWhileUnmapped();
-       break;
       case ButtonRelease:
        button_status = MB_RELEASED;
        break;
+
       case KeyRelease:
-       key_status = KEY_RELEASED;
-       break;
-      case FocusIn:
-       HandleFocusEvent(FOCUS_IN);
-       break;
-      case FocusOut:
-       HandleFocusEvent(FOCUS_OUT);
+       key_joystick_mapping = 0;
        break;
+
       default:
+       HandleOtherEvents(&event);
        break;
     }
   }
@@ -113,7 +128,7 @@ void ClearEventQueue()
 
 void SleepWhileUnmapped()
 {
-  BOOL window_unmapped = TRUE;
+  boolean window_unmapped = TRUE;
 
   XAutoRepeatOn(display);
 
@@ -125,19 +140,27 @@ void SleepWhileUnmapped()
 
     switch(event.type)
     {
-      case Expose:
-        HandleExposeEvent((XExposeEvent *) &event);
-       break;
       case ButtonRelease:
        button_status = MB_RELEASED;
        break;
+
       case KeyRelease:
-       key_status = KEY_RELEASED;
+       key_joystick_mapping = 0;
        break;
+
       case MapNotify:
        window_unmapped = FALSE;
        break;
+
+      case UnmapNotify:
+       /* this is only to surely prevent the 'should not happen' case
+        * of recursively looping between 'SleepWhileUnmapped()' and
+        * 'HandleOtherEvents()' which usually calls this funtion.
+        */
+       break;
+
       default:
+       HandleOtherEvents(&event);
        break;
     }
   }
@@ -151,21 +174,37 @@ void HandleExposeEvent(XExposeEvent *event)
   int x = event->x, y = event->y;
   int width = event->width, height = event->height;
 
-  XCopyArea(display,drawto,window,gc, x,y, width,height, x,y);
-
-  if (direct_draw_on && game_status==PLAYING)
+  if (setup.direct_draw && game_status==PLAYING)
   {
     int xx,yy;
     int x1 = (x-SX)/TILEX, y1 = (y-SY)/TILEY;
     int x2 = (x-SX+width)/TILEX, y2 = (y-SY+height)/TILEY;
 
-    for(xx=0;xx<SCR_FIELDX;xx++)
-      for(yy=0;yy<SCR_FIELDY;yy++)
+    SetDrawtoField(DRAW_BACKBUFFER);
+
+    for(xx=0; xx<SCR_FIELDX; xx++)
+      for(yy=0; yy<SCR_FIELDY; yy++)
        if (xx>=x1 && xx<=x2 && yy>=y1 && yy<=y2)
          DrawScreenField(xx,yy);
-    DrawLevelElement(JX,JY,EL_SPIELFIGUR);
+    DrawAllPlayers();
+
+    SetDrawtoField(DRAW_DIRECT);
   }
 
+  if (setup.soft_scrolling && game_status == PLAYING)
+  {
+    int fx = FX, fy = FY;
+
+    fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
+    fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
+
+    XCopyArea(display,fieldbuffer,backbuffer,gc,
+             fx,fy, SXSIZE,SYSIZE,
+             SX,SY);
+  }
+
+  XCopyArea(display,drawto,window,gc, x,y, width,height, x,y);
+
   XFlush(display);
 }
 
@@ -190,34 +229,40 @@ void HandleMotionEvent(XMotionEvent *event)
 
 void HandleKeyEvent(XKeyEvent *event)
 {
-  static KeySym old_keycode = 0;
-  int new_keycode = event->keycode;
-  KeySym new_key = XLookupKeysym(event,event->state);
-  int new_key_status = (event->type==KeyPress ? KEY_PRESSED : KEY_RELEASED);
+  int key_status = (event->type == KeyPress ? KEY_PRESSED : KEY_RELEASED);
+  unsigned int event_state = (game_status != PLAYING ? event->state : 0);
+  KeySym key = XLookupKeysym(event, event_state);
+
+  HandleKey(key, key_status);
+}
 
-  if (game_status==PLAYING &&
-      (old_keycode!=new_keycode || key_status!=new_key_status))
+void HandleFocusEvent(XFocusChangeEvent *event)
+{
+  static int old_joystick_status = -1;
+
+  if (event->type == FocusOut)
   {
-    DigField(0,0,DF_NO_PUSH);
-    SnapField(0,0);
+    XAutoRepeatOn(display);
+    old_joystick_status = joystick_status;
+    joystick_status = JOYSTICK_OFF;
+    key_joystick_mapping = 0;
   }
-
-  if (event->type==KeyPress)
+  else if (event->type == FocusIn)
   {
-    key_status = KEY_PRESSED;
-    HandleKey(new_key);
-    old_keycode = new_keycode;
+    if (game_status == PLAYING)
+      XAutoRepeatOff(display);
+    if (old_joystick_status != -1)
+      joystick_status = old_joystick_status;
   }
-  else if (key_status==KEY_PRESSED && old_keycode==new_keycode)
-    key_status = KEY_RELEASED;
 }
 
-void HandleFocusEvent(int focus_status)
+void HandleClientMessageEvent(XClientMessageEvent *event)
 {
-  if (focus_status==FOCUS_OUT)
-    XAutoRepeatOn(display);
-  else if (game_status==PLAYING)
-    XAutoRepeatOff(display);
+#ifndef MSDOS
+  if ((event->window == window) &&
+      (event->data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", FALSE)))
+    CloseAllAndExit(0);
+#endif
 }
 
 void HandleButton(int mx, int my, int button)
@@ -234,72 +279,153 @@ void HandleButton(int mx, int my, int button)
     old_mx = mx;
     old_my = my;
 
-    HandleVideoButtons(mx,my,button);
-    HandleSoundButtons(mx,my,button);
-    HandleGameButtons(mx,my,button);
+    HandleVideoButtons(mx,my, button);
+    HandleSoundButtons(mx,my, button);
+    HandleGameButtons(mx,my, button);
   }
 
   switch(game_status)
   {
     case MAINMENU:
-      HandleMainMenu(mx,my,0,0,button);
+      HandleMainMenu(mx,my, 0,0, button);
       break;
+
     case TYPENAME:
-      HandleTypeName(0,XK_Return);
+      HandleTypeName(0, XK_Return);
       break;
+
     case CHOOSELEVEL:
-      HandleChooseLevel(mx,my,0,0,button);
+      HandleChooseLevel(mx,my, 0,0, button);
       break;
+
     case HALLOFFAME:
       HandleHallOfFame(button);
       break;
+
     case LEVELED:
-      LevelEd(mx,my,button);
+      LevelEd(mx,my, button);
       break;
+
     case HELPSCREEN:
       HandleHelpScreen(button);
       break;
+
     case SETUP:
-      HandleSetupScreen(mx,my,0,0,button);
+      HandleSetupScreen(mx,my, 0,0, button);
+      break;
+
+    case SETUPINPUT:
+      HandleSetupInputScreen(mx,my, 0,0, button);
       break;
+
     case PLAYING:
-      if (!LevelSolved)
+#ifdef DEBUG
+      if (button == MB_RELEASED)
       {
-       switch(GameActions(mx,my,button))
+       int sx = (mx - SX) / TILEX;
+       int sy = (my - SY) / TILEY;
+
+       if (IN_VIS_FIELD(sx,sy))
        {
-         case ACT_GAME_OVER:
-           game_status = MAINMENU;
-           DrawMainMenu();
-           BackToFront();
-           break;
-         case ACT_NEW_GAME:
-           game_status = PLAYING;
-           InitGame();
-           break;
-         case ACT_GO_ON:
-           break;
-         default:
-           break;
+         int x = LEVELX(sx);
+         int y = LEVELY(sy);
+
+         printf("INFO: Feld[%d][%d] == %d\n", x,y, Feld[x][y]);
+         printf("      Store[%d][%d] == %d\n", x,y, Store[x][y]);
+         printf("      Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
+         printf("      StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
+         printf("      MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
+         printf("      MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
+         printf("      MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
+         printf("\n");
        }
       }
-      BackToFront();
-      Delay(10000);
+#endif
       break;
+
     default:
       break;
   }
 }
 
-void HandleKey(KeySym key)
+void HandleKey(KeySym key, int key_status)
 {
-  static KeySym old_key = 0;
+  int joy = 0;
+  static struct SetupKeyboardInfo custom_key;
+  static struct
+  {
+    KeySym *keysym_custom;
+    KeySym keysym_default;
+    byte action;
+  } key_info[] =
+  {
+    { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
+    { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
+    { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
+    { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
+    { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
+    { &custom_key.bomb,  DEFAULT_KEY_BOMB,  JOY_BUTTON_2 }
+  };
+
+  if (game_status == PLAYING)
+  {
+    int pnr;
+
+    for (pnr=0; pnr<MAX_PLAYERS; pnr++)
+    {
+      int i;
+      byte key_action = 0;
 
-  if (!key)
-    key = old_key;
+      if (setup.input[pnr].use_joystick)
+       continue;
+
+      custom_key = setup.input[pnr].key;
+
+      for (i=0; i<6; i++)
+       if (key == *key_info[i].keysym_custom)
+         key_action |= key_info[i].action;
+
+      if (key_status == KEY_PRESSED)
+       stored_player[pnr].action |= key_action;
+      else
+       stored_player[pnr].action &= ~key_action;
+    }
+  }
   else
-    old_key = key;
+  {
+    int i;
+
+    for (i=0; i<6; i++)
+      if (key == key_info[i].keysym_default)
+       joy |= key_info[i].action;
+  }
+
+  if (joy)
+  {
+    if (key_status == KEY_PRESSED)
+      key_joystick_mapping |= joy;
+    else
+      key_joystick_mapping &= ~joy;
+
+    HandleJoystick();
+  }
 
-  if (key==XK_Escape && game_status!=MAINMENU) /* quick quit to MAINMENU */
+  if (game_status != PLAYING)
+    key_joystick_mapping = 0;
+
+  if (key_status == KEY_RELEASED)
+    return;
+
+  if (key == XK_Return && game_status == PLAYING && AllPlayersGone)
+  {
+    CloseDoor(DOOR_CLOSE_1);
+    game_status = MAINMENU;
+    DrawMainMenu();
+    return;
+  }
+
+  /* allow quick escape to the main menu with the Escape key */
+  if (key == XK_Escape && game_status != MAINMENU)
   {
     CloseDoor(DOOR_CLOSE_1 | DOOR_NO_DELAY);
     game_status = MAINMENU;
@@ -307,84 +433,49 @@ void HandleKey(KeySym key)
     return;
   }
 
-  if (game_status==PLAYING && (tape.playing || tape.pausing))
+
+
+#ifndef DEBUG
+
+  if (game_status == PLAYING && (tape.playing || tape.pausing))
     return;
 
+#endif
+
+
+
   switch(game_status)
   {
     case TYPENAME:
-      HandleTypeName(0,key);
+      HandleTypeName(0, key);
       break;
+
     case MAINMENU:
     case CHOOSELEVEL:
     case SETUP:
-    {
-      int dx = 0, dy = 0;
-
+    case SETUPINPUT:
       switch(key)
       {
        case XK_Return:
-         if (game_status==MAINMENU)
-           HandleMainMenu(0,0,0,0,MB_MENU_CHOICE);
-          else if (game_status==CHOOSELEVEL)
-            HandleChooseLevel(0,0,0,0,MB_MENU_CHOICE);
-         else if (game_status==SETUP)
-           HandleSetupScreen(0,0,0,0,MB_MENU_CHOICE);
-         break;
-       case XK_Left:
-#ifdef XK_KP_Left
-       case XK_KP_Left:
-#endif
-       case XK_KP_4:
-       case XK_J:
-       case XK_j:
-         dx = -1;
-         break;
-       case XK_Right:
-#ifdef XK_KP_Right
-       case XK_KP_Right:
-#endif
-       case XK_KP_6:
-       case XK_K:
-       case XK_k:
-         dx = 1;
-         break;
-       case XK_Up:
-#ifdef XK_KP_Up
-       case XK_KP_Up:
-#endif
-       case XK_KP_8:
-       case XK_I:
-       case XK_i:
-         dy = -1;
-         break;
-       case XK_Down:
-#ifdef XK_KP_Down
-       case XK_KP_Down:
-#endif
-       case XK_KP_2:
-       case XK_M:
-       case XK_m:
-         dy = 1;
+         if (game_status == MAINMENU)
+           HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
+          else if (game_status == CHOOSELEVEL)
+            HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
+         else if (game_status == SETUP)
+           HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
+         else if (game_status == SETUPINPUT)
+           HandleSetupInputScreen(0,0, 0,0, MB_MENU_CHOICE);
          break;
+
        default:
          break;
       }
-
-      if (dx || dy)
-      {
-       if (game_status==MAINMENU)
-         HandleMainMenu(0,0,dx,dy,MB_MENU_MARK);
-        else if (game_status==CHOOSELEVEL)
-          HandleChooseLevel(0,0,dx,dy,MB_MENU_MARK);
-       else if (game_status==SETUP)
-         HandleSetupScreen(0,0,dx,dy,MB_MENU_MARK);
-      }
       break;
-    }
+
     case HELPSCREEN:
       HandleHelpScreen(MB_RELEASED);
       break;
+
     case HALLOFFAME:
       switch(key)
       {
@@ -393,137 +484,109 @@ void HandleKey(KeySym key)
          DrawMainMenu();
          BackToFront();
          break;
+
        default:
          break;
       }
       break;
+
     case LEVELED:
       LevelNameTyping(key);
       break;
+
     case PLAYING:
     {
-      int mvx = 0, mvy = 0;
-      int sbx = 0, sby = 0;
-      int joy = 0;
-      BOOL bomb = FALSE;
-      BOOL moved = FALSE, snapped = FALSE, bombed = FALSE;
-
       switch(key)
       {
-       case XK_Left:           /* normale Richtungen */
-#ifdef XK_KP_Left
-       case XK_KP_Left:
-#endif
-       case XK_KP_4:
-       case XK_J:
-       case XK_j:
-         mvx = -1;
-         joy = JOY_LEFT;
+
+#ifdef DEBUG
+       case XK_0:
+       case XK_1:
+       case XK_2:
+       case XK_3:
+       case XK_4:
+       case XK_5:
+       case XK_6:
+       case XK_7:
+       case XK_8:
+       case XK_9:
+         if (key == XK_0)
+         {
+           if (GameFrameDelay == 500)
+             GameFrameDelay = GAME_FRAME_DELAY;
+           else
+             GameFrameDelay = 500;
+         }
+         else
+           GameFrameDelay = (key - XK_0) * 10;
+         printf("Game speed == %d%% (%d ms delay between two frames)\n",
+                GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
+         break;
+
+
+#if 0
+       case XK_a:
+         if (ScrollStepSize == TILEX/8)
+           ScrollStepSize = TILEX/4;
+         else
+           ScrollStepSize = TILEX/8;
+         printf("ScrollStepSize == %d\n", ScrollStepSize);
          break;
-       case XK_Right:
-#ifdef XK_KP_Right
-       case XK_KP_Right:
 #endif
-       case XK_KP_6:
-       case XK_K:
-       case XK_k:
-         mvx = 1;
-         joy = JOY_RIGHT;
+
+       case XK_f:
+         ScrollStepSize = TILEX/8;
+         printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
          break;
-       case XK_Up:
-#ifdef XK_KP_Up
-       case XK_KP_Up:
-#endif
-       case XK_KP_8:
-       case XK_I:
-       case XK_i:
-         mvy = -1;
-         joy = JOY_UP;
+
+       case XK_g:
+         ScrollStepSize = TILEX/4;
+         printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
          break;
-       case XK_Down:
-#ifdef XK_KP_Down
-       case XK_KP_Down:
-#endif
-       case XK_KP_2:
-       case XK_M:
-       case XK_m:
-         mvy = 1;
-         joy = JOY_DOWN;
+
+       case XK_h:
+         ScrollStepSize = TILEX/2;
+         printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
          break;
-#ifdef XK_KP_Home
-       case XK_KP_Home:        /* Diagonalrichtungen */
-#endif
-       case XK_KP_7:
-         mvx = -1;
-         mvy = -1;
-         joy = JOY_UP | JOY_LEFT;
+
+       case XK_l:
+         ScrollStepSize = TILEX;
+         printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
          break;
-#ifdef XK_KP_Page_Up
-       case XK_KP_Page_Up:
+
+#ifndef MSDOS
+       case XK_Q:
 #endif
-       case XK_KP_9:
-         mvx = 1;
-         mvy = -1;
-         joy = JOY_UP | JOY_RIGHT;
+       case XK_q:
+         local_player->dynamite = 1000;
          break;
-#ifdef XK_KP_End
-       case XK_KP_End:
-#endif
-       case XK_KP_1:
-         mvx = -1;
-         mvy = 1;
-         joy = JOY_DOWN | JOY_LEFT;
+
+
+
+#if 0
+
+       case XK_z:
+         {
+           int i;
+
+           for(i=0; i<MAX_PLAYERS; i++)
+           {
+             printf("Player %d:\n", i);
+             printf("  jx == %d, jy == %d\n",
+                    stored_player[i].jx, stored_player[i].jy);
+             printf("  last_jx == %d, last_jy == %d\n",
+                    stored_player[i].last_jx, stored_player[i].last_jy);
+           }
+           printf("\n");
+         }
+
          break;
-#ifdef XK_KP_Page_Down
-       case XK_KP_Page_Down:
 #endif
-       case XK_KP_3:
-         mvx = 1;
-         mvy = 1;
-         joy = JOY_DOWN | JOY_RIGHT;
-         break;
-       case XK_S:              /* Feld entfernen */
-       case XK_s:
-         sbx = -1;
-         joy = JOY_BUTTON_1 | JOY_LEFT;
-         break;
-       case XK_D:
-       case XK_d:
-         sbx = 1;
-         joy = JOY_BUTTON_1 | JOY_RIGHT;
-         break;
-       case XK_E:
-       case XK_e:
-         sby = -1;
-         joy = JOY_BUTTON_1 | JOY_UP;
-         break;
-       case XK_X:
-       case XK_x:
-         sby = 1;
-         joy = JOY_BUTTON_1 | JOY_DOWN;
-         break;
-       case XK_B:              /* Bombe legen */
-       case XK_b:
-         bomb = TRUE;
-         joy = JOY_BUTTON_2;
-         break;
-       case XK_Q:
-         Dynamite = 1000;
-         break;
+#endif
+
        default:
          break;
       }
-
-      if (mvx || mvy)
-       moved = MoveFigure(mvx,mvy);
-      else if (sbx || sby)
-       snapped = SnapField(sbx,sby);
-      else if (bomb)
-       bombed = PlaceBomb();
-
-      if (tape.recording && (moved || snapped || bombed))
-       TapeRecordAction(joy);
-
       break;
     }
     default:
@@ -533,91 +596,76 @@ void HandleKey(KeySym key)
 
 void HandleNoXEvent()
 {
-  if (button_status)
+  if (button_status && game_status != PLAYING)
   {
-    HandleButton(-1,-1,button_status);
+    HandleButton(-1,-1, button_status);
     return;
   }
 
-  switch(game_status)
+#ifndef MSDOS
+  if (options.network)
+    HandleNetworking();
+#endif
+
+  HandleJoystick();
+
+  if (game_status == PLAYING)
+    HandleGameActions();
+}
+
+static int HandleJoystickForAllPlayers()
+{
+  int i;
+  int result = 0;
+
+  for (i=0; i<MAX_PLAYERS; i++)
   {
-    case MAINMENU:
-    case CHOOSELEVEL:
-    case HALLOFFAME:
-    case HELPSCREEN:
-    case SETUP:
-      HandleJoystick();
-      break;
-    case PLAYING:
-      HandleJoystick();
-      if (key_status)
-       HandleKey(0);
-      if (game_status!=PLAYING)
-       break;
+    byte joy_action = 0;
 
-      if (!LevelSolved)
-      {
-       switch(GameActions(0,0,MB_NOT_PRESSED))
-       {
-         case ACT_GAME_OVER:
-           game_status = MAINMENU;
-           DrawMainMenu();
-           BackToFront();
-           break;
-         case ACT_NEW_GAME:
-           game_status = PLAYING;
-           InitGame();
-           break;
-         case ACT_GO_ON:
-           break;
-         default:
-           break;
-       }
-      }
+    /*
+    if (!setup.input[i].use_joystick)
+      continue;
+      */
 
-      Delay(10000);
+    joy_action = Joystick(i);
+    result |= joy_action;
 
-      break;
-    default:
-      break;
+
+    if (!setup.input[i].use_joystick)
+      continue;
+
+
+    stored_player[i].action = joy_action;
   }
+
+  return result;
 }
 
 void HandleJoystick()
 {
-  int joy      = Joystick();
+  int joystick = HandleJoystickForAllPlayers();
+  int keyboard = key_joystick_mapping;
+  int joy      = (joystick | keyboard);
   int left     = joy & JOY_LEFT;
   int right    = joy & JOY_RIGHT;
   int up       = joy & JOY_UP;
   int down     = joy & JOY_DOWN;
   int button   = joy & JOY_BUTTON;
-  int button1  = joy & JOY_BUTTON_1;
-  int button2  = joy & JOY_BUTTON_2;
-  int newbutton        = (JoystickButton()==JOY_BUTTON_NEW_PRESSED);
-
-  if (button_status || key_status)
-    return;
+  int newbutton        = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
+  int dx       = (left ? -1    : right ? 1     : 0);
+  int dy       = (up   ? -1    : down  ? 1     : 0);
 
   switch(game_status)
   {
     case MAINMENU:
     case CHOOSELEVEL:
     case SETUP:
+    case SETUPINPUT:
     {
-      int dx = 0, dy = 0;
-      static long joystickmove_delay = 0;
+      static unsigned long joystickmove_delay = 0;
 
-      if (DelayReached(&joystickmove_delay,15) || button)
-      {
-       if (left)
-         dx = -1;
-       else if (right)
-         dx = 1;
-       if (up)
-         dy = -1;
-       else if (down)
-         dy = 1;
-      }
+      if (joystick && !button && !DelayReached(&joystickmove_delay, 150))
+       newbutton = dx = dy = 0;
 
       if (game_status==MAINMENU)
        HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
@@ -625,41 +673,25 @@ void HandleJoystick()
         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
       else if (game_status==SETUP)
        HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
+      else if (game_status==SETUPINPUT)
+       HandleSetupInputScreen(0,0,dx,dy,
+                              newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
       break;
     }
+
     case HALLOFFAME:
       HandleHallOfFame(!newbutton);
       break;
+
     case HELPSCREEN:
       HandleHelpScreen(!newbutton);
       break;
-    case PLAYING:
-    {
-      int mvx = 0, mvy = 0;
-      BOOL moved = FALSE, snapped = FALSE, bombed = FALSE;
-
-      if (tape.playing)
-      {
-       joy     = TapePlayAction();
-
-       left    = joy & JOY_LEFT;
-       right   = joy & JOY_RIGHT;
-       up      = joy & JOY_UP;
-       down    = joy & JOY_DOWN;
-       button  = joy & JOY_BUTTON;
-       button1 = joy & JOY_BUTTON_1;
-       button2 = joy & JOY_BUTTON_2;
-      }
-      else if (tape.pausing)
-       joy = 0;
 
-      if (!joy)
-      {
-       DigField(0,0,DF_NO_PUSH);
-       break;
-      }
+    case PLAYING:
+      if (tape.playing || keyboard)
+       newbutton = ((joy & JOY_BUTTON) != 0);
 
-      if (GameOver && newbutton)
+      if (AllPlayersGone && newbutton)
       {
        CloseDoor(DOOR_CLOSE_1);
        game_status = MAINMENU;
@@ -667,35 +699,8 @@ void HandleJoystick()
        return;
       }
 
-      if (left)
-       mvx = -1;
-      else if (right)
-       mvx = 1;
-      if (up)
-       mvy = -1;
-      else if (down)
-       mvy = 1;
-
-      if (button1)
-       snapped = SnapField(mvx,mvy);
-      else
-      {
-       if (button2)
-         bombed = PlaceBomb();
-       moved = MoveFigure(mvx,mvy);
-      }
-
-      if (tape.recording && (moved || snapped || bombed))
-      {
-       if (bombed && !moved)
-         joy &= JOY_BUTTON;
-       TapeRecordAction(joy);
-      }
-      else if (tape.playing && snapped)
-       SnapField(0,0);
-
       break;
-    }
+
     default:
       break;
   }
index 1916ea327357eb14721600037352dbe5a91b9535..a7980e5a0912991376cc13efda8fd263642ccd48 100644 (file)
@@ -1,13 +1,12 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  events.h                                                *
 ***********************************************************/
@@ -18,6 +17,7 @@
 #include "main.h"
 
 void EventLoop(void);
+void HandleOtherEvents(XEvent *);
 void ClearEventQueue(void);
 void SleepWhileUnmapped(void);
 
@@ -25,11 +25,13 @@ void HandleExposeEvent(XExposeEvent *);
 void HandleButtonEvent(XButtonEvent *);
 void HandleMotionEvent(XMotionEvent *);
 void HandleKeyEvent(XKeyEvent *);
-void HandleFocusEvent(int);
+void HandleFocusEvent(XFocusChangeEvent *);
+void HandleClientMessageEvent(XClientMessageEvent *event);
+
 void HandleNoXEvent(void);
 
 void HandleButton(int, int, int);
-void HandleKey(KeySym);
+void HandleKey(KeySym, int);
 void HandleJoystick();
 
 #endif
index e349c062a9366d9971933f415ade98c6a96dd33d..21309b825bf505ea3fc4ba98271d55874a3f0fb0 100644 (file)
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  files.h                                                 *
 ***********************************************************/
 
+#include <ctype.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
 #include "files.h"
 #include "tools.h"
 #include "misc.h"
-
-BOOL CreateNewScoreFile()
+#include "tape.h"
+#include "joystick.h"
+
+#define MAX_FILENAME_LEN       256     /* maximal filename length */
+#define MAX_LINE_LEN           1000    /* maximal input line length */
+#define CHUNK_ID_LEN           4       /* IFF style chunk id length */
+#define LEVEL_HEADER_SIZE      80      /* size of level file header */
+#define LEVEL_HEADER_UNUSED    18      /* unused level header bytes */
+#define TAPE_HEADER_SIZE       20      /* size of tape file header */
+#define TAPE_HEADER_UNUSED     7       /* unused tape header bytes */
+#define FILE_VERSION_1_0       10      /* old 1.0 file version */
+#define FILE_VERSION_1_2       12      /* actual file version */
+
+/* file identifier strings */
+#define LEVEL_COOKIE           "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.2"
+#define SCORE_COOKIE           "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
+#define TAPE_COOKIE            "ROCKSNDIAMONDS_TAPE_FILE_VERSION_1.2"
+#define SETUP_COOKIE           "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2"
+#define LEVELSETUP_COOKIE      "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2"
+#define LEVELINFO_COOKIE       "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2"
+/* old file identifiers for backward compatibility */
+#define LEVEL_COOKIE_10                "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.0"
+#define TAPE_COOKIE_10         "ROCKSNDIAMONDS_LEVELREC_FILE_VERSION_1.0"
+
+/* file names and filename extensions */
+#ifndef MSDOS
+#define USERDATA_DIRECTORY     ".rocksndiamonds"
+#define SETUP_FILENAME         "setup.conf"
+#define LEVELSETUP_FILENAME    "levelsetup.conf"
+#define LEVELINFO_FILENAME     "levelinfo.conf"
+#define LEVELFILE_EXTENSION    "level"
+#define TAPEFILE_EXTENSION     "tape"
+#define SCOREFILE_EXTENSION    "score"
+#else
+#define USERDATA_DIRECTORY     "userdata"
+#define SETUP_FILENAME         "setup.cnf"
+#define LEVELSETUP_FILENAME    "lvlsetup.cnf"
+#define LEVELINFO_FILENAME     "lvlinfo.cnf"
+#define LEVELFILE_EXTENSION    "lvl"
+#define TAPEFILE_EXTENSION     "tap"
+#define SCOREFILE_EXTENSION    "sco"
+#define ERROR_FILENAME         "error.out"
+#endif
+
+/* file permissions for newly written files */
+#define MODE_R_ALL             (S_IRUSR | S_IRGRP | S_IROTH)
+#define MODE_W_ALL             (S_IWUSR | S_IWGRP | S_IWOTH)
+#define MODE_X_ALL             (S_IXUSR | S_IXGRP | S_IXOTH)
+#define USERDATA_DIR_MODE      (MODE_R_ALL | MODE_X_ALL | S_IWUSR)
+#define LEVEL_PERMS            (MODE_R_ALL | MODE_W_ALL)
+#define SCORE_PERMS            LEVEL_PERMS
+#define TAPE_PERMS             LEVEL_PERMS
+#define SETUP_PERMS            LEVEL_PERMS
+
+static void SaveUserLevelInfo();               /* for 'InitUserLevelDir()' */
+static char *getSetupLine(char *, int);                /* for 'SaveUserLevelInfo()' */
+
+static char *getGlobalDataDir()
 {
-  int i,j,k;
-  char filename[MAX_FILENAME];
-  char empty_alias[MAX_NAMELEN];
-  FILE *file;
-
-  sprintf(filename,"%s/%s/%s",
-         SCORE_PATH,leveldir[leveldir_nr].filename,SCORE_FILENAME);
-
-  if (!(file=fopen(filename,"w")))
-    return(FALSE);
+  return GAME_DIR;
+}
 
-  for(i=0;i<MAX_NAMELEN;i++)
-    empty_alias[i] = 0;
-  strncpy(empty_alias,EMPTY_ALIAS,MAX_NAMELEN-1);
+char *getUserDataDir()
+{
+  static char *userdata_dir = NULL;
 
-  fputs(SCORE_COOKIE,file);            /* Formatkennung */
-  for(i=0;i<LEVELDIR_SIZE(leveldir[leveldir_nr]);i++)
+  if (!userdata_dir)
   {
-    for(j=0;j<MAX_SCORE_ENTRIES;j++)
-    {
-      for(k=0;k<MAX_NAMELEN;k++)
-       fputc(empty_alias[k],file);
-      fputc(0,file);
-      fputc(0,file);
-    }
+    char *home_dir = getHomeDir();
+    char *data_dir = USERDATA_DIRECTORY;
+
+    userdata_dir = getPath2(home_dir, data_dir);
   }
-  fclose(file);
 
-  chmod(filename, SCORE_PERMS);
-  return(TRUE);
+  return userdata_dir;
 }
 
-BOOL CreateNewNamesFile(int mode)
+static char *getSetupDir()
 {
-  char filename[MAX_FILENAME];
-  FILE *file;
+  return getUserDataDir();
+}
 
-  if (mode==PLAYER_LEVEL)
-    sprintf(filename,"%s/%s/%s",
-           NAMES_PATH,leveldir[leveldir_nr].filename,NAMES_FILENAME);
-  else
-    sprintf(filename,"%s/%s",CONFIG_PATH,NAMES_FILENAME);
+static char *getUserLevelDir(char *level_subdir)
+{
+  static char *userlevel_dir = NULL;
+  char *data_dir = getUserDataDir();
+  char *userlevel_subdir = LEVELS_DIRECTORY;
 
-  if (!(file=fopen(filename,"w")))
-    return(FALSE);
+  if (userlevel_dir)
+    free(userlevel_dir);
 
-  fputs(NAMES_COOKIE,file);            /* Formatkennung */
-  fclose(file);
+  if (strlen(level_subdir) > 0)
+    userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
+  else
+    userlevel_dir = getPath2(data_dir, userlevel_subdir);
 
-  chmod(filename, NAMES_PERMS);
-  return(TRUE);
+  return userlevel_dir;
 }
 
-BOOL LoadLevelInfo()
+static char *getTapeDir(char *level_subdir)
 {
-  int i;
-  char filename[MAX_FILENAME];
-  char cookie[MAX_FILENAME];
-  FILE *file;
+  static char *tape_dir = NULL;
+  char *data_dir = getUserDataDir();
+  char *tape_subdir = TAPES_DIRECTORY;
 
-  sprintf(filename,"%s/%s",LEVEL_PATH,LEVDIR_FILENAME);
+  if (tape_dir)
+    free(tape_dir);
 
-  if (!(file=fopen(filename,"r")))
-  {
-    fprintf(stderr,"%s: cannot load level info '%s'!\n",progname,filename);
-    return(FALSE);
-  }
+  if (strlen(level_subdir) > 0)
+    tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
+  else
+    tape_dir = getPath2(data_dir, tape_subdir);
 
-  fscanf(file,"%s\n",cookie);
-  if (strcmp(cookie,LEVELDIR_COOKIE))  /* ungültiges Format? */
-  {
-    fprintf(stderr,"%s: wrong format of level info file!\n",progname);
-    fclose(file);
-    return(FALSE);
-  }
+  return tape_dir;
+}
 
-  num_leveldirs = 0;
-  leveldir_nr = 0;
-  for(i=0;i<MAX_LEVDIR_ENTRIES;i++)
-  {
-    fscanf(file,"%s",leveldir[i].filename);
-    fscanf(file,"%s",leveldir[i].name);
-    fscanf(file,"%d",&leveldir[i].num_ready);
-    fscanf(file,"%d",&leveldir[i].num_free);
-    if (feof(file))
-      break;
+static char *getScoreDir(char *level_subdir)
+{
+  static char *score_dir = NULL;
+  char *data_dir = getGlobalDataDir();
+  char *score_subdir = SCORES_DIRECTORY;
 
-    num_leveldirs++;
-  }
+  if (score_dir)
+    free(score_dir);
 
-  if (!num_leveldirs)
-  {
-    fprintf(stderr,"%s: empty level info '%s'!\n",progname,filename);
-    return(FALSE);
-  }
+  if (strlen(level_subdir) > 0)
+    score_dir = getPath3(data_dir, score_subdir, level_subdir);
+  else
+    score_dir = getPath2(data_dir, score_subdir);
 
-  return(TRUE);
+  return score_dir;
 }
 
-void LoadLevel(int level_nr)
+static char *getLevelFilename(int nr)
 {
-  int i,x,y;
-  char filename[MAX_FILENAME];
-  char cookie[MAX_FILENAME];
-  FILE *file;
+  static char *filename = NULL;
+  char basename[MAX_FILENAME_LEN];
 
-  sprintf(filename,"%s/%s/%d",
-         LEVEL_PATH,leveldir[leveldir_nr].filename,level_nr);
+  if (filename != NULL)
+    free(filename);
 
-  if (!(file=fopen(filename,"r")))
-  {
-/*
-    fprintf(stderr,"%s: cannot load level '%s'!\n",progname,filename);
-*/
-  }
-  else
-  {
-    fgets(cookie,LEVEL_COOKIE_LEN,file);
-    fgetc(file);
-    if (strcmp(cookie,LEVEL_COOKIE))   /* ungültiges Format? */
-    {
-      fprintf(stderr,"%s: wrong format of level file '%s'!\n",
-             progname,filename);
-      fclose(file);
-      file = NULL;
-    }
-  }
+  sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
+  filename = getPath3((leveldir[leveldir_nr].user_defined ?
+                      getUserLevelDir("") :
+                      options.level_directory),
+                     leveldir[leveldir_nr].filename,
+                     basename);
 
-  if (file)
-  {
-    lev_fieldx = level.fieldx = fgetc(file);
-    lev_fieldy = level.fieldy = fgetc(file);
+  return filename;
+}
 
-    level.time         = (fgetc(file)<<8) | fgetc(file);
-    level.edelsteine   = (fgetc(file)<<8) | fgetc(file);
-    for(i=0;i<MAX_LEVNAMLEN;i++)
-      level.name[i]    = fgetc(file);
-    for(i=0;i<MAX_SC_ENTRIES;i++)
-      level.score[i]   = fgetc(file);
-    level.amoebe_inhalt = fgetc(file);
-    for(i=0;i<4;i++)
-      for(y=0;y<3;y++)
-       for(x=0;x<3;x++)
-         level.mampfer_inhalt[i][x][y] = fgetc(file);
-    level.tempo_amoebe = fgetc(file);
-    level.dauer_sieb   = fgetc(file);
-    level.dauer_ablenk = fgetc(file);
+static char *getTapeFilename(int nr)
+{
+  static char *filename = NULL;
+  char basename[MAX_FILENAME_LEN];
 
-    for(i=0;i<19;i++)  /* Rest reserviert / Headergröße 80 Bytes */
-      fgetc(file);
+  if (filename != NULL)
+    free(filename);
 
-    for(y=0;y<MAX_LEV_FIELDY;y++) 
-      for(x=0;x<MAX_LEV_FIELDX;x++) 
-       Feld[x][y] = Ur[x][y] = EL_ERDREICH;
+  sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
+  filename = getPath2(getTapeDir(leveldir[leveldir_nr].filename), basename);
 
-    for(y=0;y<lev_fieldy;y++) 
-      for(x=0;x<lev_fieldx;x++) 
-       Feld[x][y] = Ur[x][y] = fgetc(file);
+  return filename;
+}
 
-    fclose(file);
+static char *getScoreFilename(int nr)
+{
+  static char *filename = NULL;
+  char basename[MAX_FILENAME_LEN];
 
-    if (level.time<=10)        /* Mindestspieldauer */
-      level.time = 10;
-  }
-  else
-  {
-    lev_fieldx = level.fieldx = STD_LEV_FIELDX;
-    lev_fieldy = level.fieldy = STD_LEV_FIELDY;
+  if (filename != NULL)
+    free(filename);
 
-    level.time         = 100;
-    level.edelsteine   = 0;
-    strncpy(level.name,"Nameless Level",MAX_LEVNAMLEN-1);
-    for(i=0;i<MAX_SC_ENTRIES;i++)
-      level.score[i]   = 10;
-    level.amoebe_inhalt = EL_DIAMANT;
-    for(i=0;i<4;i++)
-      for(y=0;y<3;y++)
-       for(x=0;x<3;x++)
-         level.mampfer_inhalt[i][x][y] = EL_FELSBROCKEN;
-    level.tempo_amoebe = 10;
-    level.dauer_sieb   = 10;
-    level.dauer_ablenk = 10;
+  sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
+  filename = getPath2(getScoreDir(leveldir[leveldir_nr].filename), basename);
 
-    for(y=0;y<STD_LEV_FIELDY;y++) 
-      for(x=0;x<STD_LEV_FIELDX;x++) 
-       Feld[x][y] = Ur[x][y] = EL_ERDREICH;
-    Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
-    Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
-      Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
-  }
+  return filename;
 }
 
-void LoadLevelTape(int level_nr)
+static void createDirectory(char *dir, char *text)
 {
-  int i;
-  char filename[MAX_FILENAME];
-  char cookie[MAX_FILENAME];
-  FILE *file;
+  if (access(dir, F_OK) != 0)
+    if (mkdir(dir, USERDATA_DIR_MODE) != 0)
+      Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
+}
+
+static void InitUserDataDirectory()
+{
+  createDirectory(getUserDataDir(), "user data");
+}
+
+static void InitTapeDirectory(char *level_subdir)
+{
+  createDirectory(getUserDataDir(), "user data");
+  createDirectory(getTapeDir(""), "main tape");
+  createDirectory(getTapeDir(level_subdir), "level tape");
+}
 
-  sprintf(filename,"%s/%s/%d.tape",
-         LEVEL_PATH,leveldir[leveldir_nr].filename,level_nr);
+static void InitScoreDirectory(char *level_subdir)
+{
+  createDirectory(getScoreDir(""), "main score");
+  createDirectory(getScoreDir(level_subdir), "level score");
+}
 
-  if ((file=fopen(filename,"r")))
+static void InitUserLevelDirectory(char *level_subdir)
+{
+  if (access(getUserLevelDir(level_subdir), F_OK) != 0)
   {
-    fgets(cookie,LEVELREC_COOKIE_LEN,file);
-    fgetc(file);
-    if (strcmp(cookie,LEVELREC_COOKIE))        /* ungültiges Format? */
-    {
-      fprintf(stderr,"%s: wrong format of level recording file '%s'!\n",
-             progname,filename);
-      fclose(file);
-      file = NULL;
-    }
+    createDirectory(getUserDataDir(), "user data");
+    createDirectory(getUserLevelDir(""), "main user level");
+    createDirectory(getUserLevelDir(level_subdir), "user level");
+
+    SaveUserLevelInfo();
   }
+}
 
-  if (!file)
-    return;
+static void setLevelInfoToDefaults()
+{
+  int i, x, y;
 
-  tape.random_seed =
-    (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
-  tape.date =
-    (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
-  tape.length =
-    (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
+  lev_fieldx = level.fieldx = STD_LEV_FIELDX;
+  lev_fieldy = level.fieldy = STD_LEV_FIELDY;
 
-  tape.level_nr = level_nr;
-  tape.counter = 0;
-  tape.recording = FALSE;
-  tape.playing = FALSE;
-  tape.pausing = FALSE;
+  for(x=0; x<MAX_LEV_FIELDX; x++) 
+    for(y=0; y<MAX_LEV_FIELDY; y++) 
+      Feld[x][y] = Ur[x][y] = EL_ERDREICH;
 
-  for(i=0;i<tape.length;i++)
-  {
-    if (i>=MAX_TAPELEN)
-      break;
-    tape.pos[i].joystickdata = fgetc(file);
-    tape.pos[i].delay        = fgetc(file);
-    if (feof(file))
-      break;
-  }
+  level.time = 100;
+  level.edelsteine = 0;
+  level.tempo_amoebe = 10;
+  level.dauer_sieb = 10;
+  level.dauer_ablenk = 10;
+  level.amoebe_inhalt = EL_DIAMANT;
 
-  if (i != tape.length)
-    fprintf(stderr,"%s: level recording file '%s' corrupted!\n",
-           progname,filename);
+  strcpy(level.name, "Nameless Level");
 
-  fclose(file);
+  for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
+    level.score[i] = 10;
+
+  for(i=0; i<4; i++)
+    for(x=0; x<3; x++)
+      for(y=0; y<3; y++)
+       level.mampfer_inhalt[i][x][y] = EL_FELSBROCKEN;
 
-  master_tape = tape;
+  Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
+  Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
+    Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
 }
 
-void LoadScore(int level_nr)
+void LoadLevel(int level_nr)
 {
-  int i,j;
-  char filename[MAX_FILENAME];
-  char cookie[MAX_FILENAME];
+  int i, x, y;
+  char *filename = getLevelFilename(level_nr);
+  char cookie[MAX_LINE_LEN];
+  char chunk[CHUNK_ID_LEN + 1];
+  int file_version = FILE_VERSION_1_2; /* last version of level files */
+  int chunk_length;
   FILE *file;
 
-  sprintf(filename,"%s/%s/%s",
-         SCORE_PATH,leveldir[leveldir_nr].filename,SCORE_FILENAME);
+  /* always start with reliable default values */
+  setLevelInfoToDefaults();
 
-  if (!(file=fopen(filename,"r")))
+  if (!(file = fopen(filename, "r")))
   {
-    if (!CreateNewScoreFile())
-    {
-      fprintf(stderr,"%s: cannot create score file '%s'!\n",
-             progname,filename);
-    }
-    else if (!(file=fopen(filename,"r"))) 
-    {
-      fprintf(stderr,"%s: cannot load score for level %d!\n",
-             progname,level_nr);
-    }
+    Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
+    return;
+  }
+
+  /* check file identifier */
+  fgets(cookie, MAX_LINE_LEN, file);
+  if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
+    cookie[strlen(cookie) - 1] = '\0';
+
+  if (strcmp(cookie, LEVEL_COOKIE_10) == 0)    /* old 1.0 level format */
+    file_version = FILE_VERSION_1_0;
+  else if (strcmp(cookie, LEVEL_COOKIE) != 0)  /* unknown level format */
+  {
+    Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
+    fclose(file);
+    return;
   }
 
-  if (file)
+  /* read chunk "HEAD" */
+  if (file_version >= FILE_VERSION_1_2)
   {
-    fgets(cookie,SCORE_COOKIE_LEN,file);
-    if (strcmp(cookie,SCORE_COOKIE))   /* ungültiges Format? */
+    /* first check header chunk identifier and chunk length */
+    fgets(chunk, CHUNK_ID_LEN + 1, file);
+    chunk_length =
+      (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
+    if (strcmp(chunk, "HEAD") || chunk_length != LEVEL_HEADER_SIZE)
     {
-      fprintf(stderr,"%s: wrong format of score file!\n",progname);
+      Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
       fclose(file);
-      file = NULL;
+      return;
     }
   }
 
-  if (file)
+  lev_fieldx = level.fieldx = fgetc(file);
+  lev_fieldy = level.fieldy = fgetc(file);
+
+  level.time           = (fgetc(file)<<8) | fgetc(file);
+  level.edelsteine     = (fgetc(file)<<8) | fgetc(file);
+
+  for(i=0; i<MAX_LEVNAMLEN; i++)
+    level.name[i]      = fgetc(file);
+  level.name[MAX_LEVNAMLEN - 1] = 0;
+
+  for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
+    level.score[i]     = fgetc(file);
+
+  for(i=0; i<4; i++)
+    for(y=0; y<3; y++)
+      for(x=0; x<3; x++)
+       level.mampfer_inhalt[i][x][y] = fgetc(file);
+
+  level.tempo_amoebe   = fgetc(file);
+  level.dauer_sieb     = fgetc(file);
+  level.dauer_ablenk   = fgetc(file);
+  level.amoebe_inhalt = fgetc(file);
+
+  for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* skip unused header bytes */
+    fgetc(file);
+
+  /* read chunk "BODY" */
+  if (file_version >= FILE_VERSION_1_2)
   {
-    fseek(file,
-         SCORE_COOKIE_LEN-1+level_nr*(MAX_SCORE_ENTRIES*(MAX_NAMELEN+2)),
-         SEEK_SET);
-    for(i=0;i<MAX_SCORE_ENTRIES;i++)
+    /* next check body chunk identifier and chunk length */
+    fgets(chunk, CHUNK_ID_LEN + 1, file);
+    chunk_length =
+      (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
+    if (strcmp(chunk, "BODY") || chunk_length != lev_fieldx * lev_fieldy)
     {
-      for(j=0;j<MAX_NAMELEN;j++)
-       highscore[i].Name[j] = fgetc(file);
-      highscore[i].Score = (fgetc(file)<<8) | fgetc(file);
+      Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
+      fclose(file);
+      return;
     }
-    fclose(file);
   }
-  else
+
+  for(y=0; y<lev_fieldy; y++) 
+    for(x=0; x<lev_fieldx; x++) 
+      Feld[x][y] = Ur[x][y] = fgetc(file);
+
+  fclose(file);
+
+  if (level.time <= 10)                /* minimum playing time of each level */
+    level.time = 10;
+}
+
+void SaveLevel(int level_nr)
+{
+  int i, x, y;
+  char *filename = getLevelFilename(level_nr);
+  FILE *file;
+  int chunk_length;
+
+  if (!(file = fopen(filename, "w")))
   {
-    for(i=0;i<MAX_SCORE_ENTRIES;i++)
-    {
-      strcpy(highscore[i].Name,EMPTY_ALIAS);
-      highscore[i].Score = 0;
-    }
+    Error(ERR_WARN, "cannot save level file '%s'", filename);
+    return;
   }
+
+  fputs(LEVEL_COOKIE, file);           /* file identifier */
+  fputc('\n', file);
+
+  fputs("HEAD", file);                 /* chunk identifier for file header */
+
+  chunk_length = LEVEL_HEADER_SIZE;
+
+  fputc((chunk_length >>  24) & 0xff, file);
+  fputc((chunk_length >>  16) & 0xff, file);
+  fputc((chunk_length >>   8) & 0xff, file);
+  fputc((chunk_length >>   0) & 0xff, file);
+
+  fputc(level.fieldx, file);
+  fputc(level.fieldy, file);
+  fputc(level.time / 256, file);
+  fputc(level.time % 256, file);
+  fputc(level.edelsteine / 256, file);
+  fputc(level.edelsteine % 256, file);
+
+  for(i=0; i<MAX_LEVNAMLEN; i++)
+    fputc(level.name[i], file);
+  for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
+    fputc(level.score[i], file);
+  for(i=0; i<4; i++)
+    for(y=0; y<3; y++)
+      for(x=0; x<3; x++)
+       fputc(level.mampfer_inhalt[i][x][y], file);
+  fputc(level.tempo_amoebe, file);
+  fputc(level.dauer_sieb, file);
+  fputc(level.dauer_ablenk, file);
+  fputc(level.amoebe_inhalt, file);
+
+  for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
+    fputc(0, file);
+
+  fputs("BODY", file);                 /* chunk identifier for file body */
+  chunk_length = lev_fieldx * lev_fieldy;
+
+  fputc((chunk_length >>  24) & 0xff, file);
+  fputc((chunk_length >>  16) & 0xff, file);
+  fputc((chunk_length >>   8) & 0xff, file);
+  fputc((chunk_length >>   0) & 0xff, file);
+
+  for(y=0; y<lev_fieldy; y++) 
+    for(x=0; x<lev_fieldx; x++) 
+      fputc(Ur[x][y], file);
+
+  fclose(file);
+
+  chmod(filename, LEVEL_PERMS);
 }
 
-void LoadPlayerInfo(int mode)
+void LoadTape(int level_nr)
 {
-  int i;
-  char filename[MAX_FILENAME];
-  char cookie[MAX_FILENAME];
+  int i, j;
+  char *filename = getTapeFilename(level_nr);
+  char cookie[MAX_LINE_LEN];
+  char chunk[CHUNK_ID_LEN + 1];
   FILE *file;
-  char *login_name = GetLoginName();
-  struct PlayerInfo default_player, new_player;
+  int num_participating_players;
+  int file_version = FILE_VERSION_1_2; /* last version of tape files */
+  int chunk_length;
 
-  if (mode==PLAYER_LEVEL)
-    sprintf(filename,"%s/%s/%s",
-           NAMES_PATH,leveldir[leveldir_nr].filename,NAMES_FILENAME);
-  else
-    sprintf(filename,"%s/%s",CONFIG_PATH,NAMES_FILENAME);
+  /* always start with reliable default values (empty tape) */
+  TapeErase();
 
-  for(i=0;i<MAX_NAMELEN;i++)
-    default_player.login_name[i] = default_player.alias_name[i] = 0;
-  strncpy(default_player.login_name,login_name,MAX_NAMELEN-1);
-  strncpy(default_player.alias_name,login_name,MAX_NAMELEN-1);
-  default_player.handicap = 0;
-  default_player.setup = DEFAULT_SETUP;
-  default_player.leveldir_nr = 0;
+  /* default values (also for pre-1.2 tapes) with only the first player */
+  tape.player_participates[0] = TRUE;
+  for(i=1; i<MAX_PLAYERS; i++)
+    tape.player_participates[i] = FALSE;
 
-  new_player = default_player;
+  /* at least one (default: the first) player participates in every tape */
+  num_participating_players = 1;
 
-  if (!(file=fopen(filename,"r")))
+  if (!(file = fopen(filename, "r")))
+    return;
+
+  /* check file identifier */
+  fgets(cookie, MAX_LINE_LEN, file);
+  if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
+    cookie[strlen(cookie) - 1] = '\0';
+
+  if (strcmp(cookie, TAPE_COOKIE_10) == 0)     /* old 1.0 tape format */
+    file_version = FILE_VERSION_1_0;
+  else if (strcmp(cookie, TAPE_COOKIE) != 0)   /* unknown tape format */
   {
-    if (!CreateNewNamesFile(mode))
-    {
-      fprintf(stderr,"%s: cannot create names file '%s'!\n",
-             progname,filename);
-    }
-    else if (!(file=fopen(filename,"r"))) 
-    {
-      fprintf(stderr,"%s: cannot load player information '%s'!\n",
-             progname,filename);
-    }
+    Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
+    fclose(file);
+    return;
   }
 
-  if (file)
+  /* read chunk "HEAD" */
+  if (file_version >= FILE_VERSION_1_2)
   {
-    fgets(cookie,NAMES_COOKIE_LEN,file);
-    if (strcmp(cookie,NAMES_COOKIE))   /* ungültiges Format? */
+    /* first check header chunk identifier and chunk length */
+    fgets(chunk, CHUNK_ID_LEN + 1, file);
+    chunk_length =
+      (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
+
+    if (strcmp(chunk, "HEAD") || chunk_length != TAPE_HEADER_SIZE)
     {
-      fprintf(stderr,"%s: wrong format of names file '%s'!\n",
-             progname,filename);
+      Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
       fclose(file);
-      file = NULL;
+      return;
     }
   }
 
-  if (!file)
-  {
-    player = default_player;
-    level_nr = default_player.handicap;
-    return;
-  }
+  tape.random_seed =
+    (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
+  tape.date =
+    (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
+  tape.length =
+    (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
 
-  while(1)
+  /* read header fields that are new since version 1.2 */
+  if (file_version >= FILE_VERSION_1_2)
   {
-    for(i=0;i<MAX_NAMELEN;i++)
-      new_player.login_name[i] = fgetc(file);
-    for(i=0;i<MAX_NAMELEN;i++)
-      new_player.alias_name[i] = fgetc(file);
-    new_player.handicap = fgetc(file);
-    new_player.setup = (fgetc(file)<<8) | fgetc(file);
-    new_player.leveldir_nr = fgetc(file);
+    byte store_participating_players = fgetc(file);
 
-    if (feof(file))            /* Spieler noch nicht in Liste enthalten */
+    for(i=0; i<TAPE_HEADER_UNUSED; i++)                /* skip unused header bytes */
+      fgetc(file);
+
+    /* since version 1.2, tapes store which players participate in the tape */
+    num_participating_players = 0;
+    for(i=0; i<MAX_PLAYERS; i++)
     {
-      new_player = default_player;
+      tape.player_participates[i] = FALSE;
 
-      fclose(file);
-      if (!(file=fopen(filename,"a")))
-      {
-       fprintf(stderr,"%s: cannot append new player to names file '%s'!\n",
-               progname,filename);
-      }
-      else
+      if (store_participating_players & (1 << i))
       {
-       for(i=0;i<MAX_NAMELEN;i++)
-         fputc(new_player.login_name[i],file);
-       for(i=0;i<MAX_NAMELEN;i++)
-         fputc(new_player.alias_name[i],file);
-       fputc(new_player.handicap,file);
-       fputc(new_player.setup / 256,file);
-       fputc(new_player.setup % 256,file);
-       fputc(new_player.leveldir_nr,file);
+       tape.player_participates[i] = TRUE;
+       num_participating_players++;
       }
-      break;
     }
-    else                       /* prüfen, ob Spieler in Liste enthalten */
-      if (!strncmp(new_player.login_name,login_name,MAX_NAMELEN-1))
-       break;
   }
 
-  if (mode==PLAYER_SETUP)
+  tape.level_nr = level_nr;
+  tape.counter = 0;
+  tape.changed = FALSE;
+
+  tape.recording = FALSE;
+  tape.playing = FALSE;
+  tape.pausing = FALSE;
+
+  /* read chunk "BODY" */
+  if (file_version >= FILE_VERSION_1_2)
   {
-    player = new_player;
-    if (player.leveldir_nr < num_leveldirs)
-      leveldir_nr = player.leveldir_nr;
-    else
-      leveldir_nr = 0;
+    /* next check body chunk identifier and chunk length */
+    fgets(chunk, CHUNK_ID_LEN + 1, file);
+    chunk_length =
+      (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
+    if (strcmp(chunk, "BODY") ||
+       chunk_length != (num_participating_players + 1) * tape.length)
+    {
+      Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
+      fclose(file);
+      return;
+    }
   }
-  else
-    player.handicap = new_player.handicap;
 
-  level_nr = player.handicap;
-  fclose(file);
-}
+  for(i=0; i<tape.length; i++)
+  {
+    if (i >= MAX_TAPELEN)
+      break;
 
-void SaveLevel(int level_nr)
-{
-  int i,x,y;
-  char filename[MAX_FILENAME];
-  FILE *file;
+    for(j=0; j<MAX_PLAYERS; j++)
+    {
+      tape.pos[i].action[j] = MV_NO_MOVING;
 
-  sprintf(filename,"%s/%s/%d",
-         LEVEL_PATH,leveldir[leveldir_nr].filename,level_nr);
+      if (tape.player_participates[j])
+       tape.pos[i].action[j] = fgetc(file);
+    }
 
-  if (!(file=fopen(filename,"w")))
-  {
-    fprintf(stderr,"%s: cannot save level file '%s'!\n",progname,filename);
-    return;
-  }
+    tape.pos[i].delay = fgetc(file);
+
+    if (file_version == FILE_VERSION_1_0)
+    {
+      /* eliminate possible diagonal moves in old tapes */
+      /* this is only for backward compatibility */
+
+      byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
+      byte action = tape.pos[i].action[0];
+      int k, num_moves = 0;
+
+      for (k=0; k<4; k++)
+      {
+       if (action & joy_dir[k])
+       {
+         tape.pos[i + num_moves].action[0] = joy_dir[k];
+         if (num_moves > 0)
+           tape.pos[i + num_moves].delay = 0;
+         num_moves++;
+       }
+      }
+
+      if (num_moves > 1)
+      {
+       num_moves--;
+       i += num_moves;
+       tape.length += num_moves;
+      }
+    }
 
-  fputs(LEVEL_COOKIE,file);            /* Formatkennung */
-  fputc(0x0a,file);
-
-  fputc(level.fieldx,file);
-  fputc(level.fieldy,file);
-  fputc(level.time / 256,file);
-  fputc(level.time % 256,file);
-  fputc(level.edelsteine / 256,file);
-  fputc(level.edelsteine % 256,file);
-
-  for(i=0;i<MAX_LEVNAMLEN;i++)
-    fputc(level.name[i],file);
-  for(i=0;i<MAX_SC_ENTRIES;i++)
-    fputc(level.score[i],file);
-  fputc(level.amoebe_inhalt,file);
-  for(i=0;i<4;i++)
-    for(y=0;y<3;y++)
-      for(x=0;x<3;x++)
-       fputc(level.mampfer_inhalt[i][x][y],file);
-  fputc(level.tempo_amoebe,file);
-  fputc(level.dauer_sieb,file);
-  fputc(level.dauer_ablenk,file);
-
-  for(i=0;i<19;i++)    /* Rest reserviert / Headergröße 80 Bytes */
-    fputc(0,file);
-
-  for(y=0;y<lev_fieldy;y++) 
-    for(x=0;x<lev_fieldx;x++) 
-      fputc(Ur[x][y],file);
+    if (feof(file))
+      break;
+  }
 
   fclose(file);
 
-  chmod(filename, LEVEL_PERMS);
+  if (i != tape.length)
+    Error(ERR_WARN, "level recording file '%s' corrupted", filename);
+
+  tape.length_seconds = GetTapeLength();
 }
 
-void SaveLevelTape(int level_nr)
+void SaveTape(int level_nr)
 {
   int i;
-  char filename[MAX_FILENAME];
+  char *filename = getTapeFilename(level_nr);
   FILE *file;
-  BOOL new_tape = TRUE;
+  boolean new_tape = TRUE;
+  byte store_participating_players;
+  int num_participating_players;
+  int chunk_length;
 
-  sprintf(filename,"%s/%s/%d.tape",
-         LEVEL_PATH,leveldir[leveldir_nr].filename,level_nr);
+  InitTapeDirectory(leveldir[leveldir_nr].filename);
 
-  /* Testen, ob bereits eine Aufnahme existiert */
-  if ((file=fopen(filename,"r")))
+  /* if a tape still exists, ask to overwrite it */
+  if (access(filename, F_OK) == 0)
   {
     new_tape = FALSE;
-    fclose(file);
-
-    if (!AreYouSure("Replace old tape ?",AYS_ASK))
+    if (!Request("Replace old tape ?", REQ_ASK))
       return;
   }
 
-  if (!(file=fopen(filename,"w")))
+  /* count number of players and set corresponding bits for compact storage */
+  store_participating_players = 0;
+  num_participating_players = 0;
+  for(i=0; i<MAX_PLAYERS; i++)
+  {
+    if (tape.player_participates[i])
+    {
+      num_participating_players++;
+      store_participating_players |= (1 << i);
+    }
+  }
+
+  if (!(file = fopen(filename, "w")))
   {
-    fprintf(stderr,"%s: cannot save level recording file '%s'!\n",
-           progname,filename);
+    Error(ERR_WARN, "cannot save level recording file '%s'", filename);
     return;
   }
 
-  fputs(LEVELREC_COOKIE,file);         /* Formatkennung */
-  fputc(0x0a,file);
+  fputs(TAPE_COOKIE, file);            /* file identifier */
+  fputc('\n', file);
 
-  tape = master_tape;
+  fputs("HEAD", file);                 /* chunk identifier for file header */
 
-  fputc((tape.random_seed >> 24) & 0xff,file);
-  fputc((tape.random_seed >> 16) & 0xff,file);
-  fputc((tape.random_seed >>  8) & 0xff,file);
-  fputc((tape.random_seed >>  0) & 0xff,file);
+  chunk_length = TAPE_HEADER_SIZE;
 
-  fputc((tape.date >>  24) & 0xff,file);
-  fputc((tape.date >>  16) & 0xff,file);
-  fputc((tape.date >>   8) & 0xff,file);
-  fputc((tape.date >>   0) & 0xff,file);
+  fputc((chunk_length >>  24) & 0xff, file);
+  fputc((chunk_length >>  16) & 0xff, file);
+  fputc((chunk_length >>   8) & 0xff, file);
+  fputc((chunk_length >>   0) & 0xff, file);
 
-  fputc((tape.length >>  24) & 0xff,file);
-  fputc((tape.length >>  16) & 0xff,file);
-  fputc((tape.length >>   8) & 0xff,file);
-  fputc((tape.length >>   0) & 0xff,file);
+  fputc((tape.random_seed >> 24) & 0xff, file);
+  fputc((tape.random_seed >> 16) & 0xff, file);
+  fputc((tape.random_seed >>  8) & 0xff, file);
+  fputc((tape.random_seed >>  0) & 0xff, file);
 
-  for(i=0;i<tape.length;i++)
-  {
-    fputc(tape.pos[i].joystickdata,file);
-    fputc(tape.pos[i].delay,file);
-  }
+  fputc((tape.date >>  24) & 0xff, file);
+  fputc((tape.date >>  16) & 0xff, file);
+  fputc((tape.date >>   8) & 0xff, file);
+  fputc((tape.date >>   0) & 0xff, file);
 
-  fclose(file);
+  fputc((tape.length >>  24) & 0xff, file);
+  fputc((tape.length >>  16) & 0xff, file);
+  fputc((tape.length >>   8) & 0xff, file);
+  fputc((tape.length >>   0) & 0xff, file);
 
-  chmod(filename, LEVREC_PERMS);
+  fputc(store_participating_players, file);
 
-  if (new_tape)
-    AreYouSure("tape saved !",AYS_CONFIRM);
-}
+  for(i=0; i<TAPE_HEADER_UNUSED; i++)  /* set unused header bytes to zero */
+    fputc(0, file);
 
-void SaveScore(int level_nr)
-{
-  int i,j;
-  char filename[MAX_FILENAME];
-  FILE *file;
+  fputs("BODY", file);                 /* chunk identifier for file body */
+  chunk_length = (num_participating_players + 1) * tape.length;
 
-  sprintf(filename,"%s/%s/%s",
-         SCORE_PATH,leveldir[leveldir_nr].filename,SCORE_FILENAME);
+  fputc((chunk_length >>  24) & 0xff, file);
+  fputc((chunk_length >>  16) & 0xff, file);
+  fputc((chunk_length >>   8) & 0xff, file);
+  fputc((chunk_length >>   0) & 0xff, file);
 
-  if (!(file=fopen(filename,"r+")))
+  for(i=0; i<tape.length; i++)
   {
-    fprintf(stderr,"%s: cannot save score for level %d!\n",
-           progname,level_nr);
-    return;
-  }
+    int j;
 
-  fseek(file,
-       SCORE_COOKIE_LEN-1+level_nr*(MAX_SCORE_ENTRIES*(MAX_NAMELEN+2)),
-       SEEK_SET);
-  for(i=0;i<MAX_SCORE_ENTRIES;i++)
-  {
-    for(j=0;j<MAX_NAMELEN;j++)
-      fputc(highscore[i].Name[j],file);
-    fputc(highscore[i].Score / 256,file);
-    fputc(highscore[i].Score % 256,file);
+    for(j=0; j<MAX_PLAYERS; j++)
+      if (tape.player_participates[j])
+       fputc(tape.pos[i].action[j], file);
+
+    fputc(tape.pos[i].delay, file);
   }
+
   fclose(file);
+
+  chmod(filename, TAPE_PERMS);
+
+  tape.changed = FALSE;
+
+  if (new_tape)
+    Request("tape saved !", REQ_CONFIRM);
 }
 
-void SavePlayerInfo(int mode)
+void LoadScore(int level_nr)
 {
   int i;
-  char filename[MAX_FILENAME];
-  char cookie[MAX_FILENAME];
+  char *filename = getScoreFilename(level_nr);
+  char cookie[MAX_LINE_LEN];
+  char line[MAX_LINE_LEN];
+  char *line_ptr;
   FILE *file;
-  struct PlayerInfo default_player;
-
-  if (mode==PLAYER_LEVEL)
-    sprintf(filename,"%s/%s/%s",
-           NAMES_PATH,leveldir[leveldir_nr].filename,NAMES_FILENAME);
-  else
-    sprintf(filename,"%s/%s",CONFIG_PATH,NAMES_FILENAME);
 
-  if (!(file=fopen(filename,"r+")))
+  /* always start with reliable default values */
+  for(i=0; i<MAX_SCORE_ENTRIES; i++)
   {
-    fprintf(stderr,"%s: cannot save player information '%s'!\n",
-           progname,filename);
-    return;
+    strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
+    highscore[i].Score = 0;
   }
 
-  fgets(cookie,NAMES_COOKIE_LEN,file);
-  if (strcmp(cookie,NAMES_COOKIE))     /* ungültiges Format? */
+  if (!(file = fopen(filename, "r")))
+    return;
+
+  /* check file identifier */
+  fgets(cookie, MAX_LINE_LEN, file);
+  if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
+    cookie[strlen(cookie) - 1] = '\0';
+
+  if (strcmp(cookie, SCORE_COOKIE) != 0)
   {
-    fprintf(stderr,"%s: wrong format of names file '%s'!\n",
-           progname,filename);
+    Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
     fclose(file);
     return;
   }
 
-  while(1)
+  for(i=0; i<MAX_SCORE_ENTRIES; i++)
   {
-    for(i=0;i<MAX_NAMELEN;i++)
-      default_player.login_name[i] = fgetc(file);
-    for(i=0;i<MAX_NAMELEN;i++)
-      default_player.alias_name[i] = fgetc(file);
-    default_player.handicap = fgetc(file);
-    default_player.setup = (fgetc(file)<<8) | fgetc(file);
-    default_player.leveldir_nr = fgetc(file);
+    fscanf(file, "%d", &highscore[i].Score);
+    fgets(line, MAX_LINE_LEN, file);
 
-    if (feof(file))            /* Spieler noch nicht in Liste enthalten */
-      break;
-    else                       /* prüfen, ob Spieler in Liste enthalten */
-      if (!strncmp(default_player.login_name,player.login_name,MAX_NAMELEN-1))
+    if (line[strlen(line)-1] == '\n')
+      line[strlen(line)-1] = '\0';
+
+    for (line_ptr = line; *line_ptr; line_ptr++)
+    {
+      if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
       {
-       fseek(file,-(2*MAX_NAMELEN+1+2+1),SEEK_CUR);
+       strncpy(highscore[i].Name, line_ptr, MAX_NAMELEN - 1);
+       highscore[i].Name[MAX_NAMELEN - 1] = '\0';
        break;
       }
+    }
   }
 
-  for(i=0;i<MAX_NAMELEN;i++)
-    fputc(player.login_name[i],file);
-  for(i=0;i<MAX_NAMELEN;i++)
-    fputc(player.alias_name[i],file);
-  fputc(player.handicap,file);
-  fputc(player.setup / 256,file);
-  fputc(player.setup % 256,file);
-  fputc(player.leveldir_nr,file);
-
   fclose(file);
 }
 
-void LoadJoystickData()
+void SaveScore(int level_nr)
 {
   int i;
-  char cookie[256];
+  char *filename = getScoreFilename(level_nr);
   FILE *file;
 
-  if (joystick_status==JOYSTICK_OFF)
-    return;
-
-  if (!(file=fopen(JOYDAT_FILE,"r")))
-    return;
+  InitScoreDirectory(leveldir[leveldir_nr].filename);
 
-  fscanf(file,"%s",cookie);
-  if (strcmp(cookie,JOYSTICK_COOKIE))  /* ungültiges Format? */
+  if (!(file = fopen(filename, "w")))
   {
-    fprintf(stderr,"%s: wrong format of joystick file!\n",progname);
-    fclose(file);
+    Error(ERR_WARN, "cannot save score for level %d", level_nr);
     return;
   }
 
-  for(i=0;i<2;i++)
-  {
-    fscanf(file,"%s",cookie);
-    fscanf(file, "%d %d %d \n",
-          &joystick[i].xleft, &joystick[i].xmiddle, &joystick[i].xright);
-    fscanf(file, "%d %d %d \n",
-          &joystick[i].yupper, &joystick[i].ymiddle, &joystick[i].ylower);
-  }
-  fclose(file);
+  fprintf(file, "%s\n\n", SCORE_COOKIE);
 
-  CheckJoystickData();
+  for(i=0; i<MAX_SCORE_ENTRIES; i++)
+    fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
+
+  fclose(file);
+
+  chmod(filename, SCORE_PERMS);
+}
+
+#define TOKEN_STR_FILE_IDENTIFIER      "file_identifier"
+#define TOKEN_STR_LAST_LEVEL_SERIES    "last_level_series"
+#define TOKEN_STR_PLAYER_PREFIX                "player_"
+
+#define TOKEN_VALUE_POSITION           30
+
+/* global setup */
+#define SETUP_TOKEN_PLAYER_NAME                0
+#define SETUP_TOKEN_SOUND              1
+#define SETUP_TOKEN_SOUND_LOOPS                2
+#define SETUP_TOKEN_SOUND_MUSIC                3
+#define SETUP_TOKEN_SOUND_SIMPLE       4
+#define SETUP_TOKEN_TOONS              5
+#define SETUP_TOKEN_DOUBLE_BUFFERING   6
+#define SETUP_TOKEN_SCROLL_DELAY       7
+#define SETUP_TOKEN_SOFT_SCROLLING     8
+#define SETUP_TOKEN_FADING             9
+#define SETUP_TOKEN_AUTORECORD         10
+#define SETUP_TOKEN_QUICK_DOORS                11
+#define SETUP_TOKEN_TEAM_MODE          12
+
+/* player setup */
+#define SETUP_TOKEN_USE_JOYSTICK       13
+#define SETUP_TOKEN_JOY_DEVICE_NAME    14
+#define SETUP_TOKEN_JOY_XLEFT          15
+#define SETUP_TOKEN_JOY_XMIDDLE                16
+#define SETUP_TOKEN_JOY_XRIGHT         17
+#define SETUP_TOKEN_JOY_YUPPER         18
+#define SETUP_TOKEN_JOY_YMIDDLE                19
+#define SETUP_TOKEN_JOY_YLOWER         20
+#define SETUP_TOKEN_JOY_SNAP           21
+#define SETUP_TOKEN_JOY_BOMB           22
+#define SETUP_TOKEN_KEY_LEFT           23
+#define SETUP_TOKEN_KEY_RIGHT          24
+#define SETUP_TOKEN_KEY_UP             25
+#define SETUP_TOKEN_KEY_DOWN           26
+#define SETUP_TOKEN_KEY_SNAP           27
+#define SETUP_TOKEN_KEY_BOMB           28
+
+/* level directory info */
+#define LEVELINFO_TOKEN_NAME           29
+#define LEVELINFO_TOKEN_LEVELS         30
+#define LEVELINFO_TOKEN_SORT_PRIORITY  31
+#define LEVELINFO_TOKEN_READONLY       32
+
+#define FIRST_GLOBAL_SETUP_TOKEN       SETUP_TOKEN_PLAYER_NAME
+#define LAST_GLOBAL_SETUP_TOKEN                SETUP_TOKEN_TEAM_MODE
+
+#define FIRST_PLAYER_SETUP_TOKEN       SETUP_TOKEN_USE_JOYSTICK
+#define LAST_PLAYER_SETUP_TOKEN                SETUP_TOKEN_KEY_BOMB
+
+#define FIRST_LEVELINFO_TOKEN          LEVELINFO_TOKEN_NAME
+#define LAST_LEVELINFO_TOKEN           LEVELINFO_TOKEN_READONLY
+
+#define TYPE_BOOLEAN                   1
+#define TYPE_SWITCH                    2
+#define TYPE_KEYSYM                    3
+#define TYPE_INTEGER                   4
+#define TYPE_STRING                    5
+
+static struct SetupInfo si;
+static struct SetupInputInfo sii;
+static struct LevelDirInfo ldi;
+static struct
+{
+  int type;
+  void *value;
+  char *text;
+} token_info[] =
+{
+  /* global setup */
+  { TYPE_STRING,  &si.player_name,     "player_name"                   },
+  { TYPE_SWITCH,  &si.sound,           "sound"                         },
+  { TYPE_SWITCH,  &si.sound_loops,     "repeating_sound_loops"         },
+  { TYPE_SWITCH,  &si.sound_music,     "background_music"              },
+  { TYPE_SWITCH,  &si.sound_simple,    "simple_sound_effects"          },
+  { TYPE_SWITCH,  &si.toons,           "toons"                         },
+  { TYPE_SWITCH,  &si.double_buffering,        "double_buffering"              },
+  { TYPE_SWITCH,  &si.scroll_delay,    "scroll_delay"                  },
+  { TYPE_SWITCH,  &si.soft_scrolling,  "soft_scrolling"                },
+  { TYPE_SWITCH,  &si.fading,          "screen_fading"                 },
+  { TYPE_SWITCH,  &si.autorecord,      "automatic_tape_recording"      },
+  { TYPE_SWITCH,  &si.quick_doors,     "quick_doors"                   },
+  { TYPE_SWITCH,  &si.team_mode,       "team_mode"                     },
+
+  /* player setup */
+  { TYPE_BOOLEAN, &sii.use_joystick,   ".use_joystick"                 },
+  { TYPE_STRING,  &sii.joy.device_name,        ".joy.device_name"              },
+  { TYPE_INTEGER, &sii.joy.xleft,      ".joy.xleft"                    },
+  { TYPE_INTEGER, &sii.joy.xmiddle,    ".joy.xmiddle"                  },
+  { TYPE_INTEGER, &sii.joy.xright,     ".joy.xright"                   },
+  { TYPE_INTEGER, &sii.joy.yupper,     ".joy.yupper"                   },
+  { TYPE_INTEGER, &sii.joy.ymiddle,    ".joy.ymiddle"                  },
+  { TYPE_INTEGER, &sii.joy.ylower,     ".joy.ylower"                   },
+  { TYPE_INTEGER, &sii.joy.snap,       ".joy.snap_field"               },
+  { TYPE_INTEGER, &sii.joy.bomb,       ".joy.place_bomb"               },
+  { TYPE_KEYSYM,  &sii.key.left,       ".key.move_left"                },
+  { TYPE_KEYSYM,  &sii.key.right,      ".key.move_right"               },
+  { TYPE_KEYSYM,  &sii.key.up,         ".key.move_up"                  },
+  { TYPE_KEYSYM,  &sii.key.down,       ".key.move_down"                },
+  { TYPE_KEYSYM,  &sii.key.snap,       ".key.snap_field"               },
+  { TYPE_KEYSYM,  &sii.key.bomb,       ".key.place_bomb"               },
+
+  /* level directory info */
+  { TYPE_STRING,  &ldi.name,           "name"                          },
+  { TYPE_INTEGER, &ldi.levels,         "levels"                        },
+  { TYPE_INTEGER, &ldi.sort_priority,  "sort_priority"                 },
+  { TYPE_BOOLEAN, &ldi.readonly,       "readonly"                      }
+};
+
+static char *string_tolower(char *s)
+{
+  static char s_lower[100];
+  int i;
+
+  if (strlen(s) >= 100)
+    return s;
+
+  strcpy(s_lower, s);
+
+  for (i=0; i<strlen(s_lower); i++)
+    s_lower[i] = tolower(s_lower[i]);
+
+  return s_lower;
+}
+
+static int get_string_integer_value(char *s)
+{
+  static char *number_text[][3] =
+  {
+    { "0", "zero", "null", },
+    { "1", "one", "first" },
+    { "2", "two", "second" },
+    { "3", "three", "third" },
+    { "4", "four", "fourth" },
+    { "5", "five", "fifth" },
+    { "6", "six", "sixth" },
+    { "7", "seven", "seventh" },
+    { "8", "eight", "eighth" },
+    { "9", "nine", "ninth" },
+    { "10", "ten", "tenth" },
+    { "11", "eleven", "eleventh" },
+    { "12", "twelve", "twelfth" },
+  };
+
+  int i, j;
+
+  for (i=0; i<13; i++)
+    for (j=0; j<3; j++)
+      if (strcmp(string_tolower(s), number_text[i][j]) == 0)
+       return i;
+
+  return atoi(s);
+}
+
+static boolean get_string_boolean_value(char *s)
+{
+  if (strcmp(string_tolower(s), "true") == 0 ||
+      strcmp(string_tolower(s), "yes") == 0 ||
+      strcmp(string_tolower(s), "on") == 0 ||
+      get_string_integer_value(s) == 1)
+    return TRUE;
+  else
+    return FALSE;
 }
 
-void SaveJoystickData()
+static char *getFormattedSetupEntry(char *token, char *value)
 {
   int i;
+  static char entry[MAX_LINE_LEN];
+
+  sprintf(entry, "%s:", token);
+  for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
+    entry[i] = ' ';
+  entry[i] = '\0';
+
+  strcat(entry, value);
+
+  return entry;
+}
+
+static void freeSetupFileList(struct SetupFileList *setup_file_list)
+{
+  if (!setup_file_list)
+    return;
+
+  if (setup_file_list->token)
+    free(setup_file_list->token);
+  if (setup_file_list->value)
+    free(setup_file_list->value);
+  if (setup_file_list->next)
+    freeSetupFileList(setup_file_list->next);
+  free(setup_file_list);
+}
+
+static struct SetupFileList *newSetupFileList(char *token, char *value)
+{
+  struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
+
+  new->token = checked_malloc(strlen(token) + 1);
+  strcpy(new->token, token);
+
+  new->value = checked_malloc(strlen(value) + 1);
+  strcpy(new->value, value);
+
+  new->next = NULL;
+
+  return new;
+}
+
+static char *getTokenValue(struct SetupFileList *setup_file_list,
+                          char *token)
+{
+  if (!setup_file_list)
+    return NULL;
+
+  if (strcmp(setup_file_list->token, token) == 0)
+    return setup_file_list->value;
+  else
+    return getTokenValue(setup_file_list->next, token);
+}
+
+static void setTokenValue(struct SetupFileList *setup_file_list,
+                         char *token, char *value)
+{
+  if (!setup_file_list)
+    return;
+
+  if (strcmp(setup_file_list->token, token) == 0)
+  {
+    free(setup_file_list->value);
+    setup_file_list->value = checked_malloc(strlen(value) + 1);
+    strcpy(setup_file_list->value, value);
+  }
+  else if (setup_file_list->next == NULL)
+    setup_file_list->next = newSetupFileList(token, value);
+  else
+    setTokenValue(setup_file_list->next, token, value);
+}
+
+#ifdef DEBUG
+static void printSetupFileList(struct SetupFileList *setup_file_list)
+{
+  if (!setup_file_list)
+    return;
+
+  printf("token: '%s'\n", setup_file_list->token);
+  printf("value: '%s'\n", setup_file_list->value);
+
+  printSetupFileList(setup_file_list->next);
+}
+#endif
+
+static struct SetupFileList *loadSetupFileList(char *filename)
+{
+  int line_len;
+  char line[MAX_LINE_LEN];
+  char *token, *value, *line_ptr;
+  struct SetupFileList *setup_file_list = newSetupFileList("", "");
+  struct SetupFileList *first_valid_list_entry;
+
   FILE *file;
 
-  if (joystick_status==JOYSTICK_OFF)
+  if (!(file = fopen(filename, "r")))
+  {
+    Error(ERR_WARN, "cannot open configuration file '%s'", filename);
+    return NULL;
+  }
+
+  while(!feof(file))
+  {
+    /* read next line of input file */
+    if (!fgets(line, MAX_LINE_LEN, file))
+      break;
+
+    /* cut trailing comment or whitespace from input line */
+    for (line_ptr = line; *line_ptr; line_ptr++)
+    {
+      if (*line_ptr == '#' || *line_ptr == '\n')
+      {
+       *line_ptr = '\0';
+       break;
+      }
+    }
+
+    /* cut trailing whitespaces from input line */
+    for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
+      if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
+       *line_ptr = '\0';
+
+    /* ignore empty lines */
+    if (*line == '\0')
+      continue;
+
+    line_len = strlen(line);
+
+    /* cut leading whitespaces from token */
+    for (token = line; *token; token++)
+      if (*token != ' ' && *token != '\t')
+       break;
+
+    /* find end of token */
+    for (line_ptr = token; *line_ptr; line_ptr++)
+    {
+      if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
+      {
+       *line_ptr = '\0';
+       break;
+      }
+    }
+
+    if (line_ptr < line + line_len)
+      value = line_ptr + 1;
+    else
+      value = "\0";
+
+    /* cut leading whitespaces from value */
+    for (; *value; value++)
+      if (*value != ' ' && *value != '\t')
+       break;
+
+    if (*token && *value)
+      setTokenValue(setup_file_list, token, value);
+  }
+
+  fclose(file);
+
+  first_valid_list_entry = setup_file_list->next;
+
+  /* free empty list header */
+  setup_file_list->next = NULL;
+  freeSetupFileList(setup_file_list);
+
+  if (first_valid_list_entry == NULL)
+    Error(ERR_WARN, "configuration file '%s' is empty", filename);
+
+  return first_valid_list_entry;
+}
+
+static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
+                                        char *identifier)
+{
+  if (!setup_file_list)
     return;
 
-  CheckJoystickData();
+  if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
+  {
+    if (strcmp(setup_file_list->value, identifier) != 0)
+    {
+      Error(ERR_WARN, "configuration file has wrong version");
+      return;
+    }
+    else
+      return;
+  }
 
-  if (!(file=fopen(JOYDAT_FILE,"w")))
+  if (setup_file_list->next)
+    checkSetupFileListIdentifier(setup_file_list->next, identifier);
+  else
   {
-    fprintf(stderr,"%s: cannot save joystick calibration data!\n",progname);
+    Error(ERR_WARN, "configuration file has no version information");
+    return;
+  }
+}
+
+static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
+{
+  ldi->name = getStringCopy("non-existing");
+  ldi->levels = 0;
+  ldi->sort_priority = 999;    /* default: least priority */
+  ldi->readonly = TRUE;
+}
+
+static void setSetupInfoToDefaults(struct SetupInfo *si)
+{
+  int i;
+
+  si->player_name = getStringCopy(getLoginName());
+
+  si->sound = TRUE;
+  si->sound_loops = TRUE;
+  si->sound_music = TRUE;
+  si->sound_simple = TRUE;
+  si->toons = TRUE;
+  si->double_buffering = TRUE;
+  si->direct_draw = !si->double_buffering;
+  si->scroll_delay = TRUE;
+  si->soft_scrolling = TRUE;
+  si->fading = FALSE;
+  si->autorecord = TRUE;
+  si->quick_doors = FALSE;
+
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+    si->input[i].use_joystick = FALSE;
+    si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
+    si->input[i].joy.xleft   = JOYSTICK_XLEFT;
+    si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
+    si->input[i].joy.xright  = JOYSTICK_XRIGHT;
+    si->input[i].joy.yupper  = JOYSTICK_YUPPER;
+    si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
+    si->input[i].joy.ylower  = JOYSTICK_YLOWER;
+    si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
+    si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
+    si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KEY_UNDEFINDED);
+    si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KEY_UNDEFINDED);
+    si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KEY_UNDEFINDED);
+    si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KEY_UNDEFINDED);
+    si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KEY_UNDEFINDED);
+    si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KEY_UNDEFINDED);
+  }
+}
+
+static void setSetupInfo(int token_nr, char *token_value)
+{
+  int token_type = token_info[token_nr].type;
+  void *setup_value = token_info[token_nr].value;
+
+  if (token_value == NULL)
+    return;
+
+  /* set setup field to corresponding token value */
+  switch (token_type)
+  {
+    case TYPE_BOOLEAN:
+    case TYPE_SWITCH:
+      *(boolean *)setup_value = get_string_boolean_value(token_value);
+      break;
+
+    case TYPE_KEYSYM:
+      *(KeySym *)setup_value = getKeySymFromX11KeyName(token_value);
+      break;
+
+    case TYPE_INTEGER:
+      *(int *)setup_value = get_string_integer_value(token_value);
+      break;
+
+    case TYPE_STRING:
+      if (*(char **)setup_value != NULL)
+       free(*(char **)setup_value);
+      *(char **)setup_value = getStringCopy(token_value);
+      break;
+
+    default:
+      break;
+  }
+}
+
+static void decodeSetupFileList(struct SetupFileList *setup_file_list)
+{
+  int i, pnr;
+
+  if (!setup_file_list)
     return;
+
+  /* handle global setup values */
+  si = setup;
+  for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
+    setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
+  setup = si;
+
+  /* handle player specific setup values */
+  for (pnr=0; pnr<MAX_PLAYERS; pnr++)
+  {
+    char prefix[30];
+
+    sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
+
+    sii = setup.input[pnr];
+    for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
+    {
+      char full_token[100];
+
+      sprintf(full_token, "%s%s", prefix, token_info[i].text);
+      setSetupInfo(i, getTokenValue(setup_file_list, full_token));
+    }
+    setup.input[pnr] = sii;
   }
+}
+
+int getLevelSeriesNrFromLevelSeriesName(char *level_series_name)
+{
+  int i;
+
+  if (!level_series_name)
+    return 0;
+
+  for (i=0; i<num_leveldirs; i++)
+    if (strcmp(level_series_name, leveldir[i].filename) == 0)
+      return i;
 
-  fprintf(file,"%s\n",JOYSTICK_COOKIE);        /* Formatkennung */
-  for(i=0;i<2;i++)
+  return 0;
+}
+
+int getLastPlayedLevelOfLevelSeries(char *level_series_name)
+{
+  char *token_value;
+  int level_series_nr = getLevelSeriesNrFromLevelSeriesName(level_series_name);
+  int last_level_nr = 0;
+
+  if (!level_series_name)
+    return 0;
+
+  token_value = getTokenValue(level_setup_list, level_series_name);
+
+  if (token_value)
   {
-    fprintf(file,"JOYSTICK_%d_DATA\n",i);
-    fprintf(file, "%d %d %d \n",
-           joystick[i].xleft, joystick[i].xmiddle, joystick[i].xright);
-    fprintf(file, "%d %d %d \n",
-           joystick[i].yupper, joystick[i].ymiddle, joystick[i].ylower);
+    int highest_level_nr = leveldir[level_series_nr].levels - 1;
+
+    last_level_nr = atoi(token_value);
+
+    if (last_level_nr < 0)
+      last_level_nr = 0;
+    if (last_level_nr > highest_level_nr)
+      last_level_nr = highest_level_nr;
   }
+
+  return last_level_nr;
+}
+
+static int compareLevelDirInfoEntries(const void *object1, const void *object2)
+{
+  const struct LevelDirInfo *entry1 = object1;
+  const struct LevelDirInfo *entry2 = object2;
+  int compare_result;
+
+  if (entry1->sort_priority != entry2->sort_priority)
+    compare_result = entry1->sort_priority - entry2->sort_priority;
+  else
+  {
+    char *name1 = getStringToLower(entry1->name);
+    char *name2 = getStringToLower(entry2->name);
+
+    compare_result = strcmp(name1, name2);
+
+    free(name1);
+    free(name2);
+  }
+
+  return compare_result;
+}
+
+static int LoadLevelInfoFromLevelDir(char *level_directory, int start_entry)
+{
+  DIR *dir;
+  struct stat file_status;
+  char *directory = NULL;
+  char *filename = NULL;
+  struct SetupFileList *setup_file_list = NULL;
+  struct dirent *dir_entry;
+  int i, current_entry = start_entry;
+
+  if ((dir = opendir(level_directory)) == NULL)
+  {
+    Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
+    return current_entry;
+  }
+
+  while (current_entry < MAX_LEVDIR_ENTRIES)
+  {
+    if ((dir_entry = readdir(dir)) == NULL)    /* last directory entry */
+      break;
+
+    /* skip entries for current and parent directory */
+    if (strcmp(dir_entry->d_name, ".")  == 0 ||
+       strcmp(dir_entry->d_name, "..") == 0)
+      continue;
+
+    /* find out if directory entry is itself a directory */
+    directory = getPath2(level_directory, dir_entry->d_name);
+    if (stat(directory, &file_status) != 0 ||          /* cannot stat file */
+       (file_status.st_mode & S_IFMT) != S_IFDIR)      /* not a directory */
+    {
+      free(directory);
+      continue;
+    }
+
+    filename = getPath2(directory, LEVELINFO_FILENAME);
+    setup_file_list = loadSetupFileList(filename);
+
+    if (setup_file_list)
+    {
+      checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
+      setLevelDirInfoToDefaults(&leveldir[current_entry]);
+
+      ldi = leveldir[current_entry];
+      for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
+       setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
+      leveldir[current_entry] = ldi;
+
+      leveldir[current_entry].filename = getStringCopy(dir_entry->d_name);
+      leveldir[current_entry].user_defined =
+       (level_directory == options.level_directory ? FALSE : TRUE);
+
+      freeSetupFileList(setup_file_list);
+      current_entry++;
+    }
+    else
+      Error(ERR_WARN, "ignoring level directory '%s'", directory);
+
+    free(directory);
+    free(filename);
+  }
+
+  if (current_entry == MAX_LEVDIR_ENTRIES)
+    Error(ERR_WARN, "using %d level directories -- ignoring the rest",
+         current_entry);
+
+  closedir(dir);
+
+  if (current_entry == start_entry)
+    Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
+         level_directory);
+
+  return current_entry;
+}
+
+void LoadLevelInfo()
+{
+  InitUserLevelDirectory(getLoginName());
+
+  num_leveldirs = 0;
+  leveldir_nr = 0;
+
+  num_leveldirs = LoadLevelInfoFromLevelDir(options.level_directory,
+                                           num_leveldirs);
+  num_leveldirs = LoadLevelInfoFromLevelDir(getUserLevelDir(""),
+                                           num_leveldirs);
+
+  if (num_leveldirs == 0)
+    Error(ERR_EXIT, "cannot find any valid level series in any directory");
+
+  if (num_leveldirs > 1)
+    qsort(leveldir, num_leveldirs, sizeof(struct LevelDirInfo),
+         compareLevelDirInfoEntries);
+}
+
+static void SaveUserLevelInfo()
+{
+  char *filename;
+  FILE *file;
+  int i;
+
+  filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
+
+  if (!(file = fopen(filename, "w")))
+  {
+    Error(ERR_WARN, "cannot write level info file '%s'", filename);
+    free(filename);
+    return;
+  }
+
+  ldi.name = getLoginName();
+  ldi.levels = 100;
+  ldi.sort_priority = 300;
+  ldi.readonly = FALSE;
+
+  fprintf(file, "%s\n\n",
+         getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
+
+  for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
+    fprintf(file, "%s\n", getSetupLine("", i));
+
+  fclose(file);
+  free(filename);
+
+  chmod(filename, SETUP_PERMS);
+}
+
+void LoadSetup()
+{
+  char *filename;
+  struct SetupFileList *setup_file_list = NULL;
+
+  /* always start with reliable default values */
+  setSetupInfoToDefaults(&setup);
+
+  filename = getPath2(getSetupDir(), SETUP_FILENAME);
+
+  setup_file_list = loadSetupFileList(filename);
+
+  if (setup_file_list)
+  {
+    checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
+    decodeSetupFileList(setup_file_list);
+
+    setup.direct_draw = !setup.double_buffering;
+
+    freeSetupFileList(setup_file_list);
+
+    /* needed to work around problems with fixed length strings */
+    if (strlen(setup.player_name) >= MAX_NAMELEN)
+      setup.player_name[MAX_NAMELEN - 1] = '\0';
+    else if (strlen(setup.player_name) < MAX_NAMELEN - 1)
+    {
+      char *new_name = checked_malloc(MAX_NAMELEN);
+
+      strcpy(new_name, setup.player_name);
+      free(setup.player_name);
+      setup.player_name = new_name;
+    }
+  }
+  else
+    Error(ERR_WARN, "using default setup values");
+
+  free(filename);
+}
+
+static char *getSetupLine(char *prefix, int token_nr)
+{
+  int i;
+  static char entry[MAX_LINE_LEN];
+  int token_type = token_info[token_nr].type;
+  void *setup_value = token_info[token_nr].value;
+  char *token_text = token_info[token_nr].text;
+
+  /* start with the prefix, token and some spaces to format output line */
+  sprintf(entry, "%s%s:", prefix, token_text);
+  for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
+    strcat(entry, " ");
+
+  /* continue with the token's value (which can have different types) */
+  switch (token_type)
+  {
+    case TYPE_BOOLEAN:
+      strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
+      break;
+
+    case TYPE_SWITCH:
+      strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
+      break;
+
+    case TYPE_KEYSYM:
+      {
+       KeySym keysym = *(KeySym *)setup_value;
+       char *keyname = getKeyNameFromKeySym(keysym);
+
+       strcat(entry, getX11KeyNameFromKeySym(keysym));
+       for (i=strlen(entry); i<50; i++)
+         strcat(entry, " ");
+
+       /* add comment, if useful */
+       if (strcmp(keyname, "(undefined)") != 0 &&
+           strcmp(keyname, "(unknown)") != 0)
+       {
+         strcat(entry, "# ");
+         strcat(entry, keyname);
+       }
+      }
+      break;
+
+    case TYPE_INTEGER:
+      {
+       char buffer[MAX_LINE_LEN];
+
+       sprintf(buffer, "%d", *(int *)setup_value);
+       strcat(entry, buffer);
+      }
+      break;
+
+    case TYPE_STRING:
+      strcat(entry, *(char **)setup_value);
+      break;
+
+    default:
+      break;
+  }
+
+  return entry;
+}
+
+void SaveSetup()
+{
+  int i, pnr;
+  char *filename;
+  FILE *file;
+
+  InitUserDataDirectory();
+
+  filename = getPath2(getSetupDir(), SETUP_FILENAME);
+
+  if (!(file = fopen(filename, "w")))
+  {
+    Error(ERR_WARN, "cannot write setup file '%s'", filename);
+    free(filename);
+    return;
+  }
+
+  fprintf(file, "%s\n",
+         getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
+  fprintf(file, "\n");
+
+  /* handle global setup values */
+  si = setup;
+  for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
+  {
+    fprintf(file, "%s\n", getSetupLine("", i));
+
+    /* just to make things nicer :) */
+    if (i == SETUP_TOKEN_PLAYER_NAME)
+      fprintf(file, "\n");
+  }
+
+  /* handle player specific setup values */
+  for (pnr=0; pnr<MAX_PLAYERS; pnr++)
+  {
+    char prefix[30];
+
+    sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
+    fprintf(file, "\n");
+
+    sii = setup.input[pnr];
+    for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
+      fprintf(file, "%s\n", getSetupLine(prefix, i));
+  }
+
+  fclose(file);
+  free(filename);
+
+  chmod(filename, SETUP_PERMS);
+}
+
+void LoadLevelSetup()
+{
+  char *filename;
+
+  /* always start with reliable default values */
+  leveldir_nr = 0;
+  level_nr = 0;
+
+  filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
+
+  if (level_setup_list)
+    freeSetupFileList(level_setup_list);
+
+  level_setup_list = loadSetupFileList(filename);
+
+  if (level_setup_list)
+  {
+    char *last_level_series =
+      getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
+
+    leveldir_nr = getLevelSeriesNrFromLevelSeriesName(last_level_series);
+    level_nr = getLastPlayedLevelOfLevelSeries(last_level_series);
+
+    checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
+  }
+  else
+  {
+    level_setup_list = newSetupFileList(TOKEN_STR_FILE_IDENTIFIER,
+                                       LEVELSETUP_COOKIE);
+    Error(ERR_WARN, "using default setup values");
+  }
+
+  free(filename);
+}
+
+void SaveLevelSetup()
+{
+  char *filename;
+  struct SetupFileList *list_entry = level_setup_list;
+  FILE *file;
+
+  InitUserDataDirectory();
+
+  setTokenValue(level_setup_list,
+               TOKEN_STR_LAST_LEVEL_SERIES, leveldir[leveldir_nr].filename);
+
+  setTokenValue(level_setup_list,
+               leveldir[leveldir_nr].filename, int2str(level_nr, 0));
+
+  filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
+
+  if (!(file = fopen(filename, "w")))
+  {
+    Error(ERR_WARN, "cannot write setup file '%s'", filename);
+    free(filename);
+    return;
+  }
+
+  fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
+                                                LEVELSETUP_COOKIE));
+  while (list_entry)
+  {
+    if (strcmp(list_entry->token, TOKEN_STR_FILE_IDENTIFIER) != 0)
+      fprintf(file, "%s\n",
+             getFormattedSetupEntry(list_entry->token, list_entry->value));
+
+    /* just to make things nicer :) */
+    if (strcmp(list_entry->token, TOKEN_STR_LAST_LEVEL_SERIES) == 0)
+      fprintf(file, "\n");
+
+    list_entry = list_entry->next;
+  }
+
   fclose(file);
+  free(filename);
+
+  chmod(filename, SETUP_PERMS);
+}
+
+#ifdef MSDOS
+static boolean initErrorFile()
+{
+  char *filename;
+  FILE *error_file;
 
-  chmod(JOYDAT_FILE, JOYDAT_PERMS);
+  InitUserDataDirectory();
+
+  filename = getPath2(getUserDataDir(), ERROR_FILENAME);
+  error_file = fopen(filename, "w");
+  free(filename);
+
+  if (error_file == NULL)
+    return FALSE;
+
+  fclose(error_file);
+
+  return TRUE;
+}
+
+FILE *openErrorFile()
+{
+  static boolean first_access = TRUE;
+  char *filename;
+  FILE *error_file;
+
+  if (first_access)
+  {
+    if (!initErrorFile())
+      return NULL;
+
+    first_access = FALSE;
+  }
+
+  filename = getPath2(getUserDataDir(), ERROR_FILENAME);
+  error_file = fopen(filename, "a");
+  free(filename);
+
+  return error_file;
+}
+
+void dumpErrorFile()
+{
+  char *filename;
+  FILE *error_file;
+
+  filename = getPath2(getUserDataDir(), ERROR_FILENAME);
+  error_file = fopen(filename, "r");
+  free(filename);
+
+  if (error_file != NULL)
+  {
+    while (!feof(error_file))
+      fputc(fgetc(error_file), stderr);
+
+    fclose(error_file);
+  }
 }
+#endif
index aee5e42179b21bb0268aa6c8d99022e01cf1f2c0..d95cce18fc76076f732c2ee7d3333e9684a45ab6 100644 (file)
@@ -1,13 +1,12 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  files.h                                                 *
 ***********************************************************/
 
 #include "main.h"
 
-/* names file mode: level or setup */
-#define PLAYER_LEVEL   0
-#define PLAYER_SETUP   1
-
-/* Setup-Bits */
-#define SETUP_TOONS                    (1<<0)
-#define SETUP_SOUND                    (1<<1)
-#define SETUP_SOUND_LOOPS              (1<<2)
-#define SETUP_SOUND_MUSIC              (1<<3)
-#define SETUP_DIRECT_DRAW              (1<<4)
-#define SETUP_FADING                   (1<<5)
-#define SETUP_RECORD_EACH_GAME         (1<<6)
-#define SETUP_2ND_JOYSTICK             (1<<7)
-#define SETUP_QUICK_DOORS              (1<<8)
-
-#define DEFAULT_SETUP                  (SETUP_TOONS |          \
-                                        SETUP_SOUND |          \
-                                        SETUP_SOUND_LOOPS |    \
-                                        SETUP_SOUND_MUSIC)
+void LoadLevel(int);
+void SaveLevel(int);
 
-/* Setup-Voreinstellungen */
-#define SETUP_TOONS_ON(x)              (((x) & SETUP_TOONS) != 0)
-#define SETUP_SOUND_ON(x)              (((x) & SETUP_SOUND) != 0)
-#define SETUP_SOUND_LOOPS_ON(x)                (((x) & SETUP_SOUND_LOOPS) != 0)
-#define SETUP_SOUND_MUSIC_ON(x)                (((x) & SETUP_SOUND_MUSIC) != 0)
-#define SETUP_DIRECT_DRAW_ON(x)                (((x) & SETUP_DIRECT_DRAW) != 0)
-#define SETUP_FADING_ON(x)             (((x) & SETUP_FADING) != 0)
-#define SETUP_RECORD_EACH_GAME_ON(x)   (((x) & SETUP_RECORD_EACH_GAME) != 0)
-#define SETUP_2ND_JOYSTICK_ON(x)       (((x) & SETUP_2ND_JOYSTICK) != 0)
-#define SETUP_QUICK_DOORS_ON(x)                (((x) & SETUP_QUICK_DOORS) != 0)
+void LoadTape(int);
+void SaveTape(int);
 
-BOOL CreateNewScoreFile(void);
-BOOL CreateNewNamesFile(int);
-BOOL LoadLevelInfo(void);
-void LoadLevel(int);
-void LoadLevelTape(int);
 void LoadScore(int);
-void LoadPlayerInfo(int);
-void SaveLevel(int);
-void SaveLevelTape(int);
 void SaveScore(int);
-void SavePlayerInfo(int);
-void LoadJoystickData(void);
-void SaveJoystickData(void);
 
+int getLastPlayedLevelOfLevelSeries(char *);
+
+void LoadLevelInfo(void);
+void LoadSetup(void);
+void SaveSetup(void);
+void LoadLevelSetup(void);
+void SaveLevelSetup(void);
+
+#ifdef MSDOS
+FILE *openErrorFile();
+void dumpErrorFile();
 #endif
+
+#endif /* FILES_H */
index 8d88167b277ece9f7ddce9e0ebfcd0a030f523aa..26cc84cc2a8a61d0e95a9cd347f74368a549ebff 100644 (file)
@@ -1,13 +1,12 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  game.c                                                  *
 ***********************************************************/
 #include "init.h"
 #include "buttons.h"
 #include "files.h"
+#include "tape.h"
+#include "joystick.h"
+#include "network.h"
+
+/* for DigField() */
+#define DF_NO_PUSH             0
+#define DF_DIG                 1
+#define DF_SNAP                        2
+
+/* for MoveFigure() */
+#define MF_NO_ACTION           0
+#define MF_MOVING              1
+#define MF_ACTION              2
+
+/* for ScrollFigure() */
+#define SCROLL_INIT            0
+#define SCROLL_GO_ON           1
+
+/* for Explode() */
+#define EX_PHASE_START         0
+#define EX_NORMAL              0
+#define EX_CENTER              1
+#define EX_BORDER              2
+
+/* special positions in the game control window (relative to control window) */
+#define XX_LEVEL               37
+#define YY_LEVEL               20
+#define XX_EMERALDS            29
+#define YY_EMERALDS            54
+#define XX_DYNAMITE            29
+#define YY_DYNAMITE            89
+#define XX_KEYS                        18
+#define YY_KEYS                        123
+#define XX_SCORE               15
+#define YY_SCORE               159
+#define XX_TIME                        29
+#define YY_TIME                        194
+
+/* special positions in the game control window (relative to main window) */
+#define DX_LEVEL               (DX + XX_LEVEL)
+#define DY_LEVEL               (DY + YY_LEVEL)
+#define DX_EMERALDS            (DX + XX_EMERALDS)
+#define DY_EMERALDS            (DY + YY_EMERALDS)
+#define DX_DYNAMITE            (DX + XX_DYNAMITE)
+#define DY_DYNAMITE            (DY + YY_DYNAMITE)
+#define DX_KEYS                        (DX + XX_KEYS)
+#define DY_KEYS                        (DY + YY_KEYS)
+#define DX_SCORE               (DX + XX_SCORE)
+#define DY_SCORE               (DY + YY_SCORE)
+#define DX_TIME                        (DX + XX_TIME)
+#define DY_TIME                        (DY + YY_TIME)
+
+#define IS_LOOP_SOUND(s)       ((s)==SND_KLAPPER || (s)==SND_ROEHR ||  \
+                                (s)==SND_NJAM || (s)==SND_MIEP)
+#define IS_MUSIC_SOUND(s)      ((s)==SND_ALCHEMY || (s)==SND_CHASE || \
+                                (s)==SND_NETWORK || (s)==SND_CZARDASZ || \
+                                (s)==SND_TYGER || (s)==SND_VOYAGER || \
+                                (s)==SND_TWILIGHT)
+
+/* score for elements */
+#define SC_EDELSTEIN           0
+#define SC_DIAMANT             1
+#define SC_KAEFER              2
+#define SC_FLIEGER             3
+#define SC_MAMPFER             4
+#define SC_ROBOT               5
+#define SC_PACMAN              6
+#define SC_KOKOSNUSS           7
+#define SC_DYNAMIT             8
+#define SC_SCHLUESSEL          9
+#define SC_ZEITBONUS           10
+
+/* values for game_emulation */
+#define EMU_NONE               0
+#define EMU_BOULDERDASH                1
+#define EMU_SOKOBAN            2
+
+/* to control special behaviour of certain game elements */
+int game_emulation = EMU_NONE;
 
 void GetPlayerConfig()
 {
-  int old_joystick_nr = joystick_nr;
+  if (sound_status == SOUND_OFF)
+    setup.sound = FALSE;
 
-  if (sound_status==SOUND_OFF)
-    player.setup &= ~SETUP_SOUND;
   if (!sound_loops_allowed)
   {
-    player.setup &= ~SETUP_SOUND_LOOPS;
-    player.setup &= ~SETUP_SOUND_MUSIC;
+    setup.sound_loops = FALSE;
+    setup.sound_music = FALSE;
   }
 
-  sound_on = SETUP_SOUND_ON(player.setup);
-  sound_loops_on = SETUP_SOUND_LOOPS_ON(player.setup);
-  sound_music_on = SETUP_SOUND_MUSIC_ON(player.setup);
-  toons_on = SETUP_TOONS_ON(player.setup);
-  direct_draw_on = SETUP_DIRECT_DRAW_ON(player.setup);
-  fading_on = SETUP_FADING_ON(player.setup);
-  autorecord_on = SETUP_RECORD_EACH_GAME_ON(player.setup);
-  joystick_nr = SETUP_2ND_JOYSTICK_ON(player.setup);
-  quick_doors = SETUP_QUICK_DOORS_ON(player.setup);
+  setup.sound_simple = setup.sound;
 
-  if (joystick_nr != old_joystick_nr)
-  {
-    if (joystick_device)
-      close(joystick_device);
-    InitJoystick();
-  }
+  InitJoysticks();
 }
 
 void InitGame()
 {
-  int i,x,y;
+  int i, j, x, y;
+  boolean emulate_bd = TRUE;   /* unless non-BOULDERDASH elements found */
+  boolean emulate_sb = TRUE;   /* unless non-SOKOBAN     elements found */
+
+  /* don't play tapes over network */
+  network_playing = (options.network && !tape.playing);
+
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+    struct PlayerInfo *player = &stored_player[i];
+
+    player->index_nr = i;
+    player->element_nr = EL_SPIELER1 + i;
+
+    player->present = FALSE;
+    player->active = FALSE;
+
+    player->action = 0;
+    player->effective_action = 0;
+
+    player->score = 0;
+    player->gems_still_needed = level.edelsteine;
+    player->sokobanfields_still_needed = 0;
+    player->lights_still_needed = 0;
+    player->friends_still_needed = 0;
+
+    for (j=0; j<4; j++)
+      player->key[j] = FALSE;
+
+    player->dynamite = 0;
+    player->dynabomb_count = 0;
+    player->dynabomb_size = 0;
+    player->dynabombs_left = 0;
+    player->dynabomb_xl = FALSE;
+
+    player->MovDir = MV_NO_MOVING;
+    player->MovPos = 0;
+    player->Pushing = FALSE;
+    player->GfxPos = 0;
+    player->Frame = 0;
+
+    player->actual_frame_counter = 0;
+
+    player->frame_reset_delay = 0;
+
+    player->push_delay = 0;
+    player->push_delay_value = 5;
+
+    player->move_delay = 0;
+    player->last_move_dir = MV_NO_MOVING;
+
+    player->snapped = FALSE;
+
+    player->gone = FALSE;
+
+    player->last_jx = player->last_jy = 0;
+    player->jx = player->jy = 0;
+
+    DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
+    SnapField(player, 0, 0);
+
+    player->LevelSolved = FALSE;
+    player->GameOver = FALSE;
+  }
+
+  network_player_action_received = FALSE;
+
+#ifndef MSDOS
+  /* initial null action */
+  if (network_playing)
+    SendToServer_MovePlayer(MV_NO_MOVING);
+#endif
+
+  ZX = ZY = -1;
 
-  Dynamite = Score = 0;
-  Gems = level.edelsteine;
-  Key[0] = Key[1] = Key[2] = Key[3] = FALSE;
   MampferNr = 0;
+  FrameCounter = 0;
+  TimeFrames = 0;
   TimeLeft = level.time;
-  CheckMoving = TRUE;
-  CheckExploding = FALSE;
-  LevelSolved = GameOver = SiebAktiv = FALSE;
-  JX = JY = 0;
-  ZX = ZY = -1;
 
-  if (tape.recording)
-    TapeStartRecording();
-  else if (tape.playing)
-    TapeStartPlaying();
+  ScreenMovDir = MV_NO_MOVING;
+  ScreenMovPos = 0;
+  ScreenGfxPos = 0;
 
-  DigField(0,0,DF_NO_PUSH);
-  SnapField(0,0);
+  AllPlayersGone = SiebAktiv = FALSE;
 
-  for(i=0;i<MAX_NUM_AMOEBA;i++)
-    AmoebaCnt[i] = 0;
+  for (i=0; i<MAX_NUM_AMOEBA; i++)
+    AmoebaCnt[i] = AmoebaCnt2[i] = 0;
 
-  for(y=0;y<lev_fieldy;y++) for(x=0;x<lev_fieldx;x++)
+  for (x=0; x<lev_fieldx; x++)
   {
-    Feld[x][y] = Ur[x][y];
-    MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
-    Store[x][y] = Store2[x][y] = Frame[x][y] = AmoebaNr[x][y] = 0;
+    for (y=0; y<lev_fieldy; y++)
+    {
+      Feld[x][y] = Ur[x][y];
+      MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
+      Store[x][y] = Store2[x][y] = StorePlayer[x][y] = 0;
+      Frame[x][y] = 0;
+      AmoebaNr[x][y] = 0;
+      JustHit[x][y] = 0;
+      Stop[x][y] = FALSE;
+    }
+  }
+
+  for(y=0; y<lev_fieldy; y++) for(x=0; x<lev_fieldx; x++)
+  {
+    if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
+      emulate_bd = FALSE;
+    if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
+      emulate_sb = FALSE;
 
-    switch(Feld[x][y])
+    switch (Feld[x][y])
     {
       case EL_SPIELFIGUR:
+       Feld[x][y] = EL_SPIELER1;
+       /* no break! */
       case EL_SPIELER1:
-       Feld[x][y] = EL_LEERRAUM;
-       JX = x;
-       JY = y;
-       break;
       case EL_SPIELER2:
+      case EL_SPIELER3:
+      case EL_SPIELER4:
+      {
+       struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_SPIELER1];
+       int jx = player->jx, jy = player->jy;
+
+       player->present = TRUE;
+
+       /*
+       if (!network_playing || player->connected)
+       */
+
+       if (!options.network || player->connected)
+       {
+         player->active = TRUE;
+
+         /* remove potentially duplicate players */
+         if (StorePlayer[jx][jy] == Feld[x][y])
+           StorePlayer[jx][jy] = 0;
+
+         StorePlayer[x][y] = Feld[x][y];
+
+         if (options.verbose)
+         {
+           printf("Player %d activated.\n", player->element_nr);
+           printf("[Local player is %d and currently %s.]\n",
+                  local_player->element_nr,
+                  local_player->active ? "active" : "not active");
+         }
+       }
+
        Feld[x][y] = EL_LEERRAUM;
+       player->jx = player->last_jx = x;
+       player->jy = player->last_jy = y;
+
        break;
+      }
       case EL_BADEWANNE:
-       if (x<lev_fieldx-1 && Feld[x+1][y]==EL_SALZSAEURE)
+       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_SALZSAEURE)
          Feld[x][y] = EL_BADEWANNE1;
-       else if (x>0 && Feld[x-1][y]==EL_SALZSAEURE)
+       else if (x > 0 && Feld[x-1][y] == EL_SALZSAEURE)
          Feld[x][y] = EL_BADEWANNE2;
-       else if (y>0 && Feld[x][y-1]==EL_BADEWANNE1)
+       else if (y > 0 && Feld[x][y-1] == EL_BADEWANNE1)
          Feld[x][y] = EL_BADEWANNE3;
-       else if (y>0 && Feld[x][y-1]==EL_SALZSAEURE)
+       else if (y > 0 && Feld[x][y-1] == EL_SALZSAEURE)
          Feld[x][y] = EL_BADEWANNE4;
-       else if (y>0 && Feld[x][y-1]==EL_BADEWANNE2)
+       else if (y > 0 && Feld[x][y-1] == EL_BADEWANNE2)
          Feld[x][y] = EL_BADEWANNE5;
        break;
       case EL_KAEFER_R:
@@ -116,79 +293,216 @@ void InitGame()
       case EL_FLIEGER_L:
       case EL_FLIEGER_U:
       case EL_FLIEGER:
+      case EL_BUTTERFLY_R:
+      case EL_BUTTERFLY_O:
+      case EL_BUTTERFLY_L:
+      case EL_BUTTERFLY_U:
+      case EL_BUTTERFLY:
+      case EL_FIREFLY_R:
+      case EL_FIREFLY_O:
+      case EL_FIREFLY_L:
+      case EL_FIREFLY_U:
+      case EL_FIREFLY:
       case EL_PACMAN_R:
       case EL_PACMAN_O:
       case EL_PACMAN_L:
       case EL_PACMAN_U:
       case EL_MAMPFER:
-      case EL_ZOMBIE:
+      case EL_MAMPFER2:
+      case EL_ROBOT:
       case EL_PACMAN:
-       InitMovDir(x,y);
+       InitMovDir(x, y);
        break;
       case EL_AMOEBE_VOLL:
-       InitAmoebaNr(x,y);
+      case EL_AMOEBE_BD:
+       InitAmoebaNr(x, y);
        break;
       case EL_TROPFEN:
-       if (y==lev_fieldy-1)
+       if (y == lev_fieldy - 1)
        {
          Feld[x][y] = EL_AMOEBING;
          Store[x][y] = EL_AMOEBE_NASS;
        }
        break;
+      case EL_DYNAMIT:
+       MovDelay[x][y] = 96;
+       break;
+      case EL_BIRNE_AUS:
+       local_player->lights_still_needed++;
+       break;
+      case EL_SOKOBAN_FELD_LEER:
+       local_player->sokobanfields_still_needed++;
+       break;
+      case EL_MAULWURF:
+      case EL_PINGUIN:
+       local_player->friends_still_needed++;
+       break;
+      case EL_SCHWEIN:
+      case EL_DRACHE:
+       MovDir[x][y] = 1 << RND(4);
+       break;
       default:
        break;
     }
   }
 
+  /* check if any connected player was not found in playfield */
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+    struct PlayerInfo *player = &stored_player[i];
+
+    if (player->connected && !player->present)
+    {
+      for (j=0; j<MAX_PLAYERS; j++)
+      {
+       struct PlayerInfo *some_player = &stored_player[j];
+       int jx = some_player->jx, jy = some_player->jy;
+
+       /* assign first free player found that is present in the playfield */
+       if (some_player->present && !some_player->connected)
+       {
+         player->present = TRUE;
+         player->active = TRUE;
+         some_player->present = FALSE;
+
+         StorePlayer[jx][jy] = player->element_nr;
+         player->jx = player->last_jx = jx;
+         player->jy = player->last_jy = jy;
+
+         break;
+       }
+      }
+    }
+  }
+
+  if (tape.playing)
+  {
+    /* when playing a tape, eliminate all players who do not participate */
+
+    for (i=0; i<MAX_PLAYERS; i++)
+    {
+      if (stored_player[i].active && !tape.player_participates[i])
+      {
+       struct PlayerInfo *player = &stored_player[i];
+       int jx = player->jx, jy = player->jy;
+
+       player->active = FALSE;
+       StorePlayer[jx][jy] = 0;
+       Feld[jx][jy] = EL_LEERRAUM;
+      }
+    }
+  }
+  else if (!options.network && !setup.team_mode)       /* && !tape.playing */
+  {
+    /* when in single player mode, eliminate all but the first active player */
+
+    for (i=0; i<MAX_PLAYERS; i++)
+    {
+      if (stored_player[i].active)
+      {
+       for (j=i+1; j<MAX_PLAYERS; j++)
+       {
+         if (stored_player[j].active)
+         {
+           struct PlayerInfo *player = &stored_player[j];
+           int jx = player->jx, jy = player->jy;
+
+           player->active = FALSE;
+           StorePlayer[jx][jy] = 0;
+           Feld[jx][jy] = EL_LEERRAUM;
+         }
+       }
+      }
+    }
+  }
+
+  /* when recording the game, store which players take part in the game */
+  if (tape.recording)
+  {
+    for (i=0; i<MAX_PLAYERS; i++)
+      if (stored_player[i].active)
+       tape.player_participates[i] = TRUE;
+  }
+
+  if (options.verbose)
+  {
+    for (i=0; i<MAX_PLAYERS; i++)
+    {
+      struct PlayerInfo *player = &stored_player[i];
+
+      printf("Player %d: present == %d, connected == %d, active == %d.\n",
+            i+1,
+            player->present,
+            player->connected,
+            player->active);
+      if (local_player == player)
+       printf("Player  %d is local player.\n", i+1);
+    }
+  }
+
+  game_emulation = (emulate_bd ? EMU_BOULDERDASH :
+                   emulate_sb ? EMU_SOKOBAN : EMU_NONE);
+
   scroll_x = scroll_y = -1;
-  if (JX>=MIDPOSX-1)
-    scroll_x =
-      (JX<=lev_fieldx-MIDPOSX ? JX-MIDPOSX : lev_fieldx-SCR_FIELDX+1);
-  if (JY>=MIDPOSY-1)
-    scroll_y =
-      (JY<=lev_fieldy-MIDPOSY ? JY-MIDPOSY : lev_fieldy-SCR_FIELDY+1);
+  if (local_player->jx >= MIDPOSX-1)
+    scroll_x = (local_player->jx <= lev_fieldx-MIDPOSX ?
+               local_player->jx - MIDPOSX :
+               lev_fieldx - SCR_FIELDX + 1);
+  if (local_player->jy >= MIDPOSY-1)
+    scroll_y = (local_player->jy <= lev_fieldy-MIDPOSY ?
+               local_player->jy - MIDPOSY :
+               lev_fieldy - SCR_FIELDY + 1);
+
+  CloseDoor(DOOR_CLOSE_1);
 
   DrawLevel();
-  DrawLevelElement(JX,JY,EL_SPIELFIGUR);
+  DrawAllPlayers();
   FadeToFront();
 
-  XCopyArea(display,pix[PIX_DOOR],pix[PIX_DB_DOOR],gc,
-           DOOR_GFX_PAGEX5,DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
-           DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY1);
-  DrawTextExt(pix[PIX_DB_DOOR],gc,
-             DOOR_GFX_PAGEX1+XX_LEVEL,DOOR_GFX_PAGEY1+YY_LEVEL,
-             int2str(level_nr,2),FS_SMALL,FC_YELLOW);
-  DrawTextExt(pix[PIX_DB_DOOR],gc,
-             DOOR_GFX_PAGEX1+XX_EMERALDS,DOOR_GFX_PAGEY1+YY_EMERALDS,
-             int2str(Gems,3),FS_SMALL,FC_YELLOW);
-  DrawTextExt(pix[PIX_DB_DOOR],gc,
-             DOOR_GFX_PAGEX1+XX_DYNAMITE,DOOR_GFX_PAGEY1+YY_DYNAMITE,
-             int2str(Dynamite,3),FS_SMALL,FC_YELLOW);
-  DrawTextExt(pix[PIX_DB_DOOR],gc,
-             DOOR_GFX_PAGEX1+XX_SCORE,DOOR_GFX_PAGEY1+YY_SCORE,
-             int2str(Score,5),FS_SMALL,FC_YELLOW);
-  DrawTextExt(pix[PIX_DB_DOOR],gc,
-             DOOR_GFX_PAGEX1+XX_TIME,DOOR_GFX_PAGEY1+YY_TIME,
-             int2str(TimeLeft,3),FS_SMALL,FC_YELLOW);
+  XCopyArea(display, pix[PIX_DOOR], pix[PIX_DB_DOOR], gc,
+           DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
+           DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+  DrawTextExt(pix[PIX_DB_DOOR], gc,
+             DOOR_GFX_PAGEX1 + XX_LEVEL, DOOR_GFX_PAGEY1 + YY_LEVEL,
+             int2str(level_nr, 2), FS_SMALL, FC_YELLOW);
+  DrawTextExt(pix[PIX_DB_DOOR], gc,
+             DOOR_GFX_PAGEX1 + XX_EMERALDS, DOOR_GFX_PAGEY1 + YY_EMERALDS,
+             int2str(local_player->gems_still_needed,3), FS_SMALL, FC_YELLOW);
+  DrawTextExt(pix[PIX_DB_DOOR], gc,
+             DOOR_GFX_PAGEX1 + XX_DYNAMITE, DOOR_GFX_PAGEY1 + YY_DYNAMITE,
+             int2str(local_player->dynamite, 3), FS_SMALL, FC_YELLOW);
+  DrawTextExt(pix[PIX_DB_DOOR], gc,
+             DOOR_GFX_PAGEX1 + XX_SCORE, DOOR_GFX_PAGEY1 + YY_SCORE,
+             int2str(local_player->score, 5), FS_SMALL, FC_YELLOW);
+  DrawTextExt(pix[PIX_DB_DOOR], gc,
+             DOOR_GFX_PAGEX1 + XX_TIME, DOOR_GFX_PAGEY1 + YY_TIME,
+             int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
 
   DrawGameButton(BUTTON_GAME_STOP);
   DrawGameButton(BUTTON_GAME_PAUSE);
   DrawGameButton(BUTTON_GAME_PLAY);
-  DrawSoundDisplay(BUTTON_SOUND_MUSIC | (BUTTON_ON * sound_music_on));
-  DrawSoundDisplay(BUTTON_SOUND_LOOPS | (BUTTON_ON * sound_loops_on));
-  DrawSoundDisplay(BUTTON_SOUND_SOUND | (BUTTON_ON * sound_on));
-  XCopyArea(display,drawto,pix[PIX_DB_DOOR],gc,
-           DX+GAME_CONTROL_XPOS,DY+GAME_CONTROL_YPOS,
-           GAME_CONTROL_XSIZE,2*GAME_CONTROL_YSIZE,
-           DOOR_GFX_PAGEX1+GAME_CONTROL_XPOS,
-           DOOR_GFX_PAGEY1+GAME_CONTROL_YPOS);
+  DrawSoundDisplay(BUTTON_SOUND_MUSIC  | (setup.sound_music  ? BUTTON_ON : 0));
+  DrawSoundDisplay(BUTTON_SOUND_LOOPS  | (setup.sound_loops  ? BUTTON_ON : 0));
+  DrawSoundDisplay(BUTTON_SOUND_SIMPLE | (setup.sound_simple ? BUTTON_ON : 0));
+  XCopyArea(display, drawto, pix[PIX_DB_DOOR], gc,
+           DX + GAME_CONTROL_XPOS, DY + GAME_CONTROL_YPOS,
+           GAME_CONTROL_XSIZE, 2 * GAME_CONTROL_YSIZE,
+           DOOR_GFX_PAGEX1 + GAME_CONTROL_XPOS,
+           DOOR_GFX_PAGEY1 + GAME_CONTROL_YPOS);
 
   OpenDoor(DOOR_OPEN_1);
 
-  if (sound_music_on)
+  if (setup.sound_music)
     PlaySoundLoop(background_loop[level_nr % num_bg_loops]);
 
   XAutoRepeatOff(display);
+
+  if (options.verbose)
+  {
+    for (i=0; i<4; i++)
+      printf("Spieler %d %saktiv.\n",
+            i+1, (stored_player[i].active ? "" : "nicht "));
+  }
 }
 
 void InitMovDir(int x, int y)
@@ -196,15 +510,15 @@ void InitMovDir(int x, int y)
   int i, element = Feld[x][y];
   static int xy[4][2] =
   {
-    0,+1,
-    +1,0,
-    0,-1,
-    -1,0
+    {  0, +1 },
+    { +1,  0 },
+    {  0, -1 },
+    { -1,  0 }
   };
   static int direction[2][4] =
   {
-    MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN,
-    MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP
+    { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
+    { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP }
   };
 
   switch(element)
@@ -214,42 +528,57 @@ void InitMovDir(int x, int y)
     case EL_KAEFER_L:
     case EL_KAEFER_U:
       Feld[x][y] = EL_KAEFER;
-      MovDir[x][y] = direction[0][element-EL_KAEFER_R];
+      MovDir[x][y] = direction[0][element - EL_KAEFER_R];
       break;
     case EL_FLIEGER_R:
     case EL_FLIEGER_O:
     case EL_FLIEGER_L:
     case EL_FLIEGER_U:
       Feld[x][y] = EL_FLIEGER;
-      MovDir[x][y] = direction[0][element-EL_FLIEGER_R];
+      MovDir[x][y] = direction[0][element - EL_FLIEGER_R];
+      break;
+    case EL_BUTTERFLY_R:
+    case EL_BUTTERFLY_O:
+    case EL_BUTTERFLY_L:
+    case EL_BUTTERFLY_U:
+      Feld[x][y] = EL_BUTTERFLY;
+      MovDir[x][y] = direction[0][element - EL_BUTTERFLY_R];
+      break;
+    case EL_FIREFLY_R:
+    case EL_FIREFLY_O:
+    case EL_FIREFLY_L:
+    case EL_FIREFLY_U:
+      Feld[x][y] = EL_FIREFLY;
+      MovDir[x][y] = direction[0][element - EL_FIREFLY_R];
       break;
     case EL_PACMAN_R:
     case EL_PACMAN_O:
     case EL_PACMAN_L:
     case EL_PACMAN_U:
       Feld[x][y] = EL_PACMAN;
-      MovDir[x][y] = direction[0][element-EL_PACMAN_R];
+      MovDir[x][y] = direction[0][element - EL_PACMAN_R];
       break;
     default:
-      MovDir[x][y] = 1<<RND(4);
-      if (element!=EL_KAEFER && element!=EL_FLIEGER)
+      MovDir[x][y] = 1 << RND(4);
+      if (element != EL_KAEFER &&
+         element != EL_FLIEGER &&
+         element != EL_BUTTERFLY &&
+         element != EL_FIREFLY)
        break;
 
-      for(i=0;i<4;i++)
+      for (i=0; i<4; i++)
       {
-       int x1,y1;
+       int x1 = x + xy[i][0];
+       int y1 = y + xy[i][1];
 
-       x1 = x+xy[i][0];
-       y1 = y+xy[i][1];
-
-       if (!IN_LEV_FIELD(x1,y1) || !IS_FREE(x1,y1))
+       if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
        {
-         if (element==EL_KAEFER)
+         if (element == EL_KAEFER || element == EL_BUTTERFLY)
          {
            MovDir[x][y] = direction[0][i];
            break;
          }
-         else if (element==EL_FLIEGER)
+         else if (element == EL_FLIEGER || element == EL_FIREFLY)
          {
            MovDir[x][y] = direction[1][i];
            break;
@@ -263,13 +592,13 @@ void InitMovDir(int x, int y)
 void InitAmoebaNr(int x, int y)
 {
   int i;
-  int group_nr = AmoebeNachbarNr(x,y);
+  int group_nr = AmoebeNachbarNr(x, y);
 
-  if (group_nr==0)
+  if (group_nr == 0)
   {
-    for(i=1;i<MAX_NUM_AMOEBA;i++)
+    for (i=1; i<MAX_NUM_AMOEBA; i++)
     {
-      if (AmoebaCnt[i]==0)
+      if (AmoebaCnt[i] == 0)
       {
        group_nr = i;
        break;
@@ -279,6 +608,7 @@ void InitAmoebaNr(int x, int y)
 
   AmoebaNr[x][y] = group_nr;
   AmoebaCnt[group_nr]++;
+  AmoebaCnt2[group_nr]++;
 }
 
 void GameWon()
@@ -286,41 +616,53 @@ void GameWon()
   int hi_pos;
   int bumplevel = FALSE;
 
-  if (sound_loops_on)
-    PlaySoundExt(SND_SIRR,PSND_MAX_VOLUME,PSND_MAX_RIGHT,PSND_LOOP);
+  if (local_player->MovPos)
+    return;
+
+  local_player->LevelSolved = FALSE;
 
-  if (TimeLeft>0) 
+  if (TimeLeft)
   {
-    for(;TimeLeft>=0;TimeLeft--)
+    if (setup.sound_loops)
+      PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
+
+    while(TimeLeft > 0)
     {
-      if (!sound_loops_on)
-       PlaySoundStereo(SND_SIRR,PSND_MAX_RIGHT);
+      if (!setup.sound_loops)
+       PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
       if (TimeLeft && !(TimeLeft % 10))
        RaiseScore(level.score[SC_ZEITBONUS]);
-      DrawText(DX_TIME,DY_TIME,int2str(TimeLeft,3),FS_SMALL,FC_YELLOW);
+      if (TimeLeft > 100 && !(TimeLeft % 10))
+       TimeLeft -= 10;
+      else
+       TimeLeft--;
+      DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
       BackToFront();
-      Delay(10000);
+      Delay(10);
     }
+
+    if (setup.sound_loops)
+      StopSound(SND_SIRR);
   }
 
-  if (sound_loops_on)
-    StopSound(SND_SIRR);
   FadeSounds();
 
+  /* Hero disappears */
+  DrawLevelField(ExitX, ExitY);
+  BackToFront();
+
   if (tape.playing)
     return;
 
   CloseDoor(DOOR_CLOSE_1);
 
-  if (level_nr==player.handicap &&
-      level_nr<leveldir[leveldir_nr].num_ready) 
-  { 
-    player.handicap++; 
-    bumplevel = TRUE;
-    SavePlayerInfo(PLAYER_LEVEL);
+  if (tape.recording)
+  {
+    TapeStop();
+    SaveTape(tape.level_nr);           /* Ask to save tape */
   }
 
-  if ((hi_pos=NewHiScore())>=0) 
+  if ((hi_pos = NewHiScore()) >= 0) 
   {
     game_status = HALLOFFAME;
     DrawHallOfFame(hi_pos);
@@ -334,82 +676,85 @@ void GameWon()
       level_nr++;
     DrawMainMenu();
   }
+
   BackToFront();
 }
 
-BOOL NewHiScore()
+boolean NewHiScore()
 {
-  int k,l;
+  int k, l;
   int position = -1;
 
   LoadScore(level_nr);
 
-  if (!strcmp(player.alias_name,EMPTY_ALIAS) ||
-      Score<highscore[MAX_SCORE_ENTRIES-1].Score) 
-    return(-1);
+  if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
+      local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score) 
+    return -1;
 
-  for(k=0;k<MAX_SCORE_ENTRIES;k++) 
+  for (k=0; k<MAX_SCORE_ENTRIES; k++) 
   {
-    if (Score>highscore[k].Score)      /* Spieler kommt in Highscore-Liste */
+    if (local_player->score > highscore[k].Score)
     {
-      if (k<MAX_SCORE_ENTRIES-1)
+      /* player has made it to the hall of fame */
+
+      if (k < MAX_SCORE_ENTRIES - 1)
       {
-       int m = MAX_SCORE_ENTRIES-1;
+       int m = MAX_SCORE_ENTRIES - 1;
 
 #ifdef ONE_PER_NAME
-       for(l=k;l<MAX_SCORE_ENTRIES;l++)
-         if (!strcmp(player.alias_name,highscore[l].Name))
+       for (l=k; l<MAX_SCORE_ENTRIES; l++)
+         if (!strcmp(setup.player_name, highscore[l].Name))
            m = l;
-       if (m==k)       /* Spieler überschreibt seine alte Position */
+       if (m == k)     /* player's new highscore overwrites his old one */
          goto put_into_list;
 #endif
 
-       for(l=m;l>k;l--)
+       for (l=m; l>k; l--)
        {
-         strcpy(highscore[l].Name,highscore[l-1].Name);
-         highscore[l].Score = highscore[l-1].Score;
+         strcpy(highscore[l].Name, highscore[l - 1].Name);
+         highscore[l].Score = highscore[l - 1].Score;
        }
       }
 
 #ifdef ONE_PER_NAME
       put_into_list:
 #endif
-      sprintf(highscore[k].Name,player.alias_name);
-      highscore[k].Score = Score; 
+      strncpy(highscore[k].Name, setup.player_name, MAX_NAMELEN - 1);
+      highscore[k].Name[MAX_NAMELEN - 1] = '\0';
+      highscore[k].Score = local_player->score; 
       position = k;
       break;
     }
 
 #ifdef ONE_PER_NAME
-    else if (!strcmp(player.alias_name,highscore[k].Name))
-      break;   /* Spieler schon mit besserer Punktzahl in der Liste */
+    else if (!strncmp(setup.player_name, highscore[k].Name, MAX_NAMELEN - 1))
+      break;   /* player already there with a higher score */
 #endif
 
   }
 
-  if (position>=0) 
+  if (position >= 0) 
     SaveScore(level_nr);
 
-  return(position);
+  return position;
 }
 
 void InitMovingField(int x, int y, int direction)
 {
-  int newx = x + (direction==MV_LEFT ? -1 : direction==MV_RIGHT ? +1 : 0);
-  int newy = y + (direction==MV_UP   ? -1 : direction==MV_DOWN  ? +1 : 0);
+  int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
+  int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
 
-  CheckMoving = TRUE;
   MovDir[x][y] = direction;
   MovDir[newx][newy] = direction;
-  if (Feld[newx][newy]==EL_LEERRAUM)
+  if (Feld[newx][newy] == EL_LEERRAUM)
     Feld[newx][newy] = EL_BLOCKED;
 }
 
 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
 {
   int direction = MovDir[x][y];
-  int newx = x + (direction==MV_LEFT ? -1 : direction==MV_RIGHT ? +1 : 0);
-  int newy = y + (direction==MV_UP   ? -1 : direction==MV_DOWN  ? +1 : 0);
+  int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
+  int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
 
   *goes_to_x = newx;
   *goes_to_y = newy;
@@ -420,13 +765,13 @@ void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
   int oldx = x, oldy = y;
   int direction = MovDir[x][y];
 
-  if (direction==MV_LEFT)
+  if (direction == MV_LEFT)
     oldx++;
-  else if (direction==MV_RIGHT)
+  else if (direction == MV_RIGHT)
     oldx--;
-  else if (direction==MV_UP)
+  else if (direction == MV_UP)
     oldy++;
-  else if (direction==MV_DOWN)
+  else if (direction == MV_DOWN)
     oldy--;
 
   *comes_from_x = oldx;
@@ -437,140 +782,213 @@ int MovingOrBlocked2Element(int x, int y)
 {
   int element = Feld[x][y];
 
-  if (element==EL_BLOCKED)
+  if (element == EL_BLOCKED)
   {
-    int oldx,oldy;
+    int oldx, oldy;
 
-    Blocked2Moving(x,y,&oldx,&oldy);
-    return(Feld[oldx][oldy]);
+    Blocked2Moving(x, y, &oldx, &oldy);
+    return Feld[oldx][oldy];
   }
   else
-    return(element);
+    return element;
+}
+
+static void RemoveField(int x, int y)
+{
+  Feld[x][y] = EL_LEERRAUM;
+  MovPos[x][y] = 0;
+  MovDir[x][y] = 0;
+  MovDelay[x][y] = 0;
 }
 
 void RemoveMovingField(int x, int y)
 {
-  int oldx=x,oldy=y, newx=x,newy=y;
+  int oldx = x, oldy = y, newx = x, newy = y;
 
-  if (Feld[x][y]!=EL_BLOCKED && !IS_MOVING(x,y))
+  if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
     return;
 
-  if (IS_MOVING(x,y))
+  if (IS_MOVING(x, y))
   {
-    Moving2Blocked(x,y,&newx,&newy);
-    if (Feld[newx][newy]!=EL_BLOCKED)
+    Moving2Blocked(x, y, &newx, &newy);
+    if (Feld[newx][newy] != EL_BLOCKED)
       return;
   }
-  else if (Feld[x][y]==EL_BLOCKED)
+  else if (Feld[x][y] == EL_BLOCKED)
   {
-    Blocked2Moving(x,y,&oldx,&oldy);
-    if (!IS_MOVING(oldx,oldy))
+    Blocked2Moving(x, y, &oldx, &oldy);
+    if (!IS_MOVING(oldx, oldy))
       return;
   }
 
-  Feld[oldx][oldy] = EL_LEERRAUM;
+  if (Feld[x][y] == EL_BLOCKED &&
+      (Store[oldx][oldy] == EL_MORAST_LEER ||
+       Store[oldx][oldy] == EL_SIEB_LEER ||
+       Store[oldx][oldy] == EL_SIEB2_LEER ||
+       Store[oldx][oldy] == EL_AMOEBE_NASS))
+  {
+    Feld[oldx][oldy] = Store[oldx][oldy];
+    Store[oldx][oldy] = Store2[oldx][oldy] = 0;
+  }
+  else
+    Feld[oldx][oldy] = EL_LEERRAUM;
+
   Feld[newx][newy] = EL_LEERRAUM;
   MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
   MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
-  DrawLevelField(oldx,oldy);
-  DrawLevelField(newx,newy);
+
+  DrawLevelField(oldx, oldy);
+  DrawLevelField(newx, newy);
 }
 
 void DrawDynamite(int x, int y)
 {
-  int phase = (48-MovDelay[x][y])/6;
+  int sx = SCREENX(x), sy = SCREENY(y);
+  int graphic = el2gfx(Feld[x][y]);
+  int phase;
 
-  if (!IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
+  if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
     return;
 
-  if (phase>6)
-    phase = 6;
-
   if (Store[x][y])
+    DrawGraphic(sx, sy, el2gfx(Store[x][y]));
+
+  if (Feld[x][y] == EL_DYNAMIT)
   {
-    DrawGraphic(SCROLLX(x),SCROLLY(y),el2gfx(Store[x][y]));
-    if (PLAYER(x,y))
-      DrawGraphicThruMask(SCROLLX(JX),SCROLLY(JY),GFX_SPIELFIGUR);
+    if ((phase = (96 - MovDelay[x][y]) / 12) > 6)
+      phase = 6;
+  }
+  else
+  {
+    if ((phase = ((96 - MovDelay[x][y]) / 6) % 8) > 3)
+      phase = 7 - phase;
   }
-  else if (PLAYER(x,y))
-    DrawGraphic(SCROLLX(JX),SCROLLY(JY),GFX_SPIELFIGUR);
 
-  if (Store[x][y] || PLAYER(x,y))
-    DrawGraphicThruMask(SCROLLX(x),SCROLLY(y),GFX_DYNAMIT+phase);
+  if (Store[x][y])
+    DrawGraphicThruMask(sx, sy, graphic + phase);
   else
-    DrawGraphic(SCROLLX(x),SCROLLY(y),GFX_DYNAMIT+phase);
+    DrawGraphic(sx, sy, graphic + phase);
 }
 
 void CheckDynamite(int x, int y)
 {
-  CheckExploding=TRUE;
-
-  if (MovDelay[x][y])          /* neues Dynamit / in Wartezustand */
+  if (MovDelay[x][y])          /* dynamite is still waiting to explode */
   {
     MovDelay[x][y]--;
     if (MovDelay[x][y])
     {
-      if (!(MovDelay[x][y] % 6))
-      {
-       DrawDynamite(x,y);
-       PlaySoundLevel(x,y,SND_ZISCH);
-      }
+      if (!(MovDelay[x][y] % 12))
+       PlaySoundLevel(x, y, SND_ZISCH);
+
+      if (Feld[x][y] == EL_DYNAMIT && !(MovDelay[x][y] % 12))
+       DrawDynamite(x, y);
+      else if (Feld[x][y] == EL_DYNABOMB && !(MovDelay[x][y] % 6))
+       DrawDynamite(x, y);
 
       return;
     }
   }
 
   StopSound(SND_ZISCH);
-  Bang(x,y);
+  Bang(x, y);
 }
 
-void Explode(int ex, int ey, int phase)
+void Explode(int ex, int ey, int phase, int mode)
 {
-  int x,y;
-  int num_phase = 9, delay = 1;
-  int last_phase = num_phase*delay;
-  int half_phase = (num_phase/2)*delay;
+  int x, y;
+  int num_phase = 9, delay = 2;
+  int last_phase = num_phase * delay;
+  int half_phase = (num_phase / 2) * delay;
 
-  if (phase==0)                        /* Feld 'Store' initialisieren */
+  if (phase == EX_PHASE_START)         /* initialize 'Store[][]' field */
   {
     int center_element = Feld[ex][ey];
 
-    if (center_element==EL_BLOCKED)
-      center_element = MovingOrBlocked2Element(ex,ey);
+    if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
+    {
+      center_element = MovingOrBlocked2Element(ex, ey);
+      RemoveMovingField(ex, ey);
+    }
 
-    for(y=ey-1;y<ey+2;y++) for(x=ex-1;x<ex+2;x++)
+    for (y=ey-1; y<ey+2; y++) for(x=ex-1; x<ex+2; x++)
     {
       int element = Feld[x][y];
 
-      if (!IN_LEV_FIELD(x,y) || IS_MASSIV(element))
+      if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
+      {
+       element = MovingOrBlocked2Element(x, y);
+       RemoveMovingField(x, y);
+      }
+
+      if (!IN_LEV_FIELD(x, y) || IS_MASSIV(element) || element == EL_BURNING)
        continue;
 
-      if (center_element==EL_AMOEBA2DIAM && (x!=ex || y!=ey))
+      if ((mode!=EX_NORMAL || center_element == EL_AMOEBA2DIAM) &&
+         (x!=ex || y!=ey))
        continue;
 
-      if (element==EL_EXPLODING)
+      if (element == EL_EXPLODING)
        element = Store2[x][y];
 
-      if (PLAYER(ex,ey) || center_element==EL_KAEFER)
-       Store[x][y] = ((x==ex && y==ey) ? EL_DIAMANT : EL_EDELSTEIN);
-      else if (center_element==EL_MAMPFER)
+      if (IS_PLAYER(ex, ey))
+      {
+       switch(StorePlayer[ex][ey])
+       {
+         case EL_SPIELER2:
+           Store[x][y] = EL_EDELSTEIN_ROT;
+           break;
+         case EL_SPIELER3:
+           Store[x][y] = EL_EDELSTEIN;
+           break;
+         case EL_SPIELER4:
+           Store[x][y] = EL_EDELSTEIN_LILA;
+           break;
+         case EL_SPIELER1:
+         default:
+           Store[x][y] = EL_EDELSTEIN_GELB;
+           break;
+       }
+      }
+      else if (center_element == EL_MAULWURF)
+       Store[x][y] = EL_EDELSTEIN_ROT;
+      else if (center_element == EL_PINGUIN)
+       Store[x][y] = EL_EDELSTEIN_LILA;
+      else if (center_element == EL_KAEFER)
+       Store[x][y] = ((x == ex && y == ey) ? EL_DIAMANT : EL_EDELSTEIN);
+      else if (center_element == EL_BUTTERFLY)
+       Store[x][y] = EL_EDELSTEIN_BD;
+      else if (center_element == EL_MAMPFER)
        Store[x][y] = level.mampfer_inhalt[MampferNr][x-ex+1][y-ey+1];
-      else if (center_element==EL_AMOEBA2DIAM)
+      else if (center_element == EL_AMOEBA2DIAM)
        Store[x][y] = level.amoebe_inhalt;
-      else if (element==EL_ERZ_EDEL)
+      else if (element == EL_ERZ_EDEL)
        Store[x][y] = EL_EDELSTEIN;
-      else if (element==EL_ERZ_DIAM)
+      else if (element == EL_ERZ_DIAM)
        Store[x][y] = EL_DIAMANT;
+      else if (element == EL_ERZ_EDEL_BD)
+       Store[x][y] = EL_EDELSTEIN_BD;
+      else if (element == EL_ERZ_EDEL_GELB)
+       Store[x][y] = EL_EDELSTEIN_GELB;
+      else if (element == EL_ERZ_EDEL_ROT)
+       Store[x][y] = EL_EDELSTEIN_ROT;
+      else if (element == EL_ERZ_EDEL_LILA)
+       Store[x][y] = EL_EDELSTEIN_LILA;
       else if (!IS_PFORTE(Store[x][y]))
        Store[x][y] = EL_LEERRAUM;
 
-      if (x!=ex || y!=ey || center_element==EL_AMOEBA2DIAM)
+      if (x != ex || y != ey ||
+         center_element == EL_AMOEBA2DIAM || mode == EX_BORDER)
        Store2[x][y] = element;
 
-      if (AmoebaNr[x][y] && (element==EL_AMOEBE_VOLL || element==EL_AMOEBING))
+      if (AmoebaNr[x][y] &&
+         (element == EL_AMOEBE_VOLL ||
+          element == EL_AMOEBE_BD ||
+          element == EL_AMOEBING))
+      {
        AmoebaCnt[AmoebaNr[x][y]]--;
+       AmoebaCnt2[AmoebaNr[x][y]]--;
+      }
 
-      RemoveMovingField(x,y);
       Feld[x][y] = EL_EXPLODING;
       MovDir[x][y] = MovPos[x][y] = 0;
       AmoebaNr[x][y] = 0;
@@ -578,7 +996,7 @@ void Explode(int ex, int ey, int phase)
       Stop[x][y] = TRUE;
     }
 
-    if (center_element==EL_MAMPFER)
+    if (center_element == EL_MAMPFER)
       MampferNr = (MampferNr+1) % 4;
 
     return;
@@ -592,26 +1010,23 @@ void Explode(int ex, int ey, int phase)
 
   Frame[x][y] = (phase<last_phase ? phase+1 : 0);
 
-  if (phase==half_phase)
+  if (phase == half_phase)
   {
     int element = Store2[x][y];
 
-    if (PLAYER(x,y))
-      KillHero();
-    else if (element==EL_BOMBE ||
-            element==EL_DYNAMIT ||
-            element==EL_DYNAMIT_AUS ||
-            element==EL_KAEFER)
+    if (IS_PLAYER(x, y))
+      KillHero(PLAYERINFO(x, y));
+    else if (IS_EXPLOSIVE(element))
     {
       Feld[x][y] = Store2[x][y];
       Store2[x][y] = 0;
-      Bang(x,y);
+      Bang(x, y);
     }
-    else if (element==EL_AMOEBA2DIAM)
-      AmoebeUmwandeln(x,y);
+    else if (element == EL_AMOEBA2DIAM)
+      AmoebeUmwandeln(x, y);
   }
 
-  if (phase==last_phase)
+  if (phase == last_phase)
   {
     int element;
 
@@ -619,90 +1034,136 @@ void Explode(int ex, int ey, int phase)
     Store[x][y] = Store2[x][y] = 0;
     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
     if (CAN_MOVE(element) || COULD_MOVE(element))
-      InitMovDir(x,y);
-    DrawLevelField(x,y);
+      InitMovDir(x, y);
+    DrawLevelField(x, y);
+  }
+  else if (!(phase%delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+  {
+    if (phase == delay)
+      ErdreichAnbroeckeln(SCREENX(x), SCREENY(y));
+
+    DrawGraphic(SCREENX(x), SCREENY(y), GFX_EXPLOSION+(phase/delay-1));
   }
-  else if (!(phase%delay) && IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
+}
+
+void DynaExplode(int ex, int ey)
+{
+  int i, j;
+  struct PlayerInfo *player = &stored_player[Store2[ex][ey] - EL_SPIELER1];
+  static int xy[4][2] =
+  {
+    { 0, -1 },
+    { -1, 0 },
+    { +1, 0 },
+    { 0, +1 }
+  };
+
+  Store2[ex][ey] = 0;  /* delete player information */
+
+  Explode(ex, ey, EX_PHASE_START, EX_CENTER);
+
+  for (i=0; i<4; i++)
   {
-    if (phase==delay)
-      ErdreichAnbroeckeln(SCROLLX(x),SCROLLY(y));
+    for (j=1; j<=player->dynabomb_size; j++)
+    {
+      int x = ex+j*xy[i%4][0];
+      int y = ey+j*xy[i%4][1];
+      int element;
 
-    DrawGraphic(SCROLLX(x),SCROLLY(y),GFX_EXPLOSION+(phase/delay-1));
+      if (!IN_LEV_FIELD(x, y) || IS_MASSIV(Feld[x][y]))
+       break;
+
+      element = Feld[x][y];
+      Explode(x, y, EX_PHASE_START, EX_BORDER);
+
+      if (element != EL_LEERRAUM &&
+         element != EL_ERDREICH &&
+         element != EL_EXPLODING &&
+         !player->dynabomb_xl)
+       break;
+    }
   }
 
-  CheckExploding=TRUE;
+  player->dynabombs_left++;
 }
 
 void Bang(int x, int y)
 {
   int element = Feld[x][y];
 
-  CheckExploding=TRUE;
-  PlaySoundLevel(x,y,SND_ROAAAR);
+  PlaySoundLevel(x, y, SND_ROAAAR);
+
+  if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
+    element = EL_LEERRAUM;
 
   switch(element)
   {
     case EL_KAEFER:
-      RaiseScore(level.score[SC_KAEFER]);
-      break;
     case EL_FLIEGER:
-      RaiseScore(level.score[SC_FLIEGER]);
-      break;
+    case EL_BUTTERFLY:
+    case EL_FIREFLY:
     case EL_MAMPFER:
-      RaiseScore(level.score[SC_MAMPFER]);
+    case EL_MAMPFER2:
+    case EL_ROBOT:
+    case EL_PACMAN:
+      RaiseScoreElement(element);
+      Explode(x, y, EX_PHASE_START, EX_NORMAL);
       break;
-    case EL_ZOMBIE:
-      RaiseScore(level.score[SC_ZOMBIE]);
+    case EL_DYNABOMB:
+    case EL_DYNABOMB_NR:
+    case EL_DYNABOMB_SZ:
+    case EL_DYNABOMB_XL:
+      DynaExplode(x, y);
       break;
-    case EL_PACMAN:
-      RaiseScore(level.score[SC_PACMAN]);
+    case EL_MAULWURF:
+    case EL_PINGUIN:
+    case EL_BIRNE_AUS:
+    case EL_BIRNE_EIN:
+      Explode(x, y, EX_PHASE_START, EX_CENTER);
       break;
     default:
+      Explode(x, y, EX_PHASE_START, EX_NORMAL);
       break;
   }
-
-  Explode(x,y,0);
 }
 
 void Blurb(int x, int y)
 {
   int element = Feld[x][y];
 
-  if (element!=EL_BLURB_LEFT && element!=EL_BLURB_RIGHT) /* Anfang */
+  if (element != EL_BLURB_LEFT && element != EL_BLURB_RIGHT)   /* start */
   {
-    PlaySoundLevel(x,y,SND_BLURB);
-    if (IN_LEV_FIELD(x-1,y) && IS_FREE(x-1,y) &&
-       (!IN_LEV_FIELD(x-1,y-1) ||
-        !CAN_FALL(MovingOrBlocked2Element(x-1,y-1))))
+    PlaySoundLevel(x, y, SND_BLURB);
+    if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
+       (!IN_LEV_FIELD(x-1, y-1) ||
+        !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
     {
       Feld[x-1][y] = EL_BLURB_LEFT;
     }
-    if (IN_LEV_FIELD(x+1,y) && IS_FREE(x+1,y) &&
-       (!IN_LEV_FIELD(x+1,y-1) ||
-        !CAN_FALL(MovingOrBlocked2Element(x+1,y-1))))
+    if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
+       (!IN_LEV_FIELD(x+1, y-1) ||
+        !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
     {
       Feld[x+1][y] = EL_BLURB_RIGHT;
     }
   }
-  else                                                  /* Blubbern */
+  else                                                         /* go on */
   {
     int graphic = (element==EL_BLURB_LEFT ? GFX_BLURB_LEFT : GFX_BLURB_RIGHT);
 
-    CheckExploding=TRUE;
-
-    if (!MovDelay[x][y])       /* neue Phase / noch nicht gewartet */
-      MovDelay[x][y] = 5;
+    if (!MovDelay[x][y])       /* initialize animation counter */
+      MovDelay[x][y] = 9;
 
-    if (MovDelay[x][y])                /* neue Phase / in Wartezustand */
+    if (MovDelay[x][y])                /* continue animation */
     {
       MovDelay[x][y]--;
-      if (MovDelay[x][y] && IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-       DrawGraphic(SCROLLX(x),SCROLLY(y),graphic+4-MovDelay[x][y]);
+      if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+       DrawGraphic(SCREENX(x), SCREENY(y), graphic+4-MovDelay[x][y]/2);
 
       if (!MovDelay[x][y])
       {
        Feld[x][y] = EL_LEERRAUM;
-       DrawLevelField(x,y);
+       DrawLevelField(x, y);
       }
     }
   }
@@ -710,35 +1171,41 @@ void Blurb(int x, int y)
 
 void Impact(int x, int y)
 {
-  BOOL lastline = (y==lev_fieldy-1);
-  BOOL object_hit = FALSE;
+  boolean lastline = (y == lev_fieldy-1);
+  boolean object_hit = FALSE;
   int element = Feld[x][y];
+  int smashed = 0;
+
+  if (!lastline)       /* check if element below was hit */
+  {
+    if (Feld[x][y+1] == EL_PLAYER_IS_LEAVING)
+      return;
 
-  /* Element darunter berührt? */
-  if (!lastline)
-    object_hit = (!IS_FREE(x,y+1) && (!IS_MOVING(x,y+1) ||
+    object_hit = (!IS_FREE(x, y+1) && (!IS_MOVING(x, y+1) ||
                                      MovDir[x][y+1]!=MV_DOWN ||
                                      MovPos[x][y+1]<=TILEY/2));
+    if (object_hit)
+      smashed = MovingOrBlocked2Element(x, y+1);
+  }
 
-  /* Auftreffendes Element fällt in Salzsäure */
-  if (!lastline && Feld[x][y+1]==EL_SALZSAEURE)
+  if (!lastline && smashed == EL_SALZSAEURE)   /* element falls into acid */
   {
-    Blurb(x,y);
+    Blurb(x, y);
     return;
   }
 
-  /* Auftreffendes Element ist Bombe */
-  if (element==EL_BOMBE && (lastline || object_hit))
+  if (element == EL_BOMBE && (lastline || object_hit)) /* element is bomb */
   {
-    Bang(x,y);
+    Bang(x, y);
     return;
   }
 
-  /* Auftreffendes Element ist Säuretropfen */
-  if (element==EL_TROPFEN && (lastline || object_hit))
+  if (element == EL_TROPFEN && (lastline || object_hit))       /* acid drop */
   {
-    if (object_hit && PLAYER(x,y+1))
-      KillHero();
+    if (object_hit && IS_PLAYER(x, y+1))
+      KillHero(PLAYERINFO(x, y+1));
+    else if (object_hit && (smashed == EL_MAULWURF || smashed == EL_PINGUIN))
+      Bang(x, y+1);
     else
     {
       Feld[x][y] = EL_AMOEBING;
@@ -747,52 +1214,71 @@ void Impact(int x, int y)
     return;
   }
 
-  /* Welches Element kriegt was auf die Rübe? */
-  if (!lastline && object_hit)
+  if (!lastline && object_hit)         /* check which object was hit */
   {
-    int smashed = Feld[x][y+1];
+    if (CAN_CHANGE(element) && 
+       (smashed == EL_SIEB_LEER || smashed == EL_SIEB2_LEER) && !SiebAktiv)
+      SiebAktiv = level.dauer_sieb * FRAMES_PER_SECOND;
 
-    if (PLAYER(x,y+1))
+    if (IS_PLAYER(x, y+1))
+    {
+      KillHero(PLAYERINFO(x, y+1));
+      return;
+    }
+    else if (smashed == EL_MAULWURF || smashed == EL_PINGUIN)
     {
-      KillHero();
+      Bang(x, y+1);
       return;
     }
-    else if (element==EL_FELSBROCKEN)
+    else if (element == EL_EDELSTEIN_BD)
+    {
+      if (IS_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
+      {
+       Bang(x, y+1);
+       return;
+      }
+    }
+    else if (element == EL_FELSBROCKEN)
     {
-      if (IS_ENEMY(MovingOrBlocked2Element(x,y+1)))
+      if (IS_ENEMY(smashed) || smashed == EL_BOMBE || smashed == EL_SONDE ||
+         smashed == EL_SCHWEIN || smashed == EL_DRACHE)
       {
-       Bang(x,y+1);
+       Bang(x, y+1);
        return;
       }
-      else if (!IS_MOVING(x,y+1))
+      else if (!IS_MOVING(x, y+1))
       {
-       if (smashed==EL_BOMBE)
+       if (smashed == EL_BIRNE_AUS || smashed == EL_BIRNE_EIN)
        {
-         Bang(x,y+1);
+         Bang(x, y+1);
          return;
        }
-       else if (smashed==EL_KOKOSNUSS)
+       else if (smashed == EL_KOKOSNUSS)
        {
          Feld[x][y+1] = EL_CRACKINGNUT;
-         PlaySoundLevel(x,y,SND_KNACK);
-         RaiseScore(level.score[SC_KOKOSNUSS]);
+         PlaySoundLevel(x, y, SND_KNACK);
+         RaiseScoreElement(EL_KOKOSNUSS);
          return;
        }
-       else if (smashed==EL_DIAMANT)
+       else if (smashed == EL_DIAMANT)
        {
          Feld[x][y+1] = EL_LEERRAUM;
-         PlaySoundLevel(x,y,SND_QUIRK);
+         PlaySoundLevel(x, y, SND_QUIRK);
          return;
        }
       }
     }
   }
 
-  /* Kein Geräusch beim Durchqueren des Siebes */
-  if (!lastline && Feld[x][y+1]==EL_SIEB_LEER)
+  /* play sound of magic wall / mill */
+  if (!lastline &&
+      (Feld[x][y+1] == EL_SIEB_LEER || Feld[x][y+1] == EL_SIEB2_LEER))
+  {
+    PlaySoundLevel(x, y, SND_QUIRK);
     return;
+  }
 
-  /* Geräusch beim Auftreffen */
+  /* play sound of object that hits the ground */
   if (lastline || object_hit)
   {
     int sound;
@@ -800,6 +1286,10 @@ void Impact(int x, int y)
     switch(element)
     {
       case EL_EDELSTEIN:
+      case EL_EDELSTEIN_BD:
+      case EL_EDELSTEIN_GELB:
+      case EL_EDELSTEIN_ROT:
+      case EL_EDELSTEIN_LILA:
       case EL_DIAMANT:
         sound = SND_PLING;
        break;
@@ -826,169 +1316,405 @@ void Impact(int x, int y)
     }
 
     if (sound>=0)
-      PlaySoundLevel(x,y,sound);
+      PlaySoundLevel(x, y, sound);
   }
 }
 
 void TurnRound(int x, int y)
 {
+  static struct
+  {
+    int x, y;
+  } move_xy[] =
+  {
+    { 0, 0 },
+    {-1, 0 },
+    {+1, 0 },
+    { 0, 0 },
+    { 0, -1 },
+    { 0, 0 }, { 0, 0 }, { 0, 0 },
+    { 0, +1 }
+  };
+  static struct
+  {
+    int left, right, back;
+  } turn[] =
+  {
+    { 0,       0,              0 },
+    { MV_DOWN, MV_UP,          MV_RIGHT },
+    { MV_UP,   MV_DOWN,        MV_LEFT },
+    { 0,       0,              0 },
+    { MV_LEFT, MV_RIGHT,       MV_DOWN },
+    { 0,0,0 }, { 0,0,0 },      { 0,0,0 },
+    { MV_RIGHT,        MV_LEFT,        MV_UP }
+  };
+
   int element = Feld[x][y];
-  int direction = MovDir[x][y];
+  int old_move_dir = MovDir[x][y];
+  int left_dir = turn[old_move_dir].left;
+  int right_dir = turn[old_move_dir].right;
+  int back_dir = turn[old_move_dir].back;
 
-  if (element==EL_KAEFER)
+  int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
+  int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
+  int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
+  int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
+
+  int left_x = x+left_dx, left_y = y+left_dy;
+  int right_x = x+right_dx, right_y = y+right_dy;
+  int move_x = x+move_dx, move_y = y+move_dy;
+
+  if (element == EL_KAEFER || element == EL_BUTTERFLY)
+  {
+    TestIfBadThingHitsOtherBadThing(x, y);
+
+    if (IN_LEV_FIELD(right_x, right_y) &&
+       IS_FREE_OR_PLAYER(right_x, right_y))
+      MovDir[x][y] = right_dir;
+    else if (!IN_LEV_FIELD(move_x, move_y) ||
+            !IS_FREE_OR_PLAYER(move_x, move_y))
+      MovDir[x][y] = left_dir;
+
+    if (element == EL_KAEFER && MovDir[x][y] != old_move_dir)
+      MovDelay[x][y] = 9;
+    else if (element == EL_BUTTERFLY)  /* && MovDir[x][y] == left_dir) */
+      MovDelay[x][y] = 1;
+  }
+  else if (element == EL_FLIEGER || element == EL_FIREFLY)
   {
-    TestIfBadThingHitsOtherBadThing(x,y);
+    TestIfBadThingHitsOtherBadThing(x, y);
+
+    if (IN_LEV_FIELD(left_x, left_y) &&
+       IS_FREE_OR_PLAYER(left_x, left_y))
+      MovDir[x][y] = left_dir;
+    else if (!IN_LEV_FIELD(move_x, move_y) ||
+            !IS_FREE_OR_PLAYER(move_x, move_y))
+      MovDir[x][y] = right_dir;
+
+    if (element == EL_FLIEGER && MovDir[x][y] != old_move_dir)
+      MovDelay[x][y] = 9;
+    else if (element == EL_FIREFLY)    /* && MovDir[x][y] == right_dir) */
+      MovDelay[x][y] = 1;
+  }
+  else if (element == EL_MAMPFER)
+  {
+    boolean can_turn_left = FALSE, can_turn_right = FALSE;
+
+    if (IN_LEV_FIELD(left_x, left_y) &&
+       (IS_FREE_OR_PLAYER(left_x, left_y) ||
+        Feld[left_x][left_y] == EL_DIAMANT))
+      can_turn_left = TRUE;
+    if (IN_LEV_FIELD(right_x, right_y) &&
+       (IS_FREE_OR_PLAYER(right_x, right_y) ||
+        Feld[right_x][right_y] == EL_DIAMANT))
+      can_turn_right = TRUE;
+
+    if (can_turn_left && can_turn_right)
+      MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
+    else if (can_turn_left)
+      MovDir[x][y] = (RND(2) ? left_dir : back_dir);
+    else if (can_turn_right)
+      MovDir[x][y] = (RND(2) ? right_dir : back_dir);
+    else
+      MovDir[x][y] = back_dir;
 
-    if (MovDir[x][y]==MV_LEFT)
-    {
-      if (IN_LEV_FIELD(x,y-1) && IS_FREE(x,y-1))
-       MovDir[x][y]=MV_UP;
-      else if (!IN_LEV_FIELD(x-1,y) || !IS_FREE(x-1,y))
-       MovDir[x][y]=MV_DOWN;
-    }
-    else if (MovDir[x][y]==MV_RIGHT)
-    {
-      if (IN_LEV_FIELD(x,y+1) && IS_FREE(x,y+1))
-       MovDir[x][y]=MV_DOWN;
-      else if (!IN_LEV_FIELD(x+1,y) || !IS_FREE(x+1,y))
-       MovDir[x][y]=MV_UP;
-    }
-    else if (MovDir[x][y]==MV_UP)
-    {
-      if (IN_LEV_FIELD(x+1,y) && IS_FREE(x+1,y))
-       MovDir[x][y]=MV_RIGHT;
-      else if (!IN_LEV_FIELD(x,y-1) || !IS_FREE(x,y-1))
-       MovDir[x][y]=MV_LEFT;
-    }
-    else if (MovDir[x][y]==MV_DOWN)
-    {
-      if (IN_LEV_FIELD(x-1,y) && IS_FREE(x-1,y))
-       MovDir[x][y]=MV_LEFT;
-      else if (!IN_LEV_FIELD(x,y+1) || !IS_FREE(x,y+1))
-       MovDir[x][y]=MV_RIGHT;
-    }
+    MovDelay[x][y] = 16+16*RND(3);
+  }
+  else if (element == EL_MAMPFER2)
+  {
+    boolean can_turn_left = FALSE, can_turn_right = FALSE;
+
+    if (IN_LEV_FIELD(left_x, left_y) &&
+       (IS_FREE_OR_PLAYER(left_x, left_y) ||
+        IS_MAMPF2(Feld[left_x][left_y])))
+      can_turn_left = TRUE;
+    if (IN_LEV_FIELD(right_x, right_y) &&
+       (IS_FREE_OR_PLAYER(right_x, right_y) ||
+        IS_MAMPF2(Feld[right_x][right_y])))
+      can_turn_right = TRUE;
+
+    if (can_turn_left && can_turn_right)
+      MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
+    else if (can_turn_left)
+      MovDir[x][y] = (RND(2) ? left_dir : back_dir);
+    else if (can_turn_right)
+      MovDir[x][y] = (RND(2) ? right_dir : back_dir);
+    else
+      MovDir[x][y] = back_dir;
 
-    if (direction!=MovDir[x][y])
-      MovDelay[x][y]=5;
+    MovDelay[x][y] = 16+16*RND(3);
   }
-  else if (element==EL_FLIEGER)
+  else if (element == EL_PACMAN)
   {
-    TestIfBadThingHitsOtherBadThing(x,y);
+    boolean can_turn_left = FALSE, can_turn_right = FALSE;
+
+    if (IN_LEV_FIELD(left_x, left_y) &&
+       (IS_FREE_OR_PLAYER(left_x, left_y) ||
+        IS_AMOEBOID(Feld[left_x][left_y])))
+      can_turn_left = TRUE;
+    if (IN_LEV_FIELD(right_x, right_y) &&
+       (IS_FREE_OR_PLAYER(right_x, right_y) ||
+        IS_AMOEBOID(Feld[right_x][right_y])))
+      can_turn_right = TRUE;
+
+    if (can_turn_left && can_turn_right)
+      MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
+    else if (can_turn_left)
+      MovDir[x][y] = (RND(2) ? left_dir : back_dir);
+    else if (can_turn_right)
+      MovDir[x][y] = (RND(2) ? right_dir : back_dir);
+    else
+      MovDir[x][y] = back_dir;
 
-    if (MovDir[x][y]==MV_LEFT)
-    {
-      if (IN_LEV_FIELD(x,y+1) && IS_FREE(x,y+1))
-       MovDir[x][y]=MV_DOWN;
-      else if (!IN_LEV_FIELD(x-1,y) || !IS_FREE(x-1,y))
-       MovDir[x][y]=MV_UP;
-    }
-    else if (MovDir[x][y]==MV_RIGHT)
-    {
-      if (IN_LEV_FIELD(x,y-1) && IS_FREE(x,y-1))
-       MovDir[x][y]=MV_UP;
-      else if (!IN_LEV_FIELD(x+1,y) || !IS_FREE(x+1,y))
-       MovDir[x][y]=MV_DOWN;
-    }
-    else if (MovDir[x][y]==MV_UP)
-    {
-      if (IN_LEV_FIELD(x-1,y) && IS_FREE(x-1,y))
-       MovDir[x][y]=MV_LEFT;
-      else if (!IN_LEV_FIELD(x,y-1) || !IS_FREE(x,y-1))
-       MovDir[x][y]=MV_RIGHT;
-    }
-    else if (MovDir[x][y]==MV_DOWN)
+    MovDelay[x][y] = 6+RND(40);
+  }
+  else if (element == EL_SCHWEIN)
+  {
+    boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
+    boolean should_turn_left = FALSE, should_turn_right = FALSE;
+    boolean should_move_on = FALSE;
+    int rnd_value = 24;
+    int rnd = RND(rnd_value);
+
+    if (IN_LEV_FIELD(left_x, left_y) &&
+       (IS_FREE(left_x, left_y) || IS_GEM(Feld[left_x][left_y])))
+      can_turn_left = TRUE;
+    if (IN_LEV_FIELD(right_x, right_y) &&
+       (IS_FREE(right_x, right_y) || IS_GEM(Feld[right_x][right_y])))
+      can_turn_right = TRUE;
+    if (IN_LEV_FIELD(move_x, move_y) &&
+       (IS_FREE(move_x, move_y) || IS_GEM(Feld[move_x][move_y])))
+      can_move_on = TRUE;
+
+    if (can_turn_left &&
+       (!can_move_on ||
+        (IN_LEV_FIELD(x+back_dx+left_dx, y+back_dy+left_dy) &&
+         !IS_FREE(x+back_dx+left_dx, y+back_dy+left_dy))))
+      should_turn_left = TRUE;
+    if (can_turn_right &&
+       (!can_move_on ||
+        (IN_LEV_FIELD(x+back_dx+right_dx, y+back_dy+right_dy) &&
+         !IS_FREE(x+back_dx+right_dx, y+back_dy+right_dy))))
+      should_turn_right = TRUE;
+    if (can_move_on &&
+       (!can_turn_left || !can_turn_right ||
+        (IN_LEV_FIELD(x+move_dx+left_dx, y+move_dy+left_dy) &&
+         !IS_FREE(x+move_dx+left_dx, y+move_dy+left_dy)) ||
+        (IN_LEV_FIELD(x+move_dx+right_dx, y+move_dy+right_dy) &&
+         !IS_FREE(x+move_dx+right_dx, y+move_dy+right_dy))))
+      should_move_on = TRUE;
+
+    if (should_turn_left || should_turn_right || should_move_on)
     {
-      if (IN_LEV_FIELD(x+1,y) && IS_FREE(x+1,y))
-       MovDir[x][y]=MV_RIGHT;
-      else if (!IN_LEV_FIELD(x,y+1) || !IS_FREE(x,y+1))
-       MovDir[x][y]=MV_LEFT;
+      if (should_turn_left && should_turn_right && should_move_on)
+       MovDir[x][y] = (rnd < rnd_value/3 ? left_dir :
+                       rnd < 2*rnd_value/3 ? right_dir :
+                       old_move_dir);
+      else if (should_turn_left && should_turn_right)
+       MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
+      else if (should_turn_left && should_move_on)
+       MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : old_move_dir);
+      else if (should_turn_right && should_move_on)
+       MovDir[x][y] = (rnd < rnd_value/2 ? right_dir : old_move_dir);
+      else if (should_turn_left)
+       MovDir[x][y] = left_dir;
+      else if (should_turn_right)
+       MovDir[x][y] = right_dir;
+      else if (should_move_on)
+       MovDir[x][y] = old_move_dir;
     }
+    else if (can_move_on && rnd > rnd_value/8)
+      MovDir[x][y] = old_move_dir;
+    else if (can_turn_left && can_turn_right)
+      MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
+    else if (can_turn_left && rnd > rnd_value/8)
+      MovDir[x][y] = left_dir;
+    else if (can_turn_right && rnd > rnd_value/8)
+      MovDir[x][y] = right_dir;
+    else
+      MovDir[x][y] = back_dir;
 
-    if (direction!=MovDir[x][y])
-      MovDelay[x][y]=5;
+    if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y) &&
+       !IS_GEM(Feld[x+move_xy[MovDir[x][y]].x][y+move_xy[MovDir[x][y]].y]))
+      MovDir[x][y] = old_move_dir;
+
+    MovDelay[x][y] = 0;
   }
-  else if (element==EL_MAMPFER)
+  else if (element == EL_DRACHE)
   {
-    if (MovDir[x][y]==MV_LEFT || MovDir[x][y]==MV_RIGHT)
-    {
-      MovDir[x][y]=(MovDir[x][y]==MV_LEFT ? MV_RIGHT : MV_LEFT);
-      if (IN_LEV_FIELD(x,y-1) &&
-         (IS_FREE(x,y-1) || Feld[x][y-1]==EL_DIAMANT) &&
-         RND(2))
-       MovDir[x][y]=MV_UP;
-      if (IN_LEV_FIELD(x,y+1) &&
-         (IS_FREE(x,y+1) || Feld[x][y+1]==EL_DIAMANT) &&
-         RND(2))
-       MovDir[x][y]=MV_DOWN;
-    }
-    else if (MovDir[x][y]==MV_UP || MovDir[x][y]==MV_DOWN)
-    {
-      MovDir[x][y]=(MovDir[x][y]==MV_UP ? MV_DOWN : MV_UP);
-      if (IN_LEV_FIELD(x-1,y) &&
-         (IS_FREE(x-1,y) || Feld[x-1][y]==EL_DIAMANT) &&
-         RND(2))
-       MovDir[x][y]=MV_LEFT;
-      if (IN_LEV_FIELD(x+1,y) &&
-         (IS_FREE(x+1,y) || Feld[x+1][y]==EL_DIAMANT) &&
-         RND(2))
-       MovDir[x][y]=MV_RIGHT;
-    }
+    boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
+    int rnd_value = 24;
+    int rnd = RND(rnd_value);
+
+    if (IN_LEV_FIELD(left_x, left_y) && IS_FREE(left_x, left_y))
+      can_turn_left = TRUE;
+    if (IN_LEV_FIELD(right_x, right_y) && IS_FREE(right_x, right_y))
+      can_turn_right = TRUE;
+    if (IN_LEV_FIELD(move_x, move_y) && IS_FREE(move_x, move_y))
+      can_move_on = TRUE;
+
+    if (can_move_on && rnd > rnd_value/8)
+      MovDir[x][y] = old_move_dir;
+    else if (can_turn_left && can_turn_right)
+      MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
+    else if (can_turn_left && rnd > rnd_value/8)
+      MovDir[x][y] = left_dir;
+    else if (can_turn_right && rnd > rnd_value/8)
+      MovDir[x][y] = right_dir;
+    else
+      MovDir[x][y] = back_dir;
 
-    MovDelay[x][y]=8+8*RND(3);
+    if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y))
+      MovDir[x][y] = old_move_dir;
+
+    MovDelay[x][y] = 0;
   }
-  else if (element==EL_PACMAN)
+  else if (element == EL_ROBOT || element == EL_SONDE ||
+          element == EL_MAULWURF || element == EL_PINGUIN)
   {
-    if (MovDir[x][y]==MV_LEFT || MovDir[x][y]==MV_RIGHT)
+    int attr_x = -1, attr_y = -1;
+
+    if (AllPlayersGone)
     {
-      MovDir[x][y]=(MovDir[x][y]==MV_LEFT ? MV_RIGHT : MV_LEFT);
-      if (IN_LEV_FIELD(x,y-1) &&
-         (IS_FREE(x,y-1) || IS_AMOEBOID(Feld[x][y-1])) &&
-         RND(2))
-       MovDir[x][y]=MV_UP;
-      if (IN_LEV_FIELD(x,y+1) &&
-         (IS_FREE(x,y+1) || IS_AMOEBOID(Feld[x][y+1])) &&
-         RND(2))
-       MovDir[x][y]=MV_DOWN;
+      attr_x = ExitX;
+      attr_y = ExitY;
     }
-    else if (MovDir[x][y]==MV_UP || MovDir[x][y]==MV_DOWN)
+    else
     {
-      MovDir[x][y]=(MovDir[x][y]==MV_UP ? MV_DOWN : MV_UP);
-      if (IN_LEV_FIELD(x-1,y) &&
-         (IS_FREE(x-1,y) || IS_AMOEBOID(Feld[x-1][y])) &&
-         RND(2))
-       MovDir[x][y]=MV_LEFT;
-      if (IN_LEV_FIELD(x+1,y) &&
-         (IS_FREE(x+1,y) || IS_AMOEBOID(Feld[x+1][y])) &&
-         RND(2))
-       MovDir[x][y]=MV_RIGHT;
-    }
+      int i;
 
-    MovDelay[x][y]=3+RND(20);
-  }
-  else if (element==EL_ZOMBIE)
-  {
-    int attr_x = JX, attr_y = JY;
+      for (i=0; i<MAX_PLAYERS; i++)
+      {
+       struct PlayerInfo *player = &stored_player[i];
+       int jx = player->jx, jy = player->jy;
+
+       if (!player->active || player->gone)
+         continue;
+
+       if (attr_x == -1 || ABS(jx-x)+ABS(jy-y) < ABS(attr_x-x)+ABS(attr_y-y))
+       {
+         attr_x = jx;
+         attr_y = jy;
+       }
+      }
+    }
 
-    if (ZX>=0 && ZY>=0)
+    if (element == EL_ROBOT && ZX>=0 && ZY>=0)
     {
       attr_x = ZX;
       attr_y = ZY;
     }
 
-    MovDir[x][y]=MV_NO_MOVING;
-    if (attr_x<x)
-      MovDir[x][y]|=MV_LEFT;
+    if (element == EL_MAULWURF || element == EL_PINGUIN)
+    {
+      int i;
+      static int xy[4][2] =
+      {
+       { 0, -1 },
+       { -1, 0 },
+       { +1, 0 },
+       { 0, +1 }
+      };
+
+      for (i=0; i<4; i++)
+      {
+       int ex = x + xy[i%4][0];
+       int ey = y + xy[i%4][1];
+
+       if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_AUSGANG_AUF)
+       {
+         attr_x = ex;
+         attr_y = ey;
+         break;
+       }
+      }
+    }
+
+    MovDir[x][y] = MV_NO_MOVING;
+    if (attr_x<x)
+      MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
     else if (attr_x>x)
-      MovDir[x][y]|=MV_RIGHT;
+      MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
     if (attr_y<y)
-      MovDir[x][y]|=MV_UP;
+      MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
     else if (attr_y>y)
-      MovDir[x][y]|=MV_DOWN;
-    if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
-      MovDir[x][y] &= (RND(2) ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
+      MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
+
+    if (element == EL_ROBOT)
+    {
+      int newx, newy;
+
+      if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
+       MovDir[x][y] &= (RND(2) ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
+      Moving2Blocked(x, y, &newx, &newy);
+
+      if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
+       MovDelay[x][y] = 8+8*!RND(3);
+      else
+       MovDelay[x][y] = 16;
+    }
+    else
+    {
+      int newx, newy;
+
+      MovDelay[x][y] = 1;
+
+      if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
+      {
+       boolean first_horiz = RND(2);
+       int new_move_dir = MovDir[x][y];
+
+       MovDir[x][y] =
+         new_move_dir & (first_horiz ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
+       Moving2Blocked(x, y, &newx, &newy);
+
+       if (IN_LEV_FIELD(newx, newy) &&
+           (IS_FREE(newx, newy) ||
+            Feld[newx][newy] == EL_SALZSAEURE ||
+            ((element == EL_MAULWURF || element == EL_PINGUIN) &&
+             (Feld[newx][newy] == EL_AUSGANG_AUF ||
+              IS_MAMPF3(Feld[newx][newy])))))
+         return;
+
+       MovDir[x][y] =
+         new_move_dir & (!first_horiz ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
+       Moving2Blocked(x, y, &newx, &newy);
+
+       if (IN_LEV_FIELD(newx, newy) &&
+           (IS_FREE(newx, newy) ||
+            Feld[newx][newy] == EL_SALZSAEURE ||
+            ((element == EL_MAULWURF || element == EL_PINGUIN) &&
+             (Feld[newx][newy] == EL_AUSGANG_AUF ||
+              IS_MAMPF3(Feld[newx][newy])))))
+         return;
+
+       MovDir[x][y] = old_move_dir;
+       return;
+      }
+    }
+  }
+}
 
-    MovDelay[x][y] = 8+8*RND(2);
+static boolean JustBeingPushed(int x, int y)
+{
+  int i;
+
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+    struct PlayerInfo *player = &stored_player[i];
+
+    if (player->active && !player->gone &&
+       player->Pushing && player->MovPos)
+    {
+      int next_jx = player->jx + (player->jx - player->last_jx);
+      int next_jy = player->jy + (player->jy - player->last_jy);
+
+      if (x == next_jx && y == next_jy)
+       return TRUE;
+    }
   }
+
+  return FALSE;
 }
 
 void StartMoving(int x, int y)
@@ -1000,20 +1726,22 @@ void StartMoving(int x, int y)
 
   if (CAN_FALL(element) && y<lev_fieldy-1)
   {
-    if (element==EL_MORAST_VOLL)
+    if ((x>0 && IS_PLAYER(x-1, y)) || (x<lev_fieldx-1 && IS_PLAYER(x+1, y)))
+      if (JustBeingPushed(x, y))
+       return;
+
+    if (element == EL_MORAST_VOLL)
     {
-      if (IS_FREE(x,y+1))
+      if (IS_FREE(x, y+1))
       {
-       InitMovingField(x,y,MV_DOWN);
+       InitMovingField(x, y, MV_DOWN);
        Feld[x][y] = EL_FELSBROCKEN;
        Store[x][y] = EL_MORAST_LEER;
       }
-      else if (Feld[x][y+1]==EL_MORAST_LEER)
+      else if (Feld[x][y+1] == EL_MORAST_LEER)
       {
-       CheckMoving=TRUE;
-
        if (!MovDelay[x][y])
-         MovDelay[x][y] = 16;
+         MovDelay[x][y] = TILEY + 1;
 
        if (MovDelay[x][y])
        {
@@ -1026,243 +1754,480 @@ void StartMoving(int x, int y)
        Feld[x][y+1] = EL_MORAST_VOLL;
       }
     }
-    else if (element==EL_FELSBROCKEN && Feld[x][y+1]==EL_MORAST_LEER)
+    else if (element == EL_FELSBROCKEN && Feld[x][y+1] == EL_MORAST_LEER)
     {
-      InitMovingField(x,y,MV_DOWN);
+      InitMovingField(x, y, MV_DOWN);
       Store[x][y] = EL_MORAST_VOLL;
     }
-    else if (element==EL_SIEB_VOLL)
+    else if (element == EL_SIEB_VOLL)
     {
-      if (IS_FREE(x,y+1))
+      if (IS_FREE(x, y+1))
       {
-       InitMovingField(x,y,MV_DOWN);
+       InitMovingField(x, y, MV_DOWN);
        Feld[x][y] = EL_CHANGED(Store2[x][y]);
        Store[x][y] = EL_SIEB_LEER;
       }
+      else if (Feld[x][y+1] == EL_SIEB_LEER)
+      {
+       if (!MovDelay[x][y])
+         MovDelay[x][y] = TILEY/4 + 1;
+
+       if (MovDelay[x][y])
+       {
+         MovDelay[x][y]--;
+         if (MovDelay[x][y])
+           return;
+       }
+
+       Feld[x][y] = EL_SIEB_LEER;
+       Feld[x][y+1] = EL_SIEB_VOLL;
+       Store2[x][y+1] = EL_CHANGED(Store2[x][y]);
+       Store2[x][y] = 0;
+      }
+    }
+    else if (element == EL_SIEB2_VOLL)
+    {
+      if (IS_FREE(x, y+1))
+      {
+       InitMovingField(x, y, MV_DOWN);
+       Feld[x][y] = EL_CHANGED2(Store2[x][y]);
+       Store[x][y] = EL_SIEB2_LEER;
+      }
+      else if (Feld[x][y+1] == EL_SIEB2_LEER)
+      {
+       if (!MovDelay[x][y])
+         MovDelay[x][y] = TILEY/4 + 1;
+
+       if (MovDelay[x][y])
+       {
+         MovDelay[x][y]--;
+         if (MovDelay[x][y])
+           return;
+       }
+
+       Feld[x][y] = EL_SIEB2_LEER;
+       Feld[x][y+1] = EL_SIEB2_VOLL;
+       Store2[x][y+1] = EL_CHANGED2(Store2[x][y]);
+       Store2[x][y] = 0;
+      }
     }
-    else if (CAN_CHANGE(element) && Feld[x][y+1]==EL_SIEB_LEER)
+    else if (SiebAktiv && CAN_CHANGE(element) &&
+            (Feld[x][y+1] == EL_SIEB_LEER || Feld[x][y+1] == EL_SIEB2_LEER))
     {
-      InitMovingField(x,y,MV_DOWN);
-      Store[x][y] = EL_SIEB_VOLL;
+      InitMovingField(x, y, MV_DOWN);
+      Store[x][y] =
+       (Feld[x][y+1] == EL_SIEB_LEER ? EL_SIEB_VOLL : EL_SIEB2_VOLL);
       Store2[x][y+1] = element;
-      SiebAktiv = 330;
     }
-    else if (CAN_SMASH(element) && Feld[x][y+1]==EL_SALZSAEURE)
+    else if (CAN_SMASH(element) && Feld[x][y+1] == EL_SALZSAEURE)
     {
-      Blurb(x,y);
-      InitMovingField(x,y,MV_DOWN);
+      Blurb(x, y);
+      InitMovingField(x, y, MV_DOWN);
       Store[x][y] = EL_SALZSAEURE;
     }
-    else if (CAN_SMASH(element) && Feld[x][y+1]==EL_BLOCKED)
+    else if (CAN_SMASH(element) && Feld[x][y+1] == EL_BLOCKED && JustHit[x][y])
     {
-      Impact(x,y);
+      Impact(x, y);
     }
-    else if (IS_FREE(x,y+1))
+    else if (IS_FREE(x, y+1))
     {
-      InitMovingField(x,y,MV_DOWN);
+      InitMovingField(x, y, MV_DOWN);
     }
-    else if (element==EL_TROPFEN)
+    else if (element == EL_TROPFEN)
     {
       Feld[x][y] = EL_AMOEBING;
       Store[x][y] = EL_AMOEBE_NASS;
     }
     else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1])
     {
-      int left  = (x>0 && IS_FREE(x-1,y) &&
-                  (IS_FREE(x-1,y+1) || Feld[x-1][y+1]==EL_SALZSAEURE));
-      int right = (x<lev_fieldx-1 && IS_FREE(x+1,y) &&
-                  (IS_FREE(x+1,y+1) || Feld[x+1][y+1]==EL_SALZSAEURE));
+      boolean left  = (x>0 && IS_FREE(x-1, y) &&
+                      (IS_FREE(x-1, y+1) || Feld[x-1][y+1] == EL_SALZSAEURE));
+      boolean right = (x<lev_fieldx-1 && IS_FREE(x+1, y) &&
+                      (IS_FREE(x+1, y+1) || Feld[x+1][y+1] == EL_SALZSAEURE));
 
       if (left || right)
       {
-       if (left && right)
-         left = !(right=RND(2));
-       InitMovingField(x,y,left ? MV_LEFT : MV_RIGHT);
+       if (left && right && game_emulation != EMU_BOULDERDASH)
+         left = !(right = RND(2));
+
+       InitMovingField(x, y, left ? MV_LEFT : MV_RIGHT);
       }
     }
   }
   else if (CAN_MOVE(element))
   {
-    int newx,newy;
+    int newx, newy;
 
-    CheckMoving = TRUE;
+    if (element == EL_SONDE && JustBeingPushed(x, y))
+      return;
 
-    if (!MovDelay[x][y])       /* neuer Schritt / noch nicht gewartet */
+    if (!MovDelay[x][y])       /* start new movement phase */
     {
-      if (element==EL_ZOMBIE || element==EL_KAEFER || element==EL_FLIEGER)
+      /* all objects that can change their move direction after each step */
+      /* (MAMPFER, MAMPFER2 and PACMAN go straight until they hit a wall  */
+
+      if (element!=EL_MAMPFER && element!=EL_MAMPFER2 && element!=EL_PACMAN)
       {
-       TurnRound(x,y);
-       if (MovDelay[x][y] && (element==EL_KAEFER || element==EL_FLIEGER))
-         DrawLevelField(x,y);
+       TurnRound(x, y);
+       if (MovDelay[x][y] && (element == EL_KAEFER || element == EL_FLIEGER))
+         DrawLevelField(x, y);
       }
     }
 
-    if (MovDelay[x][y])                /* neuer Schritt / in Wartezustand */
+    if (MovDelay[x][y])                /* wait some time before next movement */
     {
       MovDelay[x][y]--;
 
-      if (element==EL_ZOMBIE || element==EL_MAMPFER)
+      if (element == EL_ROBOT || element == EL_MAMPFER ||
+         element == EL_MAMPFER2)
       {
        int phase = MovDelay[x][y] % 8;
 
        if (phase>3)
          phase = 7-phase;
 
-       if (IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-         DrawGraphic(SCROLLX(x),SCROLLY(y),
-                     el2gfx(element)+phase);
+       if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+         DrawGraphic(SCREENX(x), SCREENY(y), el2gfx(element)+phase);
+
+       if ((element == EL_MAMPFER || element == EL_MAMPFER2)
+           && MovDelay[x][y]%4 == 3)
+         PlaySoundLevel(x, y, SND_NJAM);
+      }
+      else if (element == EL_DRACHE)
+      {
+       int i;
+       int dir = MovDir[x][y];
+       int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
+       int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
+       int graphic = (dir == MV_LEFT   ? GFX_FLAMMEN_LEFT :
+                      dir == MV_RIGHT  ? GFX_FLAMMEN_RIGHT :
+                      dir == MV_UP     ? GFX_FLAMMEN_UP :
+                      dir == MV_DOWN   ? GFX_FLAMMEN_DOWN : GFX_LEERRAUM);
+       int phase = FrameCounter % 2;
+
+       for (i=1; i<=3; i++)
+       {
+         int xx = x + i*dx, yy = y + i*dy;
+         int sx = SCREENX(xx), sy = SCREENY(yy);
+
+         if (!IN_LEV_FIELD(xx, yy) ||
+             IS_SOLID(Feld[xx][yy]) || Feld[xx][yy] == EL_EXPLODING)
+           break;
+
+         if (MovDelay[x][y])
+         {
+           int flamed = MovingOrBlocked2Element(xx, yy);
 
-       if (element==EL_MAMPFER && MovDelay[x][y]%4==3)
-         PlaySoundLevel(x,y,SND_NJAM);
+           if (IS_ENEMY(flamed) || IS_EXPLOSIVE(flamed))
+             Bang(xx, yy);
+           else
+             RemoveMovingField(xx, yy);
+
+           Feld[xx][yy] = EL_BURNING;
+           if (IN_SCR_FIELD(sx, sy))
+             DrawGraphic(sx, sy, graphic + phase*3 + i-1);
+         }
+         else
+         {
+           if (Feld[xx][yy] == EL_BURNING)
+             Feld[xx][yy] = EL_LEERRAUM;
+           DrawLevelField(xx, yy);
+         }
+       }
       }
 
       if (MovDelay[x][y])
        return;
     }
 
-    if (element==EL_KAEFER)
+    if (element == EL_KAEFER || element == EL_BUTTERFLY)
     {
-      PlaySoundLevel(x,y,SND_KLAPPER);
+      PlaySoundLevel(x, y, SND_KLAPPER);
     }
-    else if (element==EL_FLIEGER)
+    else if (element == EL_FLIEGER || element == EL_FIREFLY)
     {
-      PlaySoundLevel(x,y,SND_ROEHR);
+      PlaySoundLevel(x, y, SND_ROEHR);
     }
 
-    /* neuer Schritt / Wartezustand beendet */
+    /* now make next step */
 
-    Moving2Blocked(x,y,&newx,&newy);   /* wohin soll's gehen? */
+    Moving2Blocked(x, y, &newx, &newy);        /* get next screen position */
 
-    if (PLAYER(newx,newy))             /* Spieler erwischt */
+    if (IS_ENEMY(element) && IS_PLAYER(newx, newy))
     {
+      /* enemy got the player */
       MovDir[x][y] = 0;
-      KillHero();
+      KillHero(PLAYERINFO(newx, newy));
       return;
     }
-    else if (element==EL_MAMPFER && IN_LEV_FIELD(newx,newy) &&
-            Feld[newx][newy]==EL_DIAMANT)
+    else if ((element == EL_MAULWURF || element == EL_PINGUIN ||
+             element == EL_ROBOT || element == EL_SONDE) &&
+            IN_LEV_FIELD(newx, newy) &&
+            MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_SALZSAEURE)
+    {
+      Blurb(x, y);
+      Store[x][y] = EL_SALZSAEURE;
+    }
+    else if ((element == EL_MAULWURF || element == EL_PINGUIN) &&
+            IN_LEV_FIELD(newx, newy))
+    {
+      if (Feld[newx][newy] == EL_AUSGANG_AUF)
+      {
+       Feld[x][y] = EL_LEERRAUM;
+       DrawLevelField(x, y);
+
+       PlaySoundLevel(newx, newy, SND_BUING);
+       if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
+         DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2gfx(element));
+
+       local_player->friends_still_needed--;
+       if (!local_player->friends_still_needed &&
+           !local_player->GameOver && AllPlayersGone)
+         local_player->LevelSolved = local_player->GameOver = TRUE;
+
+       return;
+      }
+      else if (IS_MAMPF3(Feld[newx][newy]))
+      {
+       if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
+         DrawLevelField(newx, newy);
+       else
+         MovDir[x][y] = MV_NO_MOVING;
+      }
+      else if (!IS_FREE(newx, newy))
+      {
+       if (IS_PLAYER(x, y))
+         DrawPlayerField(x, y);
+       else
+         DrawLevelField(x, y);
+       return;
+      }
+    }
+    else if (element == EL_SCHWEIN && IN_LEV_FIELD(newx, newy))
+    {
+      if (IS_GEM(Feld[newx][newy]))
+      {
+       if (IS_MOVING(newx, newy))
+         RemoveMovingField(newx, newy);
+       else
+       {
+         Feld[newx][newy] = EL_LEERRAUM;
+         DrawLevelField(newx, newy);
+       }
+      }
+      else if (!IS_FREE(newx, newy))
+      {
+       if (IS_PLAYER(x, y))
+         DrawPlayerField(x, y);
+       else
+         DrawLevelField(x, y);
+       return;
+      }
+    }
+    else if (element == EL_DRACHE && IN_LEV_FIELD(newx, newy))
+    {
+      if (!IS_FREE(newx, newy))
+      {
+       if (IS_PLAYER(x, y))
+         DrawPlayerField(x, y);
+       else
+         DrawLevelField(x, y);
+       return;
+      }
+      else
+      {
+       boolean wanna_flame = !RND(10);
+       int dx = newx - x, dy = newy - y;
+       int newx1 = newx+1*dx, newy1 = newy+1*dy;
+       int newx2 = newx+2*dx, newy2 = newy+2*dy;
+       int element1 = (IN_LEV_FIELD(newx1, newy1) ?
+                       MovingOrBlocked2Element(newx1, newy1) : EL_BETON);
+       int element2 = (IN_LEV_FIELD(newx2, newy2) ?
+                       MovingOrBlocked2Element(newx2, newy2) : EL_BETON);
+
+       if ((wanna_flame || IS_ENEMY(element1) || IS_ENEMY(element2)) &&
+           element1 != EL_DRACHE && element2 != EL_DRACHE &&
+           element1 != EL_BURNING && element2 != EL_BURNING)
+       {
+         if (IS_PLAYER(x, y))
+           DrawPlayerField(x, y);
+         else
+           DrawLevelField(x, y);
+
+         MovDelay[x][y] = 50;
+         Feld[newx][newy] = EL_BURNING;
+         if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_LEERRAUM)
+           Feld[newx1][newy1] = EL_BURNING;
+         if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_LEERRAUM)
+           Feld[newx2][newy2] = EL_BURNING;
+         return;
+       }
+      }
+    }
+    else if (element == EL_MAMPFER && IN_LEV_FIELD(newx, newy) &&
+            Feld[newx][newy] == EL_DIAMANT)
+    {
+      if (IS_MOVING(newx, newy))
+       RemoveMovingField(newx, newy);
+      else
+      {
+       Feld[newx][newy] = EL_LEERRAUM;
+       DrawLevelField(newx, newy);
+      }
+    }
+    else if (element == EL_MAMPFER2 && IN_LEV_FIELD(newx, newy) &&
+            IS_MAMPF2(Feld[newx][newy]))
     {
-      if (IS_MOVING(newx,newy))
-       RemoveMovingField(newx,newy);
+      if (AmoebaNr[newx][newy])
+      {
+       AmoebaCnt2[AmoebaNr[newx][newy]]--;
+       if (Feld[newx][newy] == EL_AMOEBE_VOLL ||
+           Feld[newx][newy] == EL_AMOEBE_BD)
+         AmoebaCnt[AmoebaNr[newx][newy]]--;
+      }
+
+      if (IS_MOVING(newx, newy))
+       RemoveMovingField(newx, newy);
       else
       {
        Feld[newx][newy] = EL_LEERRAUM;
-       DrawLevelField(newx,newy);
+       DrawLevelField(newx, newy);
       }
     }
-    else if (element==EL_PACMAN && IN_LEV_FIELD(newx,newy) &&
+    else if (element == EL_PACMAN && IN_LEV_FIELD(newx, newy) &&
             IS_AMOEBOID(Feld[newx][newy]))
     {
-      if (AmoebaNr[newx][newy] && Feld[newx][newy]==EL_AMOEBE_VOLL)
-       AmoebaCnt[AmoebaNr[newx][newy]]--;
+      if (AmoebaNr[newx][newy])
+      {
+       AmoebaCnt2[AmoebaNr[newx][newy]]--;
+       if (Feld[newx][newy] == EL_AMOEBE_VOLL ||
+           Feld[newx][newy] == EL_AMOEBE_BD)
+         AmoebaCnt[AmoebaNr[newx][newy]]--;
+      }
 
       Feld[newx][newy] = EL_LEERRAUM;
-      DrawLevelField(newx,newy);
+      DrawLevelField(newx, newy);
     }
-    else if (element==EL_ZOMBIE && IN_LEV_FIELD(newx,newy) &&
-            MovDir[x][y]==MV_DOWN && Feld[newx][newy]==EL_SALZSAEURE)
+    else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
     {
-      Blurb(x,y);
-      Store[x][y] = EL_SALZSAEURE;
-    }
-    else if (!IN_LEV_FIELD(newx,newy) || !IS_FREE(newx,newy))
-    {                                  /* gegen Wand gelaufen */
-      TurnRound(x,y);
-      DrawLevelField(x,y);
+      /* object was running against a wall */
+
+      TurnRound(x, y);
+
+      if (element == EL_KAEFER || element == EL_FLIEGER)
+       DrawLevelField(x, y);
+      else if (element == EL_BUTTERFLY || element == EL_FIREFLY)
+       DrawGraphicAnimation(x, y, el2gfx(element), 2, 4, ANIM_NORMAL);
+      else if (element == EL_SONDE)
+       DrawGraphicAnimation(x, y, GFX_SONDE_START, 8, 2, ANIM_NORMAL);
+
       return;
     }
 
-    InitMovingField(x,y,MovDir[x][y]);
+    if (element == EL_ROBOT && IN_SCR_FIELD(x, y))
+      PlaySoundLevel(x, y, SND_SCHLURF);
+
+    InitMovingField(x, y, MovDir[x][y]);
   }
 
   if (MovDir[x][y])
-    ContinueMoving(x,y);
+    ContinueMoving(x, y);
 }
 
 void ContinueMoving(int x, int y)
 {
   int element = Feld[x][y];
   int direction = MovDir[x][y];
-  int dx = (direction==MV_LEFT ? -1 : direction==MV_RIGHT ? +1 : 0);
-  int dy = (direction==MV_UP   ? -1 : direction==MV_DOWN  ? +1 : 0);
+  int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
+  int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
   int horiz_move = (dx!=0);
   int newx = x + dx, newy = y + dy;
-  int step = (horiz_move ? dx : dy)*TILEX/4;
+  int step = (horiz_move ? dx : dy) * TILEX/8;
 
   if (CAN_FALL(element) && horiz_move)
     step*=2;
-  else if (element==EL_TROPFEN)
+  else if (element == EL_TROPFEN)
     step/=2;
-  else if (Store[x][y]==EL_MORAST_VOLL || Store[x][y]==EL_MORAST_LEER)
+  else if (Store[x][y] == EL_MORAST_VOLL || Store[x][y] == EL_MORAST_LEER)
     step/=4;
 
   MovPos[x][y] += step;
 
-  if (ABS(MovPos[x][y])>=TILEX)                /* Zielfeld erreicht */
+  if (ABS(MovPos[x][y])>=TILEX)                /* object reached its destination */
   {
-    Feld[x][y]=EL_LEERRAUM;
-    Feld[newx][newy]=element;
+    Feld[x][y] = EL_LEERRAUM;
+    Feld[newx][newy] = element;
 
-    if (Store[x][y]==EL_MORAST_VOLL)
+    if (Store[x][y] == EL_MORAST_VOLL)
     {
       Store[x][y] = 0;
       Feld[newx][newy] = EL_MORAST_VOLL;
       element = EL_MORAST_VOLL;
     }
-    else if (Store[x][y]==EL_MORAST_LEER)
+    else if (Store[x][y] == EL_MORAST_LEER)
     {
       Store[x][y] = 0;
       Feld[x][y] = EL_MORAST_LEER;
     }
-    else if (Store[x][y]==EL_SIEB_VOLL)
+    else if (Store[x][y] == EL_SIEB_VOLL)
+    {
+      Store[x][y] = 0;
+      element = Feld[newx][newy] = (SiebAktiv ? EL_SIEB_VOLL : EL_SIEB_TOT);
+    }
+    else if (Store[x][y] == EL_SIEB_LEER)
+    {
+      Store[x][y] = Store2[x][y] = 0;
+      Feld[x][y] = (SiebAktiv ? EL_SIEB_LEER : EL_SIEB_TOT);
+    }
+    else if (Store[x][y] == EL_SIEB2_VOLL)
     {
       Store[x][y] = 0;
-      Feld[newx][newy] = EL_SIEB_VOLL;
-      element = EL_SIEB_VOLL;
+      element = Feld[newx][newy] = (SiebAktiv ? EL_SIEB2_VOLL : EL_SIEB2_TOT);
     }
-    else if (Store[x][y]==EL_SIEB_LEER)
+    else if (Store[x][y] == EL_SIEB2_LEER)
     {
       Store[x][y] = Store2[x][y] = 0;
-      Feld[x][y] = EL_SIEB_LEER;
+      Feld[x][y] = (SiebAktiv ? EL_SIEB2_LEER : EL_SIEB2_TOT);
     }
-    else if (Store[x][y]==EL_SALZSAEURE)
+    else if (Store[x][y] == EL_SALZSAEURE)
     {
       Store[x][y] = 0;
       Feld[newx][newy] = EL_SALZSAEURE;
       element = EL_SALZSAEURE;
     }
-    else if (Store[x][y]==EL_AMOEBE_NASS)
+    else if (Store[x][y] == EL_AMOEBE_NASS)
     {
       Store[x][y] = 0;
       Feld[x][y] = EL_AMOEBE_NASS;
     }
 
-    MovPos[x][y] = MovDir[x][y] = 0;
+    MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
+    MovDelay[newx][newy] = 0;
 
     if (!CAN_MOVE(element))
       MovDir[newx][newy] = 0;
 
-    DrawLevelField(x,y);
-    DrawLevelField(newx,newy);
+    DrawLevelField(x, y);
+    DrawLevelField(newx, newy);
 
-    Stop[newx][newy]=TRUE;
-    CheckMoving=TRUE;
+    Stop[newx][newy] = TRUE;
+    JustHit[x][newy] = 3;
 
-    if (DONT_TOUCH(element))   /* Käfer oder Flieger */
+    if (DONT_TOUCH(element))   /* object may be nasty to player or others */
     {
-      TestIfBadThingHitsHero();
-      TestIfBadThingHitsOtherBadThing(newx,newy);
+      TestIfBadThingHitsHero(newx, newy);
+      TestIfBadThingHitsFriend(newx, newy);
+      TestIfBadThingHitsOtherBadThing(newx, newy);
     }
+    else if (element == EL_PINGUIN)
+      TestIfFriendHitsBadThing(newx, newy);
 
-    if (CAN_SMASH(element) && direction==MV_DOWN &&
-       (newy==lev_fieldy-1 || !IS_FREE(x,newy+1)))
-      Impact(x,newy);
-  }
-  else                         /* noch in Bewegung */
-  {
-    DrawLevelField(x,y);
-    CheckMoving=TRUE;
+    if (CAN_SMASH(element) && direction == MV_DOWN &&
+       (newy == lev_fieldy-1 || !IS_FREE(x, newy+1)))
+      Impact(x, newy);
   }
+  else                         /* still moving on */
+    DrawLevelField(x, y);
 }
 
 int AmoebeNachbarNr(int ax, int ay)
@@ -1272,60 +2237,64 @@ int AmoebeNachbarNr(int ax, int ay)
   int group_nr = 0;
   static int xy[4][2] =
   {
-    0,-1,
-    -1,0,
-    +1,0,
-    0,+1
+    { 0, -1 },
+    { -1, 0 },
+    { +1, 0 },
+    { 0, +1 }
   };
 
-  for(i=0;i<4;i++)
+  for (i=0; i<4; i++)
   {
     int x = ax+xy[i%4][0];
     int y = ay+xy[i%4][1];
 
-    if (!IN_LEV_FIELD(x,y))
+    if (!IN_LEV_FIELD(x, y))
       continue;
 
-    if (Feld[x][y]==element && AmoebaNr[x][y]>0)
+    if (Feld[x][y] == element && AmoebaNr[x][y]>0)
       group_nr = AmoebaNr[x][y];
   }
 
-  return(group_nr);
+  return group_nr;
 }
 
 void AmoebenVereinigen(int ax, int ay)
 {
-  int i,x,y,xx,yy;
+  int i, x, y, xx, yy;
   int new_group_nr = AmoebaNr[ax][ay];
   static int xy[4][2] =
   {
-    0,-1,
-    -1,0,
-    +1,0,
-    0,+1
+    { 0, -1 },
+    { -1, 0 },
+    { +1, 0 },
+    { 0, +1 }
   };
 
   if (!new_group_nr)
     return;
 
-  for(i=0;i<4;i++)
+  for (i=0; i<4; i++)
   {
     x = ax+xy[i%4][0];
     y = ay+xy[i%4][1];
 
-    if (!IN_LEV_FIELD(x,y))
+    if (!IN_LEV_FIELD(x, y))
       continue;
 
-    if ((Feld[x][y]==EL_AMOEBE_VOLL || Feld[x][y]==EL_AMOEBE_TOT) &&
+    if ((Feld[x][y] == EL_AMOEBE_VOLL ||
+        Feld[x][y] == EL_AMOEBE_BD ||
+        Feld[x][y] == EL_AMOEBE_TOT) &&
        AmoebaNr[x][y] != new_group_nr)
     {
       int old_group_nr = AmoebaNr[x][y];
 
       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
       AmoebaCnt[old_group_nr] = 0;
+      AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
+      AmoebaCnt2[old_group_nr] = 0;
 
-      for(yy=0;yy<lev_fieldy;yy++) for(xx=0;xx<lev_fieldx;xx++)
-       if (AmoebaNr[xx][yy]==old_group_nr)
+      for (yy=0; yy<lev_fieldy; yy++) for (xx=0; xx<lev_fieldx; xx++)
+       if (AmoebaNr[xx][yy] == old_group_nr)
          AmoebaNr[xx][yy] = new_group_nr;
     }
   }
@@ -1333,73 +2302,96 @@ void AmoebenVereinigen(int ax, int ay)
 
 void AmoebeUmwandeln(int ax, int ay)
 {
-  int i,x,y;
+  int i, x, y;
   int group_nr = AmoebaNr[ax][ay];
   static int xy[4][2] =
   {
-    0,-1,
-    -1,0,
-    +1,0,
-    0,+1
+    { 0, -1 },
+    { -1, 0 },
+    { +1, 0 },
+    { 0, +1 }
   };
 
-  if (Feld[ax][ay]==EL_AMOEBE_TOT)
+  if (Feld[ax][ay] == EL_AMOEBE_TOT)
   {
-    for(y=0;y<lev_fieldy;y++) for(x=0;x<lev_fieldx;x++)
+    for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
     {
-      if (Feld[x][y]==EL_AMOEBE_TOT && AmoebaNr[x][y]==group_nr)
+      if (Feld[x][y] == EL_AMOEBE_TOT && AmoebaNr[x][y] == group_nr)
       {
        AmoebaNr[x][y] = 0;
        Feld[x][y] = EL_AMOEBA2DIAM;
       }
     }
-    Bang(ax,ay);
+    Bang(ax, ay);
   }
   else
   {
-    for(i=0;i<4;i++)
+    for (i=0; i<4; i++)
     {
       x = ax+xy[i%4][0];
       y = ay+xy[i%4][1];
 
-      if (!IN_LEV_FIELD(x,y))
+      if (!IN_LEV_FIELD(x, y))
        continue;
 
-      if (Feld[x][y]==EL_AMOEBA2DIAM)
-       Bang(x,y);
+      if (Feld[x][y] == EL_AMOEBA2DIAM)
+       Bang(x, y);
     }
   }
 }
 
-void AmoebeWaechst(int x, int y)
+void AmoebeUmwandeln2(int ax, int ay, int new_element)
 {
-  static long sound_delay = 0;
-  static int sound_delay_value = 0;
+  int x, y;
+  int group_nr = AmoebaNr[ax][ay];
+  boolean done = FALSE;
+
+  for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+  {
+    if (AmoebaNr[x][y] == group_nr &&
+       (Feld[x][y] == EL_AMOEBE_TOT ||
+        Feld[x][y] == EL_AMOEBE_BD ||
+        Feld[x][y] == EL_AMOEBING))
+    {
+      AmoebaNr[x][y] = 0;
+      Feld[x][y] = new_element;
+      DrawLevelField(x, y);
+      done = TRUE;
+    }
+  }
+
+  if (done)
+    PlaySoundLevel(ax, ay,
+                  (new_element == EL_FELSBROCKEN ? SND_KLOPF : SND_PLING));
+}
 
-  CheckExploding=TRUE;
+void AmoebeWaechst(int x, int y)
+{
+  static unsigned long sound_delay = 0;
+  static unsigned long sound_delay_value = 0;
 
-  if (!MovDelay[x][y])         /* neue Phase / noch nicht gewartet */
+  if (!MovDelay[x][y])         /* start new growing cycle */
   {
-    MovDelay[x][y] = 4;
+    MovDelay[x][y] = 7;
 
-    if (DelayReached(&sound_delay,sound_delay_value))
+    if (DelayReached(&sound_delay, sound_delay_value))
     {
-      PlaySoundLevel(x,y,SND_AMOEBE);
+      PlaySoundLevel(x, y, SND_AMOEBE);
       sound_delay_value = 30;
     }
   }
 
-  if (MovDelay[x][y])          /* neue Phase / in Wartezustand */
+  if (MovDelay[x][y])          /* wait some time before growing bigger */
   {
     MovDelay[x][y]--;
-    if (MovDelay[x][y] && IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-      DrawGraphic(SCROLLX(x),SCROLLY(y),GFX_AMOEBING+3-MovDelay[x][y]);
+    if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+      DrawGraphic(SCREENX(x), SCREENY(y), GFX_AMOEBING+3-MovDelay[x][y]/2);
 
     if (!MovDelay[x][y])
     {
       Feld[x][y] = Store[x][y];
       Store[x][y] = 0;
-      DrawLevelField(x,y);
+      DrawLevelField(x, y);
     }
   }
 }
@@ -1411,178 +2403,188 @@ void AmoebeAbleger(int ax, int ay)
   int newax = ax, neway = ay;
   static int xy[4][2] =
   {
-    0,-1,
-    -1,0,
-    +1,0,
-    0,+1
+    { 0, -1 },
+    { -1, 0 },
+    { +1, 0 },
+    { 0, +1 }
   };
 
-  CheckExploding=TRUE;
-
   if (!level.tempo_amoebe)
   {
     Feld[ax][ay] = EL_AMOEBE_TOT;
-    DrawLevelField(ax,ay);
+    DrawLevelField(ax, ay);
     return;
   }
 
-  if (!MovDelay[ax][ay])       /* neue Amoebe / noch nicht gewartet */
-    MovDelay[ax][ay] = RND(33*20/(1+level.tempo_amoebe));
+  if (!MovDelay[ax][ay])       /* start making new amoeba field */
+    MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25/(1+level.tempo_amoebe));
 
-  if (MovDelay[ax][ay])                /* neue Amoebe / in Wartezustand */
+  if (MovDelay[ax][ay])                /* wait some time before making new amoeba */
   {
     MovDelay[ax][ay]--;
     if (MovDelay[ax][ay])
       return;
   }
 
-  if (element==EL_AMOEBE_NASS) /* tropfende Amöbe */
+  if (element == EL_AMOEBE_NASS)       /* object is an acid / amoeba drop */
   {
     int start = RND(4);
     int x = ax+xy[start][0];
     int y = ay+xy[start][1];
 
-    if (!IN_LEV_FIELD(x,y))
+    if (!IN_LEV_FIELD(x, y))
       return;
 
-    if (IS_FREE(x,y) ||
-       Feld[x][y]==EL_ERDREICH || Feld[x][y]==EL_MORAST_LEER)
+    if (IS_FREE(x, y) ||
+       Feld[x][y] == EL_ERDREICH || Feld[x][y] == EL_MORAST_LEER)
     {
       newax = x;
       neway = y;
     }
 
-    if (newax==ax && neway==ay)
+    if (newax == ax && neway == ay)
       return;
   }
-  else                         /* normale oder "gefüllte" Amöbe */
+  else                         /* normal or "filled" amoeba */
   {
     int start = RND(4);
-    BOOL waiting_for_player = FALSE;
+    boolean waiting_for_player = FALSE;
 
-    for(i=0;i<4;i++)
+    for (i=0; i<4; i++)
     {
       int j = (start+i)%4;
       int x = ax+xy[j][0];
       int y = ay+xy[j][1];
 
-      if (!IN_LEV_FIELD(x,y))
+      if (!IN_LEV_FIELD(x, y))
        continue;
 
-      if (IS_FREE(x,y) ||
-         Feld[x][y]==EL_ERDREICH || Feld[x][y]==EL_MORAST_LEER)
+      if (IS_FREE(x, y) ||
+         Feld[x][y] == EL_ERDREICH || Feld[x][y] == EL_MORAST_LEER)
       {
        newax = x;
        neway = y;
        break;
       }
-      else if (PLAYER(x,y))
+      else if (IS_PLAYER(x, y))
        waiting_for_player = TRUE;
     }
 
-    if (newax==ax && neway==ay)
+    if (newax == ax && neway == ay)
     {
-      if (i==4 && !waiting_for_player)
+      if (i == 4 && !waiting_for_player)
       {
        Feld[ax][ay] = EL_AMOEBE_TOT;
-       DrawLevelField(ax,ay);
+       DrawLevelField(ax, ay);
+       AmoebaCnt[AmoebaNr[ax][ay]]--;
 
-       if (element==EL_AMOEBE_VOLL && --AmoebaCnt[AmoebaNr[ax][ay]]<=0)
-         AmoebeUmwandeln(ax,ay);
+       if (AmoebaCnt[AmoebaNr[ax][ay]]<=0)     /* amoeba is completely dead */
+       {
+         if (element == EL_AMOEBE_VOLL)
+           AmoebeUmwandeln(ax, ay);
+         else if (element == EL_AMOEBE_BD)
+           AmoebeUmwandeln2(ax, ay, level.amoebe_inhalt);
+       }
       }
       return;
     }
-    else if (element==EL_AMOEBE_VOLL)
+    else if (element == EL_AMOEBE_VOLL || element == EL_AMOEBE_BD)
     {
       int new_group_nr = AmoebaNr[ax][ay];
 
       AmoebaNr[newax][neway] = new_group_nr;
       AmoebaCnt[new_group_nr]++;
-      AmoebenVereinigen(newax,neway);
+      AmoebaCnt2[new_group_nr]++;
+      AmoebenVereinigen(newax, neway);
+
+      if (AmoebaCnt2[new_group_nr] >= 200 && element == EL_AMOEBE_BD)
+      {
+       AmoebeUmwandeln2(newax, neway, EL_FELSBROCKEN);
+       return;
+      }
     }
   }
 
-  if (element!=EL_AMOEBE_NASS || neway<ay || !IS_FREE(newax,neway) ||
-      (neway==lev_fieldy-1 && newax!=ax))
+  if (element!=EL_AMOEBE_NASS || neway<ay || !IS_FREE(newax, neway) ||
+      (neway == lev_fieldy-1 && newax!=ax))
   {
     Feld[newax][neway] = EL_AMOEBING;
     Store[newax][neway] = element;
   }
-  else if (neway==ay)
+  else if (neway == ay)
     Feld[newax][neway] = EL_TROPFEN;
   else
   {
-    InitMovingField(ax,ay,MV_DOWN);
+    InitMovingField(ax, ay, MV_DOWN);
     Feld[ax][ay] = EL_TROPFEN;
     Store[ax][ay] = EL_AMOEBE_NASS;
-    ContinueMoving(ax,ay);
+    ContinueMoving(ax, ay);
     return;
   }
 
-  DrawLevelField(newax,neway);
+  DrawLevelField(newax, neway);
 }
 
 void Life(int ax, int ay)
 {
-  int x1,y1,x2,y2;
-  static int life[4] = { 2,3,3,3 };    /* "Life"-Parameter */
-  int life_time = 20;
+  int x1, y1, x2, y2;
+  static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
+  int life_time = 40;
   int element = Feld[ax][ay];
 
-  CheckExploding=TRUE;
-
   if (Stop[ax][ay])
     return;
 
-  if (!MovDelay[ax][ay])       /* neue Phase / noch nicht gewartet */
+  if (!MovDelay[ax][ay])       /* start new "game of life" cycle */
     MovDelay[ax][ay] = life_time;
 
-  if (MovDelay[ax][ay])                /* neue Phase / in Wartezustand */
+  if (MovDelay[ax][ay])                /* wait some time before next cycle */
   {
     MovDelay[ax][ay]--;
     if (MovDelay[ax][ay])
       return;
   }
 
-  for(y1=-1;y1<2;y1++) for(x1=-1;x1<2;x1++)
+  for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
   {
     int xx = ax+x1, yy = ay+y1;
     int nachbarn = 0;
 
-    if (!IN_LEV_FIELD(xx,yy))
+    if (!IN_LEV_FIELD(xx, yy))
       continue;
 
-    for(y2=-1;y2<2;y2++) for(x2=-1;x2<2;x2++)
+    for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
     {
       int x = xx+x2, y = yy+y2;
 
-      if (!IN_LEV_FIELD(x,y) || (x==xx && y==yy))
+      if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
        continue;
 
-      if (((Feld[x][y]==element || (element==EL_LIFE && PLAYER(x,y))) &&
+      if (((Feld[x][y] == element ||
+           (element == EL_LIFE && IS_PLAYER(x, y))) &&
           !Stop[x][y]) ||
-         (IS_FREE(x,y) && Stop[x][y]))
+         (IS_FREE(x, y) && Stop[x][y]))
        nachbarn++;
     }
 
-    if (xx==ax && yy==ay)              /* mittleres Feld mit Amoebe */
+    if (xx == ax && yy == ay)          /* field in the middle */
     {
       if (nachbarn<life[0] || nachbarn>life[1])
       {
        Feld[xx][yy] = EL_LEERRAUM;
        if (!Stop[xx][yy])
-         DrawLevelField(xx,yy);
+         DrawLevelField(xx, yy);
        Stop[xx][yy] = TRUE;
       }
     }
-    else if (IS_FREE(xx,yy) || Feld[xx][yy]==EL_ERDREICH)
-    {                                  /* Randfeld ohne Amoebe */
+    else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_ERDREICH)
+    {                                  /* free border field */
       if (nachbarn>=life[2] && nachbarn<=life[3])
       {
        Feld[xx][yy] = element;
-       MovDelay[xx][yy] = (element==EL_LIFE ? 0 : life_time-1);
+       MovDelay[xx][yy] = (element == EL_LIFE ? 0 : life_time-1);
        if (!Stop[xx][yy])
-         DrawLevelField(xx,yy);
+         DrawLevelField(xx, yy);
        Stop[xx][yy] = TRUE;
       }
     }
@@ -1591,37 +2593,34 @@ void Life(int ax, int ay)
 
 void Ablenk(int x, int y)
 {
-  CheckExploding=TRUE;
+  if (!MovDelay[x][y])         /* next animation frame */
+    MovDelay[x][y] = level.dauer_ablenk * FRAMES_PER_SECOND;
 
-  if (!MovDelay[x][y])         /* neue Phase / noch nicht gewartet */
-    MovDelay[x][y] = 33*(level.dauer_ablenk/10);
-  if (MovDelay[x][y])          /* neue Phase / in Wartezustand */
+  if (MovDelay[x][y])          /* wait some time before next frame */
   {
     MovDelay[x][y]--;
     if (MovDelay[x][y])
     {
-      if (IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-       DrawGraphic(SCROLLX(x),SCROLLY(y),GFX_ABLENK+MovDelay[x][y]%4);
+      if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+       DrawGraphic(SCREENX(x), SCREENY(y), GFX_ABLENK+MovDelay[x][y]%4);
       if (!(MovDelay[x][y]%4))
-       PlaySoundLevel(x,y,SND_MIEP);
+       PlaySoundLevel(x, y, SND_MIEP);
       return;
     }
   }
 
-  Feld[x][y]=EL_ABLENK_AUS;
-  DrawLevelField(x,y);
-  if (ZX==x && ZY==y)
-    ZX=ZY=-1;
+  Feld[x][y] = EL_ABLENK_AUS;
+  DrawLevelField(x, y);
+  if (ZX == x && ZY == y)
+    ZX = ZY = -1;
 }
 
 void Birne(int x, int y)
 {
-  CheckExploding=TRUE;
-
-  if (!MovDelay[x][y])         /* neue Phase / noch nicht gewartet */
-    MovDelay[x][y] = 400;
+  if (!MovDelay[x][y])         /* next animation frame */
+    MovDelay[x][y] = 800;
 
-  if (MovDelay[x][y])          /* neue Phase / in Wartezustand */
+  if (MovDelay[x][y])          /* wait some time before next frame */
   {
     MovDelay[x][y]--;
     if (MovDelay[x][y])
@@ -1632,7 +2631,7 @@ void Birne(int x, int y)
          Feld[x][y]=EL_ABLENK_EIN;
        else
          Feld[x][y]=EL_ABLENK_AUS;
-       DrawLevelField(x,y);
+       DrawLevelField(x, y);
        Feld[x][y]=EL_ABLENK_EIN;
       }
       return;
@@ -1640,333 +2639,1132 @@ void Birne(int x, int y)
   }
 
   Feld[x][y]=EL_ABLENK_AUS;
-  DrawLevelField(x,y);
-  if (ZX==x && ZY==y)
+  DrawLevelField(x, y);
+  if (ZX == x && ZY == y)
     ZX=ZY=-1;
 }
 
 void Blubber(int x, int y)
 {
-  CheckExploding=TRUE;
-
-  if (!MovDelay[x][y])         /* neue Phase / noch nicht gewartet */
-    MovDelay[x][y] = 20;
-
-  if (MovDelay[x][y])          /* neue Phase / in Wartezustand */
-  {
-    int blubber;
-
-    MovDelay[x][y]--;
-    blubber = MovDelay[x][y]/5;
-    if (!(MovDelay[x][y]%5) && IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-      DrawGraphic(SCROLLX(x),SCROLLY(y),GFX_GEBLUBBER+3-blubber);
-  }
+  if (y > 0 && IS_MOVING(x, y-1) && MovDir[x][y-1] == MV_DOWN)
+    DrawLevelField(x, y-1);
+  else
+    DrawGraphicAnimation(x, y, GFX_GEBLUBBER, 4, 10, ANIM_NORMAL);
 }
 
 void NussKnacken(int x, int y)
 {
-  CheckExploding=TRUE;
-
-  if (!MovDelay[x][y])         /* neue Phase / noch nicht gewartet */
-    MovDelay[x][y] = 4;
+  if (!MovDelay[x][y])         /* next animation frame */
+    MovDelay[x][y] = 7;
 
-  if (MovDelay[x][y])          /* neue Phase / in Wartezustand */
+  if (MovDelay[x][y])          /* wait some time before next frame */
   {
     MovDelay[x][y]--;
-    if (MovDelay[x][y] && IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-      DrawGraphic(SCROLLX(x),SCROLLY(y),GFX_CRACKINGNUT+3-MovDelay[x][y]);
+    if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+      DrawGraphic(SCREENX(x), SCREENY(y), GFX_CRACKINGNUT+3-MovDelay[x][y]/2);
 
     if (!MovDelay[x][y])
     {
       Feld[x][y] = EL_EDELSTEIN;
-      DrawLevelField(x,y);
+      DrawLevelField(x, y);
     }
   }
 }
 
-void SiebAktivieren(int x, int y)
+void SiebAktivieren(int x, int y, int typ)
 {
-  CheckExploding=TRUE;
-
-  if (SiebAktiv>1)
-  {
-    if (SiebAktiv%2 && IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-      DrawGraphic(SCROLLX(x),SCROLLY(y),GFX_SIEB_VOLL+3-(SiebAktiv%8)/2);
-
-/*
-    if (!(SiebAktiv%4))
-      PlaySoundLevel(x,y,SND_MIEP);
-*/
-
-  }
-  else
-  {
-    Feld[x][y] = EL_SIEB_TOT;
-    DrawLevelField(x,y);
-  }
+  if (!(SiebAktiv % 4) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+    DrawGraphic(SCREENX(x), SCREENY(y),
+               (typ == 1 ? GFX_SIEB_VOLL :
+                GFX_SIEB2_VOLL) + 3 - (SiebAktiv % 16) / 4);
 }
 
 void AusgangstuerPruefen(int x, int y)
 {
-  CheckExploding=TRUE;
-
-  if (!Gems)
+  if (!local_player->gems_still_needed &&
+      !local_player->sokobanfields_still_needed &&
+      !local_player->lights_still_needed)
+  {
     Feld[x][y] = EL_AUSGANG_ACT;
+
+    PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
+                  (x > LEVELX(BX2) ? LEVELX(BX2) : x),
+                  y < LEVELY(BY1) ? LEVELY(BY1) :
+                  (y > LEVELY(BY2) ? LEVELY(BY2) : y),
+                  SND_OEFFNEN);
+  }
 }
 
 void AusgangstuerOeffnen(int x, int y)
 {
-  CheckExploding=TRUE;
+  int delay = 6;
 
-  if (!MovDelay[x][y])         /* neue Phase / noch nicht gewartet */
-    MovDelay[x][y] = 20;
+  if (!MovDelay[x][y])         /* next animation frame */
+    MovDelay[x][y] = 5*delay;
 
-  if (MovDelay[x][y])          /* neue Phase / in Wartezustand */
+  if (MovDelay[x][y])          /* wait some time before next frame */
   {
     int tuer;
 
     MovDelay[x][y]--;
-    tuer = MovDelay[x][y]/5;
-    if (!(MovDelay[x][y]%5) && IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-      DrawGraphic(SCROLLX(x),SCROLLY(y),GFX_AUSGANG_ZU+3-tuer);
+    tuer = MovDelay[x][y]/delay;
+    if (!(MovDelay[x][y]%delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+      DrawGraphic(SCREENX(x), SCREENY(y), GFX_AUSGANG_AUF-tuer);
 
     if (!MovDelay[x][y])
     {
       Feld[x][y] = EL_AUSGANG_AUF;
-      DrawLevelField(x,y);
+      DrawLevelField(x, y);
     }
   }
 }
 
-int GameActions(int mx, int my, int button)
+void AusgangstuerBlinken(int x, int y)
+{
+  DrawGraphicAnimation(x, y, GFX_AUSGANG_AUF, 4, 4, ANIM_OSCILLATE);
+}
+
+void EdelsteinFunkeln(int x, int y)
 {
-  static long time_delay=0, action_delay=0;
-  int Action;
+  if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
+    return;
 
-  if (TimeLeft>0 && DelayReached(&time_delay,100) && !tape.pausing)
+  if (Feld[x][y] == EL_EDELSTEIN_BD)
+    DrawGraphicAnimation(x, y, GFX_EDELSTEIN_BD, 4, 4, ANIM_REVERSE);
+  else
   {
-    TimeLeft--;
+    if (!MovDelay[x][y])       /* next animation frame */
+      MovDelay[x][y] = 11 * !SimpleRND(500);
 
-    if (tape.recording || tape.playing)
-      DrawVideoDisplay(VIDEO_STATE_TIME_ON,level.time-TimeLeft);
+    if (MovDelay[x][y])                /* wait some time before next frame */
+    {
+      MovDelay[x][y]--;
 
-    if (TimeLeft<=10)
-      PlaySoundStereo(SND_GONG,PSND_MAX_RIGHT);
+      if (setup.direct_draw && MovDelay[x][y])
+       SetDrawtoField(DRAW_BUFFERED);
 
-    DrawText(DX_TIME,DY_TIME,int2str(TimeLeft,3),FS_SMALL,FC_YELLOW);
-    BackToFront();
-  }
+      DrawGraphic(SCREENX(x), SCREENY(y), el2gfx(Feld[x][y]));
 
-  if (!TimeLeft)
-    KillHero();
+      if (MovDelay[x][y])
+      {
+       int phase = (MovDelay[x][y]-1)/2;
 
-  Action = (CheckMoving || CheckExploding || SiebAktiv);
+       if (phase > 2)
+         phase = 4-phase;
 
-/*
-  if (Action && DelayReached(&action_delay,3))
-*/
+       DrawGraphicThruMask(SCREENX(x), SCREENY(y), GFX_FUNKELN_WEISS + phase);
 
-  if (DelayReached(&action_delay,3))
-  {
-    int x,y,element;
+       if (setup.direct_draw)
+       {
+         int dest_x, dest_y;
 
-    if (tape.pausing || (tape.playing && !TapePlayDelay()))
-      return(ACT_GO_ON);
-    else if (tape.recording)
-      TapeRecordDelay();
+         dest_x = FX + SCREENX(x)*TILEX;
+         dest_y = FY + SCREENY(y)*TILEY;
 
-    CheckMoving = CheckExploding = FALSE;
-    for(y=0;y<lev_fieldy;y++) for(x=0;x<lev_fieldx;x++)
-      Stop[x][y] = FALSE;
+         XCopyArea(display, drawto_field, window, gc,
+                   dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
+         SetDrawtoField(DRAW_DIRECT);
+       }
+      }
+    }
+  }
+}
 
-    for(y=0;y<lev_fieldy;y++) for(x=0;x<lev_fieldx;x++)
-    {
-      element = Feld[x][y];
+void MauerWaechst(int x, int y)
+{
+  int delay = 6;
 
-      if (element==EL_LEERRAUM || element==EL_ERDREICH)
-       continue;
+  if (!MovDelay[x][y])         /* next animation frame */
+    MovDelay[x][y] = 3*delay;
 
-      if (!IS_MOVING(x,y) && (CAN_FALL(element) || CAN_MOVE(element)))
-       StartMoving(x,y);
-      else if (IS_MOVING(x,y))
-       ContinueMoving(x,y);
-      else if (element==EL_DYNAMIT)
-       CheckDynamite(x,y);
-      else if (element==EL_EXPLODING)
-       Explode(x,y,Frame[x][y]);
-      else if (element==EL_AMOEBING)
-       AmoebeWaechst(x,y);
-      else if (IS_AMOEBALIVE(element))
-       AmoebeAbleger(x,y);
-      else if (element==EL_LIFE || element==EL_LIFE_ASYNC)
-       Life(x,y);
-      else if (element==EL_ABLENK_EIN)
-       Ablenk(x,y);
-      else if (element==EL_SALZSAEURE)
-       Blubber(x,y);
-      else if (element==EL_BLURB_LEFT || element==EL_BLURB_RIGHT)
-       Blurb(x,y);
-      else if (element==EL_CRACKINGNUT)
-       NussKnacken(x,y);
-      else if (element==EL_AUSGANG_ZU)
-       AusgangstuerPruefen(x,y);
-      else if (element==EL_AUSGANG_ACT)
-       AusgangstuerOeffnen(x,y);
-
-      if (SiebAktiv && (element==EL_SIEB_LEER ||
-                       element==EL_SIEB_VOLL ||
-                       Store[x][y]==EL_SIEB_LEER))
-       SiebAktivieren(x,y);
-    }
+  if (MovDelay[x][y])          /* wait some time before next frame */
+  {
+    int phase;
 
-    if (SiebAktiv)
-      SiebAktiv--;
+    MovDelay[x][y]--;
+    phase = 2-MovDelay[x][y]/delay;
+    if (!(MovDelay[x][y]%delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+      DrawGraphic(SCREENX(x), SCREENY(y),
+                 (MovDir[x][y] == MV_LEFT  ? GFX_MAUER_LEFT  :
+                  MovDir[x][y] == MV_RIGHT ? GFX_MAUER_RIGHT :
+                  MovDir[x][y] == MV_UP    ? GFX_MAUER_UP    :
+                                             GFX_MAUER_DOWN  ) + phase);
 
-    if (CheckMoving || CheckExploding)
-      BackToFront();
-  }
+    if (!MovDelay[x][y])
+    {
+      if (MovDir[x][y] == MV_LEFT)
+      {
+       if (IN_LEV_FIELD(x-1, y) && IS_MAUER(Feld[x-1][y]))
+         DrawLevelField(x-1, y);
+      }
+      else if (MovDir[x][y] == MV_RIGHT)
+      {
+       if (IN_LEV_FIELD(x+1, y) && IS_MAUER(Feld[x+1][y]))
+         DrawLevelField(x+1, y);
+      }
+      else if (MovDir[x][y] == MV_UP)
+      {
+       if (IN_LEV_FIELD(x, y-1) && IS_MAUER(Feld[x][y-1]))
+         DrawLevelField(x, y-1);
+      }
+      else
+      {
+       if (IN_LEV_FIELD(x, y+1) && IS_MAUER(Feld[x][y+1]))
+         DrawLevelField(x, y+1);
+      }
 
-  return(ACT_GO_ON);
+      Feld[x][y] = Store[x][y];
+      Store[x][y] = 0;
+      MovDir[x][y] = MV_NO_MOVING;
+      DrawLevelField(x, y);
+    }
+  }
 }
 
-void ScrollLevel(int dx, int dy)
+void MauerAbleger(int ax, int ay)
 {
-  int x,y;
+  int element = Feld[ax][ay];
+  boolean oben_frei = FALSE, unten_frei = FALSE;
+  boolean links_frei = FALSE, rechts_frei = FALSE;
+  boolean oben_massiv = FALSE, unten_massiv = FALSE;
+  boolean links_massiv = FALSE, rechts_massiv = FALSE;
 
-  XCopyArea(display,drawto_field,drawto_field,gc,
-           SX+TILEX*(dx==-1),SY+TILEY*(dy==-1),
-           SXSIZE-TILEX*(dx!=0),SYSIZE-TILEY*(dy!=0),
-           SX+TILEX*(dx==1),SY+TILEY*(dy==1));
+  if (!MovDelay[ax][ay])       /* start building new wall */
+    MovDelay[ax][ay] = 6;
 
-  if (dx)
+  if (MovDelay[ax][ay])                /* wait some time before building new wall */
   {
-    x = dx==1 ? 0 : SCR_FIELDX-1;
-    for(y=0;y<SCR_FIELDY;y++)
-      DrawScreenField(x,y);
-  }
-  if (dy)
-  {
-    y = dy==1 ? 0 : SCR_FIELDY-1;
-    for(x=0;x<SCR_FIELDY;x++)
-      DrawScreenField(x,y);
+    MovDelay[ax][ay]--;
+    if (MovDelay[ax][ay])
+      return;
   }
 
-  redraw_mask|=REDRAW_FIELD;
-}
+  if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
+    oben_frei = TRUE;
+  if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
+    unten_frei = TRUE;
+  if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
+    links_frei = TRUE;
+  if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
+    rechts_frei = TRUE;
 
-BOOL MoveFigureOneStep(int dx, int dy)
-{
-  int oldJX,oldJY, newJX = JX+dx,newJY = JY+dy;
+  if (element == EL_MAUER_Y || element == EL_MAUER_XY)
+  {
+    if (oben_frei)
+    {
+      Feld[ax][ay-1] = EL_MAUERND;
+      Store[ax][ay-1] = element;
+      MovDir[ax][ay-1] = MV_UP;
+      if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
+       DrawGraphic(SCREENX(ax), SCREENY(ay-1), GFX_MAUER_UP);
+    }
+    if (unten_frei)
+    {
+      Feld[ax][ay+1] = EL_MAUERND;
+      Store[ax][ay+1] = element;
+      MovDir[ax][ay+1] = MV_DOWN;
+      if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
+       DrawGraphic(SCREENX(ax), SCREENY(ay+1), GFX_MAUER_DOWN);
+    }
+  }
+
+  if (element == EL_MAUER_X || element == EL_MAUER_XY ||
+      element == EL_MAUER_LEBT)
+  {
+    if (links_frei)
+    {
+      Feld[ax-1][ay] = EL_MAUERND;
+      Store[ax-1][ay] = element;
+      MovDir[ax-1][ay] = MV_LEFT;
+      if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
+       DrawGraphic(SCREENX(ax-1), SCREENY(ay), GFX_MAUER_LEFT);
+    }
+    if (rechts_frei)
+    {
+      Feld[ax+1][ay] = EL_MAUERND;
+      Store[ax+1][ay] = element;
+      MovDir[ax+1][ay] = MV_RIGHT;
+      if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
+       DrawGraphic(SCREENX(ax+1), SCREENY(ay), GFX_MAUER_RIGHT);
+    }
+  }
+
+  if (element == EL_MAUER_LEBT && (links_frei || rechts_frei))
+    DrawLevelField(ax, ay);
+
+  if (!IN_LEV_FIELD(ax, ay-1) || IS_MAUER(Feld[ax][ay-1]))
+    oben_massiv = TRUE;
+  if (!IN_LEV_FIELD(ax, ay+1) || IS_MAUER(Feld[ax][ay+1]))
+    unten_massiv = TRUE;
+  if (!IN_LEV_FIELD(ax-1, ay) || IS_MAUER(Feld[ax-1][ay]))
+    links_massiv = TRUE;
+  if (!IN_LEV_FIELD(ax+1, ay) || IS_MAUER(Feld[ax+1][ay]))
+    rechts_massiv = TRUE;
+
+  if (((oben_massiv && unten_massiv) ||
+       element == EL_MAUER_X || element == EL_MAUER_LEBT) &&
+      ((links_massiv && rechts_massiv) ||
+       element == EL_MAUER_Y))
+    Feld[ax][ay] = EL_MAUERWERK;
+}
+
+void CheckForDragon(int x, int y)
+{
+  int i, j;
+  boolean dragon_found = FALSE;
+  static int xy[4][2] =
+  {
+    { 0, -1 },
+    { -1, 0 },
+    { +1, 0 },
+    { 0, +1 }
+  };
+
+  for (i=0; i<4; i++)
+  {
+    for (j=0; j<4; j++)
+    {
+      int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
+
+      if (IN_LEV_FIELD(xx, yy) &&
+         (Feld[xx][yy] == EL_BURNING || Feld[xx][yy] == EL_DRACHE))
+      {
+       if (Feld[xx][yy] == EL_DRACHE)
+         dragon_found = TRUE;
+      }
+      else
+       break;
+    }
+  }
+
+  if (!dragon_found)
+  {
+    for (i=0; i<4; i++)
+    {
+      for (j=0; j<3; j++)
+      {
+       int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
+  
+       if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_BURNING)
+       {
+         Feld[xx][yy] = EL_LEERRAUM;
+         DrawLevelField(xx, yy);
+       }
+       else
+         break;
+      }
+    }
+  }
+}
+
+static void PlayerActions(struct PlayerInfo *player, byte player_action)
+{
+  static byte stored_player_action[MAX_PLAYERS];
+  static int num_stored_actions = 0;
+  static boolean save_tape_entry = FALSE;
+  boolean moved = FALSE, snapped = FALSE, bombed = FALSE;
+  int jx = player->jx, jy = player->jy;
+  int left     = player_action & JOY_LEFT;
+  int right    = player_action & JOY_RIGHT;
+  int up       = player_action & JOY_UP;
+  int down     = player_action & JOY_DOWN;
+  int button1  = player_action & JOY_BUTTON_1;
+  int button2  = player_action & JOY_BUTTON_2;
+  int dx       = (left ? -1    : right ? 1     : 0);
+  int dy       = (up   ? -1    : down  ? 1     : 0);
+
+  stored_player_action[player->index_nr] = 0;
+  num_stored_actions++;
+
+  if (!player->active || player->gone || tape.pausing)
+    return;
+
+  if (player_action)
+  {
+    save_tape_entry = TRUE;
+    player->frame_reset_delay = 0;
+
+    if (button1)
+      snapped = SnapField(player, dx, dy);
+    else
+    {
+      if (button2)
+       bombed = PlaceBomb(player);
+      moved = MoveFigure(player, dx, dy);
+    }
+
+    if (tape.recording && (moved || snapped || bombed))
+    {
+      if (bombed && !moved)
+       player_action &= JOY_BUTTON;
+
+      stored_player_action[player->index_nr] = player_action;
+
+#if 0
+      /* this allows cycled sequences of PlayerActions() */
+      if (num_stored_actions >= MAX_PLAYERS)
+      {
+       TapeRecordAction(stored_player_action);
+       num_stored_actions = 0;
+      }
+#endif
+
+    }
+    else if (tape.playing && snapped)
+      SnapField(player, 0, 0);                 /* stop snapping */
+  }
+  else
+  {
+    DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
+    SnapField(player, 0, 0);
+    if (++player->frame_reset_delay > MoveSpeed)
+      player->Frame = 0;
+  }
+
+  if (tape.recording && num_stored_actions >= MAX_PLAYERS && save_tape_entry)
+  {
+    TapeRecordAction(stored_player_action);
+    num_stored_actions = 0;
+    save_tape_entry = FALSE;
+  }
+
+  if (tape.playing && !tape.pausing && !player_action &&
+      tape.counter < tape.length)
+  {
+    int next_joy =
+      tape.pos[tape.counter].action[player->index_nr] & (JOY_LEFT|JOY_RIGHT);
+
+    if ((next_joy == JOY_LEFT || next_joy == JOY_RIGHT) &&
+       (player->MovDir != JOY_UP && player->MovDir != JOY_DOWN))
+    {
+      int dx = (next_joy == JOY_LEFT ? -1 : +1);
+
+      if (IN_LEV_FIELD(jx+dx, jy) && IS_PUSHABLE(Feld[jx+dx][jy]))
+      {
+       int el = Feld[jx+dx][jy];
+       int push_delay = (IS_SB_ELEMENT(el) || el == EL_SONDE ? 2 : 10);
+
+       if (tape.delay_played + push_delay >= tape.pos[tape.counter].delay)
+       {
+         player->MovDir = next_joy;
+         player->Frame = FrameCounter % 4;
+         player->Pushing = TRUE;
+       }
+      }
+    }
+  }
+}
+
+void GameActions()
+{
+  static unsigned long action_delay = 0;
+  unsigned long action_delay_value;
+  int sieb_x = 0, sieb_y = 0;
+  int i, x, y, element;
+  byte *recorded_player_action;
+  byte summarized_player_action = 0;
+
+  if (game_status != PLAYING)
+    return;
+
+  action_delay_value =
+    (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
+
+  /*
+  if (tape.playing && tape.fast_forward)
+  {
+    char buf[100];
+
+    sprintf(buf, "FFWD: %ld ms", action_delay_value);
+    print_debug(buf);
+  }
+  */
+
+
+  /* main game synchronization point */
+
+
+
+
+#if 1
+  WaitUntilDelayReached(&action_delay, action_delay_value);
+#else
+
+  while (!DelayReached(&action_delay, action_delay_value))
+  {
+    char buf[100];
+
+    sprintf(buf, "%ld %ld %ld",
+           Counter(), action_delay, action_delay_value);
+    print_debug(buf);
+  }
+  print_debug("done");
+
+#endif
+
+
+
+
+  if (network_playing && !network_player_action_received)
+  {
+    /*
+#ifdef DEBUG
+    printf("DEBUG: try to get network player actions in time\n");
+#endif
+    */
+
+#ifndef MSDOS
+    /* last chance to get network player actions without main loop delay */
+    HandleNetworking();
+#endif
+
+    if (game_status != PLAYING)
+      return;
+
+    if (!network_player_action_received)
+    {
+      /*
+#ifdef DEBUG
+      printf("DEBUG: failed to get network player actions in time\n");
+#endif
+      */
+      return;
+    }
+  }
+
+
+  /*
+  if (tape.pausing || (tape.playing && !TapePlayDelay()))
+    return;
+  else if (tape.recording)
+    TapeRecordDelay();
+  */
+
+  if (tape.pausing)
+    return;
+
+  if (tape.playing)
+    TapePlayDelay();
+  else if (tape.recording)
+    TapeRecordDelay();
+
+  recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
+
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+    summarized_player_action |= stored_player[i].action;
+
+    if (!network_playing)
+      stored_player[i].effective_action = stored_player[i].action;
+  }
+
+#ifndef MSDOS
+  if (network_playing)
+    SendToServer_MovePlayer(summarized_player_action);
+#endif
+
+  if (!options.network && !setup.team_mode)
+    local_player->effective_action = summarized_player_action;
+
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+    int actual_player_action = stored_player[i].effective_action;
+
+    if (recorded_player_action)
+      actual_player_action = recorded_player_action[i];
+
+    PlayerActions(&stored_player[i], actual_player_action);
+    ScrollFigure(&stored_player[i], SCROLL_GO_ON);
+  }
+
+  network_player_action_received = FALSE;
+
+  ScrollScreen(NULL, SCROLL_GO_ON);
+
+
+  /*
+  if (tape.pausing || (tape.playing && !TapePlayDelay()))
+    return;
+  else if (tape.recording)
+    TapeRecordDelay();
+  */
+
+
+
+
+
+#ifdef DEBUG
+  /*
+  if (TimeFrames == 0 && !local_player->gone)
+  {
+    extern unsigned int last_RND();
+
+    printf("DEBUG: %03d last RND was %d \t [state checksum is %d]\n",
+          level.time - TimeLeft,
+          last_RND(),
+          getStateCheckSum(level.time - TimeLeft));
+  }
+  */
+#endif
+
+
+
+#ifdef DEBUG
+  /*
+  if (GameFrameDelay >= 500)
+    printf("FrameCounter == %d\n", FrameCounter);
+  */
+#endif
+
+
+
+
+
+  FrameCounter++;
+  TimeFrames++;
+
+  for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+  {
+    Stop[x][y] = FALSE;
+    if (JustHit[x][y]>0)
+      JustHit[x][y]--;
+
+#if DEBUG
+    if (IS_BLOCKED(x, y))
+    {
+      int oldx, oldy;
+
+      Blocked2Moving(x, y, &oldx, &oldy);
+      if (!IS_MOVING(oldx, oldy))
+      {
+       printf("GameActions(): (BLOCKED=>MOVING) context corrupted!\n");
+       printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
+       printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
+       printf("GameActions(): This should never happen!\n");
+      }
+    }
+#endif
+  }
+
+  for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+  {
+    element = Feld[x][y];
+
+    if (IS_INACTIVE(element))
+      continue;
+
+    if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
+    {
+      StartMoving(x, y);
+
+      if (IS_GEM(element))
+       EdelsteinFunkeln(x, y);
+    }
+    else if (IS_MOVING(x, y))
+      ContinueMoving(x, y);
+    else if (element == EL_DYNAMIT || element == EL_DYNABOMB)
+      CheckDynamite(x, y);
+    else if (element == EL_EXPLODING)
+      Explode(x, y, Frame[x][y], EX_NORMAL);
+    else if (element == EL_AMOEBING)
+      AmoebeWaechst(x, y);
+    else if (IS_AMOEBALIVE(element))
+      AmoebeAbleger(x, y);
+    else if (element == EL_LIFE || element == EL_LIFE_ASYNC)
+      Life(x, y);
+    else if (element == EL_ABLENK_EIN)
+      Ablenk(x, y);
+    else if (element == EL_SALZSAEURE)
+      Blubber(x, y);
+    else if (element == EL_BLURB_LEFT || element == EL_BLURB_RIGHT)
+      Blurb(x, y);
+    else if (element == EL_CRACKINGNUT)
+      NussKnacken(x, y);
+    else if (element == EL_AUSGANG_ZU)
+      AusgangstuerPruefen(x, y);
+    else if (element == EL_AUSGANG_ACT)
+      AusgangstuerOeffnen(x, y);
+    else if (element == EL_AUSGANG_AUF)
+      AusgangstuerBlinken(x, y);
+    else if (element == EL_MAUERND)
+      MauerWaechst(x, y);
+    else if (element == EL_MAUER_LEBT ||
+            element == EL_MAUER_X ||
+            element == EL_MAUER_Y ||
+            element == EL_MAUER_XY)
+      MauerAbleger(x, y);
+    else if (element == EL_BURNING)
+      CheckForDragon(x, y);
+
+    if (SiebAktiv)
+    {
+      boolean sieb = FALSE;
+      int jx = local_player->jx, jy = local_player->jy;
+
+      if (element == EL_SIEB_LEER || element == EL_SIEB_VOLL ||
+         Store[x][y] == EL_SIEB_LEER)
+      {
+       SiebAktivieren(x, y, 1);
+       sieb = TRUE;
+      }
+      else if (element == EL_SIEB2_LEER || element == EL_SIEB2_VOLL ||
+              Store[x][y] == EL_SIEB2_LEER)
+      {
+       SiebAktivieren(x, y, 2);
+       sieb = TRUE;
+      }
+
+      /* play the element sound at the position nearest to the player */
+      if (sieb && ABS(x-jx)+ABS(y-jy) < ABS(sieb_x-jx)+ABS(sieb_y-jy))
+      {
+       sieb_x = x;
+       sieb_y = y;
+      }
+    }
+  }
+
+  if (SiebAktiv)
+  {
+    if (!(SiebAktiv%4))
+      PlaySoundLevel(sieb_x, sieb_y, SND_MIEP);
+    SiebAktiv--;
+    if (!SiebAktiv)
+    {
+      for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+      {
+       element = Feld[x][y];
+       if (element == EL_SIEB_LEER || element == EL_SIEB_VOLL)
+       {
+         Feld[x][y] = EL_SIEB_TOT;
+         DrawLevelField(x, y);
+       }
+       else if (element == EL_SIEB2_LEER || element == EL_SIEB2_VOLL)
+       {
+         Feld[x][y] = EL_SIEB2_TOT;
+         DrawLevelField(x, y);
+       }
+      }
+    }
+  }
+
+  if (TimeLeft>0 && TimeFrames>=(1000/GameFrameDelay) && !tape.pausing)
+  {
+    TimeFrames = 0;
+    TimeLeft--;
+
+    if (tape.recording || tape.playing)
+      DrawVideoDisplay(VIDEO_STATE_TIME_ON, level.time-TimeLeft);
+
+    if (TimeLeft<=10)
+      PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
+
+    DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
+
+    if (!TimeLeft)
+      for (i=0; i<MAX_PLAYERS; i++)
+       KillHero(&stored_player[i]);
+  }
+
+  DrawAllPlayers();
+}
+
+static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
+{
+  int min_x = x, min_y = y, max_x = x, max_y = y;
+  int i;
+
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+    int jx = stored_player[i].jx, jy = stored_player[i].jy;
+
+    if (!stored_player[i].active || stored_player[i].gone ||
+       &stored_player[i] == player)
+      continue;
+
+    min_x = MIN(min_x, jx);
+    min_y = MIN(min_y, jy);
+    max_x = MAX(max_x, jx);
+    max_y = MAX(max_y, jy);
+  }
+
+  return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
+}
+
+static boolean AllPlayersInVisibleScreen()
+{
+  int i;
+
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+    int jx = stored_player[i].jx, jy = stored_player[i].jy;
+
+    if (!stored_player[i].active || stored_player[i].gone)
+      continue;
+
+    if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
+      return FALSE;
+  }
+
+  return TRUE;
+}
+
+void ScrollLevel(int dx, int dy)
+{
+  int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
+  int x, y;
+
+  XCopyArea(display, drawto_field, drawto_field, gc,
+           FX + TILEX*(dx == -1) - softscroll_offset,
+           FY + TILEY*(dy == -1) - softscroll_offset,
+           SXSIZE - TILEX*(dx!=0) + 2*softscroll_offset,
+           SYSIZE - TILEY*(dy!=0) + 2*softscroll_offset,
+           FX + TILEX*(dx == 1) - softscroll_offset,
+           FY + TILEY*(dy == 1) - softscroll_offset);
+
+  if (dx)
+  {
+    x = (dx == 1 ? BX1 : BX2);
+    for (y=BY1; y<=BY2; y++)
+      DrawScreenField(x, y);
+  }
+  if (dy)
+  {
+    y = (dy == 1 ? BY1 : BY2);
+    for (x=BX1; x<=BX2; x++)
+      DrawScreenField(x, y);
+  }
+
+  redraw_mask |= REDRAW_FIELD;
+}
+
+boolean MoveFigureOneStep(struct PlayerInfo *player,
+                         int dx, int dy, int real_dx, int real_dy)
+{
+  int jx = player->jx, jy = player->jy;
+  int new_jx = jx+dx, new_jy = jy+dy;
   int element;
   int can_move;
 
-  if (!dx && !dy)
-    return(MF_NO_ACTION);
-  if (!IN_LEV_FIELD(newJX,newJY))
-    return(MF_NO_ACTION);
+  if (player->gone || (!dx && !dy))
+    return MF_NO_ACTION;
 
-  element = MovingOrBlocked2Element(newJX,newJY);
+  player->MovDir = (dx < 0 ? MV_LEFT :
+                   dx > 0 ? MV_RIGHT :
+                   dy < 0 ? MV_UP :
+                   dy > 0 ? MV_DOWN :  MV_NO_MOVING);
+
+  if (!IN_LEV_FIELD(new_jx, new_jy))
+    return MF_NO_ACTION;
+
+  if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
+    return MF_NO_ACTION;
+
+  element = MovingOrBlocked2Element(new_jx, new_jy);
 
   if (DONT_GO_TO(element))
   {
-    if (element==EL_SALZSAEURE && dx==0 && dy==1)
+    if (element == EL_SALZSAEURE && dx == 0 && dy == 1)
+    {
+      Blurb(jx, jy);
+      Feld[jx][jy] = EL_SPIELFIGUR;
+      InitMovingField(jx, jy, MV_DOWN);
+      Store[jx][jy] = EL_SALZSAEURE;
+      ContinueMoving(jx, jy);
+      BuryHero(player);
+    }
+    else
+      KillHero(player);
+
+    return MF_MOVING;
+  }
+
+  can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
+  if (can_move != MF_MOVING)
+    return can_move;
+
+  StorePlayer[jx][jy] = 0;
+  player->last_jx = jx;
+  player->last_jy = jy;
+  jx = player->jx = new_jx;
+  jy = player->jy = new_jy;
+  StorePlayer[jx][jy] = player->element_nr;
+
+  player->MovPos = (dx > 0 || dy > 0 ? -1 : 1) * 7*TILEX/8;
+
+  ScrollFigure(player, SCROLL_INIT);
+
+  return MF_MOVING;
+}
+
+boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
+{
+  int jx = player->jx, jy = player->jy;
+  int old_jx = jx, old_jy = jy;
+  int moved = MF_NO_ACTION;
+
+  if (player->gone || (!dx && !dy))
+    return FALSE;
+
+  if (!FrameReached(&player->move_delay, MoveSpeed) && !tape.playing)
+    return FALSE;
+
+  if (player->MovPos)
+  {
+    /* should only happen if pre-1.2 tape recordings are played */
+    /* this is only for backward compatibility */
+
+#if DEBUG
+    printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES.\n");
+#endif
+
+    while (player->MovPos)
     {
-      Blurb(JX,JY);
-      Feld[JX][JY] = EL_SPIELFIGUR;
-      InitMovingField(JX,JY,MV_DOWN);
-      Store[JX][JY] = EL_SALZSAEURE;
-      ContinueMoving(JX,JY);
+      ScrollFigure(player, SCROLL_GO_ON);
+      ScrollScreen(NULL, SCROLL_GO_ON);
+      FrameCounter++;
+      DrawAllPlayers();
+      BackToFront();
+    }
+  }
 
-      PlaySoundLevel(JX,JY,SND_AUTSCH);
-      PlaySoundLevel(JX,JY,SND_LACHEN);
-      GameOver = TRUE;
-      JX = JY = -1;
+  if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
+  {
+    if (!(moved |= MoveFigureOneStep(player, 0, dy, dx, dy)))
+      moved |= MoveFigureOneStep(player, dx, 0, dx, dy);
+  }
+  else
+  {
+    if (!(moved |= MoveFigureOneStep(player, dx, 0, dx, dy)))
+      moved |= MoveFigureOneStep(player, 0, dy, dx, dy);
+  }
+
+  jx = player->jx;
+  jy = player->jy;
+
+  if (moved & MF_MOVING && !ScreenMovPos &&
+      (player == local_player || !options.network))
+  {
+    int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
+    int offset = (setup.scroll_delay ? 3 : 0);
+
+    if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
+    {
+      /* actual player has left the screen -- scroll in that direction */
+      if (jx != old_jx)                /* player has moved horizontally */
+       scroll_x += (jx - old_jx);
+      else                     /* player has moved vertically */
+       scroll_y += (jy - old_jy);
     }
     else
-      KillHero();
+    {
+      if (jx != old_jx)                /* player has moved horizontally */
+      {
+       if ((player->MovDir == MV_LEFT && scroll_x > jx-MIDPOSX+offset) ||
+           (player->MovDir == MV_RIGHT && scroll_x < jx-MIDPOSX-offset))
+         scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
+
+       /* don't scroll over playfield boundaries */
+       if (scroll_x < -1 || scroll_x > lev_fieldx - SCR_FIELDX + 1)
+         scroll_x = (scroll_x < -1 ? -1 : lev_fieldx - SCR_FIELDX + 1);
+
+       /* don't scroll more than one field at a time */
+       scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
+
+       /* don't scroll against the player's moving direction */
+       if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
+           (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
+         scroll_x = old_scroll_x;
+      }
+      else                     /* player has moved vertically */
+      {
+       if ((player->MovDir == MV_UP && scroll_y > jy-MIDPOSY+offset) ||
+           (player->MovDir == MV_DOWN && scroll_y < jy-MIDPOSY-offset))
+         scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
+
+       /* don't scroll over playfield boundaries */
+       if (scroll_y < -1 || scroll_y > lev_fieldy - SCR_FIELDY + 1)
+         scroll_y = (scroll_y < -1 ? -1 : lev_fieldy - SCR_FIELDY + 1);
+
+       /* don't scroll more than one field at a time */
+       scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
+
+       /* don't scroll against the player's moving direction */
+       if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
+           (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
+         scroll_y = old_scroll_y;
+      }
+    }
+
+    if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
+    {
+      if (!options.network && !AllPlayersInVisibleScreen())
+      {
+       scroll_x = old_scroll_x;
+       scroll_y = old_scroll_y;
+      }
+      else
+      {
+       ScrollScreen(player, SCROLL_INIT);
+       ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
+      }
+    }
+  }
+
+  if (!(moved & MF_MOVING) && !player->Pushing)
+    player->Frame = 0;
+  else
+    player->Frame = (player->Frame + 1) % 4;
+
+  if (moved & MF_MOVING)
+  {
+    if (old_jx != jx && old_jy == jy)
+      player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
+    else if (old_jx == jx && old_jy != jy)
+      player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
+
+    DrawLevelField(jx, jy);    /* for "ErdreichAnbroeckeln()" */
+
+    player->last_move_dir = player->MovDir;
+  }
+  else
+    player->last_move_dir = MV_NO_MOVING;
+
+  TestIfHeroHitsBadThing(jx, jy);
+
+  if (player->gone)
+    RemoveHero(player);
+
+  return moved;
+}
+
+void ScrollFigure(struct PlayerInfo *player, int mode)
+{
+  int jx = player->jx, jy = player->jy;
+  int last_jx = player->last_jx, last_jy = player->last_jy;
+
+  if (!player->active || player->gone || !player->MovPos)
+    return;
+
+  if (mode == SCROLL_INIT)
+  {
+    player->actual_frame_counter = FrameCounter;
+    player->GfxPos = ScrollStepSize * (player->MovPos / ScrollStepSize);
+
+    if (Feld[last_jx][last_jy] == EL_LEERRAUM)
+      Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
+
+    DrawPlayer(player);
+    return;
+  }
+  else if (!FrameReached(&player->actual_frame_counter, 1))
+    return;
+
+  player->MovPos += (player->MovPos > 0 ? -1 : 1) * TILEX/8;
+  player->GfxPos = ScrollStepSize * (player->MovPos / ScrollStepSize);
+
+  if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
+    Feld[last_jx][last_jy] = EL_LEERRAUM;
+
+  DrawPlayer(player);
+
+  if (!player->MovPos)
+  {
+    player->last_jx = jx;
+    player->last_jy = jy;
+
+    if (Feld[jx][jy] == EL_AUSGANG_AUF)
+    {
+      RemoveHero(player);
 
-    return(MF_MOVING);
+      if (!local_player->friends_still_needed)
+       player->LevelSolved = player->GameOver = TRUE;
+    }
   }
+}
 
-  can_move = DigField(newJX,newJY,DF_DIG);
-  if (can_move != MF_MOVING)
-    return(can_move);
+void ScrollScreen(struct PlayerInfo *player, int mode)
+{
+  static unsigned long screen_frame_counter = 0;
 
-  oldJX = JX;
-  oldJY = JY;
-  JX = newJX;
-  JY = newJY;
+  if (mode == SCROLL_INIT)
+  {
+    screen_frame_counter = FrameCounter;
+    ScreenMovDir = player->MovDir;
+    ScreenMovPos = player->MovPos;
+    ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
+    return;
+  }
+  else if (!FrameReached(&screen_frame_counter, 1))
+    return;
 
-  if (Store[oldJX][oldJY])
+  if (ScreenMovPos)
   {
-    DrawGraphic(SCROLLX(oldJX),SCROLLY(oldJY),el2gfx(Store[oldJX][oldJY]));
-    DrawGraphicThruMask(SCROLLX(oldJX),SCROLLY(oldJY),
-                       el2gfx(Feld[oldJX][oldJY]));
+    ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * TILEX/8;
+    ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
+    redraw_mask |= REDRAW_FIELD;
   }
-  else if (Feld[oldJX][oldJY]==EL_DYNAMIT)
-    DrawDynamite(oldJX,oldJY);
   else
-    DrawLevelField(oldJX,oldJY);
-
-  return(MF_MOVING);
+    ScreenMovDir = MV_NO_MOVING;
 }
 
-BOOL MoveFigure(int dx, int dy)
+void TestIfGoodThingHitsBadThing(int goodx, int goody)
 {
-  static long move_delay = 0;
-  int moved = MF_NO_ACTION;
-
-  if (GameOver || (!dx && !dy))
-    return(FALSE);
-
-  if (!DelayReached(&move_delay,10) && !tape.playing)
-    return(FALSE);
-
-  if (moved |= MoveFigureOneStep(dx,0))
-    moved |= MoveFigureOneStep(0,dy);
-  else
+  int i, killx = goodx, killy = goody;
+  static int xy[4][2] =
   {
-    moved |= MoveFigureOneStep(0,dy);
-    moved |= MoveFigureOneStep(dx,0);
-  }
-
-  if (moved & MF_MOVING)
+    { 0, -1 },
+    { -1, 0 },
+    { +1, 0 },
+    { 0, +1 }
+  };
+  static int harmless[4] =
   {
-    int old_scroll_x=scroll_x, old_scroll_y=scroll_y;
-
-    if (scroll_x!=JX-MIDPOSX && JX>=MIDPOSX-1 && JX<=lev_fieldx-MIDPOSX)
-      scroll_x = JX-MIDPOSX;
-    if (scroll_y!=JY-MIDPOSY && JY>=MIDPOSY-1 && JY<=lev_fieldy-MIDPOSY)
-      scroll_y = JY-MIDPOSY;
+    MV_UP,
+    MV_LEFT,
+    MV_RIGHT,
+    MV_DOWN
+  };
 
-    if (scroll_x!=old_scroll_x || scroll_y!=old_scroll_y)
-      ScrollLevel(old_scroll_x-scroll_x,old_scroll_y-scroll_y);
+  for (i=0; i<4; i++)
+  {
+    int x, y, element;
 
-    if (Feld[JX][JY]==EL_LEERRAUM)
-      DrawLevelElement(JX,JY,EL_SPIELFIGUR);
-    else
-      DrawGraphicThruMask(SCROLLX(JX),SCROLLY(JY),GFX_SPIELFIGUR);
-  }
+    x = goodx + xy[i][0];
+    y = goody + xy[i][1];
+    if (!IN_LEV_FIELD(x, y))
+      continue;
 
-  TestIfHeroHitsBadThing();
+    element = Feld[x][y];
 
-  BackToFront();
+    if (DONT_TOUCH(element))
+    {
+      if (MovDir[x][y] == harmless[i])
+       continue;
 
-  if (LevelSolved)
-    GameWon();
+      killx = x;
+      killy = y;
+      break;
+    }
+  }
 
-  return(moved);
+  if (killx != goodx || killy != goody)
+  {
+    if (IS_PLAYER(goodx, goody))
+      KillHero(PLAYERINFO(goodx, goody));
+    else
+      Bang(goodx, goody);
+  }
 }
 
-void TestIfHeroHitsBadThing()
+void TestIfBadThingHitsGoodThing(int badx, int bady)
 {
-  int i, killx = JX,killy = JY;
+  int i, killx = badx, killy = bady;
   static int xy[4][2] =
   {
-    0,-1,
-    -1,0,
-    +1,0,
-    0,+1
+    { 0, -1 },
+    { -1, 0 },
+    { +1, 0 },
+    { 0, +1 }
   };
   static int harmless[4] =
   {
@@ -1976,20 +3774,26 @@ void TestIfHeroHitsBadThing()
     MV_DOWN
   };
 
-  for(i=0;i<4;i++)
+  for (i=0; i<4; i++)
   {
-    int x,y,element;
+    int x, y, element;
 
-    x = JX+xy[i][0];
-    y = JY+xy[i][1];
-    if (!IN_LEV_FIELD(x,y))
+    x = badx + xy[i][0];
+    y = bady + xy[i][1];
+    if (!IN_LEV_FIELD(x, y))
       continue;
 
     element = Feld[x][y];
 
-    if (DONT_TOUCH(element))
+    if (IS_PLAYER(x, y))
+    {
+      killx = x;
+      killy = y;
+      break;
+    }
+    else if (element == EL_PINGUIN)
     {
-      if (MovDir[x][y]==harmless[i])
+      if (MovDir[x][y] == harmless[i] && IS_MOVING(x, y))
        continue;
 
       killx = x;
@@ -1998,118 +3802,195 @@ void TestIfHeroHitsBadThing()
     }
   }
 
-  if (killx!=JX || killy!=JY)
-    KillHero();
+  if (killx != badx || killy != bady)
+  {
+    if (IS_PLAYER(killx, killy))
+      KillHero(PLAYERINFO(killx, killy));
+    else
+      Bang(killx, killy);
+  }
+}
+
+void TestIfHeroHitsBadThing(int x, int y)
+{
+  TestIfGoodThingHitsBadThing(x, y);
+}
+
+void TestIfBadThingHitsHero(int x, int y)
+{
+  TestIfBadThingHitsGoodThing(x, y);
+}
+
+void TestIfFriendHitsBadThing(int x, int y)
+{
+  TestIfGoodThingHitsBadThing(x, y);
 }
 
-void TestIfBadThingHitsHero()
+void TestIfBadThingHitsFriend(int x, int y)
 {
-  TestIfHeroHitsBadThing();
+  TestIfBadThingHitsGoodThing(x, y);
 }
 
 void TestIfBadThingHitsOtherBadThing(int badx, int bady)
 {
-  int i, killx=badx, killy=bady;
+  int i, killx = badx, killy = bady;
   static int xy[4][2] =
   {
-    0,-1,
-    -1,0,
-    +1,0,
-    0,+1
+    { 0, -1 },
+    { -1, 0 },
+    { +1, 0 },
+    { 0, +1 }
   };
 
-  for(i=0;i<4;i++)
+  for (i=0; i<4; i++)
   {
-    int x,y,element;
+    int x, y, element;
 
-    x=badx+xy[i][0];
-    y=bady+xy[i][1];
-    if (!IN_LEV_FIELD(x,y))
+    x=badx + xy[i][0];
+    y=bady + xy[i][1];
+    if (!IN_LEV_FIELD(x, y))
       continue;
 
-    element=Feld[x][y];
-    if (IS_AMOEBOID(element) || element==EL_LIFE ||
-       element==EL_AMOEBING || element==EL_TROPFEN)
+    element = Feld[x][y];
+    if (IS_AMOEBOID(element) || element == EL_LIFE ||
+       element == EL_AMOEBING || element == EL_TROPFEN)
     {
-      killx=x;
-      killy=y;
+      killx = x;
+      killy = y;
       break;
     }
   }
 
-  if (killx!=badx || killy!=bady)
-    Bang(badx,bady);
+  if (killx != badx || killy != bady)
+    Bang(badx, bady);
 }
 
-void KillHero()
+void KillHero(struct PlayerInfo *player)
 {
-  if (PLAYER(-1,-1))
+  int jx = player->jx, jy = player->jy;
+
+  if (player->gone)
+    return;
+
+  if (IS_PFORTE(Feld[jx][jy]))
+    Feld[jx][jy] = EL_LEERRAUM;
+
+  Bang(jx, jy);
+  BuryHero(player);
+}
+
+void BuryHero(struct PlayerInfo *player)
+{
+  int jx = player->jx, jy = player->jy;
+
+  if (player->gone)
     return;
 
-  if (IS_PFORTE(Feld[JX][JY]))
-    Feld[JX][JY] = EL_LEERRAUM;
+  PlaySoundLevel(jx, jy, SND_AUTSCH);
+  PlaySoundLevel(jx, jy, SND_LACHEN);
 
-  PlaySoundLevel(JX,JY,SND_AUTSCH);
-  PlaySoundLevel(JX,JY,SND_LACHEN);
-  Bang(JX,JY);
-  GameOver = TRUE;
-  JX = JY = -1;
+  player->GameOver = TRUE;
+  RemoveHero(player);
 }
 
-int DigField(int x, int y, int mode)
+void RemoveHero(struct PlayerInfo *player)
 {
-  int dx=x-JX, dy=y-JY;
+  int jx = player->jx, jy = player->jy;
+  int i, found = FALSE;
+
+  player->gone = TRUE;
+  StorePlayer[jx][jy] = 0;
+
+  for (i=0; i<MAX_PLAYERS; i++)
+    if (stored_player[i].active && !stored_player[i].gone)
+      found = TRUE;
+
+  if (!found)
+    AllPlayersGone = TRUE;
+
+  ExitX = ZX = jx;
+  ExitY = ZY = jy;
+}
+
+int DigField(struct PlayerInfo *player,
+            int x, int y, int real_dx, int real_dy, int mode)
+{
+  int jx = player->jx, jy = player->jy;
+  int dx = x - jx, dy = y - jy;
   int element;
-  static long push_delay = 0;
-  static int push_delay_value = 20;
 
-  if (mode==DF_NO_PUSH)
+  if (!player->MovPos)
+    player->Pushing = FALSE;
+
+  if (mode == DF_NO_PUSH)
   {
-    push_delay = 0;
-    return(MF_NO_ACTION);
+    player->push_delay = 0;
+    return MF_NO_ACTION;
   }
 
-  if (IS_MOVING(x,y))
-    return(MF_NO_ACTION);
+  if (IS_MOVING(x, y) || IS_PLAYER(x, y))
+    return MF_NO_ACTION;
 
   element = Feld[x][y];
 
   switch(element)
   {
     case EL_LEERRAUM:
-      CheckMoving=TRUE;
       break;
+
     case EL_ERDREICH:
-      Feld[x][y]=EL_LEERRAUM;
-      CheckMoving=TRUE;
+      Feld[x][y] = EL_LEERRAUM;
       break;
+
     case EL_EDELSTEIN:
-      Feld[x][y]=EL_LEERRAUM;
-      CheckMoving=TRUE;
-      if (Gems>0)
-       Gems--;
-      RaiseScore(level.score[SC_EDELSTEIN]);
-      DrawText(DX_EMERALDS,DY_EMERALDS,int2str(Gems,3),FS_SMALL,FC_YELLOW);
-      PlaySoundLevel(x,y,SND_PONG);
-      break;
+    case EL_EDELSTEIN_BD:
+    case EL_EDELSTEIN_GELB:
+    case EL_EDELSTEIN_ROT:
+    case EL_EDELSTEIN_LILA:
     case EL_DIAMANT:
-      Feld[x][y]=EL_LEERRAUM;
-      CheckMoving=TRUE;
-      Gems -= 3;
-      if (Gems<0)
-       Gems=0;
-      RaiseScore(level.score[SC_DIAMANT]);
-      DrawText(DX_EMERALDS,DY_EMERALDS,int2str(Gems,3),FS_SMALL,FC_YELLOW);
-      PlaySoundLevel(x,y,SND_PONG);
+      RemoveField(x, y);
+      local_player->gems_still_needed -= (element == EL_DIAMANT ? 3 : 1);
+      if (local_player->gems_still_needed < 0)
+       local_player->gems_still_needed = 0;
+      RaiseScoreElement(element);
+      DrawText(DX_EMERALDS, DY_EMERALDS,
+              int2str(local_player->gems_still_needed, 3),
+              FS_SMALL, FC_YELLOW);
+      PlaySoundLevel(x, y, SND_PONG);
       break;
+
     case EL_DYNAMIT_AUS:
-      Feld[x][y]=EL_LEERRAUM;
-      CheckMoving=TRUE;
-      Dynamite++;
-      RaiseScore(level.score[SC_DYNAMIT]);
-      DrawText(DX_DYNAMITE,DY_DYNAMITE,int2str(Dynamite,3),FS_SMALL,FC_YELLOW);
-      PlaySoundLevel(x,y,SND_PONG);
+      RemoveField(x, y);
+      player->dynamite++;
+      RaiseScoreElement(EL_DYNAMIT);
+      DrawText(DX_DYNAMITE, DY_DYNAMITE,
+              int2str(local_player->dynamite, 3),
+              FS_SMALL, FC_YELLOW);
+      PlaySoundLevel(x, y, SND_PONG);
       break;
+
+    case EL_DYNABOMB_NR:
+      RemoveField(x, y);
+      player->dynabomb_count++;
+      player->dynabombs_left++;
+      RaiseScoreElement(EL_DYNAMIT);
+      PlaySoundLevel(x, y, SND_PONG);
+      break;
+
+    case EL_DYNABOMB_SZ:
+      RemoveField(x, y);
+      player->dynabomb_size++;
+      RaiseScoreElement(EL_DYNAMIT);
+      PlaySoundLevel(x, y, SND_PONG);
+      break;
+
+    case EL_DYNABOMB_XL:
+      RemoveField(x, y);
+      player->dynabomb_xl = TRUE;
+      RaiseScoreElement(EL_DYNAMIT);
+      PlaySoundLevel(x, y, SND_PONG);
+      break;
+
     case EL_SCHLUESSEL1:
     case EL_SCHLUESSEL2:
     case EL_SCHLUESSEL3:
@@ -2117,363 +3998,373 @@ int DigField(int x, int y, int mode)
     {
       int key_nr = element-EL_SCHLUESSEL1;
 
-      Feld[x][y] = EL_LEERRAUM;
-      CheckMoving = TRUE;
-      Key[key_nr] = TRUE;
-      RaiseScore(level.score[SC_SCHLUESSEL]);
-      DrawMiniGraphicExtHiRes(drawto,gc,
-                             DX_KEYS+key_nr*MINI_TILEX,DY_KEYS,
-                             GFX_SCHLUESSEL1+key_nr);
-      DrawMiniGraphicExtHiRes(window,gc,
-                             DX_KEYS+key_nr*MINI_TILEX,DY_KEYS,
-                             GFX_SCHLUESSEL1+key_nr);
-      PlaySoundLevel(x,y,SND_PONG);
+      RemoveField(x, y);
+      player->key[key_nr] = TRUE;
+      RaiseScoreElement(EL_SCHLUESSEL);
+      DrawMiniGraphicExt(drawto, gc,
+                        DX_KEYS+key_nr*MINI_TILEX, DY_KEYS,
+                        GFX_SCHLUESSEL1+key_nr);
+      DrawMiniGraphicExt(window, gc,
+                        DX_KEYS+key_nr*MINI_TILEX, DY_KEYS,
+                        GFX_SCHLUESSEL1+key_nr);
+      PlaySoundLevel(x, y, SND_PONG);
       break;
     }
+
     case EL_ABLENK_AUS:
       Feld[x][y] = EL_ABLENK_EIN;
-      CheckExploding=TRUE;
-      ZX=x;
-      ZY=y;
-      DrawLevelField(x,y);
-      return(MF_ACTION);
+      ZX = x;
+      ZY = y;
+      DrawLevelField(x, y);
+      return MF_ACTION;
       break;
+
     case EL_FELSBROCKEN:
     case EL_BOMBE:
     case EL_KOKOSNUSS:
     case EL_ZEIT_LEER:
-      if (mode==DF_SNAP)
-       return(MF_NO_ACTION);
-      if (dy || !IN_LEV_FIELD(x+dx,y+dy) || Feld[x+dx][y+dy]!=EL_LEERRAUM)
-       return(MF_NO_ACTION);
+      if (dy || mode == DF_SNAP)
+       return MF_NO_ACTION;
 
-      if (Counter() > push_delay+4*push_delay_value)
-       push_delay = Counter();
-      if (!DelayReached(&push_delay,push_delay_value) && !tape.playing)
-       return(MF_NO_ACTION);
+      player->Pushing = TRUE;
 
-      Feld[x][y] = EL_LEERRAUM;
+      if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy))
+       return MF_NO_ACTION;
+
+      if (real_dy)
+      {
+       if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
+         return MF_NO_ACTION;
+      }
+
+      if (player->push_delay == 0)
+       player->push_delay = FrameCounter;
+      if (!FrameReached(&player->push_delay, player->push_delay_value) &&
+         !tape.playing)
+       return MF_NO_ACTION;
+
+      RemoveField(x, y);
       Feld[x+dx][y+dy] = element;
-      push_delay_value = 10+RND(30);
-      CheckMoving = TRUE;
-      DrawLevelField(x+dx,y+dy);
-      if (element==EL_FELSBROCKEN)
-       PlaySoundLevel(x+dx,y+dy,SND_PUSCH);
-      else if (element==EL_KOKOSNUSS)
-       PlaySoundLevel(x+dx,y+dy,SND_KNURK);
+
+      player->push_delay_value = 2+RND(8);
+
+      DrawLevelField(x+dx, y+dy);
+      if (element == EL_FELSBROCKEN)
+       PlaySoundLevel(x+dx, y+dy, SND_PUSCH);
+      else if (element == EL_KOKOSNUSS)
+       PlaySoundLevel(x+dx, y+dy, SND_KNURK);
       else
-       PlaySoundLevel(x+dx,y+dy,SND_KLOPF);
+       PlaySoundLevel(x+dx, y+dy, SND_KLOPF);
       break;
+
     case EL_PFORTE1:
     case EL_PFORTE2:
     case EL_PFORTE3:
     case EL_PFORTE4:
-      if (!Key[element-EL_PFORTE1])
-       return(MF_NO_ACTION);
+      if (!player->key[element-EL_PFORTE1])
+       return MF_NO_ACTION;
       break;
+
     case EL_PFORTE1X:
     case EL_PFORTE2X:
     case EL_PFORTE3X:
     case EL_PFORTE4X:
-      if (!Key[element-EL_PFORTE1X])
-       return(MF_NO_ACTION);
+      if (!player->key[element-EL_PFORTE1X])
+       return MF_NO_ACTION;
       break;
+
     case EL_AUSGANG_ZU:
     case EL_AUSGANG_ACT:
-      /* Tür ist (noch) nicht offen! */
-      return(MF_NO_ACTION);
+      /* door is not (yet) open */
+      return MF_NO_ACTION;
       break;
+
     case EL_AUSGANG_AUF:
-      if (mode==DF_SNAP || Gems>0)
-       return(MF_NO_ACTION);
-      LevelSolved = GameOver = TRUE;
-      PlaySoundLevel(x,y,SND_BUING);
+      if (mode == DF_SNAP)
+       return MF_NO_ACTION;
+
+      PlaySoundLevel(x, y, SND_BUING);
+
+      /*
+      player->gone = TRUE;
+      PlaySoundLevel(x, y, SND_BUING);
+
+      if (!local_player->friends_still_needed)
+       player->LevelSolved = player->GameOver = TRUE;
+      */
+
       break;
+
     case EL_BIRNE_AUS:
       Feld[x][y] = EL_BIRNE_EIN;
-      DrawLevelField(x,y);
-      PlaySoundLevel(x,y,SND_DENG);
-      return(MF_ACTION);
+      local_player->lights_still_needed--;
+      DrawLevelField(x, y);
+      PlaySoundLevel(x, y, SND_DENG);
+      return MF_ACTION;
       break;
+
     case EL_ZEIT_VOLL:
       Feld[x][y] = EL_ZEIT_LEER;
-      DrawLevelField(x,y);
-      PlaySoundStereo(SND_GONG,PSND_MAX_RIGHT);
-      return(MF_ACTION);
+      TimeLeft += 10;
+      DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
+      DrawLevelField(x, y);
+      PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
+      return MF_ACTION;
       break;
-    default:
-      return(MF_NO_ACTION);
+
+    case EL_SOKOBAN_FELD_LEER:
       break;
-  }
-  push_delay=0;
-  return(MF_MOVING);
-}
 
-BOOL SnapField(int dx, int dy)
-{
-  int x = JX+dx, y = JY+dy;
-  static int snapped = FALSE;
+    case EL_SOKOBAN_FELD_VOLL:
+    case EL_SOKOBAN_OBJEKT:
+    case EL_SONDE:
+      if (mode == DF_SNAP)
+       return MF_NO_ACTION;
 
-  if (GameOver || !IN_LEV_FIELD(x,y))
-    return(FALSE);
-  if (dx && dy)
-    return(FALSE);
-  if (!dx && !dy)
-  {
-    snapped = FALSE;
-    return(FALSE);
-  }
-  if (snapped)
-    return(FALSE);
+      player->Pushing = TRUE;
 
-  if (!DigField(x,y,DF_SNAP))
-    return(FALSE);
+      if (!IN_LEV_FIELD(x+dx, y+dy)
+         || (!IS_FREE(x+dx, y+dy)
+             && (Feld[x+dx][y+dy] != EL_SOKOBAN_FELD_LEER
+                 || !IS_SB_ELEMENT(element))))
+       return MF_NO_ACTION;
 
-  snapped = TRUE;
-  DrawLevelField(x,y);
-  BackToFront();
+      if (dx && real_dy)
+      {
+       if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
+         return MF_NO_ACTION;
+      }
+      else if (dy && real_dx)
+      {
+       if (IN_LEV_FIELD(jx+real_dx, jy) && !IS_SOLID(Feld[jx+real_dx][jy]))
+         return MF_NO_ACTION;
+      }
 
-  return(TRUE);
-}
+      if (player->push_delay == 0)
+       player->push_delay = FrameCounter;
+      if (!FrameReached(&player->push_delay, player->push_delay_value) &&
+         !tape.playing)
+       return MF_NO_ACTION;
 
-BOOL PlaceBomb(void)
-{
-  if (Dynamite==0 || Feld[JX][JY]==EL_DYNAMIT)
-    return(FALSE);
+      if (IS_SB_ELEMENT(element))
+      {
+       if (element == EL_SOKOBAN_FELD_VOLL)
+       {
+         Feld[x][y] = EL_SOKOBAN_FELD_LEER;
+         local_player->sokobanfields_still_needed++;
+       }
+       else
+         RemoveField(x, y);
 
-  if (Feld[JX][JY]!=EL_LEERRAUM)
-    Store[JX][JY] = Feld[JX][JY];
-  Feld[JX][JY] = EL_DYNAMIT;
-  MovDelay[JX][JY] = 48;
-  Dynamite--;
-  DrawText(DX_DYNAMITE,DY_DYNAMITE,int2str(Dynamite,3),FS_SMALL,FC_YELLOW);
-  DrawGraphicThruMask(SCROLLX(JX),SCROLLY(JY),GFX_DYNAMIT);
-  CheckExploding = TRUE;
-  return(TRUE);
-}
+       if (Feld[x+dx][y+dy] == EL_SOKOBAN_FELD_LEER)
+       {
+         Feld[x+dx][y+dy] = EL_SOKOBAN_FELD_VOLL;
+         local_player->sokobanfields_still_needed--;
+         if (element == EL_SOKOBAN_OBJEKT)
+           PlaySoundLevel(x, y, SND_DENG);
+       }
+       else
+         Feld[x+dx][y+dy] = EL_SOKOBAN_OBJEKT;
+      }
+      else
+      {
+       RemoveField(x, y);
+       Feld[x+dx][y+dy] = element;
+      }
 
-void PlaySoundLevel(int x, int y, int sound_nr)
-{
-  int sx = SCROLLX(x), sy = SCROLLY(y);
-  int volume, stereo;
+      player->push_delay_value = 2;
 
-  if (!sound_loops_on && IS_LOOP_SOUND(sound_nr))
-    return;
+      DrawLevelField(x, y);
+      DrawLevelField(x+dx, y+dy);
+      PlaySoundLevel(x+dx, y+dy, SND_PUSCH);
 
-  if (!IN_LEV_FIELD(x,y))
-    return;
+      if (IS_SB_ELEMENT(element) &&
+         local_player->sokobanfields_still_needed == 0 &&
+         game_emulation == EMU_SOKOBAN)
+      {
+       player->LevelSolved = player->GameOver = TRUE;
+       PlaySoundLevel(x, y, SND_BUING);
+      }
 
-  volume = PSND_MAX_VOLUME;
-  stereo = (sx-SCR_FIELDX/2)*12;
+      break;
 
-  if (!IN_SCR_FIELD(sx,sy))
-  {
-    if (sx<0 || sx>=SCR_FIELDX)
-      volume = PSND_MAX_VOLUME - 2*ABS(sx-SCR_FIELDX/2);
-    else
-      volume = PSND_MAX_VOLUME - 2*ABS(sy-SCR_FIELDY/2);
+    case EL_MAULWURF:
+    case EL_PINGUIN:
+    case EL_SCHWEIN:
+    case EL_DRACHE:
+      break;
+
+    default:
+      return MF_NO_ACTION;
+      break;
   }
 
-  PlaySoundExt(sound_nr, volume, stereo, PSND_NO_LOOP);
-}
+  player->push_delay = 0;
 
-void RaiseScore(int value)
-{
-  Score += value;
-  DrawText(DX_SCORE,DY_SCORE,int2str(Score,5),FS_SMALL,FC_YELLOW);
-  BackToFront();
+  return MF_MOVING;
 }
 
-void TapeInitRecording()
+boolean SnapField(struct PlayerInfo *player, int dx, int dy)
 {
-  time_t zeit1 = time(NULL);
-  struct tm *zeit2 = localtime(&zeit1);
-
-  if (tape.recording || tape.playing)
-    return;
+  int jx = player->jx, jy = player->jy;
+  int x = jx + dx, y = jy + dy;
 
-  tape.level_nr = level_nr;
-  tape.recording = TRUE;
-  tape.pausing = TRUE;
-  tape.date =
-    10000*(zeit2->tm_year%100) + 100*zeit2->tm_mon + zeit2->tm_mday;
+  if (player->gone || !IN_LEV_FIELD(x, y))
+    return FALSE;
 
-  DrawVideoDisplay(VIDEO_STATE_REC_ON | VIDEO_STATE_PAUSE_ON,0);
-  DrawVideoDisplay(VIDEO_STATE_DATE_ON,tape.date);
-  DrawVideoDisplay(VIDEO_STATE_TIME_ON,0);
-}
-
-void TapeStartRecording()
-{
-  tape.length = 0;
-  tape.counter = 0;
-  tape.pos[tape.counter].delay = 0;
-  tape.recording = TRUE;
-  tape.playing = FALSE;
-  tape.pausing = FALSE;
-  tape.random_seed = InitRND(NEW_RANDOMIZE);
-  DrawVideoDisplay(VIDEO_STATE_REC_ON | VIDEO_STATE_PAUSE_OFF,0);
-}
+  if (dx && dy)
+    return FALSE;
 
-void TapeStopRecording()
-{
-  if (!tape.recording)
-    return;
+  if (!dx && !dy)
+  {
+    player->snapped = FALSE;
+    return FALSE;
+  }
 
-  tape.length = tape.counter;
-  tape.recording = FALSE;
-  tape.pausing = FALSE;
-  DrawVideoDisplay(VIDEO_STATE_REC_OFF,0);
+  if (player->snapped)
+    return FALSE;
 
-  master_tape = tape;
-}
+  player->MovDir = (dx < 0 ? MV_LEFT :
+                   dx > 0 ? MV_RIGHT :
+                   dy < 0 ? MV_UP :
+                   dy > 0 ? MV_DOWN :  MV_NO_MOVING);
 
-void TapeRecordAction(int joy)
-{
-  if (!tape.recording || tape.pausing)
-    return;
+  if (!DigField(player, x, y, 0, 0, DF_SNAP))
+    return FALSE;
 
-  if (tape.counter>=MAX_TAPELEN-1)
-  {
-    TapeStopRecording();
-    return;
-  }
+  player->snapped = TRUE;
+  DrawLevelField(x, y);
+  BackToFront();
 
-  if (joy)
-  {
-    tape.pos[tape.counter].joystickdata = joy;
-    tape.counter++;
-    tape.pos[tape.counter].delay = 0;
-  }
+  return TRUE;
 }
 
-void TapeRecordDelay()
+boolean PlaceBomb(struct PlayerInfo *player)
 {
-  if (!tape.recording || tape.pausing)
-    return;
+  int jx = player->jx, jy = player->jy;
+  int element;
 
-  if (tape.counter>=MAX_TAPELEN)
-  {
-    TapeStopRecording();
-    return;
-  }
+  if (player->gone || player->MovPos)
+    return FALSE;
 
-  tape.pos[tape.counter].delay++;
+  element = Feld[jx][jy];
 
-  if (tape.pos[tape.counter].delay>=255)
-  {
-    tape.pos[tape.counter].joystickdata = 0;
-    tape.counter++;
-    tape.pos[tape.counter].delay = 0;
-  }
-}
+  if ((player->dynamite == 0 && player->dynabombs_left == 0) ||
+      element == EL_DYNAMIT || element == EL_DYNABOMB ||
+      element == EL_EXPLODING)
+    return FALSE;
 
-void TapeTogglePause()
-{
-  if (!tape.recording && !tape.playing)
-    return;
+  if (element != EL_LEERRAUM)
+    Store[jx][jy] = element;
 
-  if (tape.pausing)
+  if (player->dynamite)
   {
-    tape.pausing = FALSE;
-    DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
-    if (game_status==MAINMENU)
-      HandleMainMenu(SX+16,SY+7*32+16,0,0,MB_MENU_CHOICE);
+    Feld[jx][jy] = EL_DYNAMIT;
+    MovDelay[jx][jy] = 96;
+    player->dynamite--;
+    DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->dynamite, 3),
+            FS_SMALL, FC_YELLOW);
+    if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
+      DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), GFX_DYNAMIT);
   }
   else
   {
-    tape.pausing = TRUE;
-    DrawVideoDisplay(VIDEO_STATE_PAUSE_ON,0);
+    Feld[jx][jy] = EL_DYNABOMB;
+    Store2[jx][jy] = player->element_nr;       /* for DynaExplode() */
+    MovDelay[jx][jy] = 96;
+    player->dynabombs_left--;
+    if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
+      DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), GFX_DYNABOMB);
   }
-}
-
-void TapeInitPlaying()
-{
-  if (tape.recording || tape.playing || TAPE_IS_EMPTY(tape))
-    return;
 
-  tape.playing = TRUE;
-  tape.pausing = TRUE;
-  DrawVideoDisplay(VIDEO_STATE_PLAY_ON | VIDEO_STATE_PAUSE_ON,0);
-  DrawVideoDisplay(VIDEO_STATE_DATE_ON,tape.date);
-  DrawVideoDisplay(VIDEO_STATE_TIME_ON,0);
+  return TRUE;
 }
 
-void TapeStartPlaying()
+void PlaySoundLevel(int x, int y, int sound_nr)
 {
-  tape = master_tape;
+  int sx = SCREENX(x), sy = SCREENY(y);
+  int volume, stereo;
+  int silence_distance = 8;
 
-  tape.counter = 0;
-  tape.recording = FALSE;
-  tape.playing = TRUE;
-  tape.pausing = FALSE;
-  InitRND(tape.random_seed);
-  DrawVideoDisplay(VIDEO_STATE_PLAY_ON | VIDEO_STATE_PAUSE_OFF,0);
-}
+  if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
+      (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
+    return;
 
-void TapeStopPlaying()
-{
-  if (!tape.playing)
+  if (!IN_LEV_FIELD(x, y) ||
+      sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
+      sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
     return;
 
-  tape.playing = FALSE;
-  tape.pausing = FALSE;
-  DrawVideoDisplay(VIDEO_STATE_PLAY_OFF,0);
-}
+  volume = PSND_MAX_VOLUME;
 
-int TapePlayAction()
-{
-  if (!tape.playing || tape.pausing)
-    return(0);
+#ifndef MSDOS
+  stereo = (sx-SCR_FIELDX/2)*12;
+#else
+  stereo = PSND_MIDDLE+(2*sx-(SCR_FIELDX-1))*5;
+  if(stereo > PSND_MAX_RIGHT) stereo = PSND_MAX_RIGHT;
+  if(stereo < PSND_MAX_LEFT) stereo = PSND_MAX_LEFT;
+#endif
 
-  if (tape.counter>=tape.length)
+  if (!IN_SCR_FIELD(sx, sy))
   {
-    TapeStopPlaying();
-    return(0);
-  }
+    int dx = ABS(sx-SCR_FIELDX/2)-SCR_FIELDX/2;
+    int dy = ABS(sy-SCR_FIELDY/2)-SCR_FIELDY/2;
 
-  if (!tape.pos[tape.counter].delay)
-  {
-    tape.counter++;
-    return(tape.pos[tape.counter-1].joystickdata);
+    volume -= volume*(dx > dy ? dx : dy)/silence_distance;
   }
-  else
-    return(0);
+
+  PlaySoundExt(sound_nr, volume, stereo, PSND_NO_LOOP);
 }
 
-BOOL TapePlayDelay()
+void RaiseScore(int value)
 {
-  if (!tape.playing || tape.pausing)
-    return(0);
-
-  if (tape.counter>=tape.length)
-  {
-    TapeStopPlaying();
-    return(TRUE);
-  }
-
-  if (tape.pos[tape.counter].delay)
-  {
-    tape.pos[tape.counter].delay--;
-    return(TRUE);
-  }
-  else
-    return(FALSE);
+  local_player->score += value;
+  DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5),
+          FS_SMALL, FC_YELLOW);
 }
 
-void TapeStop()
+void RaiseScoreElement(int element)
 {
-  TapeStopRecording();
-  TapeStopPlaying();
-  DrawVideoDisplay(VIDEO_ALL_OFF,0);
-  if (tape.date && tape.length)
+  switch(element)
   {
-    DrawVideoDisplay(VIDEO_STATE_DATE_ON,tape.date);
-    DrawVideoDisplay(VIDEO_STATE_TIME_ON,0);
+    case EL_EDELSTEIN:
+    case EL_EDELSTEIN_BD:
+    case EL_EDELSTEIN_GELB:
+    case EL_EDELSTEIN_ROT:
+    case EL_EDELSTEIN_LILA:
+      RaiseScore(level.score[SC_EDELSTEIN]);
+      break;
+    case EL_DIAMANT:
+      RaiseScore(level.score[SC_DIAMANT]);
+      break;
+    case EL_KAEFER:
+    case EL_BUTTERFLY:
+      RaiseScore(level.score[SC_KAEFER]);
+      break;
+    case EL_FLIEGER:
+    case EL_FIREFLY:
+      RaiseScore(level.score[SC_FLIEGER]);
+      break;
+    case EL_MAMPFER:
+    case EL_MAMPFER2:
+      RaiseScore(level.score[SC_MAMPFER]);
+      break;
+    case EL_ROBOT:
+      RaiseScore(level.score[SC_ROBOT]);
+      break;
+    case EL_PACMAN:
+      RaiseScore(level.score[SC_PACMAN]);
+      break;
+    case EL_KOKOSNUSS:
+      RaiseScore(level.score[SC_KOKOSNUSS]);
+      break;
+    case EL_DYNAMIT:
+      RaiseScore(level.score[SC_DYNAMIT]);
+      break;
+    case EL_SCHLUESSEL:
+      RaiseScore(level.score[SC_SCHLUESSEL]);
+      break;
+    default:
+      break;
   }
 }
-
-void TapeErase()
-{
-  tape.length = 0;
-}
index e7150b6be6ac963c8d9f6dc1a5844c1c888acccb..4834b79646ef8b84fdc1effea4d8b88f38e133f0 100644 (file)
@@ -1,13 +1,12 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  game.h                                                  *
 ***********************************************************/
 
 #include "main.h"
 
-#define DF_DIG         0
-#define DF_SNAP                1
-#define DF_NO_PUSH     2
-
-#define MF_NO_ACTION   0
-#define MF_MOVING      1
-#define MF_ACTION      2
-
 void GetPlayerConfig(void);
 void InitGame(void);
 void InitMovDir(int, int);
 void InitAmoebaNr(int, int);
 void GameWon(void);
-BOOL NewHiScore(void);
+boolean NewHiScore(void);
 void InitMovingField(int, int, int);
 void Moving2Blocked(int, int, int *, int *);
 void Blocked2Moving(int, int, int *, int *);
@@ -38,7 +29,8 @@ int MovingOrBlocked2Element(int, int);
 void RemoveMovingField(int, int);
 void DrawDynamite(int, int);
 void CheckDynamite(int, int);
-void Explode(int, int, int);
+void Explode(int, int, int, int);
+void DynaExplode(int, int);
 void Bang(int, int);
 void Blurb(int, int);
 void Impact(int, int);
@@ -47,39 +39,43 @@ void StartMoving(int, int);
 void ContinueMoving(int, int);
 int AmoebeNachbarNr(int, int);
 void AmoebeUmwandeln(int, int);
+void AmoebeUmwandeln2(int, int, int);
 void AmoebeWaechst(int, int);
 void AmoebeAbleger(int, int);
 void Life(int, int);
 void Ablenk(int, int);
 void Blubber(int, int);
 void NussKnacken(int, int);
-void SiebAktivieren(int x, int y);
-void AusgangstuerPruefen(int x, int y);
-void AusgangstuerOeffnen(int x, int y);
-int GameActions(int, int, int);
+void SiebAktivieren(int, int, int);
+void AusgangstuerPruefen(int, int);
+void AusgangstuerOeffnen(int, int);
+void AusgangstuerBlinken(int, int);
+void EdelsteinFunkeln(int, int);
+void MauerWaechst(int, int);
+void MauerAbleger(int, int);
+void GameActions(void);
 void ScrollLevel(int, int);
-BOOL MoveFigure(int, int);
-void TestIfHeroHitsBadThing(void);
-void TestIfBadThingHitsHero(void);
+
+boolean MoveFigureOneStep(struct PlayerInfo *, int, int, int, int);
+boolean MoveFigure(struct PlayerInfo *, int, int);
+void ScrollFigure(struct PlayerInfo *, int);
+void ScrollScreen(struct PlayerInfo *, int);
+
+void TestIfGoodThingHitsBadThing(int, int);
+void TestIfBadThingHitsGoodThing(int, int);
+void TestIfHeroHitsBadThing(int, int);
+void TestIfBadThingHitsHero(int, int);
+void TestIfFriendHitsBadThing(int, int);
+void TestIfBadThingHitsFriend(int, int);
 void TestIfBadThingHitsOtherBadThing(int, int);
-void KillHero(void);
-int DigField(int, int, int);
-BOOL SnapField(int, int);
-BOOL PlaceBomb(void);
+void KillHero(struct PlayerInfo *);
+void BuryHero(struct PlayerInfo *);
+void RemoveHero(struct PlayerInfo *);
+int DigField(struct PlayerInfo *, int, int, int, int, int);
+boolean SnapField(struct PlayerInfo *, int, int);
+boolean PlaceBomb(struct PlayerInfo *);
 void PlaySoundLevel(int, int, int);
 void RaiseScore(int);
-void TapeInitRecording(void);
-void TapeStartRecording(void);
-void TapeStopRecording(void);
-void TapeRecordAction(int);
-void TapeRecordDelay(void);
-void TapeTogglePause(void);
-void TapeInitPlaying(void);
-void TapeStartPlaying(void);
-void TapeStopPlaying(void);
-int TapePlayAction(void);
-BOOL TapePlayDelay(void);
-void TapeStop(void);
-void TapeErase(void);
+void RaiseScoreElement(int);
 
 #endif
diff --git a/src/image.c b/src/image.c
new file mode 100644 (file)
index 0000000..09a75fc
--- /dev/null
@@ -0,0 +1,554 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
+*----------------------------------------------------------*
+*  image.c                                                 *
+***********************************************************/
+
+#include "image.h"
+#include "pcx.h"
+#include "misc.h"
+
+/* exclude all except newImage() and freeImage() */
+#ifndef MSDOS
+
+/* extra colors to try allocating in private color maps to minimize flashing */
+#define NOFLASH_COLORS 256
+
+/* architecture independent value-to-memory conversion
+   note: the internal format is big endian */
+
+#define value_to_memory(value, ptr, length) (                          \
+(length) == 1 ? (*( (byte *)(ptr)   ) = ( value     ) ) :              \
+(length) == 2 ? (*( (byte *)(ptr)   ) = (((unsigned long)(value))>> 8),        \
+                *(((byte *)(ptr))+1) = ( value     ) ) :               \
+(length) == 3 ? (*( (byte *)(ptr)   ) = (((unsigned long)(value))>>16),        \
+                *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8), \
+                *(((byte *)(ptr))+2) = ( value     ) ) :               \
+                (*( (byte *)(ptr)   ) = (((unsigned long)(value))>>24),        \
+                *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16), \
+                *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8), \
+                *(((byte *)(ptr))+3) = ( value     ) ))
+
+static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
+{
+  byte *src_ptr, *dst_ptr, *dst_ptr2;
+  unsigned int bytes_per_row;
+  unsigned int x, y;
+  byte bitmask;
+  byte *mask_data;
+  Pixmap mask_pixmap;
+
+  bytes_per_row = (image->width + 7) / 8;
+  mask_data = checked_calloc(bytes_per_row * image->height);
+
+  src_ptr = image->data;
+  dst_ptr = mask_data;
+
+  /* create bitmap data which can be used by 'XCreateBitmapFromData()'
+   * directly to create a pixmap of depth 1 for use as a clip mask for
+   * the corresponding image pixmap
+   */
+
+  for (y=0; y<image->height; y++)
+  {
+    bitmask = 0x01;            /* start with leftmost bit in the byte     */
+    dst_ptr2 = dst_ptr;                /* start with leftmost byte in the row     */
+
+    for (x=0; x<image->width; x++)
+    {
+      if (*src_ptr++)          /* source pixel solid? (pixel index != 0)  */
+       *dst_ptr2 |= bitmask;   /* then write a bit into the image mask    */
+
+      if ((bitmask <<= 1) == 0)        /* bit at rightmost byte position reached? */
+      {
+       bitmask = 0x01;         /* start again with leftmost bit position  */
+       dst_ptr2++;             /* continue with next byte in image mask   */
+      }
+    }
+
+    dst_ptr += bytes_per_row;  /* continue with leftmost byte of next row */
+  }
+
+  mask_pixmap = XCreateBitmapFromData(display, window, (char *)mask_data,
+                                     image->width, image->height);
+  free(mask_data);
+
+  return mask_pixmap;
+}
+
+static int bitsPerPixelAtDepth(Display *display, int screen, int depth)
+{
+  XPixmapFormatValues *pixmap_format;
+  int i, num_pixmap_formats, bits_per_pixel = -1;
+
+  /* get Pixmap formats supported by the X server */
+  pixmap_format = XListPixmapFormats(display, &num_pixmap_formats);
+
+  /* find format that matches the given depth */
+  for (i=0; i<num_pixmap_formats; i++)
+    if (pixmap_format[i].depth == depth)
+      bits_per_pixel = pixmap_format[i].bits_per_pixel;
+
+  XFree(pixmap_format);
+
+  if (bits_per_pixel == -1)
+    Error(ERR_EXIT, "cannot find pixmap format for depth %d", depth);
+
+  return bits_per_pixel;
+}
+
+XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
+                           Window window, GC gc, int depth, Image *image)
+{
+  static XColor xcolor_private[NOFLASH_COLORS];
+  static int colorcell_used[NOFLASH_COLORS];
+  static Colormap global_cmap = 0;
+  static Pixel *global_cmap_index;
+  static int num_cmap_entries, free_cmap_entries;
+  static boolean private_cmap = FALSE;
+  Pixel *redvalue, *greenvalue, *bluevalue;
+  unsigned int a, c = 0, x, y, bytes_per_pixel, bits_per_pixel;
+  XColor xcolor;
+  XImage *ximage;
+  XImageInfo *ximageinfo;
+  byte *src_ptr, *dst_ptr;
+
+  if (!global_cmap)
+  {
+    if (visual == DefaultVisual(display, screen))
+      global_cmap = DefaultColormap(display, screen);
+    else
+    {
+      global_cmap = XCreateColormap(display, RootWindow(display, screen),
+                                   visual, AllocNone);
+      private_cmap = TRUE;
+    }
+  }
+
+  xcolor.flags = DoRed | DoGreen | DoBlue;
+  redvalue = greenvalue = bluevalue = NULL;
+  ximageinfo = checked_malloc(sizeof(XImageInfo));
+  ximageinfo->display = display;
+  ximageinfo->depth = depth;
+
+  switch (visual->class)
+  {
+    case TrueColor:
+    case DirectColor:
+    {
+      Pixel pixval;
+      unsigned int redcolors, greencolors, bluecolors;
+      unsigned int redstep, greenstep, bluestep;
+      unsigned int redbottom, greenbottom, bluebottom;
+      unsigned int redtop, greentop, bluetop;
+
+      redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
+      greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
+      bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
+
+      ximageinfo->cmap = global_cmap;
+
+      retry_direct: /* tag we hit if a DirectColor allocation fails on
+                    * default colormap */
+
+      /* calculate number of distinct colors in each band */
+
+      redcolors = greencolors = bluecolors = 1;
+      for (pixval=1; pixval; pixval <<= 1)
+      {
+       if (pixval & visual->red_mask)
+         redcolors <<= 1;
+       if (pixval & visual->green_mask)
+         greencolors <<= 1;
+       if (pixval & visual->blue_mask)
+         bluecolors <<= 1;
+      }
+      
+      /* consistency check */
+      if (redcolors > visual->map_entries ||
+         greencolors > visual->map_entries ||
+         bluecolors > visual->map_entries)
+       Error(ERR_WARN, "inconsistency in color information");
+
+      redstep = 256 / redcolors;
+      greenstep = 256 / greencolors;
+      bluestep = 256 / bluecolors;
+      redbottom = greenbottom = bluebottom = 0;
+      redtop = greentop = bluetop = 0;
+      for (a=0; a<visual->map_entries; a++)
+      {
+       if (redbottom < 256)
+         redtop = redbottom + redstep;
+       if (greenbottom < 256)
+         greentop = greenbottom + greenstep;
+       if (bluebottom < 256)
+         bluetop = bluebottom + bluestep;
+
+       xcolor.red = (redtop - 1) << 8;
+       xcolor.green = (greentop - 1) << 8;
+       xcolor.blue = (bluetop - 1) << 8;
+       if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
+       {
+         /* if an allocation fails for a DirectColor default visual then
+            we should create a private colormap and try again. */
+
+         if ((visual->class == DirectColor) &&
+             (visual == DefaultVisual(display, screen)))
+         {
+           global_cmap = XCopyColormapAndFree(display, global_cmap);
+           ximageinfo->cmap = global_cmap;
+           private_cmap = TRUE;
+
+           goto retry_direct;
+         }
+
+         /* something completely unexpected happened */
+
+         fprintf(stderr, "imageToXImage: XAllocColor failed on a TrueColor/Directcolor visual\n");
+          free(redvalue);
+          free(greenvalue);
+          free(bluevalue);
+          free(ximageinfo);
+         return NULL;
+       }
+
+       /* fill in pixel values for each band at this intensity */
+
+       while ((redbottom < 256) && (redbottom < redtop))
+         redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
+       while ((greenbottom < 256) && (greenbottom < greentop))
+         greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
+       while ((bluebottom < 256) && (bluebottom < bluetop))
+         bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
+      }
+      break;
+    }
+
+    case PseudoColor:
+
+      ximageinfo->cmap = global_cmap;
+
+      for (a=0; a<MAX_COLORS; a++)
+      {
+       XColor xcolor2;
+       unsigned short mask;
+       int color_found;
+       int i;
+
+       if (!image->rgb.color_used[a])
+         continue;
+
+       xcolor.red = *(image->rgb.red + a);
+       xcolor.green = *(image->rgb.green + a);
+       xcolor.blue = *(image->rgb.blue + a);
+  
+       /* look if this color already exists in our colormap */
+       if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
+       {
+         if (!private_cmap)
+         {
+           if (options.verbose)
+             Error(ERR_RETURN, "switching to private colormap");
+
+           /* we just filled up the default colormap -- get a private one
+              which contains all already allocated colors */
+
+           global_cmap = XCopyColormapAndFree(display, global_cmap);
+           ximageinfo->cmap = global_cmap;
+           private_cmap = TRUE;
+
+           /* allocate the rest of the color cells read/write */
+           global_cmap_index =
+             (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
+           for (i=0; i<NOFLASH_COLORS; i++)
+             if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
+                                   global_cmap_index + i, 1))
+               break;
+           num_cmap_entries = free_cmap_entries = i;
+
+           /*
+           printf("We've got %d free colormap entries.\n", free_cmap_entries);
+           */
+
+           /* to minimize colormap flashing, copy default colors and try
+              to keep them as near as possible to the old values */
+
+           for(i=0; i<num_cmap_entries; i++)
+           {
+             xcolor2.pixel = *(global_cmap_index + i);
+             XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
+             XStoreColor(display, global_cmap, &xcolor2);
+             xcolor_private[xcolor2.pixel] = xcolor2;
+             colorcell_used[xcolor2.pixel] = FALSE;
+           }
+
+           /* now we have the default colormap private: all colors we
+              successfully allocated so far are read-only, which is okay,
+              because we don't want to change them anymore -- if we need
+              an existing color again, we get it by XAllocColor; all other
+              colors are read/write and we can set them by XStoreColor,
+              but we will try to overwrite those color cells with our new
+              color which are as close as possible to our new color */
+         }
+
+         /* look for an existing default color close the one we want */
+
+         mask = 0xf000;
+         color_found = FALSE;
+
+         while (!color_found)
+         {
+           for (i=num_cmap_entries-1; i>=0; i--)
+           {
+             xcolor2.pixel = *(global_cmap_index + i);
+             xcolor2 = xcolor_private[xcolor2.pixel];
+
+             if (colorcell_used[xcolor2.pixel])
+               continue;
+
+             if ((xcolor.red & mask) == (xcolor2.red & mask) &&
+                 (xcolor.green & mask) == (xcolor2.green & mask) &&
+                 (xcolor.blue & mask) == (xcolor2.blue & mask))
+             {
+               /*
+               printf("replacing color cell %ld with a close color\n",
+                      xcolor2.pixel);
+                      */
+               color_found = TRUE;
+               break;
+             }
+           }
+
+           if (mask == 0x0000)
+             break;
+
+           mask = (mask << 1) & 0xffff;
+         }
+
+         if (!color_found)             /* no more free color cells */
+           Error(ERR_EXIT, "cannot allocate enough color cells");
+
+         xcolor.pixel = xcolor2.pixel;
+         xcolor_private[xcolor.pixel] = xcolor;
+         colorcell_used[xcolor.pixel] = TRUE;
+         XStoreColor(display, ximageinfo->cmap, &xcolor);
+         free_cmap_entries--;
+       }
+
+       *(ximageinfo->index + a) = xcolor.pixel;
+      }
+
+      /*
+      printf("still %d free colormap entries\n", free_cmap_entries);
+      */
+
+      ximageinfo->no = a;      /* number of pixels allocated for this image */
+      break;
+  
+    default:
+      Error(ERR_RETURN, "display class not supported");
+      Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
+      break;
+  }
+
+#if DEBUG_TIMING
+  debug_print_timestamp(2, "   ALLOCATING IMAGE COLORS:   ");
+#endif
+
+  /* create XImage from internal image structure and convert it to Pixmap */
+
+  bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
+  bytes_per_pixel = (bits_per_pixel + 7) / 8;
+
+  ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
+                       NULL, image->width, image->height,
+                       8, image->width * bytes_per_pixel);
+  ximage->data =
+    checked_malloc(image->width * image->height * bytes_per_pixel);
+  ximage->byte_order = MSBFirst;
+
+  src_ptr = image->data;
+  dst_ptr = (byte *)ximage->data;
+
+  switch (visual->class)
+  {
+    case DirectColor:
+    case TrueColor:
+    {
+      Pixel pixval;
+
+      for (y=0; y<image->height; y++)          /* general case */
+      {
+       for (x=0; x<image->width; x++)
+       {
+         pixval = *src_ptr++;
+         pixval =
+           redvalue[image->rgb.red[pixval] >> 8] |
+           greenvalue[image->rgb.green[pixval] >> 8] |
+           bluevalue[image->rgb.blue[pixval] >> 8];
+         value_to_memory(pixval, dst_ptr, bytes_per_pixel);
+         dst_ptr += bytes_per_pixel;
+       }
+      }
+      break;
+    }
+
+    case PseudoColor:
+    {
+      if (bytes_per_pixel == 1)                        /* (common) special case */
+      {
+       for (y=0; y<image->height; y++)
+         for (x=0; x<image->width; x++)
+           *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
+      }
+      else                                     /* general case */
+      {
+       for (y=0; y<image->height; y++)
+       {
+         for (x=0; x<image->width; x++)
+         {
+           value_to_memory(ximageinfo->index[c + *src_ptr++],
+                           dst_ptr, bytes_per_pixel);
+           dst_ptr += bytes_per_pixel;
+         }
+       }
+      }
+      break;
+    }
+
+    default:
+      Error(ERR_RETURN, "display class not supported");
+      Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
+      break;
+  }
+
+  if (redvalue)
+  {
+    free((byte *)redvalue);
+    free((byte *)greenvalue);
+    free((byte *)bluevalue);
+  }
+
+#if DEBUG_TIMING
+  debug_print_timestamp(2, "   CONVERTING IMAGE TO XIMAGE:");
+#endif
+
+  ximageinfo->pixmap = XCreatePixmap(display, window,
+                                    ximage->width, ximage->height,
+                                    ximageinfo->depth);
+
+  XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
+           ximage, 0, 0, 0, 0, ximage->width, ximage->height);
+
+  free(ximage->data);
+  ximage->data = NULL;
+  XDestroyImage(ximage);
+
+  return(ximageinfo);
+}
+
+void freeXImage(Image *image, XImageInfo *ximageinfo)
+{
+  if (ximageinfo->index != NULL && ximageinfo->no > 0)
+    XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
+               ximageinfo->no, 0);
+  /* this       ^^^^^^^^^^^^^^ is wrong, because the used color cells
+   * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
+   * used color cells, but they are not at array position 0 - 'ximageinfo->no'
+   */
+
+  free(ximageinfo);
+}
+
+#endif /* !MSDOS */
+
+Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
+{
+  Image *image;
+  const unsigned int bytes_per_pixel = 1;
+  int i;
+
+  if (depth > 8)
+    Error(ERR_EXIT, "images with more than 256 colors are not supported");
+
+  depth = 8;
+  image = checked_malloc(sizeof(Image));
+  image->data = checked_malloc(width * height * bytes_per_pixel);
+  image->width = width;
+  image->height = height;
+  image->depth = depth;
+  image->rgb.used = 0;
+  for (i=0; i<MAX_COLORS; i++)
+    image->rgb.color_used[i] = FALSE;
+
+  return image;
+}
+
+void freeImage(Image *image)
+{
+  free(image->data);
+  free(image);
+}
+
+#ifndef MSDOS
+
+int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
+                      Pixmap *pixmap, Pixmap *pixmap_mask)
+{
+  Image *image;
+  XImageInfo *ximageinfo;
+  int screen;
+  Visual *visual;
+  int depth;
+
+#if DEBUG_TIMING
+  debug_print_timestamp(2, NULL);      /* initialize timestamp function */
+#endif
+
+  /* read the graphic file in PCX format to image structure */
+  if ((image = Read_PCX_to_Image(filename)) == NULL)
+    return PCX_FileInvalid;
+
+#if DEBUG_TIMING
+  printf("%s:\n", filename);
+  debug_print_timestamp(2, "   READING PCX FILE TO IMAGE: ");
+#endif
+
+  screen = DefaultScreen(display);
+  visual = DefaultVisual(display, screen);
+  depth = DefaultDepth(display, screen);
+
+  /* convert image structure to X11 Pixmap */
+  if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
+                                    window, gc, depth, image)))
+    Error(ERR_EXIT, "cannot convert Image to Pixmap");
+
+  /* if a private colormap has been created, install it */
+  if (ximageinfo->cmap != DefaultColormap(display, screen))
+    XSetWindowColormap(display, window, ximageinfo->cmap);
+
+#if DEBUG_TIMING
+  debug_print_timestamp(2, "   CONVERTING IMAGE TO PIXMAP:");
+#endif
+
+  /* create clip mask for the image */
+  ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
+
+#if DEBUG_TIMING
+  debug_print_timestamp(2, "   CONVERTING IMAGE TO MASK:  ");
+#endif
+
+  *pixmap = ximageinfo->pixmap;
+  *pixmap_mask = ximageinfo->pixmap_mask;
+
+  return(PCX_Success);
+}
+
+#endif /* !MSDOS */
diff --git a/src/image.h b/src/image.h
new file mode 100644 (file)
index 0000000..6315340
--- /dev/null
@@ -0,0 +1,58 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
+*----------------------------------------------------------*
+*  image.h                                                 *
+***********************************************************/
+
+#ifndef IMAGE_H
+#define IMAGE_H
+
+#include "main.h"
+
+#define MAX_COLORS     256     /* maximal number of colors for each image */
+
+typedef unsigned short Intensity;      /* RGB intensity for X11 */
+
+typedef struct
+{
+  Display  *display;           /* destination display             */
+  int       depth;             /* depth of destination drawable   */
+  Pixel     index[MAX_COLORS]; /* array of pixel values           */
+  int       no;                        /* number of pixels in the array   */
+  Colormap  cmap;              /* colormap used for image         */
+  Pixmap   pixmap;             /* final pixmap                    */
+  Pixmap   pixmap_mask;                /* final pixmap of mask            */
+} XImageInfo;
+
+struct RGBMap
+{
+  unsigned int used;                   /* number of colors used in RGB map */
+  Intensity    red[MAX_COLORS];                /* color values in X style          */
+  Intensity    green[MAX_COLORS];
+  Intensity    blue[MAX_COLORS];
+  boolean      color_used[MAX_COLORS]; /* flag if color cell is used       */
+};
+
+typedef struct
+{
+  struct RGBMap rgb;           /* RGB map of image if IRGB type       */
+  unsigned int  width;         /* width of image in pixels            */
+  unsigned int  height;                /* height of image in pixels           */
+  unsigned int  depth;         /* depth of image in bits if IRGB type */
+  byte         *data;          /* image data                          */
+} Image;
+
+int Read_PCX_to_Pixmap(Display *, Window, GC, char *, Pixmap *, Pixmap *);
+
+Image *newImage(unsigned int, unsigned int, unsigned int);
+void freeImage(Image *);
+void freeXImage(Image *, XImageInfo *);
+
+#endif /* IMAGE_H */
index 8032ffb3d82bd798bd690dea25364937cdc4c3a8..bc72b99a2787ca8f660bfafabbb38ca8baf9b712 100644 (file)
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  init.c                                                  *
 ***********************************************************/
 
+#include <signal.h>
+
 #include "init.h"
 #include "misc.h"
 #include "sound.h"
 #include "screens.h"
+#include "tools.h"
 #include "files.h"
-#include <signal.h>
+#include "joystick.h"
+#include "image.h"
+#include "pcx.h"
+#include "network.h"
+#include "netserv.h"
+
+struct PictureFileInfo
+{
+  char *picture_filename;
+  boolean picture_with_mask;
+};
+
+struct IconFileInfo
+{
+  char *picture_filename;
+  char *picturemask_filename;
+};
 
 static int sound_process_id = 0;
 
+static void InitLevelAndPlayerInfo(void);
+static void InitNetworkServer(void);
+static void InitDisplay(void);
+static void InitSound(void);
+static void InitSoundServer(void);
+static void InitWindow(int, char **);
+static void InitGfx(void);
+static void LoadGfx(int, struct PictureFileInfo *);
+static void InitElementProperties(void);
+
 void OpenAll(int argc, char *argv[])
 {
+  if (options.serveronly)
+  {
+    NetworkServer(options.server_port, options.serveronly);
+
+    /* never reached */
+    exit(0);
+  }
+
   InitLevelAndPlayerInfo();
 
   InitCounter();
   InitSound();
-  InitSoundProcess();
-  InitJoystick();
+  InitSoundServer();
+  InitJoysticks();
   InitRND(NEW_RANDOMIZE);
 
-  signal(SIGINT, CloseAll);
-  signal(SIGTERM, CloseAll);
+  signal(SIGINT, CloseAllAndExit);
+  signal(SIGTERM, CloseAllAndExit);
 
-  InitDisplay(argc, argv);
+  InitDisplay();
   InitWindow(argc, argv);
+
+  XMapWindow(display, window);
+  XFlush(display);
+
   InitGfx();
   InitElementProperties();
 
   DrawMainMenu();
 
-  XMapWindow(display, window);
-  XFlush(display);
+  InitNetworkServer();
 }
 
 void InitLevelAndPlayerInfo()
 {
-  if (!LoadLevelInfo())                        /* global level info */
-    CloseAll();
+  int i;
 
-  LoadPlayerInfo(PLAYER_SETUP);                /* global setup info */
-  LoadPlayerInfo(PLAYER_LEVEL);                /* level specific info */
+  /* choose default local player */
+  local_player = &stored_player[0];
+
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+    stored_player[i].joystick_fd = -1; /* joystick device closed */
+    stored_player[i].connected = FALSE;
+  }
+
+  local_player->connected = TRUE;
+
+  LoadLevelInfo();                     /* global level info */
+  LoadSetup();                         /* global setup info */
+  LoadLevelSetup();                    /* info about last played level */
+}
+
+void InitNetworkServer()
+{
+#ifndef MSDOS
+  int nr_wanted;
+#endif
+
+  if (!options.network)
+    return;
+
+#ifndef MSDOS
+  nr_wanted = Request("Choose player", REQ_PLAYER | REQ_STAY_CLOSED);
+
+  if (!ConnectToServer(options.server_host, options.server_port))
+    Error(ERR_EXIT, "cannot connect to network game server");
+
+  SendToServer_PlayerName(setup.player_name);
+  SendToServer_ProtocolVersion();
+
+  if (nr_wanted)
+    SendToServer_NrWanted(nr_wanted);
+#endif
 }
 
 void InitSound()
 {
   int i;
 
-  if (sound_status==SOUND_OFF)
+  if (sound_status == SOUND_OFF)
     return;
 
-  if (access(sound_device_name,W_OK)<0)
+#ifndef MSDOS
+  if (access(sound_device_name, W_OK) != 0)
   {
-    fprintf(stderr,"%s: cannot access sound device - no sounds\n",progname);
-    sound_status=SOUND_OFF;
+    Error(ERR_WARN, "cannot access sound device - no sounds");
+    sound_status = SOUND_OFF;
     return;
   }
 
-  if ((sound_device=open(sound_device_name,O_WRONLY))<0)
+  if ((sound_device = open(sound_device_name,O_WRONLY))<0)
   {
-    fprintf(stderr,"%s: cannot open sound device - no sounds\n",progname);
-    sound_status=SOUND_OFF;
+    Error(ERR_WARN, "cannot open sound device - no sounds");
+    sound_status = SOUND_OFF;
     return;
   }
 
   close(sound_device);
-  sound_status=SOUND_AVAILABLE;
+  sound_status = SOUND_AVAILABLE;
 
 #ifdef VOXWARE
   sound_loops_allowed = TRUE;
-  sound_loops_on = TRUE;
+
+  /*
+  setup.sound_loops_on = TRUE;
+  */
+
 #endif
+#else /* MSDOS */
+  sound_loops_allowed = TRUE;
+
+  /*
+  setup.sound_loops_on = TRUE;
+  */
 
-  for(i=0;i<NUM_SOUNDS;i++)
+#endif /* MSDOS */
+
+  for(i=0; i<NUM_SOUNDS; i++)
   {
+#ifdef MSDOS
+    sprintf(sound_name[i], "%d", i + 1);
+#endif
+
     Sound[i].name = sound_name[i];
     if (!LoadSound(&Sound[i]))
     {
-      sound_status=SOUND_OFF;
+      sound_status = SOUND_OFF;
       return;
     }
   }
 }
 
-void InitSoundProcess()
+void InitSoundServer()
 {
-  if (sound_status==SOUND_OFF)
+  if (sound_status == SOUND_OFF)
     return;
 
+#ifndef MSDOS
+
   if (pipe(sound_pipe)<0)
   {
-    fprintf(stderr,"%s: cannot create pipe - no sounds\n",progname);
-    sound_status=SOUND_OFF;
+    Error(ERR_WARN, "cannot create pipe - no sounds");
+    sound_status = SOUND_OFF;
     return;
   }
 
-  if ((sound_process_id=fork())<0)
+  if ((sound_process_id = fork()) < 0)
   {       
-    fprintf(stderr,"%s: cannot create child process - no sounds\n",progname);
-    sound_status=SOUND_OFF;
+    Error(ERR_WARN, "cannot create sound server process - no sounds");
+    sound_status = SOUND_OFF;
     return;
   }
 
   if (!sound_process_id)       /* we are child */
+  {
     SoundServer();
+
+    /* never reached */
+    exit(0);
+  }
   else                         /* we are parent */
     close(sound_pipe[0]);      /* no reading from pipe needed */
+
+#else /* MSDOS */
+
+  SoundServer();
+
+#endif /* MSDOS */
 }
 
-void InitJoystick()
+void InitJoysticks()
 {
-  if (global_joystick_status==JOYSTICK_OFF)
+  int i;
+
+  if (global_joystick_status == JOYSTICK_OFF)
     return;
 
-  if (access(joystick_device_name[joystick_nr],R_OK)<0)
+  joystick_status = JOYSTICK_OFF;
+
+#ifndef MSDOS
+  for (i=0; i<MAX_PLAYERS; i++)
   {
-    fprintf(stderr,"%s: cannot access joystick device '%s'\n",
-           progname,joystick_device_name[joystick_nr]);
-    joystick_status = JOYSTICK_OFF;
-    return;
+    char *device_name = setup.input[i].joy.device_name;
+
+    /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
+    if (stored_player[i].joystick_fd != -1)
+    {
+      close(stored_player[i].joystick_fd);
+      stored_player[i].joystick_fd = -1;
+    }
+
+    if (!setup.input[i].use_joystick)
+      continue;
+
+    if (access(device_name, R_OK) != 0)
+    {
+      Error(ERR_WARN, "cannot access joystick device '%s'", device_name);
+      continue;
+    }
+
+    if ((stored_player[i].joystick_fd = open(device_name, O_RDONLY)) < 0)
+    {
+      Error(ERR_WARN, "cannot open joystick device '%s'", device_name);
+      continue;
+    }
+
+    joystick_status = JOYSTICK_AVAILABLE;
   }
 
-  if ((joystick_device=open(joystick_device_name[joystick_nr],O_RDONLY))<0)
+#else /* MSDOS */
+
+  /* try to access two joysticks; if that fails, try to access just one */
+  if (install_joystick(JOY_TYPE_2PADS) == 0 ||
+      install_joystick(JOY_TYPE_AUTODETECT) == 0)
+    joystick_status = JOYSTICK_AVAILABLE;
+
+  /*
+  load_joystick_data(JOYSTICK_FILENAME);
+  */
+
+  for (i=0; i<MAX_PLAYERS; i++)
   {
-    fprintf(stderr,"%s: cannot open joystick device '%s'\n",
-           progname,joystick_device_name[joystick_nr]);
-    joystick_status = JOYSTICK_OFF;
-    return;
-  }
+    char *device_name = setup.input[i].joy.device_name;
+    int joystick_nr = getJoystickNrFromDeviceName(device_name);
 
-  joystick_status = JOYSTICK_AVAILABLE;
-  LoadJoystickData();
+    if (joystick_nr >= num_joysticks)
+      joystick_nr = -1;
+
+    /* misuse joystick file descriptor variable to store joystick number */
+    stored_player[i].joystick_fd = joystick_nr;
+  }
+#endif
 }
 
-void InitDisplay(int argc, char *argv[])
+void InitDisplay()
 {
-  char *display_name = NULL;
-  int i;
+#ifndef MSDOS
+  XVisualInfo vinfo_template, *vinfo;
+  int num_visuals;
+#endif
+  unsigned int depth;
 
-  /* get X server to connect to, if given as an argument */
-  for (i=1;i<argc-1;i++)
-  {
-    char *dispstr="-display";
-    int len=MAX(strlen(dispstr),strlen(argv[i]));
+  /* connect to X server */
+  if (!(display = XOpenDisplay(options.display_name)))
+    Error(ERR_EXIT, "cannot connect to X server %s",
+         XDisplayName(options.display_name));
 
-    if (len<4)
-      continue;
-    else if (!strncmp(argv[i],dispstr,len))
-    {
-      display_name=argv[i+1];
-      break;
-    }
+  screen = DefaultScreen(display);
+  visual = DefaultVisual(display, screen);
+  depth  = DefaultDepth(display, screen);
+  cmap   = DefaultColormap(display, screen);
+
+#ifndef MSDOS
+  /* look for good enough visual */
+  vinfo_template.screen = screen;
+  vinfo_template.class = (depth == 8 ? PseudoColor : TrueColor);
+  vinfo_template.depth = depth;
+  if ((vinfo = XGetVisualInfo(display, VisualScreenMask | VisualClassMask |
+                             VisualDepthMask, &vinfo_template, &num_visuals)))
+  {
+    visual = vinfo->visual;
+    XFree((void *)vinfo);
   }
 
-  /* connect to X server */
-  if (!(display=XOpenDisplay(display_name)))
+  /* got appropriate visual? */
+  if (depth < 8)
   {
-    fprintf(stderr,"%s: cannot connect to X server %s\n", 
-           progname, XDisplayName(display_name));
+    printf("Sorry, displays with less than 8 bits per pixel not supported.\n");
     exit(-1);
   }
-  
-  screen = DefaultScreen(display);
-  cmap   = DefaultColormap(display, screen);
-  pen_fg = WhitePixel(display,screen);
-  pen_bg = BlackPixel(display,screen);
+  else if ((depth ==8 && visual->class != PseudoColor) ||
+          (depth > 8 && visual->class != TrueColor &&
+           visual->class != DirectColor))
+  {
+    printf("Sorry, cannot get appropriate visual.\n");
+    exit(-1);
+  }
+#endif
 }
 
 void InitWindow(int argc, char *argv[])
 {
   unsigned int border_width = 4;
+  XGCValues gc_values;
+  unsigned long gc_valuemask;
+#ifndef MSDOS
+  XTextProperty windowName, iconName;
   Pixmap icon_pixmap, iconmask_pixmap;
-  unsigned int icon_width,icon_height;
-  int icon_hot_x,icon_hot_y;
+  unsigned int icon_width, icon_height;
+  int icon_hot_x, icon_hot_y;
   char icon_filename[256];
   XSizeHints size_hints;
   XWMHints wm_hints;
   XClassHint class_hints;
-  XTextProperty windowName, iconName;
-  XGCValues gc_values;
-  unsigned long gc_valuemask;
-  char *window_name = "Rocks'n'Diamonds";
-  char *icon_name = "Rocks'n'Diamonds";
+  char *window_name = WINDOWTITLE_STRING;
+  char *icon_name = WINDOWTITLE_STRING;
   long window_event_mask;
-  static struct PictureFile icon_pic =
+  Atom proto_atom = None, delete_atom = None;
+#endif
+  int screen_width, screen_height;
+  int win_xpos = WIN_XPOS, win_ypos = WIN_YPOS;
+  unsigned long pen_fg = WhitePixel(display,screen);
+  unsigned long pen_bg = BlackPixel(display,screen);
+  const int width = WIN_XSIZE, height = WIN_YSIZE;
+
+#ifndef MSDOS
+  static struct IconFileInfo icon_pic =
   {
     "rocks_icon.xbm",
     "rocks_iconmask.xbm"
   };
+#endif
 
-  width = WIN_XSIZE;
-  height = WIN_YSIZE;
+  screen_width = XDisplayWidth(display, screen);
+  screen_height = XDisplayHeight(display, screen);
 
-  window = XCreateSimpleWindow(display, RootWindow(display, screen),
-                           WIN_XPOS, WIN_YPOS, width, height, border_width,
-                           pen_fg, pen_bg);
+  win_xpos = (screen_width - width) / 2;
+  win_ypos = (screen_height - height) / 2;
 
-  sprintf(icon_filename,"%s/%s",GFX_PATH,icon_pic.picture_filename);
+  window = XCreateSimpleWindow(display, RootWindow(display, screen),
+                              win_xpos, win_ypos, width, height, border_width,
+                              pen_fg, pen_bg);
+
+#ifndef MSDOS
+  proto_atom = XInternAtom(display, "WM_PROTOCOLS", FALSE);
+  delete_atom = XInternAtom(display, "WM_DELETE_WINDOW", FALSE);
+  if ((proto_atom != None) && (delete_atom != None))
+    XChangeProperty(display, window, proto_atom, XA_ATOM, 32,
+                   PropModePrepend, (unsigned char *) &delete_atom, 1);
+
+  sprintf(icon_filename, "%s/%s/%s",
+         options.base_directory, GRAPHICS_DIRECTORY,
+         icon_pic.picture_filename);
   XReadBitmapFile(display,window,icon_filename,
                  &icon_width,&icon_height,
                  &icon_pixmap,&icon_hot_x,&icon_hot_y);
   if (!icon_pixmap)
-  {
-    fprintf(stderr, "%s: cannot read icon bitmap file '%s'.\n",
-           progname,icon_filename);
-    exit(-1);
-  }
+    Error(ERR_EXIT, "cannot read icon bitmap file '%s'", icon_filename);
 
-  sprintf(icon_filename,"%s/%s",GFX_PATH,icon_pic.picturemask_filename);
+  sprintf(icon_filename, "%s/%s/%s",
+         options.base_directory, GRAPHICS_DIRECTORY,
+         icon_pic.picturemask_filename);
   XReadBitmapFile(display,window,icon_filename,
                  &icon_width,&icon_height,
                  &iconmask_pixmap,&icon_hot_x,&icon_hot_y);
   if (!iconmask_pixmap)
-  {
-    fprintf(stderr, "%s: cannot read icon bitmap file '%s'.\n",
-           progname,icon_filename);
-    exit(-1);
-  }
+    Error(ERR_EXIT, "cannot read icon bitmap file '%s'", icon_filename);
 
-  size_hints.flags = PSize | PMinSize | PMaxSize;
   size_hints.width  = size_hints.min_width  = size_hints.max_width  = width;
   size_hints.height = size_hints.min_height = size_hints.max_height = height;
+  size_hints.flags = PSize | PMinSize | PMaxSize;
 
-  if (!XStringListToTextProperty(&window_name, 1, &windowName))
+  if (win_xpos || win_ypos)
   {
-    fprintf(stderr, "%s: structure allocation for windowName failed.\n",
-           progname);
-    exit(-1);
+    size_hints.x = win_xpos;
+    size_hints.y = win_ypos;
+    size_hints.flags |= PPosition;
   }
 
+  if (!XStringListToTextProperty(&window_name, 1, &windowName))
+    Error(ERR_EXIT, "structure allocation for windowName failed");
+
   if (!XStringListToTextProperty(&icon_name, 1, &iconName))
-  {
-    fprintf(stderr, "%s: structure allocation for iconName failed.\n",
-           progname);
-    exit(-1);
-  }
+    Error(ERR_EXIT, "structure allocation for iconName failed");
 
   wm_hints.initial_state = NormalState;
   wm_hints.input = True;
@@ -253,7 +423,7 @@ void InitWindow(int argc, char *argv[])
   wm_hints.icon_mask = iconmask_pixmap;
   wm_hints.flags = StateHint | IconPixmapHint | IconMaskHint | InputHint;
 
-  class_hints.res_name = progname;
+  class_hints.res_name = program_name;
   class_hints.res_class = "Rocks'n'Diamonds";
 
   XSetWMProperties(display, window, &windowName, &iconName, 
@@ -268,6 +438,7 @@ void InitWindow(int argc, char *argv[])
                       ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
                       KeyPressMask | KeyReleaseMask;
   XSelectInput(display, window, window_event_mask);
+#endif
 
   /* create GC for drawing with window depth */
   gc_values.graphics_exposures = False;
@@ -277,105 +448,107 @@ void InitWindow(int argc, char *argv[])
   gc = XCreateGC(display, window, gc_valuemask, &gc_values);
 }
 
+void DrawInitText(char *text, int ypos, int color)
+{
+  if (display && window && pix[PIX_SMALLFONT])
+  {
+    XFillRectangle(display,window,gc,0,ypos, WIN_XSIZE,FONT2_YSIZE);
+    DrawTextExt(window,gc,(WIN_XSIZE-strlen(text)*FONT2_XSIZE)/2,
+               ypos,text,FS_SMALL,color);
+    XFlush(display);
+  }
+}
+
 void InitGfx()
 {
-  int i,j,x,y;
-  int xpm_err, xbm_err;
-  unsigned int width,height;
-  int hot_x,hot_y;
-  XGCValues gc_values;
-  unsigned long gc_valuemask;
+  int i,j;
+  GC copy_clipmask_gc;
   XGCValues clip_gc_values;
   unsigned long clip_gc_valuemask;
-  char filename[256];
-  Pixmap shapemask;
 
-  static struct PictureFile pic[NUM_PICTURES] =
+#ifdef MSDOS
+  static struct PictureFileInfo pic[NUM_PICTURES] =
   {
-    "RocksScreen.xpm",         "RocksScreenMaske.xbm",
-    "RocksDoor.xpm",           "RocksDoorMaske.xbm",
-    "RocksToons.xpm",          "RocksToonsMaske.xbm",
-    "RocksFont.xpm",           NULL,
-    "RocksFont2.xpm",          NULL
+    { "Screen",        TRUE },
+    { "Door",  TRUE },
+    { "Heroes",        TRUE },
+    { "Toons", TRUE },
+    { "Font",  FALSE },
+    { "Font2", FALSE }
   }; 
-
-  for(i=0;i<NUM_PICTURES;i++)
+#else
+  static struct PictureFileInfo pic[NUM_PICTURES] =
   {
-    if (pic[i].picture_filename)
-    {
-      sprintf(filename,"%s/%s",GFX_PATH,pic[i].picture_filename);
+    { "RocksScreen",   TRUE },
+    { "RocksDoor",     TRUE },
+    { "RocksHeroes",   TRUE },
+    { "RocksToons",    TRUE },
+    { "RocksFont",     FALSE },
+    { "RocksFont2",    FALSE }
+  }; 
+#endif
 
-      xpm_att[i].valuemask = XpmCloseness;
-      xpm_att[i].closeness = 20000;
-      xpm_err = XpmReadFileToPixmap(display,window,filename,
-                                   &pix[i],&shapemask,&xpm_att[i]);
-      switch(xpm_err)
-      {
-       case XpmOpenFailed:
-          fprintf(stderr,"Xpm file open failed on '%s' !\n",filename);
-         CloseAll();
-         exit(-1);
-       case XpmFileInvalid:
-         fprintf(stderr,"Invalid Xpm file '%s'!\n",filename);
-         CloseAll();
-         exit(-1);
-       case XpmNoMemory:
-         fprintf(stderr,"Not enough memory !\n");      
-         CloseAll();
-         exit(1);
-       case XpmColorFailed:
-         fprintf(stderr,"Can`t get any colors...\n");
-         CloseAll();
-         exit(-1);
-       default:
-         break;
-      }
-      if (!pix[i])
-      {
-       fprintf(stderr, "%s: cannot read Xpm file '%s'.\n",
-               progname,filename);
-       CloseAll();
-       exit(-1);
-      }
-    }
+  static struct
+  {
+    int start;
+    int count;
+  }
+  tile_needs_clipping[] =
+  {
+    { GFX_SPIELER1_UP, 4 },
+    { GFX_SPIELER1_DOWN, 4 },
+    { GFX_SPIELER1_LEFT, 4 },
+    { GFX_SPIELER1_RIGHT, 4 },
+    { GFX_SPIELER1_PUSH_LEFT, 4 },
+    { GFX_SPIELER1_PUSH_RIGHT, 4 },
+    { GFX_SPIELER2_UP, 4 },
+    { GFX_SPIELER2_DOWN, 4 },
+    { GFX_SPIELER2_LEFT, 4 },
+    { GFX_SPIELER2_RIGHT, 4 },
+    { GFX_SPIELER2_PUSH_LEFT, 4 },
+    { GFX_SPIELER2_PUSH_RIGHT, 4 },
+    { GFX_SPIELER3_UP, 4 },
+    { GFX_SPIELER3_DOWN, 4 },
+    { GFX_SPIELER3_LEFT, 4 },
+    { GFX_SPIELER3_RIGHT, 4 },
+    { GFX_SPIELER3_PUSH_LEFT, 4 },
+    { GFX_SPIELER3_PUSH_RIGHT, 4 },
+    { GFX_SPIELER4_UP, 4 },
+    { GFX_SPIELER4_DOWN, 4 },
+    { GFX_SPIELER4_LEFT, 4 },
+    { GFX_SPIELER4_RIGHT, 4 },
+    { GFX_SPIELER4_PUSH_LEFT, 4 },
+    { GFX_SPIELER4_PUSH_RIGHT, 4 },
+    { GFX_GEBLUBBER, 4 },
+    { GFX_DYNAMIT, 7 },
+    { GFX_DYNABOMB, 4 },
+    { GFX_EXPLOSION, 8 },
+    { GFX_SOKOBAN_OBJEKT, 1 },
+    { GFX_FUNKELN_BLAU, 3 },
+    { GFX_FUNKELN_WEISS, 3 },
+    { -1, 0 }
+  };
 
-    if (pic[i].picturemask_filename)
-    {
-      sprintf(filename,"%s/%s",GFX_PATH,pic[i].picturemask_filename);
+#if DEBUG_TIMING
+  debug_print_timestamp(0, NULL);      /* initialize timestamp function */
+#endif
 
-      xbm_err = XReadBitmapFile(display,window,filename,
-                               &width,&height,&clipmask[i],&hot_x,&hot_y);
-      switch(xbm_err)
-      {
-       case BitmapSuccess:
-          break;
-       case BitmapOpenFailed:
-         fprintf(stderr,"Bitmap file open failed on '%s' !\n",filename);
-         CloseAll();
-         exit(-1);
-         break;
-       case BitmapFileInvalid:
-         fprintf(stderr,"Bitmap file invalid: '%s' !\n",filename);
-         CloseAll();
-         exit(-1);
-         break;
-       case BitmapNoMemory:
-         fprintf(stderr,"No memory for file '%s' !\n",filename);
-         CloseAll();
-         exit(-1);
-         break;
-       default:
-         break;
-      }
-      if (!clipmask[i])
-      {
-       fprintf(stderr, "%s: cannot read X11 bitmap file '%s'.\n",
-               progname,filename);
-       CloseAll();
-       exit(-1);
-      }
-    }
-  }
+  LoadGfx(PIX_SMALLFONT,&pic[PIX_SMALLFONT]);
+  DrawInitText(WINDOWTITLE_STRING,20,FC_YELLOW);
+  DrawInitText(COPYRIGHT_STRING,50,FC_RED);
+#ifdef MSDOS
+  DrawInitText("MSDOS version done by Guido Schulz",210,FC_BLUE);
+  rest(200);
+#endif /* MSDOS */
+  DrawInitText("Loading graphics:",120,FC_GREEN);
+
+  for(i=0; i<NUM_PICTURES; i++)
+    if (i != PIX_SMALLFONT)
+      LoadGfx(i,&pic[i]);
+
+#if DEBUG_TIMING
+  debug_print_timestamp(0, "SUMMARY LOADING ALL GRAPHICS:");
+#endif
 
   pix[PIX_DB_BACK] = XCreatePixmap(display, window,
                                   WIN_XSIZE,WIN_YSIZE,
@@ -383,54 +556,233 @@ void InitGfx()
   pix[PIX_DB_DOOR] = XCreatePixmap(display, window,
                                   3*DXSIZE,DYSIZE+VYSIZE,
                                   XDefaultDepth(display,screen));
+  pix[PIX_DB_FIELD] = XCreatePixmap(display, window,
+                                   FXSIZE,FYSIZE,
+                                   XDefaultDepth(display,screen));
+
+  clip_gc_values.graphics_exposures = False;
+  clip_gc_valuemask = GCGraphicsExposures;
+  copy_clipmask_gc =
+    XCreateGC(display,clipmask[PIX_BACK],clip_gc_valuemask,&clip_gc_values);
+
+  clip_gc_values.graphics_exposures = False;
+  clip_gc_valuemask = GCGraphicsExposures;
+  tile_clip_gc =
+    XCreateGC(display,window,clip_gc_valuemask,&clip_gc_values);
+
+  /* initialize pixmap array to Pixmap 'None' */
+  for(i=0; i<NUM_TILES; i++)
+    tile_clipmask[i] = None;
+
+  /* create only those clipping Pixmaps we really need */
+  for(i=0; tile_needs_clipping[i].start>=0; i++)
+  {
+    for(j=0; j<tile_needs_clipping[i].count; j++)
+    {
+      int tile = tile_needs_clipping[i].start + j;
+      int graphic = tile;
+      int src_x, src_y;
+      Pixmap src_pixmap;
 
-  clipmask[PIX_FADEMASK] = XCreatePixmap(display, window,
-                                        SXSIZE+TILEX,SYSIZE+TILEY,1);
+      if (graphic >= GFX_START_ROCKSSCREEN &&
+         graphic <= GFX_END_ROCKSSCREEN)
+      {
+       src_pixmap = clipmask[PIX_BACK];
+       graphic -= GFX_START_ROCKSSCREEN;
+       src_x = SX + (graphic % GFX_PER_LINE) * TILEX;
+       src_y = SY + (graphic / GFX_PER_LINE) * TILEY;
+      }
+      else if (graphic >= GFX_START_ROCKSHEROES &&
+              graphic <= GFX_END_ROCKSHEROES)
+      {
+       src_pixmap = clipmask[PIX_HEROES];
+       graphic -= GFX_START_ROCKSHEROES;
+       src_x = (graphic % HEROES_PER_LINE) * TILEX;
+       src_y = (graphic / HEROES_PER_LINE) * TILEY;
+      }
+      else if (graphic >= GFX_START_ROCKSFONT &&
+              graphic <= GFX_END_ROCKSFONT)
+      {
+       src_pixmap = clipmask[PIX_BIGFONT];
+       graphic -= GFX_START_ROCKSFONT;
+       src_x = (graphic % FONT_CHARS_PER_LINE) * TILEX;
+       src_y = (graphic / FONT_CHARS_PER_LINE) * TILEY +
+         FC_SPECIAL1 * FONT_LINES_PER_FONT * TILEY;
+      }
+      else
+       break;
 
-  if (!pix[PIX_DB_BACK] || !pix[PIX_DB_DOOR] || !clipmask[PIX_FADEMASK])
-  {
-    fprintf(stderr, "%s: cannot create additional Pixmaps!\n",progname);
-    CloseAll();
-    exit(-1);
-  }
+      tile_clipmask[tile] = XCreatePixmap(display, window, TILEX,TILEY, 1);
 
-  /* create GC for drawing with bitplane depth */
-  gc_values.graphics_exposures = False;
-  gc_values.foreground = pen_bg;
-  gc_values.background = pen_bg;
-  gc_valuemask = GCGraphicsExposures | GCForeground | GCBackground;
-  plane_gc = XCreateGC(display, clipmask[PIX_BACK], gc_valuemask, &gc_values);
+      XCopyArea(display,src_pixmap,tile_clipmask[tile],copy_clipmask_gc,
+               src_x,src_y, TILEX,TILEY, 0,0);
+    }
+  }
 
-  for(y=0;y<=SCR_FIELDY;y++) for(x=0;x<=SCR_FIELDX;x++)
-    XCopyArea(display,clipmask[PIX_BACK],clipmask[PIX_FADEMASK],plane_gc,
-             SX+2*TILEX,SY+10*TILEY,TILEX,TILEY,x*TILEX,y*TILEY);
+  if (!pix[PIX_DB_BACK] || !pix[PIX_DB_DOOR])
+    Error(ERR_EXIT, "cannot create additional pixmaps");
 
-  for(i=0;i<NUM_PIXMAPS;i++)
+  for(i=0; i<NUM_PIXMAPS; i++)
   {
     if (clipmask[i])
     {
       clip_gc_values.graphics_exposures = False;
-      clip_gc_values.foreground = pen_fg;
-      clip_gc_values.background = pen_bg;
       clip_gc_values.clip_mask = clipmask[i];
-      clip_gc_valuemask =
-       GCGraphicsExposures | GCForeground | GCBackground | GCClipMask;
+      clip_gc_valuemask = GCGraphicsExposures | GCClipMask;
       clip_gc[i] = XCreateGC(display,window,clip_gc_valuemask,&clip_gc_values);
     }
   }
 
-  drawto = drawto_field = backbuffer = pix[PIX_DB_BACK];
+  drawto = backbuffer = pix[PIX_DB_BACK];
+  fieldbuffer = pix[PIX_DB_FIELD];
+  SetDrawtoField(DRAW_BACKBUFFER);
 
   XCopyArea(display,pix[PIX_BACK],backbuffer,gc,
            0,0, WIN_XSIZE,WIN_YSIZE, 0,0);
-  XFillRectangle(display,backbuffer,gc,
+  XFillRectangle(display,pix[PIX_DB_BACK],gc,
                 REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE);
+  XFillRectangle(display,pix[PIX_DB_DOOR],gc,
+                0,0, 3*DXSIZE,DYSIZE+VYSIZE);
+
+  for(i=0; i<MAX_BUF_XSIZE; i++)
+    for(j=0; j<MAX_BUF_YSIZE; j++)
+      redraw[i][j] = 0;
+  redraw_tiles = 0;
+  redraw_mask = REDRAW_ALL;
+}
+
+void LoadGfx(int pos, struct PictureFileInfo *pic)
+{
+  char basefilename[256];
+  char filename[256];
+
+#ifdef USE_XPM_LIBRARY
+  int xpm_err, xbm_err;
+  unsigned int width,height;
+  int hot_x,hot_y;
+  Pixmap shapemask;
+  char *picture_ext = ".xpm";
+  char *picturemask_ext = "Mask.xbm";
+#else
+  int pcx_err;
+  char *picture_ext = ".pcx";
+#endif
+
+  /* Grafik laden */
+  if (pic->picture_filename)
+  {
+    sprintf(basefilename, "%s%s", pic->picture_filename, picture_ext);
+    DrawInitText(basefilename, 150, FC_YELLOW);
+    sprintf(filename, "%s/%s/%s",
+           options.base_directory, GRAPHICS_DIRECTORY, basefilename);
+
+#ifdef MSDOS
+    rest(100);
+#endif /* MSDOS */
+
+#if DEBUG_TIMING
+    debug_print_timestamp(1, NULL);    /* initialize timestamp function */
+#endif
+
+#ifdef USE_XPM_LIBRARY
+
+    xpm_att[pos].valuemask = XpmCloseness;
+    xpm_att[pos].closeness = 20000;
+    xpm_err = XpmReadFileToPixmap(display,window,filename,
+                                 &pix[pos],&shapemask,&xpm_att[pos]);
+    switch(xpm_err)
+    {
+      case XpmOpenFailed:
+       Error(ERR_EXIT, "cannot open XPM file '%s'", filename);
+      case XpmFileInvalid:
+       Error(ERR_EXIT, "invalid XPM file '%s'", filename);
+      case XpmNoMemory:
+       Error(ERR_EXIT, "not enough memory for XPM file '%s'", filename);
+      case XpmColorFailed:
+       Error(ERR_EXIT, "cannot get colors for XPM file '%s'", filename);
+      default:
+       break;
+    }
+
+#if DEBUG_TIMING
+    printf("LOADING XPM FILE %s:", filename);
+    debug_print_timestamp(1, "");
+#endif
+
+#else /* !USE_XPM_LIBRARY */
+
+    pcx_err = Read_PCX_to_Pixmap(display, window, gc, filename,
+                                &pix[pos], &clipmask[pos]);
+    switch(pcx_err)
+    {
+      case PCX_Success:
+        break;
+      case PCX_OpenFailed:
+        Error(ERR_EXIT, "cannot open PCX file '%s'", filename);
+      case PCX_ReadFailed:
+        Error(ERR_EXIT, "cannot read PCX file '%s'", filename);
+      case PCX_FileInvalid:
+       Error(ERR_EXIT, "invalid PCX file '%s'", filename);
+      case PCX_NoMemory:
+       Error(ERR_EXIT, "not enough memory for PCX file '%s'", filename);
+      case PCX_ColorFailed:
+       Error(ERR_EXIT, "cannot get colors for PCX file '%s'", filename);
+      default:
+       break;
+    }
+
+#if DEBUG_TIMING
+    printf("SUMMARY LOADING PCX FILE %s:", filename);
+    debug_print_timestamp(1, "");
+#endif
+
+#endif /* !USE_XPM_LIBRARY */
+
+    if (!pix[pos])
+      Error(ERR_EXIT, "cannot get graphics for '%s'", pic->picture_filename);
+  }
+
+  /* zugehörige Maske laden (wenn vorhanden) */
+  if (pic->picture_with_mask)
+  {
+#ifdef USE_XPM_LIBRARY
+
+    sprintf(basefilename, "%s%s", pic->picture_filename, picturemask_ext);
+    DrawInitText(basefilename, 150, FC_YELLOW);
+    sprintf(filename, "%s/%s/%s",
+           options.base_directory, GRAPHICS_DIRECTORY, basefilename);
 
-  for(i=0;i<SCR_FIELDX;i++)
-    for(j=0;j<SCR_FIELDY;j++)
-      redraw[i][j]=0;
-  redraw_tiles=0;
-  redraw_mask=REDRAW_ALL;
+#if DEBUG_TIMING
+    debug_print_timestamp(1, NULL);    /* initialize timestamp function */
+#endif
+
+    xbm_err = XReadBitmapFile(display,window,filename,
+                             &width,&height,&clipmask[pos],&hot_x,&hot_y);
+    switch(xbm_err)
+    {
+      case BitmapSuccess:
+        break;
+      case BitmapOpenFailed:
+       Error(ERR_EXIT, "cannot open XBM file '%s'", filename);
+      case BitmapFileInvalid:
+       Error(ERR_EXIT, "invalid XBM file '%s'", filename);
+      case BitmapNoMemory:
+       Error(ERR_EXIT, "not enough memory for XBM file '%s'", filename);
+       break;
+      default:
+       break;
+    }
+
+#if DEBUG_TIMING
+    printf("LOADING XBM FILE %s:", filename);
+    debug_print_timestamp(1, "");
+#endif
+
+#endif /* USE_XPM_LIBRARY */
+
+    if (!clipmask[pos])
+      Error(ERR_EXIT, "cannot get clipmask for '%s'", pic->picture_filename);
+  }
 }
 
 void InitElementProperties()
@@ -441,7 +793,8 @@ void InitElementProperties()
   {
     EL_AMOEBE_NASS,
     EL_AMOEBE_NORM,
-    EL_AMOEBE_VOLL
+    EL_AMOEBE_VOLL,
+    EL_AMOEBE_BD
   };
   static int ep_amoebalive_num = sizeof(ep_amoebalive)/sizeof(int);
 
@@ -450,20 +803,11 @@ void InitElementProperties()
     EL_AMOEBE_TOT,
     EL_AMOEBE_NASS,
     EL_AMOEBE_NORM,
-    EL_AMOEBE_VOLL
+    EL_AMOEBE_VOLL,
+    EL_AMOEBE_BD
   };
   static int ep_amoeboid_num = sizeof(ep_amoeboid)/sizeof(int);
 
-  static int ep_badewannoid[] =
-  {
-    EL_BADEWANNE1,
-    EL_BADEWANNE2,
-    EL_BADEWANNE3,
-    EL_BADEWANNE4,
-    EL_BADEWANNE5
-  };
-  static int ep_badewannoid_num = sizeof(ep_badewannoid)/sizeof(int);
-
   static int ep_schluessel[] =
   {
     EL_SCHLUESSEL1,
@@ -490,6 +834,10 @@ void InitElementProperties()
   {
     EL_BETON,
     EL_MAUERWERK,
+    EL_MAUER_LEBT,
+    EL_MAUER_X,
+    EL_MAUER_Y,
+    EL_MAUER_XY,
     EL_FELSBODEN,
     EL_AUSGANG_ZU,
     EL_AUSGANG_ACT,
@@ -498,10 +846,15 @@ void InitElementProperties()
     EL_AMOEBE_NASS,
     EL_AMOEBE_NORM,
     EL_AMOEBE_VOLL,
+    EL_AMOEBE_BD,
     EL_MORAST_VOLL,
     EL_MORAST_LEER,
     EL_SIEB_VOLL,
     EL_SIEB_LEER,
+    EL_SIEB_TOT,
+    EL_SIEB2_VOLL,
+    EL_SIEB2_LEER,
+    EL_SIEB2_TOT,
     EL_LIFE,
     EL_LIFE_ASYNC,
     EL_BADEWANNE1,
@@ -537,6 +890,10 @@ void InitElementProperties()
     EL_FELSBODEN,
     EL_FELSBROCKEN,
     EL_EDELSTEIN,
+    EL_EDELSTEIN_BD,
+    EL_EDELSTEIN_GELB,
+    EL_EDELSTEIN_ROT,
+    EL_EDELSTEIN_LILA,
     EL_DIAMANT,
     EL_BOMBE,
     EL_KOKOSNUSS,
@@ -547,7 +904,8 @@ void InitElementProperties()
     EL_BIRNE_EIN,
     EL_BIRNE_AUS,
     EL_BADEWANNE1,
-    EL_BADEWANNE2
+    EL_BADEWANNE2,
+    EL_SONDE
   };
   static int ep_slippery_num = sizeof(ep_slippery)/sizeof(int);
 
@@ -555,22 +913,54 @@ void InitElementProperties()
   {
     EL_KAEFER,
     EL_FLIEGER,
+    EL_BUTTERFLY,
+    EL_FIREFLY,
     EL_MAMPFER,
-    EL_ZOMBIE,
+    EL_MAMPFER2,
+    EL_ROBOT,
     EL_PACMAN
   };
   static int ep_enemy_num = sizeof(ep_enemy)/sizeof(int);
 
+  static int ep_mauer[] =
+  {
+    EL_BETON,
+    EL_PFORTE1,
+    EL_PFORTE2,
+    EL_PFORTE3,
+    EL_PFORTE4,
+    EL_PFORTE1X,
+    EL_PFORTE2X,
+    EL_PFORTE3X,
+    EL_PFORTE4X,
+    EL_AUSGANG_ZU,
+    EL_AUSGANG_ACT,
+    EL_AUSGANG_AUF,
+    EL_MAUERWERK,
+    EL_FELSBODEN,
+    EL_MAUER_LEBT,
+    EL_MAUER_X,
+    EL_MAUER_Y,
+    EL_MAUER_XY,
+    EL_MAUERND
+  };
+  static int ep_mauer_num = sizeof(ep_mauer)/sizeof(int);
+
   static int ep_can_fall[] =
   {
     EL_FELSBROCKEN,
     EL_EDELSTEIN,
+    EL_EDELSTEIN_BD,
+    EL_EDELSTEIN_GELB,
+    EL_EDELSTEIN_ROT,
+    EL_EDELSTEIN_LILA,
     EL_DIAMANT,
     EL_BOMBE,
     EL_KOKOSNUSS,
     EL_TROPFEN,
     EL_MORAST_VOLL,
     EL_SIEB_VOLL,
+    EL_SIEB2_VOLL,
     EL_ZEIT_VOLL,
     EL_ZEIT_LEER
   };
@@ -580,6 +970,10 @@ void InitElementProperties()
   {
     EL_FELSBROCKEN,
     EL_EDELSTEIN,
+    EL_EDELSTEIN_BD,
+    EL_EDELSTEIN_GELB,
+    EL_EDELSTEIN_ROT,
+    EL_EDELSTEIN_LILA,
     EL_DIAMANT,
     EL_SCHLUESSEL1,
     EL_SCHLUESSEL2,
@@ -597,6 +991,10 @@ void InitElementProperties()
   {
     EL_FELSBROCKEN,
     EL_EDELSTEIN,
+    EL_EDELSTEIN_BD,
+    EL_EDELSTEIN_GELB,
+    EL_EDELSTEIN_ROT,
+    EL_EDELSTEIN_LILA,
     EL_DIAMANT
   };
   static int ep_can_change_num = sizeof(ep_can_change)/sizeof(int);
@@ -605,9 +1003,17 @@ void InitElementProperties()
   {
     EL_KAEFER,
     EL_FLIEGER,
+    EL_BUTTERFLY,
+    EL_FIREFLY,
     EL_MAMPFER,
-    EL_ZOMBIE,
-    EL_PACMAN
+    EL_MAMPFER2,
+    EL_ROBOT,
+    EL_PACMAN,
+    EL_MAULWURF,
+    EL_PINGUIN,
+    EL_SCHWEIN,
+    EL_DRACHE,
+    EL_SONDE
   };
   static int ep_can_move_num = sizeof(ep_can_move)/sizeof(int);
 
@@ -621,6 +1027,14 @@ void InitElementProperties()
     EL_FLIEGER_O,
     EL_FLIEGER_L,
     EL_FLIEGER_U,
+    EL_BUTTERFLY_R,
+    EL_BUTTERFLY_O,
+    EL_BUTTERFLY_L,
+    EL_BUTTERFLY_U,
+    EL_FIREFLY_R,
+    EL_FIREFLY_O,
+    EL_FIREFLY_L,
+    EL_FIREFLY_U,
     EL_PACMAN_R,
     EL_PACMAN_O,
     EL_PACMAN_L,
@@ -631,7 +1045,9 @@ void InitElementProperties()
   static int ep_dont_touch[] =
   {
     EL_KAEFER,
-    EL_FLIEGER
+    EL_FLIEGER,
+    EL_BUTTERFLY,
+    EL_FIREFLY
   };
   static int ep_dont_touch_num = sizeof(ep_dont_touch)/sizeof(int);
 
@@ -639,84 +1055,291 @@ void InitElementProperties()
   {
     EL_KAEFER,
     EL_FLIEGER,
+    EL_BUTTERFLY,
+    EL_FIREFLY,
     EL_MAMPFER,
-    EL_ZOMBIE,
+    EL_MAMPFER2,
+    EL_ROBOT,
     EL_PACMAN,
     EL_TROPFEN,
     EL_SALZSAEURE
   };
   static int ep_dont_go_to_num = sizeof(ep_dont_go_to)/sizeof(int);
 
+  static int ep_mampf2[] =
+  {
+    EL_ERDREICH,
+    EL_KAEFER,
+    EL_FLIEGER,
+    EL_BUTTERFLY,
+    EL_FIREFLY,
+    EL_MAMPFER,
+    EL_ROBOT,
+    EL_PACMAN,
+    EL_TROPFEN,
+    EL_AMOEBE_TOT,
+    EL_AMOEBE_NASS,
+    EL_AMOEBE_NORM,
+    EL_AMOEBE_VOLL,
+    EL_AMOEBE_BD,
+    EL_EDELSTEIN,
+    EL_EDELSTEIN_BD,
+    EL_EDELSTEIN_GELB,
+    EL_EDELSTEIN_ROT,
+    EL_EDELSTEIN_LILA,
+    EL_DIAMANT
+  };
+  static int ep_mampf2_num = sizeof(ep_mampf2)/sizeof(int);
+
+  static int ep_bd_element[] =
+  {
+    EL_LEERRAUM,
+    EL_ERDREICH,
+    EL_FELSBODEN,
+    EL_FELSBROCKEN,
+    EL_EDELSTEIN_BD,
+    EL_SIEB2_LEER,
+    EL_AUSGANG_ZU,
+    EL_AUSGANG_AUF,
+    EL_BETON,
+    EL_SPIELFIGUR,
+    EL_FIREFLY,
+    EL_FIREFLY_1,
+    EL_FIREFLY_2,
+    EL_FIREFLY_3,
+    EL_FIREFLY_4,
+    EL_BUTTERFLY,
+    EL_BUTTERFLY_1,
+    EL_BUTTERFLY_2,
+    EL_BUTTERFLY_3,
+    EL_BUTTERFLY_4,
+    EL_AMOEBE_BD,
+    EL_CHAR_FRAGE
+  };
+  static int ep_bd_element_num = sizeof(ep_bd_element)/sizeof(int);
+
+  static int ep_sb_element[] =
+  {
+    EL_LEERRAUM,
+    EL_BETON,
+    EL_SOKOBAN_OBJEKT,
+    EL_SOKOBAN_FELD_LEER,
+    EL_SOKOBAN_FELD_VOLL,
+    EL_SPIELFIGUR
+  };
+  static int ep_sb_element_num = sizeof(ep_sb_element)/sizeof(int);
+
+  static int ep_gem[] =
+  {
+    EL_EDELSTEIN,
+    EL_EDELSTEIN_BD,
+    EL_EDELSTEIN_GELB,
+    EL_EDELSTEIN_ROT,
+    EL_EDELSTEIN_LILA,
+    EL_DIAMANT
+  };
+  static int ep_gem_num = sizeof(ep_gem)/sizeof(int);
+
+  static int ep_inactive[] =
+  {
+    EL_LEERRAUM,
+    EL_ERDREICH,
+    EL_MAUERWERK,
+    EL_FELSBODEN,
+    EL_SCHLUESSEL,
+    EL_BETON,
+    EL_AMOEBE_TOT,
+    EL_MORAST_LEER,
+    EL_BADEWANNE,
+    EL_ABLENK_AUS,
+    EL_SCHLUESSEL1,
+    EL_SCHLUESSEL2,
+    EL_SCHLUESSEL3,
+    EL_SCHLUESSEL4,
+    EL_PFORTE1,
+    EL_PFORTE2,
+    EL_PFORTE3,
+    EL_PFORTE4,
+    EL_PFORTE1X,
+    EL_PFORTE2X,
+    EL_PFORTE3X,
+    EL_PFORTE4X,
+    EL_DYNAMIT_AUS,
+    EL_UNSICHTBAR,
+    EL_BIRNE_AUS,
+    EL_BIRNE_EIN,
+    EL_ERZ_EDEL,
+    EL_ERZ_DIAM,
+    EL_ERZ_EDEL_BD,
+    EL_ERZ_EDEL_GELB,
+    EL_DYNABOMB_NR,
+    EL_DYNABOMB_SZ,
+    EL_DYNABOMB_XL,
+    EL_SOKOBAN_OBJEKT,
+    EL_SOKOBAN_FELD_LEER,
+    EL_SOKOBAN_FELD_VOLL,
+    EL_ERZ_EDEL_ROT,
+    EL_ERZ_EDEL_LILA,
+    EL_BADEWANNE1,
+    EL_BADEWANNE2,
+    EL_BADEWANNE3,
+    EL_BADEWANNE4,
+    EL_BADEWANNE5,
+    EL_SIEB_TOT,
+    EL_SIEB2_TOT,
+    EL_AMOEBA2DIAM,
+    EL_BLOCKED
+  };
+  static int ep_inactive_num = sizeof(ep_inactive)/sizeof(int);
+
+  static int ep_explosive[] =
+  {
+    EL_BOMBE,
+    EL_DYNAMIT,
+    EL_DYNAMIT_AUS,
+    EL_DYNABOMB,
+    EL_DYNABOMB_NR,
+    EL_DYNABOMB_SZ,
+    EL_DYNABOMB_XL,
+    EL_KAEFER,
+    EL_MAULWURF,
+    EL_PINGUIN,
+    EL_SCHWEIN,
+    EL_DRACHE,
+    EL_SONDE
+  };
+  static int ep_explosive_num = sizeof(ep_explosive)/sizeof(int);
+
+  static int ep_mampf3[] =
+  {
+    EL_EDELSTEIN,
+    EL_EDELSTEIN_BD,
+    EL_EDELSTEIN_GELB,
+    EL_EDELSTEIN_ROT,
+    EL_EDELSTEIN_LILA,
+    EL_DIAMANT
+  };
+  static int ep_mampf3_num = sizeof(ep_mampf3)/sizeof(int);
+
+  static int ep_pushable[] =
+  {
+    EL_FELSBROCKEN,
+    EL_BOMBE,
+    EL_KOKOSNUSS,
+    EL_ZEIT_LEER,
+    EL_SOKOBAN_FELD_VOLL,
+    EL_SOKOBAN_OBJEKT,
+    EL_SONDE
+  };
+  static int ep_pushable_num = sizeof(ep_pushable)/sizeof(int);
+
+  static int ep_player[] =
+  {
+    EL_SPIELFIGUR,
+    EL_SPIELER1,
+    EL_SPIELER2,
+    EL_SPIELER3,
+    EL_SPIELER4
+  };
+  static int ep_player_num = sizeof(ep_player)/sizeof(int);
+
   static long ep_bit[] =
   {
     EP_BIT_AMOEBALIVE,
     EP_BIT_AMOEBOID,
-    EP_BIT_BADEWANNOID,
     EP_BIT_SCHLUESSEL,
     EP_BIT_PFORTE,
     EP_BIT_SOLID,
     EP_BIT_MASSIV,
     EP_BIT_SLIPPERY,
     EP_BIT_ENEMY,
+    EP_BIT_MAUER,
     EP_BIT_CAN_FALL,
     EP_BIT_CAN_SMASH,
     EP_BIT_CAN_CHANGE,
     EP_BIT_CAN_MOVE,
     EP_BIT_COULD_MOVE,
     EP_BIT_DONT_TOUCH,
-    EP_BIT_DONT_GO_TO
+    EP_BIT_DONT_GO_TO,
+    EP_BIT_MAMPF2,
+    EP_BIT_BD_ELEMENT,
+    EP_BIT_SB_ELEMENT,
+    EP_BIT_GEM,
+    EP_BIT_INACTIVE,
+    EP_BIT_EXPLOSIVE,
+    EP_BIT_MAMPF3,
+    EP_BIT_PUSHABLE,
+    EP_BIT_PLAYER
   };
   static int *ep_array[] =
   {
     ep_amoebalive,
     ep_amoeboid,
-    ep_badewannoid,
     ep_schluessel,
     ep_pforte,
     ep_solid,
     ep_massiv,
     ep_slippery,
     ep_enemy,
+    ep_mauer,
     ep_can_fall,
     ep_can_smash,
     ep_can_change,
     ep_can_move,
     ep_could_move,
     ep_dont_touch,
-    ep_dont_go_to
+    ep_dont_go_to,
+    ep_mampf2,
+    ep_bd_element,
+    ep_sb_element,
+    ep_gem,
+    ep_inactive,
+    ep_explosive,
+    ep_mampf3,
+    ep_pushable,
+    ep_player
   };
   static int *ep_num[] =
   {
     &ep_amoebalive_num,
     &ep_amoeboid_num,
-    &ep_badewannoid_num,
     &ep_schluessel_num,
     &ep_pforte_num,
     &ep_solid_num,
     &ep_massiv_num,
     &ep_slippery_num,
     &ep_enemy_num,
+    &ep_mauer_num,
     &ep_can_fall_num,
     &ep_can_smash_num,
     &ep_can_change_num,
     &ep_can_move_num,
     &ep_could_move_num,
     &ep_dont_touch_num,
-    &ep_dont_go_to_num
+    &ep_dont_go_to_num,
+    &ep_mampf2_num,
+    &ep_bd_element_num,
+    &ep_sb_element_num,
+    &ep_gem_num,
+    &ep_inactive_num,
+    &ep_explosive_num,
+    &ep_mampf3_num,
+    &ep_pushable_num,
+    &ep_player_num
   };
   static int num_properties = sizeof(ep_num)/sizeof(int *);
 
-  for(i=0;i<MAX_ELEMENTS;i++)
+  for(i=0; i<MAX_ELEMENTS; i++)
     Elementeigenschaften[i] = 0;
 
-  for(i=0;i<num_properties;i++)
-    for(j=0;j<*(ep_num[i]);j++)
+  for(i=0; i<num_properties; i++)
+    for(j=0; j<*(ep_num[i]); j++)
       Elementeigenschaften[(ep_array[i])[j]] |= ep_bit[i];
-  for(i=EL_CHAR_START;i<EL_CHAR_END;i++)
-    Elementeigenschaften[i] |= EP_BIT_CHAR;
+  for(i=EL_CHAR_START; i<EL_CHAR_END; i++)
+    Elementeigenschaften[i] |= (EP_BIT_CHAR | EP_BIT_INACTIVE);
 }
 
-void CloseAll()
+void CloseAllAndExit(int exit_value)
 {
   int i;
 
@@ -727,16 +1350,18 @@ void CloseAll()
     FreeSounds(NUM_SOUNDS);
   }
 
-  for(i=0;i<NUM_PIXMAPS;i++)
+  for(i=0; i<NUM_PIXMAPS; i++)
   {
     if (pix[i])
     {
-      if (i<NUM_PICTURES)      /* XPM pictures */
+#ifdef USE_XPM_LIBRARY
+      if (i < NUM_PICTURES)    /* XPM pictures */
       {
        XFreeColors(display,DefaultColormap(display,screen),
                    xpm_att[i].pixels,xpm_att[i].npixels,0);
        XpmFreeAttributes(&xpm_att[i]);
       }
+#endif
       XFreePixmap(display,pix[i]);
     }
     if (clipmask[i])
@@ -747,10 +1372,16 @@ void CloseAll()
 
   if (gc)
     XFreeGC(display, gc);
-  if (plane_gc)
-    XFreeGC(display, plane_gc);
 
-  XCloseDisplay(display);
+  if (display)
+  {
+    XAutoRepeatOn(display);
+    XCloseDisplay(display);
+  }
+
+#ifdef MSDOS
+  dumpErrorFile();
+#endif
 
-  exit(0);
+  exit(exit_value);
 }
index c96f4844f83979109a7adedd48e075d8a536f980..b8bc9b0b5c04c2f700c90932b651199919927934 100644 (file)
@@ -1,13 +1,12 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  init.h                                                  *
 ***********************************************************/
 #include "main.h"
 
 void OpenAll(int, char **);
-void InitLevelAndPlayerInfo(void);
-void InitDisplay(int, char **);
-void InitSound(void);
-void InitSoundProcess(void);
-void InitJoystick(void);
-void InitWindow(int, char **);
-void InitGfx(void);
-void InitElementProperties(void);
-void CloseAll();
+void CloseAllAndExit(int);
+void InitJoysticks(void);
 
 #endif
diff --git a/src/joystick.c b/src/joystick.c
new file mode 100644 (file)
index 0000000..3982e0a
--- /dev/null
@@ -0,0 +1,267 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
+*----------------------------------------------------------*
+*  joystick.c                                              *
+***********************************************************/
+
+#ifdef __FreeBSD__
+#include <machine/joystick.h>
+#endif
+
+#include "joystick.h"
+#include "misc.h"
+
+void CheckJoystickData()
+{
+  int i;
+  int distance = 100;
+
+  for(i=0; i<MAX_PLAYERS; i++)
+  {
+    if (setup.input[i].joy.xmiddle <= distance)
+      setup.input[i].joy.xmiddle = distance;
+    if (setup.input[i].joy.ymiddle <= distance)
+      setup.input[i].joy.ymiddle = distance;
+
+    if (setup.input[i].joy.xleft >= setup.input[i].joy.xmiddle)
+      setup.input[i].joy.xleft = setup.input[i].joy.xmiddle - distance;
+    if (setup.input[i].joy.xright <= setup.input[i].joy.xmiddle)
+      setup.input[i].joy.xright = setup.input[i].joy.xmiddle + distance;
+
+    if (setup.input[i].joy.yupper >= setup.input[i].joy.ymiddle)
+      setup.input[i].joy.yupper = setup.input[i].joy.ymiddle - distance;
+    if (setup.input[i].joy.ylower <= setup.input[i].joy.ymiddle)
+      setup.input[i].joy.ylower = setup.input[i].joy.ymiddle + distance;
+  }
+}
+
+#ifndef MSDOS
+static int JoystickPosition(int middle, int margin, int actual)
+{
+  long range, pos;
+  int percentage;
+
+  if (margin < middle && actual > middle)
+    return 0;
+  if (margin > middle && actual < middle)
+    return 0;
+
+  range = ABS(margin - middle);
+  pos = ABS(actual - middle);
+  percentage = (int)(pos * 100 / range);
+
+  if (percentage > 100)
+    percentage = 100;
+
+  return percentage;
+}
+#endif
+
+#ifndef MSDOS
+int Joystick(int player_nr)
+{
+#ifdef __FreeBSD__
+  struct joystick joy_ctrl;
+#else
+  struct joystick_control
+  {
+    int buttons;
+    int x;
+    int y;
+  } joy_ctrl;
+#endif
+
+  int joystick_fd = stored_player[player_nr].joystick_fd;
+  int js_x,js_y, js_b1,js_b2;
+  int left, right, up, down;
+  int result = 0;
+
+  if (joystick_status == JOYSTICK_OFF)
+    return 0;
+
+  if (game_status == SETUPINPUT)
+    return 0;
+
+  if (joystick_fd < 0 || !setup.input[player_nr].use_joystick)
+    return 0;
+
+  if (read(joystick_fd, &joy_ctrl, sizeof(joy_ctrl)) != sizeof(joy_ctrl))
+  {
+    Error(ERR_WARN, "cannot read joystick device '%s'",
+         setup.input[player_nr].joy.device_name);
+    joystick_status = JOYSTICK_OFF;
+    return 0;
+  }
+
+  js_x  = joy_ctrl.x;
+  js_y  = joy_ctrl.y;
+
+#ifdef __FreeBSD__
+  js_b1 = joy_ctrl.b1;
+  js_b2 = joy_ctrl.b2;
+#else
+  js_b1 = joy_ctrl.buttons & 1;
+  js_b2 = joy_ctrl.buttons & 2;
+#endif
+
+  left  = JoystickPosition(setup.input[player_nr].joy.xmiddle,
+                          setup.input[player_nr].joy.xleft,  js_x);
+  right = JoystickPosition(setup.input[player_nr].joy.xmiddle,
+                          setup.input[player_nr].joy.xright, js_x);
+  up    = JoystickPosition(setup.input[player_nr].joy.ymiddle,
+                          setup.input[player_nr].joy.yupper, js_y);
+  down  = JoystickPosition(setup.input[player_nr].joy.ymiddle,
+                          setup.input[player_nr].joy.ylower, js_y);
+
+  if (left > JOYSTICK_PERCENT)
+    result |= JOY_LEFT;
+  else if (right > JOYSTICK_PERCENT)
+    result |= JOY_RIGHT;
+  if (up > JOYSTICK_PERCENT)
+    result |= JOY_UP;
+  else if (down > JOYSTICK_PERCENT)
+    result |= JOY_DOWN;
+
+  if (js_b1)
+    result |= JOY_BUTTON_1;
+  if (js_b2)
+    result |= JOY_BUTTON_2;
+
+  return result;
+}
+
+#else /* MSDOS */
+
+/* allegro global variables for joystick control */
+extern int num_joysticks;
+extern JOYSTICK_INFO joy[];
+
+int Joystick(int player_nr)
+{
+  int joystick_nr = stored_player[player_nr].joystick_fd;
+  int result = 0;
+
+  if (joystick_status == JOYSTICK_OFF)
+    return 0;
+
+  if (game_status == SETUPINPUT)
+    return 0;
+
+  if (joystick_nr < 0)
+    return 0;
+
+  /* the allegro global variable ïnum_joysticksï contains the number
+     of joysticks found at initialization under MSDOS / Windows */
+
+#if 0
+  if (joystick_nr >= num_joysticks || !setup.input[player_nr].use_joystick)
+    return 0;
+#else
+
+#if 1
+  if (joystick_nr >= num_joysticks ||
+      (game_status == PLAYING && !setup.input[player_nr].use_joystick))
+    return 0;
+#else
+  if (joystick_nr >= num_joysticks)
+    return 0;
+#endif
+
+#endif
+
+  poll_joystick();
+
+  if (joy[joystick_nr].stick[0].axis[0].d1)
+    result |= JOY_LEFT;
+  else if (joy[joystick_nr].stick[0].axis[0].d2)
+    result |= JOY_RIGHT;
+  if (joy[joystick_nr].stick[0].axis[1].d1)
+    result |= JOY_UP;
+  else if (joy[joystick_nr].stick[0].axis[1].d2)
+    result |= JOY_DOWN;
+
+  if (joy[joystick_nr].button[0].b)
+    result |= JOY_BUTTON_1;
+  if (joy[joystick_nr].button[1].b)
+    result |= JOY_BUTTON_2;
+
+  return result;
+}
+#endif /* MSDOS */
+
+int JoystickButton(int player_nr)
+{
+  static int last_joy_button[MAX_PLAYERS] = { 0, 0, 0, 0 };
+  int joy_button = (Joystick(player_nr) & JOY_BUTTON);
+  int result;
+
+  if (joy_button)
+  {
+    if (last_joy_button[player_nr])
+      result = JOY_BUTTON_PRESSED;
+    else
+      result = JOY_BUTTON_NEW_PRESSED;
+  }
+  else
+  {
+    if (last_joy_button[player_nr])
+      result = JOY_BUTTON_NEW_RELEASED;
+    else
+      result = JOY_BUTTON_NOT_PRESSED;
+  }
+
+  last_joy_button[player_nr] = joy_button;
+  return result;
+}
+
+int AnyJoystick()
+{
+  int i;
+  int result = 0;
+
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+
+    /*
+    if (!setup.input[i].use_joystick)
+      continue;
+      */
+
+
+    result |= Joystick(i);
+  }
+
+  return result;
+}
+
+int AnyJoystickButton()
+{
+  int i;
+  int result;
+
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+
+    /*
+    if (!setup.input[i].use_joystick)
+      continue;
+      */
+
+    /*
+    result |= JoystickButton(i);
+    */
+
+    result = JoystickButton(i);
+    if (result != JOY_BUTTON_NOT_PRESSED)
+      break;
+  }
+
+  return result;
+}
diff --git a/src/joystick.h b/src/joystick.h
new file mode 100644 (file)
index 0000000..a48c2bb
--- /dev/null
@@ -0,0 +1,72 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
+*----------------------------------------------------------*
+*  joystick.h                                              *
+***********************************************************/
+
+#ifndef JOYSTICK_H
+#define JOYSTICK_H
+
+#include "main.h"
+
+/* values for the joystick */
+#define JOYSTICK_OFF           0
+#define        JOYSTICK_AVAILABLE      1
+#ifdef __FreeBSD__
+#include <machine/joystick.h>
+#define DEV_JOYSTICK_0         "/dev/joy0"
+#define DEV_JOYSTICK_1         "/dev/joy1"
+#define DEV_JOYSTICK_2         "/dev/joy2"
+#define DEV_JOYSTICK_3         "/dev/joy3"
+#else
+#define DEV_JOYSTICK_0         "/dev/js0"
+#define DEV_JOYSTICK_1         "/dev/js1"
+#define DEV_JOYSTICK_2         "/dev/js2"
+#define DEV_JOYSTICK_3         "/dev/js3"
+#endif
+
+/* get these values from the program 'js' from the joystick package, */
+/* set JOYSTICK_PERCENT to a threshold appropriate for your joystick */
+#define JOYSTICK_XLEFT         30
+#define JOYSTICK_XMIDDLE       530
+#define JOYSTICK_XRIGHT                1250
+#define JOYSTICK_YUPPER                40
+#define JOYSTICK_YMIDDLE       680
+#define JOYSTICK_YLOWER                1440
+
+#define JOYSTICK_PERCENT       25
+
+#define JOY_LEFT               MV_LEFT
+#define JOY_RIGHT              MV_RIGHT
+#define JOY_UP                 MV_UP
+#define JOY_DOWN               MV_DOWN
+#define JOY_BUTTON_1           (1<<4)
+#define JOY_BUTTON_2           (1<<5)
+#define JOY_BUTTON             (JOY_BUTTON_1 | JOY_BUTTON_2)
+
+#define JOY_BUTTON_NOT_PRESSED 0
+#define JOY_BUTTON_PRESSED     1
+#define JOY_BUTTON_NEW_PRESSED 2
+#define JOY_BUTTON_NEW_RELEASED        3
+
+#ifdef NO_JOYSTICK
+#define JOYSTICK_STATUS                JOYSTICK_OFF
+#else
+#define JOYSTICK_STATUS                JOYSTICK_AVAILABLE
+#endif
+
+
+void CheckJoystickData(void);
+int Joystick(int);
+int JoystickButton(int);
+int AnyJoystick(void);
+int AnyJoystickButton(void);
+
+#endif
index 0e3f612bff6fb402f8952b215151142810209a0c..a0483461e302bf798c45bba8f6c3ae446f9a1f53 100644 (file)
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  main.c                                                  *
 ***********************************************************/
 
 #include "main.h"
 #include "init.h"
+#include "game.h"
 #include "events.h"
 #include "sound.h"
+#include "joystick.h"
+#include "misc.h"
+
+#ifdef MSDOS
+#include <fcntl.h>
+#endif
 
 Display        *display;
+Visual        *visual;
 int            screen;
 Window         window;
-GC             gc, plane_gc;
-GC             clip_gc[NUM_PIXMAPS];
+GC             gc, clip_gc[NUM_PIXMAPS], tile_clip_gc;
 Pixmap         pix[NUM_PIXMAPS];
-Pixmap         clipmask[NUM_PIXMAPS];
+Pixmap         clipmask[NUM_PIXMAPS], tile_clipmask[NUM_TILES];
+
+#ifdef USE_XPM_LIBRARY
 XpmAttributes  xpm_att[NUM_PICTURES];
-Drawable        drawto, drawto_field, backbuffer;
+#endif
+
+Drawable        drawto, drawto_field, backbuffer, fieldbuffer;
 Colormap       cmap;
 
 int            sound_pipe[2];
 int            sound_device;
 char          *sound_device_name = SOUND_DEVICE;
 int            joystick_device = 0;
-char          *joystick_device_name[2] = { DEV_JOYSTICK_0, DEV_JOYSTICK_1 };
-int            width, height;
-unsigned long  pen_fg, pen_bg;
+char          *joystick_device_name[MAX_PLAYERS] =
+{
+  DEV_JOYSTICK_0,
+  DEV_JOYSTICK_1,
+  DEV_JOYSTICK_2,
+  DEV_JOYSTICK_3
+};
+
+char          *program_name = NULL;
 
 int            game_status = MAINMENU;
-int            button_status = MB_NOT_PRESSED, motion_status = FALSE;
-int            key_status = KEY_NOT_PRESSED;
+boolean                network_playing = FALSE;
+int            button_status = MB_NOT_PRESSED;
+boolean                motion_status = FALSE;
+int            key_joystick_mapping = 0;
 int            global_joystick_status = JOYSTICK_STATUS;
 int            joystick_status = JOYSTICK_STATUS;
-int            sound_status = SOUND_STATUS, sound_on=TRUE;
-int            sound_loops_allowed = FALSE, sound_loops_on = FALSE;
-int            sound_music_on = FALSE;
-int            toons_on = TRUE;
-int            direct_draw_on = FALSE;
-int            fading_on = FALSE;
-int            autorecord_on = FALSE;
-int            joystick_nr = 0;
-int            quick_doors = FALSE;
-
-BOOL           redraw[SCR_FIELDX][SCR_FIELDY];
+int            sound_status = SOUND_STATUS;
+boolean                sound_loops_allowed = FALSE;
+
+boolean                redraw[MAX_BUF_XSIZE][MAX_BUF_YSIZE];
+int            redraw_x1 = 0, redraw_y1 = 0;
 int            redraw_mask;
 int            redraw_tiles;
 
-int            Feld[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-int            Ur[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-int            MovPos[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-int            MovDir[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-int            MovDelay[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-int            Store[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-int            Store2[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-int            Frame[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-int            Stop[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-int            AmoebaNr[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-int            AmoebaCnt[MAX_NUM_AMOEBA];
-long           Elementeigenschaften[MAX_ELEMENTS];
+short          Feld[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+short          Ur[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+short          MovPos[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+short          MovDir[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+short          MovDelay[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+short          Store[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+short          Store2[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+short          StorePlayer[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+short          Frame[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+boolean                Stop[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+short          JustHit[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+short          AmoebaNr[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+short          AmoebaCnt[MAX_NUM_AMOEBA], AmoebaCnt2[MAX_NUM_AMOEBA];
+unsigned long  Elementeigenschaften[MAX_ELEMENTS];
 
 int            level_nr, leveldir_nr, num_leveldirs;
 int            lev_fieldx,lev_fieldy, scroll_x,scroll_y;
 
-int            LevelSolved,GameOver, JX,JY, ZX,ZY;
-int            Gems,Dynamite,Key[4],TimeLeft,Score,MampferNr;
-int            CheckMoving,CheckExploding, SiebAktiv;
+int            FX = SX, FY = SY, ScrollStepSize = TILEX/8;
+int            ScreenMovDir = MV_NO_MOVING, ScreenMovPos = 0;
+int            ScreenGfxPos = 0;
+int            GameFrameDelay = GAME_FRAME_DELAY;
+int            FfwdFrameDelay = FFWD_FRAME_DELAY;
+int            MoveSpeed = 8;
+int            BX1 = 0, BY1 = 0, BX2 = SCR_FIELDX-1, BY2 = SCR_FIELDY-1;
+int            ZX,ZY, ExitX,ExitY;
+int            AllPlayersGone;
+int            FrameCounter, TimeFrames, TimeLeft;
+int            MampferNr, SiebAktiv;
+
+boolean                network_player_action_received = FALSE;
 
 struct LevelDirInfo    leveldir[MAX_LEVDIR_ENTRIES];
 struct LevelInfo       level;
-struct PlayerInfo      player;
+struct PlayerInfo      stored_player[MAX_PLAYERS], *local_player = NULL;
 struct HiScore         highscore[MAX_SCORE_ENTRIES];
 struct SoundInfo       Sound[NUM_SOUNDS];
-struct RecordingInfo   tape,master_tape;
-
-struct JoystickInfo joystick[2] =
-{
-  JOYSTICK_XLEFT, JOYSTICK_XRIGHT, JOYSTICK_XMIDDLE,
-  JOYSTICK_YUPPER, JOYSTICK_YLOWER, JOYSTICK_YMIDDLE,
-
-  JOYSTICK_XLEFT, JOYSTICK_XRIGHT, JOYSTICK_XMIDDLE,
-  JOYSTICK_YUPPER, JOYSTICK_YLOWER, JOYSTICK_YMIDDLE
-};
+struct TapeInfo                tape;
+struct OptionInfo      options;
+struct SetupInfo       setup;
+struct SetupFileList   *setup_list = NULL;
+struct SetupFileList   *level_setup_list = NULL;
 
 /* data needed for playing sounds */
 char *sound_name[NUM_SOUNDS] =
@@ -161,15 +180,17 @@ int background_loop[] =
 };
 int num_bg_loops = sizeof(background_loop)/sizeof(int);
 
-char           *progname;
-
 int main(int argc, char *argv[])
 {
-  progname = argv[0];
+  program_name = (strrchr(argv[0],'/') ? strrchr(argv[0],'/') + 1 : argv[0]);
+
+#ifdef MSDOS
+  _fmode = O_BINARY;
+#endif
 
+  GetOptions(argv);
   OpenAll(argc,argv);
   EventLoop();
-  CloseAll();
-
-  exit(0);
+  CloseAllAndExit(0);
+  exit(0);     /* to keep compilers happy */
 }
index 905688b335820cbe8c8dcf239d284478a5f81c7a..fed17ca6a049a68e12801eb688aaf460c4420ad8 100644 (file)
@@ -1,13 +1,12 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  main.h                                                  *
 ***********************************************************/
 #ifndef MAIN_H
 #define MAIN_H
 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#ifndef MSDOS
 #define XK_MISCELLANY
 #define XK_LATIN1
 
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
+#include <X11/Xatom.h>
 #include <X11/Xos.h>
 #include <X11/Intrinsic.h>
 #include <X11/keysymdef.h>
 
+#ifdef   XPM_INCLUDE_FILE
+#define  USE_XPM_LIBRARY
 #include XPM_INCLUDE_FILE
+#endif
+#else  /* MSDOS */
+#include "msdos.h"
+#endif  /* MSDOS */
 
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
+#ifdef DEBUG
+#define DEBUG_TIMING   0
+#endif
 
-typedef int BOOL;
+typedef unsigned char boolean;
+typedef unsigned char byte;
 
-#define TRUE           1
+#ifndef FALSE
 #define FALSE          0
+#define TRUE           (!FALSE)
+#endif
 
-#define WIN_XPOS       0
-#define WIN_YPOS       0
 #define WIN_XSIZE      672
 #define WIN_YSIZE      560
+#ifndef MSDOS
+#define WIN_XPOS       0
+#define WIN_YPOS       0
+#else  /* MSDOS */
+#define WIN_XPOS       ((XRES - WIN_XSIZE) / 2)
+#define WIN_YPOS       ((YRES - WIN_YSIZE) / 2)
+#endif /* MSDOS */
 #define SCR_FIELDX     17
 #define SCR_FIELDY     17
+#define MAX_BUF_XSIZE  (SCR_FIELDX + 2)
+#define MAX_BUF_YSIZE  (SCR_FIELDY + 2)
 
-#define MIN_LEV_FIELDX (SCR_FIELDX-2)
-#define MIN_LEV_FIELDY (SCR_FIELDY-2)
+#define MIN_LEV_FIELDX (SCR_FIELDX - 2)
+#define MIN_LEV_FIELDY (SCR_FIELDY - 2)
 #define STD_LEV_FIELDX 64
 #define STD_LEV_FIELDY 32
 #define MAX_LEV_FIELDX 128
 #define MAX_LEV_FIELDY 128
 
-#define MIN(a,b)       ((a)<(b) ? (a) : (b))
-#define MAX(a,b)       ((a)>(b) ? (a) : (b))
-#define ABS(a)         ((a)<0 ? -(a) : (a))
-#define SIGN(a)                ((a)<0 ? -1 : ((a)>0 ? 1 : 0))
-#define SCROLLX(a)     ((a)-scroll_x)
-#define SCROLLY(a)     ((a)-scroll_y)
-#define UNSCROLLX(a)   ((a)+scroll_x)
-#define UNSCROLLY(a)   ((a)+scroll_y)
-#define IN_SCR_FIELD(x,y) ((x)>=0 && (x)<SCR_FIELDX && (y)>=0 &&(y)<SCR_FIELDY)
-#define IN_LEV_FIELD(x,y) ((x)>=0 && (x)<lev_fieldx && (y)>=0 &&(y)<lev_fieldy)
-#define PLAYER(x,y)    (JX==(x) && JY==(y))
-#define IS_FREE(x,y)   (Feld[x][y]==EL_LEERRAUM && !PLAYER(x,y))
-#define IS_MOVING(x,y) (MovPos[x][y]!=0)
-#define IS_BLOCKED(x,y)        (Feld[x][y]==EL_BLOCKED)
-
-/*
-
-#define IS_AMOEBALIVE(e) ((e)==EL_AMOEBE_NASS || (e)==EL_AMOEBE_NORM || (e)==EL_AMOEBE_VOLL)
-#define IS_AMOEBOID(e) ((e)==EL_AMOEBE_TOT || IS_AMOEBALIVE(e))
-#define IS_BADEWANNOID(e) ((e)>=EL_BADEWANNE1 && (e)<=EL_BADEWANNE5)
-#define IS_SCHLUESSEL(e) ((e)>=EL_SCHLUESSEL1 && (e)<=EL_SCHLUESSEL4)
-#define IS_PFORTE(e)   ((e)>=EL_PFORTE1 && (e)<=EL_PFORTE4X)
-
-#define IS_SOLID(e)    ((e)==EL_BETON || (e)==EL_MAUERWERK || (e)==EL_FELSBODEN || (e)==EL_AUSGANG_ZU || (e)==EL_AUSGANG_ACT || (e)==EL_AUSGANG_AUF || IS_AMOEBOID(e) || (e)==EL_MORAST_VOLL || (e)==EL_MORAST_LEER || (e)==EL_SIEB_VOLL || (e)==EL_SIEB_LEER || (e)==EL_LIFE || (e)==EL_LIFE_ASYNC || IS_BADEWANNOID(e))
-
-#define IS_MASSIV(e)   ((e)==EL_BETON || (e)==EL_SALZSAEURE || IS_BADEWANNOID(e) || IS_PFORTE(e))
-
-#define IS_SLIPPERY(e) ((e)==EL_FELSBODEN || (e)==EL_FELSBROCKEN || (e)==EL_EDELSTEIN || (e)==EL_DIAMANT || (e)==EL_BOMBE || (e)==EL_KOKOSNUSS || (e)==EL_ABLENK_EIN || (e)==EL_ABLENK_AUS || (e)==EL_ZEIT_VOLL || (e)==EL_ZEIT_LEER || (e)==EL_BIRNE_EIN || (e)==EL_BIRNE_AUS || (e)==EL_BADEWANNE1 || (e)==EL_BADEWANNE2)
-
-#define IS_ENEMY(e)    ((e)==EL_KAEFER || (e)==EL_FLIEGER || (e)==EL_MAMPFER || (e)==EL_ZOMBIE || (e)==EL_PACMAN)
-
-#define CAN_FALL(e)    ((e)==EL_FELSBROCKEN || (e)==EL_EDELSTEIN || (e)==EL_DIAMANT || (e)==EL_BOMBE || (e)==EL_KOKOSNUSS || (e)==EL_TROPFEN || (e)==EL_MORAST_VOLL || (e)==EL_SIEB_VOLL || (e)==EL_ZEIT_VOLL || (e)==EL_ZEIT_LEER)
-
-#define CAN_SMASH(e)   ((e)==EL_FELSBROCKEN || (e)==EL_EDELSTEIN || (e)==EL_DIAMANT || IS_SCHLUESSEL(e) || (e)==EL_BOMBE || (e)==EL_KOKOSNUSS || (e)==EL_TROPFEN || (e)==EL_ZEIT_VOLL || (e)==EL_ZEIT_LEER)
+#define MAX_PLAYERS    4
 
-#define CAN_CHANGE(e)  ((e)==EL_FELSBROCKEN || (e)==EL_EDELSTEIN || (e)==EL_DIAMANT)
-
-#define CAN_MOVE(e)    ((e)==EL_KAEFER || (e)==EL_FLIEGER || (e)==EL_MAMPFER || (e)==EL_ZOMBIE || (e)==EL_PACMAN)
-
-#define COULD_MOVE(e)  (((e)>=EL_KAEFER_R && (e)<=EL_KAEFER_U) || ((e)>=EL_FLIEGER_R && (e)<=EL_FLIEGER_U) || ((e)>=EL_PACMAN_R && (e)==EL_PACMAN_U))
-
-#define DONT_TOUCH(e)  ((e)==EL_KAEFER || (e)==EL_FLIEGER)
-#define DONT_GO_TO(e)  (IS_ENEMY(e) || (e)==EL_TROPFEN || (e)==EL_SALZSAEURE)
-
-#define IS_CHAR(e)     ((e)>=EL_CHAR_START && (e)<=EL_CHAR_END)
-
-*/
+#ifndef MIN
+#define MIN(a,b)       ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a,b)       ((a) > (b) ? (a) : (b))
+#endif
+#ifndef ABS
+#define ABS(a)         ((a) < 0 ? -(a) : (a))
+#endif
+#ifndef SIGN
+#define SIGN(a)                ((a) < 0 ? -1 : ((a)>0 ? 1 : 0))
+#endif
 
+#define SCREENX(a)     ((a) - scroll_x)
+#define SCREENY(a)     ((a) - scroll_y)
+#define LEVELX(a)      ((a) + scroll_x)
+#define LEVELY(a)      ((a) + scroll_y)
+#define IN_VIS_FIELD(x,y) ((x)>=0 && (x)<SCR_FIELDX && (y)>=0 &&(y)<SCR_FIELDY)
+#define IN_SCR_FIELD(x,y) ((x)>=BX1 && (x)<=BX2 && (y)>=BY1 &&(y)<=BY2)
+#define IN_LEV_FIELD(x,y) ((x)>=0 && (x)<lev_fieldx && (y)>=0 &&(y)<lev_fieldy)
 
 #define EP_BIT_AMOEBALIVE      (1<<0)
 #define EP_BIT_AMOEBOID                (1<<1)
-#define EP_BIT_BADEWANNOID     (1<<2)
-#define EP_BIT_SCHLUESSEL      (1<<3)
-#define EP_BIT_PFORTE          (1<<4)
-#define EP_BIT_SOLID           (1<<5)
-#define EP_BIT_MASSIV          (1<<6)
-#define EP_BIT_SLIPPERY                (1<<7)
-#define EP_BIT_ENEMY           (1<<8)
+#define EP_BIT_SCHLUESSEL      (1<<2)
+#define EP_BIT_PFORTE          (1<<3)
+#define EP_BIT_SOLID           (1<<4)
+#define EP_BIT_MASSIV          (1<<5)
+#define EP_BIT_SLIPPERY                (1<<6)
+#define EP_BIT_ENEMY           (1<<7)
+#define EP_BIT_MAUER           (1<<8)
 #define EP_BIT_CAN_FALL                (1<<9)
 #define EP_BIT_CAN_SMASH       (1<<10)
 #define EP_BIT_CAN_CHANGE      (1<<11)
@@ -117,17 +112,26 @@ typedef int BOOL;
 #define EP_BIT_COULD_MOVE      (1<<13)
 #define EP_BIT_DONT_TOUCH      (1<<14)
 #define EP_BIT_DONT_GO_TO      (1<<15)
-#define EP_BIT_CHAR            (1<<16)
+#define EP_BIT_MAMPF2          (1<<16)
+#define EP_BIT_CHAR            (1<<17)
+#define EP_BIT_BD_ELEMENT      (1<<18)
+#define EP_BIT_SB_ELEMENT      (1<<19)
+#define EP_BIT_GEM             (1<<20)
+#define EP_BIT_INACTIVE                (1<<21)
+#define EP_BIT_EXPLOSIVE       (1<<22)
+#define EP_BIT_MAMPF3          (1<<23)
+#define EP_BIT_PUSHABLE                (1<<24)
+#define EP_BIT_PLAYER          (1<<25)
 
 #define IS_AMOEBALIVE(e)       (Elementeigenschaften[e] & EP_BIT_AMOEBALIVE)
 #define IS_AMOEBOID(e)         (Elementeigenschaften[e] & EP_BIT_AMOEBOID)
-#define IS_BADEWANNOID(e)      (Elementeigenschaften[e] & EP_BIT_BADEWANNOID)
 #define IS_SCHLUESSEL(e)       (Elementeigenschaften[e] & EP_BIT_SCHLUESSEL)
 #define IS_PFORTE(e)           (Elementeigenschaften[e] & EP_BIT_PFORTE)
 #define IS_SOLID(e)            (Elementeigenschaften[e] & EP_BIT_SOLID)
 #define IS_MASSIV(e)           (Elementeigenschaften[e] & EP_BIT_MASSIV)
 #define IS_SLIPPERY(e)         (Elementeigenschaften[e] & EP_BIT_SLIPPERY)
 #define IS_ENEMY(e)            (Elementeigenschaften[e] & EP_BIT_ENEMY)
+#define IS_MAUER(e)            (Elementeigenschaften[e] & EP_BIT_MAUER)
 #define CAN_FALL(e)            (Elementeigenschaften[e] & EP_BIT_CAN_FALL)
 #define CAN_SMASH(e)           (Elementeigenschaften[e] & EP_BIT_CAN_SMASH)
 #define CAN_CHANGE(e)          (Elementeigenschaften[e] & EP_BIT_CAN_CHANGE)
@@ -135,52 +139,71 @@ typedef int BOOL;
 #define COULD_MOVE(e)          (Elementeigenschaften[e] & EP_BIT_COULD_MOVE)
 #define DONT_TOUCH(e)          (Elementeigenschaften[e] & EP_BIT_DONT_TOUCH)
 #define DONT_GO_TO(e)          (Elementeigenschaften[e] & EP_BIT_DONT_GO_TO)
+#define IS_MAMPF2(e)           (Elementeigenschaften[e] & EP_BIT_MAMPF2)
 #define IS_CHAR(e)             (Elementeigenschaften[e] & EP_BIT_CHAR)
-
-#define EL_CHANGED(e)          ((e)==EL_FELSBROCKEN ? EL_EDELSTEIN :   \
-                                (e)==EL_EDELSTEIN   ? EL_DIAMANT :     \
+#define IS_BD_ELEMENT(e)       (Elementeigenschaften[e] & EP_BIT_BD_ELEMENT)
+#define IS_SB_ELEMENT(e)       (Elementeigenschaften[e] & EP_BIT_SB_ELEMENT)
+#define IS_GEM(e)              (Elementeigenschaften[e] & EP_BIT_GEM)
+#define IS_INACTIVE(e)         (Elementeigenschaften[e] & EP_BIT_INACTIVE)
+#define IS_EXPLOSIVE(e)                (Elementeigenschaften[e] & EP_BIT_EXPLOSIVE)
+#define IS_MAMPF3(e)           (Elementeigenschaften[e] & EP_BIT_MAMPF3)
+#define IS_PUSHABLE(e)         (Elementeigenschaften[e] & EP_BIT_PUSHABLE)
+#define ELEM_IS_PLAYER(e)      (Elementeigenschaften[e] & EP_BIT_PLAYER)
+
+#define IS_PLAYER(x,y)         (ELEM_IS_PLAYER(StorePlayer[x][y]))
+
+#define IS_FREE(x,y)           (Feld[x][y] == EL_LEERRAUM && !IS_PLAYER(x,y))
+#define IS_FREE_OR_PLAYER(x,y) (Feld[x][y] == EL_LEERRAUM)
+
+#define IS_MOVING(x,y)         (MovPos[x][y] != 0)
+#define IS_BLOCKED(x,y)                (Feld[x][y] == EL_BLOCKED)
+
+#define EL_CHANGED(e)          ((e) == EL_FELSBROCKEN    ? EL_EDELSTEIN :  \
+                                (e) == EL_EDELSTEIN      ? EL_DIAMANT :    \
+                                (e) == EL_EDELSTEIN_GELB ? EL_DIAMANT :    \
+                                (e) == EL_EDELSTEIN_ROT  ? EL_DIAMANT :    \
+                                (e) == EL_EDELSTEIN_LILA ? EL_DIAMANT :    \
+                                EL_FELSBROCKEN)
+#define EL_CHANGED2(e)         ((e) == EL_FELSBROCKEN ? EL_EDELSTEIN_BD :  \
                                 EL_FELSBROCKEN)
-#define IS_DRAWABLE(e)         ((e)<EL_BLOCKED)
-#define IS_NOT_DRAWABLE(e)     ((e)>=EL_BLOCKED)
-#define TIMESIZE               (TimeLeft*100/level.time)
-#define LEVELDIR_SIZE(x)       ((x).num_ready + (x).num_free)
+#define IS_DRAWABLE(e)         ((e) < EL_BLOCKED)
+#define IS_NOT_DRAWABLE(e)     ((e) >= EL_BLOCKED)
+#define TIMESIZE               (TimeLeft * 100 / level.time)
 #define TAPE_IS_EMPTY(x)       ((x).length == 0)
+#define TAPE_IS_STOPPED(x)     (!(x).recording && !(x).playing &&!(x).pausing)
+
+#define PLAYERINFO(x,y)                (&stored_player[StorePlayer[x][y]-EL_SPIELER1])
 
 /* Pixmaps with Xpm or X11 Bitmap files */
 #define PIX_BACK               0
 #define PIX_DOOR               1
-#define PIX_TOONS              2
-#define        PIX_BIGFONT             3
-#define PIX_SMALLFONT          4
+#define PIX_HEROES             2
+#define PIX_TOONS              3
+#define        PIX_BIGFONT             4
+#define PIX_SMALLFONT          5
 /* Pixmaps without them */
-#define PIX_DB_BACK            5
-#define PIX_DB_DOOR            6
-#define PIX_FADEMASK           7
+#define PIX_DB_BACK            6
+#define PIX_DB_DOOR            7
+#define PIX_DB_FIELD           8
 
-#define NUM_PICTURES           5
-#define NUM_PIXMAPS            8
+#define NUM_PICTURES           6
+#define NUM_PIXMAPS            9
 
 /* boundaries of arrays etc. */
 #define MAX_NAMELEN            (10+1)
-
 #define MAX_LEVNAMLEN          32
-#define MAX_SC_ENTRIES         15
-#define MAX_TAPELEN            10000
-
-#define MAX_LEVDIR_FILENAME    (64+1)
-#define MAX_LEVDIR_NAME                (16+1)
-#define MAX_LEVDIR_ENTRIES     15
-#define MAX_SCORE_ENTRIES      15
-
-#define MAX_FILENAME           256
-#define MAX_NUM_AMOEBA         100
+#define MAX_TAPELEN            (1000 * 50)     /* max. time * framerate */
+#define MAX_LEVDIR_ENTRIES     100
+#define MAX_SCORE_ENTRIES      100
 #define MAX_ELEMENTS           512
+#define MAX_NUM_AMOEBA         100
 
-struct PictureFile
-{
-  char *picture_filename;
-  char *picturemask_filename;
-};
+#define LEVEL_SCORE_ELEMENTS   16      /* level elements with score */
+
+/* fundamental game speed values */
+#define GAME_FRAME_DELAY       20      /* frame delay in milliseconds */
+#define FFWD_FRAME_DELAY       10      /* 200% speed for fast forward */
+#define FRAMES_PER_SECOND      (1000 / GAME_FRAME_DELAY)
 
 struct HiScore
 {
@@ -188,13 +211,113 @@ struct HiScore
   int Score;
 };
 
+struct OptionInfo
+{
+  char *display_name;
+  char *server_host;
+  int server_port;
+  char *base_directory;
+  char *level_directory;
+  boolean serveronly;
+  boolean network;
+  boolean verbose;
+};
+
+struct SetupJoystickInfo
+{
+  char *device_name;
+  int xleft, xmiddle, xright;
+  int yupper, ymiddle, ylower;
+  int snap;
+  int bomb;
+};
+
+struct SetupKeyboardInfo
+{
+  KeySym left;
+  KeySym right;
+  KeySym up;
+  KeySym down;
+  KeySym snap;
+  KeySym bomb;
+};
+
+struct SetupInputInfo
+{
+  boolean use_joystick;
+  struct SetupJoystickInfo joy;
+  struct SetupKeyboardInfo key;
+};
+
+struct SetupInfo
+{
+  char *player_name;
+
+  boolean sound;
+  boolean sound_loops;
+  boolean sound_music;
+  boolean sound_simple;
+  boolean toons;
+  boolean double_buffering;
+  boolean direct_draw;         /* !double_buffering (redundant!) */
+  boolean scroll_delay;
+  boolean soft_scrolling;
+  boolean fading;
+  boolean autorecord;
+  boolean quick_doors;
+  boolean team_mode;
+
+  struct SetupInputInfo input[MAX_PLAYERS];
+};
+
+struct SetupFileList
+{
+  char *token;
+  char *value;
+  struct SetupFileList *next;
+};
+
 struct PlayerInfo
 {
-  char login_name[MAX_NAMELEN];
-  char alias_name[MAX_NAMELEN];
-  int handicap;
-  unsigned int setup;
-  int leveldir_nr;
+  boolean present;             /* player present in level playfield */
+  boolean connected;           /* player connected (locally or via network) */
+  boolean active;              /* player (present && connected) */
+
+  int index_nr, client_nr, element_nr;
+
+  byte action;                 /* action from local input device */
+  byte effective_action;       /* action aknowledged from network server
+                                  or summarized over all configured input
+                                  devices when in single player mode */
+
+  int joystick_fd;             /* file descriptor of player's joystick */
+
+  int jx,jy, last_jx,last_jy;
+  int MovDir, MovPos, GfxPos;
+  int Frame;
+
+  boolean Pushing;
+  boolean gone, LevelSolved, GameOver;
+  boolean snapped;
+
+  unsigned long move_delay;
+  int last_move_dir;
+
+  unsigned long push_delay;
+  unsigned long push_delay_value;
+
+  int frame_reset_delay;
+
+  unsigned long actual_frame_counter;
+
+  int score;
+  int gems_still_needed;
+  int sokobanfields_still_needed;
+  int lights_still_needed;
+  int friends_still_needed;
+  int key[4];
+  int dynamite;
+  int dynabomb_count, dynabomb_size, dynabombs_left, dynabomb_xl;
 };
 
 struct LevelInfo
@@ -204,162 +327,181 @@ struct LevelInfo
   int time;
   int edelsteine;
   char name[MAX_LEVNAMLEN];
-  int score[MAX_SC_ENTRIES];
-  int amoebe_inhalt;
+  int score[LEVEL_SCORE_ELEMENTS];
   int mampfer_inhalt[4][3][3];
   int tempo_amoebe;
   int dauer_sieb;
   int dauer_ablenk;
+  int amoebe_inhalt;
 };
 
 struct LevelDirInfo
 {
-  char filename[MAX_LEVDIR_FILENAME];
-  char name[MAX_LEVDIR_NAME];
-  int num_ready;
-  int num_free;
+  char *filename;
+  char *name;
+  int levels;
+  int sort_priority;
+  boolean user_defined;
+  boolean readonly;
 };
 
-struct RecordingInfo
+struct TapeInfo
 {
   int level_nr;
-  unsigned int random_seed;
+  unsigned long random_seed;
   unsigned long date;
   unsigned long counter;
   unsigned long length;
-  BOOL recording, playing, pausing;
+  unsigned long length_seconds;
+  unsigned int delay_played;
+  boolean pause_before_death;
+  boolean recording, playing, pausing;
+  boolean fast_forward;
+  boolean changed;
+  boolean player_participates[MAX_PLAYERS];
   struct
   {
-    unsigned char joystickdata;
-    unsigned char delay;
+    byte action[MAX_PLAYERS];
+    byte delay;
   } pos[MAX_TAPELEN];
 };
 
-struct JoystickInfo
-{
-  int xleft, xright, xmiddle;
-  int yupper, ylower, ymiddle;
-};
-
 extern Display        *display;
+extern Visual         *visual;
 extern int             screen;
 extern Window                  window;
-extern GC                      gc, plane_gc;
-extern GC              clip_gc[];
-extern XImage         *image[];
-extern Pixmap          clipmask[];
+extern GC              gc, clip_gc[], tile_clip_gc;
 extern Pixmap          pix[];
+extern Pixmap          clipmask[], tile_clipmask[];
+
+#ifdef USE_XPM_LIBRARY
 extern XpmAttributes   xpm_att[];
-extern Drawable        drawto, drawto_field, backbuffer;
+#endif
+
+extern Drawable        drawto, drawto_field, backbuffer, fieldbuffer;
 extern Colormap                cmap;
 
 extern int             sound_pipe[2];
 extern int             sound_device;
 extern char           *sound_device_name;
 extern int             joystick_device;
-extern char           *joystick_device_name[2];
-extern int                     width, height;
-extern unsigned long   pen_fg, pen_bg;
+extern char           *joystick_device_name[];
+
+extern char           *program_name;
 
 extern int             game_status;
-extern int             button_status, motion_status;
-extern int             key_status;
+extern boolean         network_playing;
+extern int             button_status;
+extern boolean         motion_status;
+extern int             key_joystick_mapping;
 extern int             global_joystick_status, joystick_status;
-extern int             sound_status, sound_on;
-extern int             sound_loops_allowed, sound_loops_on;
-extern int             sound_music_on;
-extern int             toons_on;
-extern int             direct_draw_on;
-extern int             fading_on;
-extern int             autorecord_on;
-extern int             joystick_nr;
-extern int             quick_doors;
-
-extern BOOL            redraw[SCR_FIELDX][SCR_FIELDY];
+extern int             sound_status;
+extern boolean         sound_loops_allowed;
+
+extern boolean         redraw[MAX_BUF_XSIZE][MAX_BUF_YSIZE];
+extern int             redraw_x1, redraw_y1;
 extern int             redraw_mask;
 extern int             redraw_tiles;
 
-extern int             Feld[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-extern int             Ur[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-extern int             MovPos[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-extern int             MovDir[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-extern int             MovDelay[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-extern int             Store[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-extern int             Store2[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-extern int             Frame[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-extern int             Stop[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-extern int             AmoebaNr[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
-extern int             AmoebaCnt[MAX_NUM_AMOEBA];
-extern long            Elementeigenschaften[MAX_ELEMENTS];
+extern short           Feld[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           Ur[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           MovPos[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           MovDir[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           MovDelay[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           Store[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           Store2[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           StorePlayer[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           Frame[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern boolean         Stop[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           JustHit[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           AmoebaNr[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           AmoebaCnt[MAX_NUM_AMOEBA], AmoebaCnt2[MAX_NUM_AMOEBA];
+extern unsigned long   Elementeigenschaften[MAX_ELEMENTS];
 
 extern int             level_nr, leveldir_nr, num_leveldirs;
 extern int             lev_fieldx,lev_fieldy, scroll_x,scroll_y;
 
-extern int             LevelSolved,GameOver, JX,JY, ZX,ZY;
-extern int             Gems,Dynamite,Key[4],TimeLeft,Score,MampferNr;
-extern int             CheckMoving,CheckExploding, SiebAktiv;
+extern int             FX,FY, ScrollStepSize;
+extern int             ScreenMovDir, ScreenMovPos, ScreenGfxPos;
+extern int             GameFrameDelay;
+extern int             FfwdFrameDelay;
+extern int             MoveSpeed;
+extern int             BX1,BY1, BX2,BY2;
+extern int             ZX,ZY, ExitX,ExitY;
+extern int             AllPlayersGone;
+extern int             FrameCounter, TimeFrames, TimeLeft;
+extern int             MampferNr, SiebAktiv;
+
+extern boolean         network_player_action_received;
 
 extern struct LevelDirInfo     leveldir[];
 extern struct LevelInfo                level;
-extern struct PlayerInfo       player;
+extern struct PlayerInfo       stored_player[], *local_player;
 extern struct HiScore          highscore[];
-extern struct RecordingInfo    tape, master_tape;
+extern struct TapeInfo         tape;
+extern struct SoundInfo                Sound[];
 extern struct JoystickInfo     joystick[];
+extern struct OptionInfo       options;
+extern struct SetupInfo                setup;
+extern struct SetupFileList    *setup_list;
+extern struct SetupFileList    *level_setup_list;
 
+extern char            *sound_name[];
 extern int             background_loop[];
 extern int             num_bg_loops;
 
-extern char           *progname;
-
 
 /* often used screen positions */
-#define SX             8
-#define SY             8
-#define REAL_SX                (SX-2)
-#define REAL_SY                (SY-2)
-#define DX             566
-#define DY             60
-#define VX             DX
-#define VY             400
-#define TILEX          32
-#define TILEY          32
-#define MINI_TILEX     (TILEX/2)
-#define MINI_TILEY     (TILEY/2)
-#define MICRO_TILEX    (TILEX/8)
-#define MICRO_TILEY    (TILEY/8)
-#define MIDPOSX                (SCR_FIELDX/2)
-#define MIDPOSY                (SCR_FIELDY/2)
-#define SXSIZE         (SCR_FIELDX*TILEX)
-#define SYSIZE         (SCR_FIELDY*TILEY)
-#define DXSIZE         100
-#define DYSIZE         280
-#define VXSIZE         DXSIZE
-#define VYSIZE         100
-#define FULL_SXSIZE    (2+SXSIZE+2)
-#define FULL_SYSIZE    (2+SYSIZE+2)
-#define MICROLEV_XPOS  (SX+4*32+16)
-#define MICROLEV_YPOS  (SX+12*32)
-#define MICROLEV_XSIZE (STD_LEV_FIELDX*MICRO_TILEX)
-#define MICROLEV_YSIZE (STD_LEV_FIELDY*MICRO_TILEY)
-#define MICROLABEL_YPOS        (MICROLEV_YPOS+MICROLEV_YSIZE+12)
-#define FONT1_XSIZE    32
-#define FONT1_YSIZE    32
-#define FONT2_XSIZE    14
-#define FONT2_YSIZE    14
-#define FONT3_XSIZE    11
-#define FONT3_YSIZE    14
-#define FONT4_XSIZE    16
-#define FONT4_YSIZE    16
+#define SX                     8
+#define SY                     8
+#define REAL_SX                        (SX-2)
+#define REAL_SY                        (SY-2)
+#define DX                     566
+#define DY                     60
+#define VX                     DX
+#define VY                     400
+#define TILEX                  32
+#define TILEY                  32
+#define MINI_TILEX             (TILEX/2)
+#define MINI_TILEY             (TILEY/2)
+#define MICRO_TILEX            (TILEX/8)
+#define MICRO_TILEY            (TILEY/8)
+#define MIDPOSX                        (SCR_FIELDX/2)
+#define MIDPOSY                        (SCR_FIELDY/2)
+#define SXSIZE                 (SCR_FIELDX*TILEX)
+#define SYSIZE                 (SCR_FIELDY*TILEY)
+#define FXSIZE                 ((SCR_FIELDX+2)*TILEX)
+#define FYSIZE                 ((SCR_FIELDY+2)*TILEY)
+#define DXSIZE                 100
+#define DYSIZE                 280
+#define VXSIZE                 DXSIZE
+#define VYSIZE                 100
+#define FULL_SXSIZE            (2+SXSIZE+2)
+#define FULL_SYSIZE            (2+SYSIZE+2)
+#define MICROLEV_XPOS          (SX+4*32+16)
+#define MICROLEV_YPOS          (SX+12*32)
+#define MICROLEV_XSIZE         (STD_LEV_FIELDX*MICRO_TILEX)
+#define MICROLEV_YSIZE         (STD_LEV_FIELDY*MICRO_TILEY)
+#define MICROLABEL_YPOS                (MICROLEV_YPOS+MICROLEV_YSIZE+12)
+#define FONT1_XSIZE            32
+#define FONT1_YSIZE            32
+#define FONT2_XSIZE            14
+#define FONT2_YSIZE            14
+#define FONT3_XSIZE            11
+#define FONT3_YSIZE            14
+#define FONT4_XSIZE            16
+#define FONT4_YSIZE            16
 
 #define GFX_STARTX             SX
 #define GFX_STARTY             SY
 #define MINI_GFX_STARTX                SX
-#define MINI_GFX_STARTY                432
+#define MINI_GFX_STARTY                424
 #define MICRO_GFX_STARTX       SX
-#define MICRO_GFX_STARTY       528
+#define MICRO_GFX_STARTY       536
 #define GFX_PER_LINE           16
 #define MINI_GFX_PER_LINE      32
 #define MICRO_GFX_PER_LINE     128
+#define HEROES_PER_LINE                16
 #define FONT_CHARS_PER_LINE    16
 #define FONT_LINES_PER_FONT    4
 
@@ -368,549 +510,614 @@ extern char            *progname;
 **     256 - 511: flag elements, only used at runtime
 */
 /* "real" level elements */
-#define EL_LEERRAUM    0
-#define        EL_ERDREICH     1
-#define        EL_MAUERWERK    2
-#define        EL_FELSBODEN    3
-#define        EL_FELSBROCKEN  4
-#define        EL_SCHLUESSEL   5
-#define        EL_EDELSTEIN    6
-#define        EL_AUSGANG_ZU   7
-#define        EL_SPIELFIGUR   8
-#define EL_KAEFER      9
-#define EL_FLIEGER     10
-#define EL_MAMPFER     11
-#define EL_ZOMBIE      12
-#define EL_BETON       13
-#define EL_DIAMANT     14
-#define EL_AMOEBE_TOT  15
-#define EL_MORAST_LEER 16
-#define EL_MORAST_VOLL 17
-#define EL_TROPFEN     18
-#define EL_BOMBE       19
-#define EL_SIEB_LEER   20
-#define EL_SIEB_VOLL   21
-#define EL_SALZSAEURE  22
-#define EL_AMOEBE_NASS 23
-#define EL_AMOEBE_NORM 24
-#define EL_KOKOSNUSS   25
-#define EL_LIFE                26
-#define EL_LIFE_ASYNC  27
-#define EL_DYNAMIT     28
-#define EL_BADEWANNE   29
-#define EL_ABLENK_AUS  30
-#define EL_ABLENK_EIN  31
-#define EL_SCHLUESSEL1 32
-#define EL_SCHLUESSEL2 33
-#define EL_SCHLUESSEL3 34
-#define EL_SCHLUESSEL4 35
-#define EL_PFORTE1     36
-#define EL_PFORTE2     37
-#define EL_PFORTE3     38
-#define EL_PFORTE4     39
-#define EL_PFORTE1X    40
-#define EL_PFORTE2X    41
-#define EL_PFORTE3X    42
-#define EL_PFORTE4X    43
-#define EL_DYNAMIT_AUS 44
-#define EL_PACMAN      45
-#define EL_UNSICHTBAR  46
-#define EL_BIRNE_AUS   47
-#define EL_BIRNE_EIN   48
-#define EL_ERZ_EDEL    49
-#define EL_ERZ_DIAM    50
-#define EL_AMOEBE_VOLL 51
-#define EL_AMOEBA2DIAM 52
-#define EL_ZEIT_VOLL   53
-#define EL_ZEIT_LEER   54
-
-#define EL_SPIELER1    80
-#define EL_SPIELER2    81
-#define EL_SPIELER3    82
-#define EL_SPIELER4    83
-#define EL_KAEFER_R    84
-#define EL_KAEFER_O    85
-#define EL_KAEFER_L    86
-#define EL_KAEFER_U    87
-#define EL_FLIEGER_R   88
-#define EL_FLIEGER_O   89
-#define EL_FLIEGER_L   90
-#define EL_FLIEGER_U   91
-#define EL_PACMAN_R    92
-#define EL_PACMAN_O    93
-#define EL_PACMAN_L    94
-#define EL_PACMAN_U    95
-
-#define EL_BADEWANNE1  100
-#define EL_BADEWANNE2  101
-#define EL_BADEWANNE3  102
-#define EL_BADEWANNE4  103
-#define EL_BADEWANNE5  104
-#define EL_SIEB_TOT    105
-#define EL_AUSGANG_ACT 106
-#define EL_AUSGANG_AUF 107
-
-#define EL_CHAR_START  120
-#define EL_CHAR_ASCII0 (EL_CHAR_START-32)
-#define EL_CHAR_AUSRUF (EL_CHAR_ASCII0+33)
-#define EL_CHAR_ZOLL   (EL_CHAR_ASCII0+34)
-#define EL_CHAR_DOLLAR (EL_CHAR_ASCII0+36)
-#define EL_CHAR_PROZ   (EL_CHAR_ASCII0+37)
-#define EL_CHAR_APOSTR (EL_CHAR_ASCII0+39)
-#define EL_CHAR_KLAMM1 (EL_CHAR_ASCII0+40)
-#define EL_CHAR_KLAMM2 (EL_CHAR_ASCII0+41)
-#define EL_CHAR_PLUS   (EL_CHAR_ASCII0+43)
-#define EL_CHAR_KOMMA  (EL_CHAR_ASCII0+44)
-#define EL_CHAR_MINUS  (EL_CHAR_ASCII0+45)
-#define EL_CHAR_PUNKT  (EL_CHAR_ASCII0+46)
-#define EL_CHAR_SLASH  (EL_CHAR_ASCII0+47)
-#define EL_CHAR_0      (EL_CHAR_ASCII0+48)
-#define EL_CHAR_9      (EL_CHAR_ASCII0+57)
-#define EL_CHAR_DOPPEL (EL_CHAR_ASCII0+58)
-#define EL_CHAR_SEMIKL (EL_CHAR_ASCII0+59)
-#define EL_CHAR_LT     (EL_CHAR_ASCII0+60)
-#define EL_CHAR_GLEICH (EL_CHAR_ASCII0+61)
-#define EL_CHAR_GT     (EL_CHAR_ASCII0+62)
-#define EL_CHAR_FRAGE  (EL_CHAR_ASCII0+63)
-#define EL_CHAR_AT     (EL_CHAR_ASCII0+64)
-#define EL_CHAR_A      (EL_CHAR_ASCII0+65)
-#define EL_CHAR_Z      (EL_CHAR_ASCII0+90)
-#define EL_CHAR_AE     (EL_CHAR_ASCII0+91)
-#define EL_CHAR_OE     (EL_CHAR_ASCII0+92)
-#define EL_CHAR_UE     (EL_CHAR_ASCII0+93)
-#define EL_CHAR_COPY   (EL_CHAR_ASCII0+94)
-#define EL_CHAR_END    (EL_CHAR_START+79)
+#define EL_LEERRAUM            0
+#define        EL_ERDREICH             1
+#define        EL_MAUERWERK            2
+#define        EL_FELSBODEN            3
+#define        EL_FELSBROCKEN          4
+#define        EL_SCHLUESSEL           5
+#define        EL_EDELSTEIN            6
+#define        EL_AUSGANG_ZU           7
+#define        EL_SPIELFIGUR           8
+#define EL_KAEFER              9
+#define EL_FLIEGER             10
+#define EL_MAMPFER             11
+#define EL_ROBOT               12
+#define EL_BETON               13
+#define EL_DIAMANT             14
+#define EL_AMOEBE_TOT          15
+#define EL_MORAST_LEER         16
+#define EL_MORAST_VOLL         17
+#define EL_TROPFEN             18
+#define EL_BOMBE               19
+#define EL_SIEB_LEER           20
+#define EL_SIEB_VOLL           21
+#define EL_SALZSAEURE          22
+#define EL_AMOEBE_NASS         23
+#define EL_AMOEBE_NORM         24
+#define EL_KOKOSNUSS           25
+#define EL_LIFE                        26
+#define EL_LIFE_ASYNC          27
+#define EL_DYNAMIT             28
+#define EL_BADEWANNE           29
+#define EL_ABLENK_AUS          30
+#define EL_ABLENK_EIN          31
+#define EL_SCHLUESSEL1         32
+#define EL_SCHLUESSEL2         33
+#define EL_SCHLUESSEL3         34
+#define EL_SCHLUESSEL4         35
+#define EL_PFORTE1             36
+#define EL_PFORTE2             37
+#define EL_PFORTE3             38
+#define EL_PFORTE4             39
+#define EL_PFORTE1X            40
+#define EL_PFORTE2X            41
+#define EL_PFORTE3X            42
+#define EL_PFORTE4X            43
+#define EL_DYNAMIT_AUS         44
+#define EL_PACMAN              45
+#define EL_UNSICHTBAR          46
+#define EL_BIRNE_AUS           47
+#define EL_BIRNE_EIN           48
+#define EL_ERZ_EDEL            49
+#define EL_ERZ_DIAM            50
+#define EL_AMOEBE_VOLL         51
+#define EL_AMOEBE_BD           52
+#define EL_ZEIT_VOLL           53
+#define EL_ZEIT_LEER           54
+#define EL_MAUER_LEBT          55
+#define EL_EDELSTEIN_BD                56
+#define EL_EDELSTEIN_GELB      57
+#define EL_ERZ_EDEL_BD         58
+#define EL_ERZ_EDEL_GELB       59
+#define EL_MAMPFER2            60
+#define EL_SIEB2_LEER          61
+#define EL_SIEB2_VOLL          62
+#define EL_DYNABOMB            63
+#define EL_DYNABOMB_NR         64
+#define EL_DYNABOMB_SZ         65
+#define EL_DYNABOMB_XL         66
+#define EL_SOKOBAN_OBJEKT      67
+#define EL_SOKOBAN_FELD_LEER   68
+#define EL_SOKOBAN_FELD_VOLL   69
+#define EL_BUTTERFLY_R         70
+#define EL_BUTTERFLY_O         71
+#define EL_BUTTERFLY_L         72
+#define EL_BUTTERFLY_U         73
+#define EL_FIREFLY_R           74
+#define EL_FIREFLY_O           75
+#define EL_FIREFLY_L           76
+#define EL_FIREFLY_U           77
+#define EL_BUTTERFLY_1         EL_BUTTERFLY_U
+#define EL_BUTTERFLY_2         EL_BUTTERFLY_L
+#define EL_BUTTERFLY_3         EL_BUTTERFLY_O
+#define EL_BUTTERFLY_4         EL_BUTTERFLY_R
+#define EL_FIREFLY_1           EL_FIREFLY_L
+#define EL_FIREFLY_2           EL_FIREFLY_U
+#define EL_FIREFLY_3           EL_FIREFLY_R
+#define EL_FIREFLY_4           EL_FIREFLY_O
+#define EL_BUTTERFLY           78
+#define EL_FIREFLY             79
+#define EL_SPIELER1            80
+#define EL_SPIELER2            81
+#define EL_SPIELER3            82
+#define EL_SPIELER4            83
+#define EL_KAEFER_R            84
+#define EL_KAEFER_O            85
+#define EL_KAEFER_L            86
+#define EL_KAEFER_U            87
+#define EL_FLIEGER_R           88
+#define EL_FLIEGER_O           89
+#define EL_FLIEGER_L           90
+#define EL_FLIEGER_U           91
+#define EL_PACMAN_R            92
+#define EL_PACMAN_O            93
+#define EL_PACMAN_L            94
+#define EL_PACMAN_U            95
+#define EL_EDELSTEIN_ROT       96
+#define EL_EDELSTEIN_LILA      97
+#define EL_ERZ_EDEL_ROT                98
+#define EL_ERZ_EDEL_LILA       99
+#define EL_BADEWANNE1          100
+#define EL_BADEWANNE2          101
+#define EL_BADEWANNE3          102
+#define EL_BADEWANNE4          103
+#define EL_BADEWANNE5          104
+#define EL_SIEB_TOT            105
+#define EL_AUSGANG_ACT         106
+#define EL_AUSGANG_AUF         107
+#define EL_SIEB2_TOT           108
+#define EL_AMOEBA2DIAM         109
+#define EL_MAULWURF            110
+#define EL_PINGUIN             111
+#define EL_SONDE               112
+#define EL_PFEIL_L             113
+#define EL_PFEIL_R             114
+#define EL_PFEIL_O             115
+#define EL_PFEIL_U             116
+#define EL_SCHWEIN             117
+#define EL_DRACHE              118
+
+#define EL_UNUSED_119          119
+
+#define EL_CHAR_START          120
+#define EL_CHAR_ASCII0         (EL_CHAR_START-32)
+#define EL_CHAR_AUSRUF         (EL_CHAR_ASCII0+33)
+#define EL_CHAR_ZOLL           (EL_CHAR_ASCII0+34)
+#define EL_CHAR_DOLLAR         (EL_CHAR_ASCII0+36)
+#define EL_CHAR_PROZ           (EL_CHAR_ASCII0+37)
+#define EL_CHAR_APOSTR         (EL_CHAR_ASCII0+39)
+#define EL_CHAR_KLAMM1         (EL_CHAR_ASCII0+40)
+#define EL_CHAR_KLAMM2         (EL_CHAR_ASCII0+41)
+#define EL_CHAR_PLUS           (EL_CHAR_ASCII0+43)
+#define EL_CHAR_KOMMA          (EL_CHAR_ASCII0+44)
+#define EL_CHAR_MINUS          (EL_CHAR_ASCII0+45)
+#define EL_CHAR_PUNKT          (EL_CHAR_ASCII0+46)
+#define EL_CHAR_SLASH          (EL_CHAR_ASCII0+47)
+#define EL_CHAR_0              (EL_CHAR_ASCII0+48)
+#define EL_CHAR_9              (EL_CHAR_ASCII0+57)
+#define EL_CHAR_DOPPEL         (EL_CHAR_ASCII0+58)
+#define EL_CHAR_SEMIKL         (EL_CHAR_ASCII0+59)
+#define EL_CHAR_LT             (EL_CHAR_ASCII0+60)
+#define EL_CHAR_GLEICH         (EL_CHAR_ASCII0+61)
+#define EL_CHAR_GT             (EL_CHAR_ASCII0+62)
+#define EL_CHAR_FRAGE          (EL_CHAR_ASCII0+63)
+#define EL_CHAR_AT             (EL_CHAR_ASCII0+64)
+#define EL_CHAR_A              (EL_CHAR_ASCII0+65)
+#define EL_CHAR_Z              (EL_CHAR_ASCII0+90)
+#define EL_CHAR_AE             (EL_CHAR_ASCII0+91)
+#define EL_CHAR_OE             (EL_CHAR_ASCII0+92)
+#define EL_CHAR_UE             (EL_CHAR_ASCII0+93)
+#define EL_CHAR_COPY           (EL_CHAR_ASCII0+94)
+#define EL_CHAR_END            (EL_CHAR_START+79)
+
+#define EL_MAUER_X             200
+#define EL_MAUER_Y             201
+#define EL_MAUER_XY            202
+
+#define EL_UNUSED_200          203
+/* ... */
+#define EL_UNUSED_255          255
 
 /* "unreal" runtime elements */
-#define EL_BLOCKED     300
-#define EL_EXPLODING   301
-#define EL_CRACKINGNUT 302
-#define EL_BLURB_LEFT  303
-#define EL_BLURB_RIGHT 304
-#define EL_AMOEBING    305
-
-/* names for the graphic objects */
+#define EL_BLOCKED             300
+#define EL_EXPLODING           301
+#define EL_CRACKINGNUT         302
+#define EL_BLURB_LEFT          303
+#define EL_BLURB_RIGHT         304
+#define EL_AMOEBING            305
+#define EL_MAUERND             306
+#define EL_BURNING             307
+#define EL_PLAYER_IS_LEAVING   308
+
+/* game graphics:
+**       0 - 255: graphics from "RocksScreen"
+**     256 - 511: graphics from "RocksFont"
+**     512 - 767: graphics from "RocksHeroes"
+*/
+
+#define GFX_START_ROCKSSCREEN  0
+#define GFX_END_ROCKSSCREEN    255
+#define GFX_START_ROCKSFONT    256
+#define GFX_END_ROCKSFONT      511
+#define GFX_START_ROCKSHEROES  512
+#define GFX_END_ROCKSHEROES    767
+
+#define NUM_TILES              768
+
+/* graphics from "RocksScreen" */
 /* Zeile 0 (0) */
-#define GFX_LEERRAUM   (-1)
-#define        GFX_ERDREICH    0
-#define GFX_ERDENRAND  1
-#define GFX_MORAST_LEER        2
-#define GFX_MORAST_VOLL        3
-#define GFX_BETON      4
-#define        GFX_MAUERWERK   5
-#define        GFX_FELSBODEN   6
-#define GFX_BOMBE_MM   7
-#define        GFX_EDELSTEIN   8
-#define GFX_DIAMANT    10
-#define        GFX_FELSBROCKEN 12
+#define GFX_LEERRAUM           (-1)
+#define        GFX_ERDREICH            0
+#define GFX_ERDENRAND          1
+#define GFX_MORAST_LEER                2
+#define GFX_MORAST_VOLL                3
+#define GFX_BETON              4
+#define        GFX_MAUERWERK           5
+#define        GFX_FELSBODEN           6
+#define        GFX_EDELSTEIN           8
+#define GFX_DIAMANT            10
+#define        GFX_FELSBROCKEN         12
 /* Zeile 1 (16) */
-#define GFX_BADEWANNE1 16
-#define GFX_SALZSAEURE 17
-#define GFX_BADEWANNE2 18
-#define GFX_UNSICHTBAR 19
-#define GFX_SCHLUESSEL1        20
-#define GFX_SCHLUESSEL2        21
-#define GFX_SCHLUESSEL3        22
-#define GFX_SCHLUESSEL4        23
-#define GFX_LIFE       24
-#define GFX_LIFE_ASYNC 25
-#define GFX_BADEWANNE  26
-#define GFX_BOMBE      27
-#define GFX_KOKOSNUSS  28
-#define GFX_CRACKINGNUT        29
+#define GFX_BADEWANNE1         16
+#define GFX_SALZSAEURE         17
+#define GFX_BADEWANNE2         18
+#define GFX_UNSICHTBAR         19
+#define GFX_SCHLUESSEL1                20
+#define GFX_SCHLUESSEL2                21
+#define GFX_SCHLUESSEL3                22
+#define GFX_SCHLUESSEL4                23
+#define GFX_LIFE               24
+#define GFX_LIFE_ASYNC         25
+#define GFX_BADEWANNE          26
+#define GFX_BOMBE              27
+#define GFX_KOKOSNUSS          28
+#define GFX_CRACKINGNUT                29
 /* Zeile 2 (32) */
-#define GFX_BADEWANNE3 32
-#define GFX_BADEWANNE4 33
-#define GFX_BADEWANNE5 34
-#define        GFX_SPIELFIGUR  35
-#define GFX_PFORTE1    36
-#define GFX_PFORTE2    37
-#define GFX_PFORTE3    38
-#define GFX_PFORTE4    39
-#define GFX_PFORTE1X   40
-#define GFX_PFORTE2X   41
-#define GFX_PFORTE3X   42
-#define GFX_PFORTE4X   43
-#define        GFX_AUSGANG_ZU  44
-#define        GFX_AUSGANG_ACT 44
-#define        GFX_AUSGANG_AUF 47
+#define GFX_BADEWANNE3         32
+#define GFX_BADEWANNE4         33
+#define GFX_BADEWANNE5         34
+#define        GFX_SMILEY              35
+#define GFX_PFORTE1            36
+#define GFX_PFORTE2            37
+#define GFX_PFORTE3            38
+#define GFX_PFORTE4            39
+#define GFX_PFORTE1X           40
+#define GFX_PFORTE2X           41
+#define GFX_PFORTE3X           42
+#define GFX_PFORTE4X           43
 /* Zeile 3 (48) */
-#define GFX_DYNAMIT_AUS        48
-#define GFX_DYNAMIT    49
-#define GFX_FLIEGER    56
-#define GFX_FLIEGER_R  56
-#define GFX_FLIEGER_O  57
-#define GFX_FLIEGER_L  58
-#define GFX_FLIEGER_U  59
+#define GFX_DYNAMIT_AUS                48
+#define GFX_DYNAMIT            49
+#define GFX_FLIEGER            56
+#define GFX_FLIEGER_R          56
+#define GFX_FLIEGER_O          57
+#define GFX_FLIEGER_L          58
+#define GFX_FLIEGER_U          59
 /* Zeile 4 (64) */
-#define GFX_EXPLOSION  64
-#define GFX_KAEFER     72
-#define GFX_KAEFER_R   72
-#define GFX_KAEFER_O   73
-#define GFX_KAEFER_L   74
-#define GFX_KAEFER_U   75
+#define GFX_EXPLOSION          64
+#define GFX_KAEFER             72
+#define GFX_KAEFER_R           72
+#define GFX_KAEFER_O           73
+#define GFX_KAEFER_L           74
+#define GFX_KAEFER_U           75
 /* Zeile 5 (80) */
-#define GFX_MAMPFER    80
-#define GFX_ZOMBIE     84
-#define GFX_PACMAN     88
-#define GFX_PACMAN_R   88
-#define GFX_PACMAN_O   89
-#define GFX_PACMAN_L   90
-#define GFX_PACMAN_U   91
+#define GFX_MAMPFER            80
+#define GFX_ROBOT              84
+#define GFX_PACMAN             88
+#define GFX_PACMAN_R           88
+#define GFX_PACMAN_O           89
+#define GFX_PACMAN_L           90
+#define GFX_PACMAN_U           91
 /* Zeile 6 (96) */
-#define GFX_ABLENK     96
-#define GFX_ABLENK_EIN GFX_ABLENK
-#define GFX_ABLENK_AUS GFX_ABLENK
-#define GFX_AMOEBE_NASS        100
-#define GFX_TROPFEN    101
-#define GFX_AMOEBING   GFX_TROPFEN
-#define GFX_AMOEBE_LEBT        104
-#define GFX_AMOEBE_NORM        GFX_AMOEBE_LEBT
-#define GFX_AMOEBE_TOT 108
-#define GFX_AMOEBA2DIAM        GFX_AMOEBE_TOT
-#define GFX_ZEIT_VOLL  114
-#define GFX_ZEIT_LEER  115
+#define GFX_ABLENK             96
+#define GFX_ABLENK_EIN         GFX_ABLENK
+#define GFX_ABLENK_AUS         GFX_ABLENK
+#define GFX_AMOEBE_NASS                100
+#define GFX_TROPFEN            101
+#define GFX_AMOEBING           GFX_TROPFEN
+#define GFX_AMOEBE_LEBT                104
+#define GFX_AMOEBE_NORM                GFX_AMOEBE_LEBT
+#define GFX_AMOEBE_TOT         108
+#define GFX_AMOEBA2DIAM                GFX_AMOEBE_TOT
 /* Zeile 7 (112) */
-#define GFX_GEBLUBBER  124
+#define GFX_BIRNE_AUS          112
+#define GFX_BIRNE_EIN          113
+#define GFX_ZEIT_VOLL          114
+#define GFX_ZEIT_LEER          115
+#define GFX_SPIELER1           116
+#define GFX_SPIELER2           117
+#define GFX_SPIELER3           118
+#define GFX_SPIELER4           119
+#define GFX_AMOEBE_VOLL                120
+#define GFX_AMOEBE_BD          GFX_AMOEBE_VOLL
+#define GFX_SOKOBAN_OBJEKT     121
+#define GFX_SOKOBAN_FELD_LEER  122
+#define GFX_SOKOBAN_FELD_VOLL  123
+#define GFX_GEBLUBBER          124
 /* Zeile 8 (128) */
-#define GFX_SIEB_LEER  128
-#define GFX_SIEB_VOLL  GFX_SIEB_LEER
-#define GFX_SIEB_TOT   GFX_SIEB_LEER
-#define GFX_ERZ_EDEL   132
-#define GFX_ERZ_DIAM   133
-#define GFX_BIRNE_AUS  134
-#define GFX_BIRNE_EIN  135
-#define GFX_AMOEBE_VOLL        136
-#define GFX_KUGEL_ROT  140
-#define GFX_KUGEL_BLAU 141
-#define GFX_KUGEL_GELB 142
+#define GFX_SIEB_LEER          128
+#define GFX_SIEB_VOLL          GFX_SIEB_LEER
+#define GFX_SIEB_TOT           GFX_SIEB_LEER
+#define GFX_ERZ_EDEL           132
+#define GFX_ERZ_DIAM           133
+#define GFX_ERZ_EDEL_ROT       134
+#define GFX_ERZ_EDEL_LILA      135
+#define GFX_ERZ_EDEL_GELB      136
+#define GFX_ERZ_EDEL_BD                137
+#define GFX_EDELSTEIN_GELB     138
+#define GFX_KUGEL_ROT          140
+#define GFX_KUGEL_BLAU         141
+#define GFX_KUGEL_GELB         142
+#define GFX_KUGEL_GRAU         143
 /* Zeile 9 (144) */
-#define GFX_BLURB_LEFT 144
-#define GFX_BLURB_RIGHT        148
-
-#define        GFX_SCHLUESSEL  GFX_SCHLUESSEL1
-
-#define GFX_SPIELER1   116
-#define GFX_SPIELER2   117
-#define GFX_SPIELER3   118
-#define GFX_SPIELER4   119
-
-/* nicht in "RocksScreen" sondern woanders :) */
-#define GFX_CHAR_START 256
-#define GFX_CHAR_ASCII0        (GFX_CHAR_START-32)
-#define GFX_CHAR_AUSRUF        (GFX_CHAR_ASCII0+33)
-#define GFX_CHAR_ZOLL  (GFX_CHAR_ASCII0+34)
-#define GFX_CHAR_DOLLAR        (GFX_CHAR_ASCII0+36)
-#define GFX_CHAR_PROZ  (GFX_CHAR_ASCII0+37)
-#define GFX_CHAR_APOSTR        (GFX_CHAR_ASCII0+39)
-#define GFX_CHAR_KLAMM1        (GFX_CHAR_ASCII0+40)
-#define GFX_CHAR_KLAMM2        (GFX_CHAR_ASCII0+41)
-#define GFX_CHAR_PLUS  (GFX_CHAR_ASCII0+43)
-#define GFX_CHAR_KOMMA (GFX_CHAR_ASCII0+44)
-#define GFX_CHAR_MINUS (GFX_CHAR_ASCII0+45)
-#define GFX_CHAR_PUNKT (GFX_CHAR_ASCII0+46)
-#define GFX_CHAR_SLASH (GFX_CHAR_ASCII0+47)
-#define GFX_CHAR_0     (GFX_CHAR_ASCII0+48)
-#define GFX_CHAR_9     (GFX_CHAR_ASCII0+57)
-#define GFX_CHAR_DOPPEL        (GFX_CHAR_ASCII0+58)
-#define GFX_CHAR_SEMIKL        (GFX_CHAR_ASCII0+59)
-#define GFX_CHAR_LT    (GFX_CHAR_ASCII0+60)
-#define GFX_CHAR_GLEICH        (GFX_CHAR_ASCII0+61)
-#define GFX_CHAR_GT    (GFX_CHAR_ASCII0+62)
-#define GFX_CHAR_FRAGE (GFX_CHAR_ASCII0+63)
-#define GFX_CHAR_AT    (GFX_CHAR_ASCII0+64)
-#define GFX_CHAR_A     (GFX_CHAR_ASCII0+65)
-#define GFX_CHAR_Z     (GFX_CHAR_ASCII0+90)
-#define GFX_CHAR_AE    (GFX_CHAR_ASCII0+91)
-#define GFX_CHAR_OE    (GFX_CHAR_ASCII0+92)
-#define GFX_CHAR_UE    (GFX_CHAR_ASCII0+93)
-#define GFX_CHAR_COPY  (GFX_CHAR_ASCII0+94)
-#define GFX_CHAR_END   (GFX_CHAR_START+79)
-
-/* score for elements */
-#define SC_EDELSTEIN   0
-#define SC_DIAMANT     1
-#define SC_KAEFER      2
-#define SC_FLIEGER     3
-#define SC_MAMPFER     4
-#define SC_ZOMBIE      5
-#define SC_PACMAN      6
-#define SC_KOKOSNUSS   7
-#define SC_DYNAMIT     8
-#define SC_SCHLUESSEL  9
-#define SC_ZEITBONUS   10
+#define GFX_PINGUIN            144
+#define GFX_MAULWURF           145
+#define GFX_SCHWEIN            146
+#define GFX_DRACHE             147
+#define GFX_MAUER_XY           148
+#define GFX_MAUER_X            149
+#define GFX_MAUER_Y            150
+#define GFX_EDELSTEIN_ROT      152
+#define GFX_EDELSTEIN_LILA     154
+#define GFX_DYNABOMB_XL                156
+#define GFX_SONDE              159
+/* Zeile 10 (160) */
+#define GFX_EDELSTEIN_BD       163
+#define GFX_MAUER_RIGHT                165
+#define GFX_MAUER_R1           GFX_MAUER_RIGHT
+#define GFX_MAUER_R            167
+#define GFX_MAUER_LEFT         168
+#define GFX_MAUER_L1           GFX_MAUER_LEFT
+#define GFX_MAUER_L            170
+#define GFX_MAUER_LEBT         171
+#define GFX_SIEB2_LEER         172
+#define GFX_SIEB2_VOLL         GFX_SIEB2_LEER
+#define GFX_SIEB2_TOT          GFX_SIEB2_LEER
+/* Zeile 11 (176) */
+#define        GFX_AUSGANG_ZU          176
+#define        GFX_AUSGANG_ACT         177
+#define        GFX_AUSGANG_AUF         180
+#define GFX_MAMPFER2           184
+#define GFX_DYNABOMB           188
+#define GFX_DYNABOMB_NR                188
+#define GFX_DYNABOMB_SZ                191
+/* Zeile 12 (192) */
+#define GFX_PFEIL_L            192
+#define GFX_PFEIL_R            193
+#define GFX_PFEIL_O            194
+#define GFX_PFEIL_U            195
+#define GFX_BUTTERFLY          196
+#define GFX_FIREFLY            198
+#define GFX_BUTTERFLY_R                200
+#define GFX_BUTTERFLY_O                201
+#define GFX_BUTTERFLY_L                202
+#define GFX_BUTTERFLY_U                203
+#define GFX_FIREFLY_R          204
+#define GFX_FIREFLY_O          205
+#define GFX_FIREFLY_L          206
+#define GFX_FIREFLY_U          207
+
+#define GFX_SCHLUESSEL         GFX_SCHLUESSEL1
+#define GFX_SPIELFIGUR         GFX_SPIELER1
+
+
+/* graphics from "RocksHeroes" */
+
+#define GFX_SPIELER1_DOWN      (GFX_START_ROCKSHEROES + 0*HEROES_PER_LINE + 0)
+#define GFX_SPIELER1_UP                (GFX_START_ROCKSHEROES + 0*HEROES_PER_LINE + 4)
+#define GFX_SPIELER1_LEFT      (GFX_START_ROCKSHEROES + 1*HEROES_PER_LINE + 0)
+#define GFX_SPIELER1_RIGHT     (GFX_START_ROCKSHEROES + 1*HEROES_PER_LINE + 4)
+#define GFX_SPIELER1_PUSH_RIGHT        (GFX_START_ROCKSHEROES + 2*HEROES_PER_LINE + 0)
+#define GFX_SPIELER1_PUSH_LEFT (GFX_START_ROCKSHEROES + 2*HEROES_PER_LINE + 4)
+#define GFX_SPIELER2_DOWN      (GFX_START_ROCKSHEROES + 3*HEROES_PER_LINE + 0)
+#define GFX_SPIELER2_UP                (GFX_START_ROCKSHEROES + 3*HEROES_PER_LINE + 4)
+#define GFX_SPIELER2_LEFT      (GFX_START_ROCKSHEROES + 4*HEROES_PER_LINE + 0)
+#define GFX_SPIELER2_RIGHT     (GFX_START_ROCKSHEROES + 4*HEROES_PER_LINE + 4)
+#define GFX_SPIELER2_PUSH_RIGHT        (GFX_START_ROCKSHEROES + 5*HEROES_PER_LINE + 0)
+#define GFX_SPIELER2_PUSH_LEFT (GFX_START_ROCKSHEROES + 5*HEROES_PER_LINE + 4)
+#define GFX_SPIELER3_DOWN      (GFX_START_ROCKSHEROES + 6*HEROES_PER_LINE + 0)
+#define GFX_SPIELER3_UP                (GFX_START_ROCKSHEROES + 6*HEROES_PER_LINE + 4)
+#define GFX_SPIELER3_LEFT      (GFX_START_ROCKSHEROES + 7*HEROES_PER_LINE + 0)
+#define GFX_SPIELER3_RIGHT     (GFX_START_ROCKSHEROES + 7*HEROES_PER_LINE + 4)
+#define GFX_SPIELER3_PUSH_RIGHT        (GFX_START_ROCKSHEROES + 8*HEROES_PER_LINE + 0)
+#define GFX_SPIELER3_PUSH_LEFT (GFX_START_ROCKSHEROES + 8*HEROES_PER_LINE + 4)
+#define GFX_SPIELER4_DOWN      (GFX_START_ROCKSHEROES + 9*HEROES_PER_LINE + 0)
+#define GFX_SPIELER4_UP                (GFX_START_ROCKSHEROES + 9*HEROES_PER_LINE + 4)
+#define GFX_SPIELER4_LEFT      (GFX_START_ROCKSHEROES +10*HEROES_PER_LINE + 0)
+#define GFX_SPIELER4_RIGHT     (GFX_START_ROCKSHEROES +10*HEROES_PER_LINE + 4)
+#define GFX_SPIELER4_PUSH_RIGHT        (GFX_START_ROCKSHEROES +11*HEROES_PER_LINE + 0)
+#define GFX_SPIELER4_PUSH_LEFT (GFX_START_ROCKSHEROES +11*HEROES_PER_LINE + 4)
+#define GFX_MAUER_DOWN         (GFX_START_ROCKSHEROES +12*HEROES_PER_LINE + 0)
+#define GFX_MAUER_UP           (GFX_START_ROCKSHEROES +12*HEROES_PER_LINE + 3)
+
+#define GFX_SONDE_START                (GFX_START_ROCKSHEROES + 9*HEROES_PER_LINE + 8)
+#define GFX_SCHWEIN_DOWN       (GFX_START_ROCKSHEROES + 0*HEROES_PER_LINE + 8)
+#define GFX_SCHWEIN_UP         (GFX_START_ROCKSHEROES + 0*HEROES_PER_LINE +12)
+#define GFX_SCHWEIN_LEFT       (GFX_START_ROCKSHEROES + 1*HEROES_PER_LINE + 8)
+#define GFX_SCHWEIN_RIGHT      (GFX_START_ROCKSHEROES + 1*HEROES_PER_LINE +12)
+#define GFX_DRACHE_DOWN                (GFX_START_ROCKSHEROES + 2*HEROES_PER_LINE + 8)
+#define GFX_DRACHE_UP          (GFX_START_ROCKSHEROES + 2*HEROES_PER_LINE +12)
+#define GFX_DRACHE_LEFT                (GFX_START_ROCKSHEROES + 3*HEROES_PER_LINE + 8)
+#define GFX_DRACHE_RIGHT       (GFX_START_ROCKSHEROES + 3*HEROES_PER_LINE +12)
+#define GFX_MAULWURF_DOWN      (GFX_START_ROCKSHEROES + 4*HEROES_PER_LINE + 8)
+#define GFX_MAULWURF_UP                (GFX_START_ROCKSHEROES + 4*HEROES_PER_LINE +12)
+#define GFX_MAULWURF_LEFT      (GFX_START_ROCKSHEROES + 5*HEROES_PER_LINE + 8)
+#define GFX_MAULWURF_RIGHT     (GFX_START_ROCKSHEROES + 5*HEROES_PER_LINE +12)
+#define GFX_PINGUIN_DOWN       (GFX_START_ROCKSHEROES + 6*HEROES_PER_LINE + 8)
+#define GFX_PINGUIN_UP         (GFX_START_ROCKSHEROES + 6*HEROES_PER_LINE +12)
+#define GFX_PINGUIN_LEFT       (GFX_START_ROCKSHEROES + 7*HEROES_PER_LINE + 8)
+#define GFX_PINGUIN_RIGHT      (GFX_START_ROCKSHEROES + 7*HEROES_PER_LINE +12)
+#define GFX_BLURB_LEFT         (GFX_START_ROCKSHEROES +10*HEROES_PER_LINE + 8)
+#define GFX_BLURB_RIGHT                (GFX_START_ROCKSHEROES +10*HEROES_PER_LINE +12)
+#define GFX_FUNKELN_BLAU       (GFX_START_ROCKSHEROES +11*HEROES_PER_LINE + 8)
+#define GFX_FUNKELN_WEISS      (GFX_START_ROCKSHEROES +11*HEROES_PER_LINE +12)
+#define GFX_FLAMMEN_LEFT       (GFX_START_ROCKSHEROES +12*HEROES_PER_LINE + 8)
+#define GFX_FLAMMEN_RIGHT      (GFX_START_ROCKSHEROES +13*HEROES_PER_LINE + 8)
+#define GFX_FLAMMEN_UP         (GFX_START_ROCKSHEROES +14*HEROES_PER_LINE + 8)
+#define GFX_FLAMMEN_DOWN       (GFX_START_ROCKSHEROES +15*HEROES_PER_LINE + 8)
+
+/* graphics from "RocksFont" */
+#define GFX_CHAR_START         (GFX_START_ROCKSFONT)
+#define GFX_CHAR_ASCII0                (GFX_CHAR_START-32)
+#define GFX_CHAR_AUSRUF                (GFX_CHAR_ASCII0+33)
+#define GFX_CHAR_ZOLL          (GFX_CHAR_ASCII0+34)
+#define GFX_CHAR_DOLLAR                (GFX_CHAR_ASCII0+36)
+#define GFX_CHAR_PROZ          (GFX_CHAR_ASCII0+37)
+#define GFX_CHAR_APOSTR                (GFX_CHAR_ASCII0+39)
+#define GFX_CHAR_KLAMM1                (GFX_CHAR_ASCII0+40)
+#define GFX_CHAR_KLAMM2                (GFX_CHAR_ASCII0+41)
+#define GFX_CHAR_PLUS          (GFX_CHAR_ASCII0+43)
+#define GFX_CHAR_KOMMA         (GFX_CHAR_ASCII0+44)
+#define GFX_CHAR_MINUS         (GFX_CHAR_ASCII0+45)
+#define GFX_CHAR_PUNKT         (GFX_CHAR_ASCII0+46)
+#define GFX_CHAR_SLASH         (GFX_CHAR_ASCII0+47)
+#define GFX_CHAR_0             (GFX_CHAR_ASCII0+48)
+#define GFX_CHAR_9             (GFX_CHAR_ASCII0+57)
+#define GFX_CHAR_DOPPEL                (GFX_CHAR_ASCII0+58)
+#define GFX_CHAR_SEMIKL                (GFX_CHAR_ASCII0+59)
+#define GFX_CHAR_LT            (GFX_CHAR_ASCII0+60)
+#define GFX_CHAR_GLEICH                (GFX_CHAR_ASCII0+61)
+#define GFX_CHAR_GT            (GFX_CHAR_ASCII0+62)
+#define GFX_CHAR_FRAGE         (GFX_CHAR_ASCII0+63)
+#define GFX_CHAR_AT            (GFX_CHAR_ASCII0+64)
+#define GFX_CHAR_A             (GFX_CHAR_ASCII0+65)
+#define GFX_CHAR_Z             (GFX_CHAR_ASCII0+90)
+#define GFX_CHAR_AE            (GFX_CHAR_ASCII0+91)
+#define GFX_CHAR_OE            (GFX_CHAR_ASCII0+92)
+#define GFX_CHAR_UE            (GFX_CHAR_ASCII0+93)
+#define GFX_CHAR_COPY          (GFX_CHAR_ASCII0+94)
+#define GFX_CHAR_END           (GFX_CHAR_START+79)
 
 /* the names of the sounds */
-#define SND_ALCHEMY    0
-#define SND_AMOEBE     1
-#define SND_ANTIGRAV   2
-#define SND_AUTSCH     3
-#define SND_BLURB      4
-#define SND_BONG       5
-#define SND_BUING      6
-#define SND_CHASE      7
-#define SND_CZARDASZ   8
-#define SND_DENG       9
-#define SND_FUEL       10
-#define SND_GONG       11
-#define SND_HALLOFFAME 12
-#define SND_HOLZ       13
-#define SND_HUI                14
-#define SND_KABUMM     15
-#define SND_KINK       16
-#define SND_KLAPPER    17
-#define SND_KLING      18
-#define SND_KLOPF      19
-#define SND_KLUMPF     20
-#define SND_KNACK      21
-#define SND_KNURK      22
-#define SND_KRACH      23
-#define SND_LACHEN     24
-#define SND_LASER      25
-#define SND_MIEP       26
-#define SND_NETWORK    27
-#define SND_NJAM       28
-#define SND_OEFFNEN    29
-#define SND_PLING      30
-#define SND_PONG       31
-#define SND_PUSCH      32
-#define SND_QUIEK      33
-#define SND_QUIRK      34
-#define SND_RHYTHMLOOP 35
-#define SND_ROAAAR     36
-#define SND_ROEHR      37
-#define SND_RUMMS      38
-#define SND_SCHLOPP    39
-#define SND_SCHLURF    40
-#define SND_SCHRFF     41
-#define SND_SCHWIRR    42
-#define SND_SIRR       43
-#define SND_SLURP      44
-#define SND_SPROING    45
-#define SND_TWILIGHT   46
-#define SND_TYGER      47
-#define SND_VOYAGER    48
-#define SND_WARNTON    49
-#define SND_WHOOSH     50
-#define SND_ZISCH      51
-
-#define NUM_SOUNDS     52
-
-#define IS_LOOP_SOUND(s)       ((s)==SND_KLAPPER || (s)==SND_ROEHR ||  \
-                                (s)==SND_NJAM || (s)==SND_MIEP)
-#define IS_MUSIC_SOUND(s)      ((s)==SND_ALCHEMY || (s)==SND_CHASE || \
-                                (s)==SND_NETWORK || (s)==SND_CZARDASZ || \
-                                (s)==SND_TYGER || (s)==SND_VOYAGER || \
-                                (s)==SND_TWILIGHT)
-extern char *sound_name[NUM_SOUNDS];
-
-/* this structure contains the sound data for the sound server */
-extern struct SoundInfo Sound[NUM_SOUNDS];
+#define SND_ALCHEMY            0
+#define SND_AMOEBE             1
+#define SND_ANTIGRAV           2
+#define SND_AUTSCH             3
+#define SND_BLURB              4
+#define SND_BONG               5
+#define SND_BUING              6
+#define SND_CHASE              7
+#define SND_CZARDASZ           8
+#define SND_DENG               9
+#define SND_FUEL               10
+#define SND_GONG               11
+#define SND_HALLOFFAME         12
+#define SND_HOLZ               13
+#define SND_HUI                        14
+#define SND_KABUMM             15
+#define SND_KINK               16
+#define SND_KLAPPER            17
+#define SND_KLING              18
+#define SND_KLOPF              19
+#define SND_KLUMPF             20
+#define SND_KNACK              21
+#define SND_KNURK              22
+#define SND_KRACH              23
+#define SND_LACHEN             24
+#define SND_LASER              25
+#define SND_MIEP               26
+#define SND_NETWORK            27
+#define SND_NJAM               28
+#define SND_OEFFNEN            29
+#define SND_PLING              30
+#define SND_PONG               31
+#define SND_PUSCH              32
+#define SND_QUIEK              33
+#define SND_QUIRK              34
+#define SND_RHYTHMLOOP         35
+#define SND_ROAAAR             36
+#define SND_ROEHR              37
+#define SND_RUMMS              38
+#define SND_SCHLOPP            39
+#define SND_SCHLURF            40
+#define SND_SCHRFF             41
+#define SND_SCHWIRR            42
+#define SND_SIRR               43
+#define SND_SLURP              44
+#define SND_SPROING            45
+#define SND_TWILIGHT           46
+#define SND_TYGER              47
+#define SND_VOYAGER            48
+#define SND_WARNTON            49
+#define SND_WHOOSH             50
+#define SND_ZISCH              51
+
+#define NUM_SOUNDS             52
+
+/* default input keys */
+#define KEY_UNDEFINDED         XK_VoidSymbol
+#define DEFAULT_KEY_LEFT       XK_Left
+#define DEFAULT_KEY_RIGHT      XK_Right
+#define DEFAULT_KEY_UP         XK_Up
+#define DEFAULT_KEY_DOWN       XK_Down
+#define DEFAULT_KEY_SNAP       XK_Shift_L
+#define DEFAULT_KEY_BOMB       XK_Shift_R
+#define DEFAULT_KEY_OKAY       XK_Return
+#define DEFAULT_KEY_CANCEL     XK_Escape
 
 /* directions for moving */
-#define MV_NO_MOVING   0
-#define MV_LEFT                1
-#define MV_RIGHT       2
-#define MV_UP          4
-#define MV_DOWN                8
+#define MV_NO_MOVING           0
+#define MV_LEFT                        (1<<0)
+#define MV_RIGHT               (1<<1)
+#define MV_UP                  (1<<2)
+#define MV_DOWN                        (1<<3)
 
 /* font types */
-#define FS_SMALL       0
-#define FS_BIG         1
+#define FS_SMALL               0
+#define FS_BIG                 1
 /* font colors */
-#define FC_RED         0
-#define FC_BLUE                1
-#define FC_GREEN       2
-#define FC_YELLOW      3
-#define FC_SPECIAL1    4
-#define FC_SPECIAL2    5
+#define FC_RED                 0
+#define FC_BLUE                        1
+#define FC_GREEN               2
+#define FC_YELLOW              3
+#define FC_SPECIAL1            4
+#define FC_SPECIAL2            5
 
 /* values for game_status */
-#define MAINMENU       0
-#define PLAYING                1
-#define LEVELED                2
-#define HELPSCREEN     3
-#define CHOOSELEVEL    4
-#define TYPENAME       5
-#define HALLOFFAME     6
-#define SETUP          7
-#define EXITGAME       8
-
-/* return values for GameActions */
-#define ACT_GO_ON      0
-#define ACT_GAME_OVER  1
-#define ACT_NEW_GAME   2
-
-/* values for the joystick */
-#define JOYSTICK_OFF           0
-#define        JOYSTICK_AVAILABLE      1
-#ifdef __FreeBSD__
-#define DEV_JOYSTICK_0         "/dev/joy0"
-#define DEV_JOYSTICK_1         "/dev/joy1"
-#else
-#define DEV_JOYSTICK_0         "/dev/js0"
-#define DEV_JOYSTICK_1         "/dev/js1"
-#endif
-
-/* get these values from the program 'js' from the joystick package, */
-/* set JOYSTICK_PERCENT to a threshold appropriate for your joystick */
-#define JOYSTICK_XLEFT         30
-#define JOYSTICK_XRIGHT                1250
-#define JOYSTICK_XMIDDLE       530
-#define JOYSTICK_YUPPER                40
-#define JOYSTICK_YLOWER                1440
-#define JOYSTICK_YMIDDLE       680
-#define JOYSTICK_PERCENT       25
-#define JOY_LEFT               MV_LEFT
-#define JOY_RIGHT              MV_RIGHT
-#define JOY_UP                 MV_UP
-#define JOY_DOWN               MV_DOWN
-#define JOY_BUTTON_1           16
-#define JOY_BUTTON_2           32
-#define JOY_BUTTON             (JOY_BUTTON_1 | JOY_BUTTON_2)
-#define JOY_BUTTON_NOT_PRESSED 0
-#define JOY_BUTTON_PRESSED     1
-#define JOY_BUTTON_NEW_PRESSED 2
-#define JOY_BUTTON_NEW_RELEASED        3
-
-#ifdef NO_JOYSTICK
-#define JOYSTICK_STATUS        JOYSTICK_OFF
-#else
-#define JOYSTICK_STATUS        JOYSTICK_AVAILABLE
-#endif
+#define EXITGAME               0
+#define MAINMENU               1
+#define PLAYING                        2
+#define LEVELED                        3
+#define HELPSCREEN             4
+#define CHOOSELEVEL            5
+#define TYPENAME               6
+#define HALLOFFAME             7
+#define SETUP                  8
+#define SETUPINPUT             9
+#define CALIBRATION            10
 
 #ifndef GAME_DIR
-#define GAME_DIR       "."
+#define GAME_DIR               "."
 #endif
 
-#ifndef GFX_PATH
-#define GFX_PATH       GAME_DIR "/graphics"
-#endif
-#ifndef SND_PATH
-#define SND_PATH       GAME_DIR "/sounds"
-#endif
-#ifndef LEVEL_PATH
-#define LEVEL_PATH     GAME_DIR "/levels"
-#endif
-#ifndef SCORE_PATH
-#define SCORE_PATH     LEVEL_PATH
-#endif
-#ifndef NAMES_PATH
-#define NAMES_PATH     LEVEL_PATH
-#endif
-#ifndef CONFIG_PATH
-#define CONFIG_PATH    GAME_DIR
-#endif
-#ifndef JOYDAT_PATH
-#define JOYDAT_PATH    GAME_DIR
-#endif
+#define BASE_PATH              GAME_DIR
 
-#define SCORE_FILENAME "ROCKS.score"
-#define NAMES_FILENAME "ROCKS.names"
-#define LEVDIR_FILENAME        "ROCKS.levelinfo"
-#define JOYDAT_FILENAME        "ROCKS.joystick"
-
-#define JOYDAT_FILE    JOYDAT_PATH "/" JOYDAT_FILENAME
-
-#define LEVEL_PERMS    (S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH)
-#define SCORE_PERMS    LEVEL_PERMS
-#define NAMES_PERMS    LEVEL_PERMS
-#define LEVDIR_PERMS   LEVEL_PERMS
-#define LEVREC_PERMS   LEVEL_PERMS
-#define JOYDAT_PERMS   LEVEL_PERMS
-
-#define LEVEL_COOKIE   "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.0"
-#define SCORE_COOKIE   "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.0"
-#define NAMES_COOKIE   "ROCKSNDIAMONDS_NAMES_FILE_VERSION_1.0"
-#define LEVELDIR_COOKIE        "ROCKSNDIAMONDS_LEVELDIR_FILE_VERSION_1.0"
-#define LEVELREC_COOKIE        "ROCKSNDIAMONDS_LEVELREC_FILE_VERSION_1.0"
-#define JOYSTICK_COOKIE        "ROCKSNDIAMONDS_JOYSTICK_FILE_VERSION_1.0"
-#define LEVEL_COOKIE_LEN       (strlen(LEVEL_COOKIE)+1)
-#define SCORE_COOKIE_LEN       (strlen(SCORE_COOKIE)+1)
-#define NAMES_COOKIE_LEN       (strlen(NAMES_COOKIE)+1)
-#define LEVELDIR_COOKIE_LEN    (strlen(LEVELDIR_COOKIE)+1)
-#define LEVELREC_COOKIE_LEN    (strlen(LEVELREC_COOKIE)+1)
-#define JOYSTICK_COOKIE_LEN    (strlen(JOYSTICK_COOKIE)+1)
-
-/* Leerer Login- und Alias-Name */
-#define EMPTY_LOGIN    "NO_LOGIN"
-#define EMPTY_ALIAS    "NO_NAME"
+#define GRAPHICS_DIRECTORY     "graphics"
+#define SOUNDS_DIRECTORY       "sounds"
+#define LEVELS_DIRECTORY       "levels"
+#define TAPES_DIRECTORY                "tapes"
+#define SCORES_DIRECTORY       "scores"
+
+#define VERSION_STRING         "1.2.0"
+#define GAMETITLE_STRING       "Rocks'n'Diamonds"
+#define WINDOWTITLE_STRING     GAMETITLE_STRING " " VERSION_STRING
+#define COPYRIGHT_STRING       "Copyright ^1995-98 by Holger Schemel"
+
+/* default name for empty highscore entry */
+#define EMPTY_PLAYER_NAME      "no name"
 
 /* values for button_status */
-#define MB_NOT_PRESSED FALSE
-#define MB_RELEASED    FALSE
-#define MB_PRESSED     TRUE
-#define MB_MENU_CHOICE FALSE
-#define MB_MENU_MARK   TRUE
-#define MB_LEFT                1
-#define MB_MIDDLE      2
-#define MB_RIGHT       3
-
-/* values for key_status */
-#define KEY_NOT_PRESSED        FALSE
-#define KEY_RELEASED   FALSE
-#define KEY_PRESSED    TRUE
-
-/* values for focus_status */
-#define FOCUS_OUT      FALSE
-#define FOCUS_IN       TRUE
+#define MB_NOT_PRESSED         FALSE
+#define MB_RELEASED            FALSE
+#define MB_PRESSED             TRUE
+#define MB_MENU_CHOICE         FALSE
+#define MB_MENU_MARK           TRUE
+#define MB_MENU_INITIALIZE     (-1)
+#define MB_LEFT                        1
+#ifdef MSDOS
+#define MB_MIDDLE              4
+#define MB_RIGHT               2
+#else
+#define MB_MIDDLE              2
+#define MB_RIGHT               3
+#endif
 
 /* values for redraw_mask */
-#define REDRAW_ALL     (1L<<0)
-#define REDRAW_FIELD   (1L<<1)
-#define REDRAW_TILES   (1L<<2)
-#define REDRAW_DOOR_1  (1L<<3)
-#define REDRAW_VIDEO_1 (1L<<4)
-#define REDRAW_VIDEO_2 (1L<<5)
-#define REDRAW_VIDEO_3 (1L<<6)
-#define REDRAW_MICROLEV        (1L<<7)
+#define REDRAW_ALL             (1L<<0)
+#define REDRAW_FIELD           (1L<<1)
+#define REDRAW_TILES           (1L<<2)
+#define REDRAW_DOOR_1          (1L<<3)
+#define REDRAW_VIDEO_1         (1L<<4)
+#define REDRAW_VIDEO_2         (1L<<5)
+#define REDRAW_VIDEO_3         (1L<<6)
+#define REDRAW_MICROLEV                (1L<<7)
+#define REDRAW_FROM_BACKBUFFER (1L<<8)
 #define REDRAW_DOOR_2  (REDRAW_VIDEO_1 | REDRAW_VIDEO_2 | REDRAW_VIDEO_3)
 #define REDRAW_DOORS   (REDRAW_DOOR_1 | REDRAW_DOOR_2)
 #define REDRAW_MAIN    (REDRAW_FIELD | REDRAW_TILES | REDRAW_MICROLEV)
-#define REDRAWTILES_TH SCR_FIELDX*SCR_FIELDY/2
-
-/* positions in the game control window */
-#define XX_LEVEL       37
-#define YY_LEVEL       20
-#define XX_EMERALDS    29
-#define YY_EMERALDS    54
-#define XX_DYNAMITE    29
-#define YY_DYNAMITE    89
-#define XX_KEYS                18
-#define YY_KEYS                123
-#define XX_SCORE       15
-#define YY_SCORE       159
-#define XX_TIME                29
-#define YY_TIME                194
-
-#define DX_LEVEL       (DX+XX_LEVEL)
-#define DY_LEVEL       (DY+YY_LEVEL)
-#define DX_EMERALDS    (DX+XX_EMERALDS)
-#define DY_EMERALDS    (DY+YY_EMERALDS)
-#define DX_DYNAMITE    (DX+XX_DYNAMITE)
-#define DY_DYNAMITE    (DY+YY_DYNAMITE)
-#define DX_KEYS                (DX+XX_KEYS)
-#define DY_KEYS                (DY+YY_KEYS)
-#define DX_SCORE       (DX+XX_SCORE)
-#define DY_SCORE       (DY+YY_SCORE)
-#define DX_TIME                (DX+XX_TIME)
-#define DY_TIME                (DY+YY_TIME)
-
-/* Felder in PIX_DOOR */
-/* Bedeutung in PIX_DB_DOOR: (3 PAGEs)
-   PAGEX1: 1. Zwischenspeicher für DOOR_1
-   PAGEX2: 2. Zwischenspeicher für DOOR_1
-   PAGEX3: Pufferspeicher für Animationen
+#define REDRAWTILES_THRESHOLD  SCR_FIELDX*SCR_FIELDY/2
+
+/* areas in pixmap PIX_DOOR */
+/* meaning in PIX_DB_DOOR: (3 PAGEs)
+   PAGEX1: 1. buffer for DOOR_1
+   PAGEX2: 2. buffer for DOOR_1
+   PAGEX3: buffer for animations
 */
 
 #define DOOR_GFX_PAGESIZE      DXSIZE
-#define DOOR_GFX_PAGEX1                (0*DOOR_GFX_PAGESIZE)
-#define DOOR_GFX_PAGEX2                (1*DOOR_GFX_PAGESIZE)
-#define DOOR_GFX_PAGEX3                (2*DOOR_GFX_PAGESIZE)
-#define DOOR_GFX_PAGEX4                (3*DOOR_GFX_PAGESIZE)
-#define DOOR_GFX_PAGEX5                (4*DOOR_GFX_PAGESIZE)
-#define DOOR_GFX_PAGEX6                (5*DOOR_GFX_PAGESIZE)
+#define DOOR_GFX_PAGEX1                (0 * DOOR_GFX_PAGESIZE)
+#define DOOR_GFX_PAGEX2                (1 * DOOR_GFX_PAGESIZE)
+#define DOOR_GFX_PAGEX3                (2 * DOOR_GFX_PAGESIZE)
+#define DOOR_GFX_PAGEX4                (3 * DOOR_GFX_PAGESIZE)
+#define DOOR_GFX_PAGEX5                (4 * DOOR_GFX_PAGESIZE)
+#define DOOR_GFX_PAGEX6                (5 * DOOR_GFX_PAGESIZE)
 #define DOOR_GFX_PAGEY1                0
 #define DOOR_GFX_PAGEY2                DYSIZE
 
-#endif
+/* for DrawGraphicAnimation() [tools.c] and AnimateToon() [cartoons.c] */
+#define ANIM_NORMAL    0
+#define ANIM_OSCILLATE 1
+#define ANIM_REVERSE   2
+
+#endif /* MAIN_H */
index a17b1da5f12834dd4406b13bf268eb70a4ba4009..39d1fdc45864f5c8022f6891efdb08ca9a49b51e 100644 (file)
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  misc.c                                                  *
 ***********************************************************/
 
-#include "misc.h"
-#include "tools.h"
-#include "sound.h"
 #include <pwd.h>
 #include <unistd.h>
 #include <time.h>
 #include <sys/time.h>
 #include <sys/param.h>
 #include <sys/types.h>
+#include <stdarg.h>
+#include <ctype.h>
 
-void microsleep(unsigned long usec)
-{
-  struct timeval delay;
+#include "misc.h"
+#include "init.h"
+#include "tools.h"
+#include "sound.h"
+#include "random.h"
+#include "joystick.h"
+#include "files.h"
 
-  delay.tv_sec  = usec / 1000000;
-  delay.tv_usec = usec % 1000000;
+#ifdef MSDOS
+volatile unsigned long counter = 0;
 
-  if (select(0,NULL,NULL,NULL,&delay)!=0)
-    fprintf(stderr,"%s: in function microsleep: select failed!\n",
-           progname);
+void increment_counter()
+{
+  counter++;
 }
 
-long mainCounter(int mode)
+END_OF_FUNCTION(increment_counter);
+#endif
+
+
+
+/* maximal allowed length of a command line option */
+#define MAX_OPTION_LEN         256
+
+#ifndef MSDOS
+static unsigned long mainCounter(int mode)
 {
   static struct timeval base_time = { 0, 0 };
   struct timeval current_time;
-  long counter_ms;
+  unsigned long counter_ms;
 
-  gettimeofday(&current_time,NULL);
-  if (mode==0 || current_time.tv_sec<base_time.tv_sec)
+  gettimeofday(&current_time, NULL);
+
+  if (mode == INIT_COUNTER || current_time.tv_sec < base_time.tv_sec)
     base_time = current_time;
 
-  counter_ms = (current_time.tv_sec - base_time.tv_sec)*1000
-             + (current_time.tv_usec - base_time.tv_usec)/1000;
+  counter_ms = (current_time.tv_sec  - base_time.tv_sec)  * 1000
+             + (current_time.tv_usec - base_time.tv_usec) / 1000;
 
-  if (mode==1)
-    return(counter_ms/10);     /* return 1/100 secs since last init */
-  else
-    return(counter_ms);                /* return 1/1000 secs since last init */
+  return counter_ms;           /* return milliseconds since last init */
 }
+#endif
 
-void InitCounter() /* set counter back to zero */
+void InitCounter()             /* set counter back to zero */
 {
-  mainCounter(0);
+#ifndef MSDOS
+  mainCounter(INIT_COUNTER);
+#else
+  LOCK_VARIABLE(counter);
+  LOCK_FUNCTION(increment_counter);
+  install_int_ex(increment_counter, BPS_TO_TIMER(100));
+#endif
 }
 
-long Counter() /* returns 1/100 secs since last call of InitCounter() */
+unsigned long Counter()        /* get milliseconds since last call of InitCounter() */
 {
-  return(mainCounter(1));
+#ifndef MSDOS
+  return mainCounter(READ_COUNTER);
+#else
+  return (counter * 10);
+#endif
+}
+
+static void sleep_milliseconds(unsigned long milliseconds_delay)
+{
+  boolean do_busy_waiting = (milliseconds_delay < 5 ? TRUE : FALSE);
+
+#ifdef MSDOS
+  /* donït use select() to perform waiting operations under DOS/Windows
+     environment; always use a busy loop for waiting instead */
+  do_busy_waiting = TRUE;
+#endif
+
+  if (do_busy_waiting)
+  {
+    /* we want to wait only a few ms -- if we assume that we have a
+       kernel timer resolution of 10 ms, we would wait far to long;
+       therefore it's better to do a short interval of busy waiting
+       to get our sleeping time more accurate */
+
+    unsigned long base_counter = Counter(), actual_counter = Counter();
+
+    while (actual_counter < base_counter + milliseconds_delay &&
+          actual_counter >= base_counter)
+      actual_counter = Counter();
+  }
+  else
+  {
+    struct timeval delay;
+
+    delay.tv_sec  = milliseconds_delay / 1000;
+    delay.tv_usec = 1000 * (milliseconds_delay % 1000);
+
+    if (select(0, NULL, NULL, NULL, &delay) != 0)
+      Error(ERR_WARN, "sleep_milliseconds(): select() failed");
+  }
 }
 
-long Counter2()        /* returns 1/1000 secs since last call of InitCounter() */
+void Delay(unsigned long delay)        /* Sleep specified number of milliseconds */
 {
-  return(mainCounter(2));
+  sleep_milliseconds(delay);
 }
 
-void WaitCounter(long value)   /* wait for counter to reach value */
+boolean FrameReached(unsigned long *frame_counter_var,
+                    unsigned long frame_delay)
 {
-  long wait;
+  unsigned long actual_frame_counter = FrameCounter;
+
+  if (actual_frame_counter < *frame_counter_var+frame_delay &&
+      actual_frame_counter >= *frame_counter_var)
+    return(FALSE);
 
-  while((wait=value-Counter())>0)
-    microsleep(wait*10000);
+  *frame_counter_var = actual_frame_counter;
+  return(TRUE);
 }
 
-void WaitCounter2(long value)  /* wait for counter to reach value */
+boolean DelayReached(unsigned long *counter_var,
+                    unsigned long delay)
 {
-  long wait;
+  unsigned long actual_counter = Counter();
 
-  while((wait=value-Counter2())>0)
-    microsleep(wait*1000);
+  if (actual_counter < *counter_var + delay &&
+      actual_counter >= *counter_var)
+    return(FALSE);
+
+  *counter_var = actual_counter;
+  return(TRUE);
 }
 
-void Delay(long value)
+void WaitUntilDelayReached(unsigned long *counter_var, unsigned long delay)
 {
-  microsleep(value);
+  unsigned long actual_counter;
+
+  while(1)
+  {
+    actual_counter = Counter();
+
+    if (actual_counter < *counter_var + delay &&
+       actual_counter >= *counter_var)
+      sleep_milliseconds((*counter_var + delay - actual_counter) / 2);
+    else
+      break;
+  }
+
+  *counter_var = actual_counter;
 }
 
-BOOL DelayReached(long *counter_var, int delay)
+char *int2str(int number, int size)
 {
-  long actual_counter = Counter();
+  static char s[40];
 
-  if (actual_counter>*counter_var+delay || actual_counter<*counter_var)
+  if (size > 20)
+    size = 20;
+
+  if (size)
   {
-    *counter_var = actual_counter;
-    return(TRUE);
+    sprintf(s, "                    %09d", number);
+    return &s[strlen(s) - size];
   }
   else
-    return(FALSE);
+  {
+    sprintf(s, "%d", number);
+    return s;
+  }
 }
 
-unsigned long be2long(unsigned long *be)       /* big-endian -> longword */
+unsigned int SimpleRND(unsigned int max)
 {
-  unsigned char *ptr = (unsigned char *)be;
+  static unsigned long root = 654321;
+  struct timeval current_time;
 
-  return(ptr[0]<<24 | ptr[1]<<16 | ptr[2]<<8 | ptr[3]);
+  gettimeofday(&current_time,NULL);
+  root = root * 4253261 + current_time.tv_sec + current_time.tv_usec;
+  return (root % max);
 }
 
-char *int2str(int ct, int nr)
-{
-  static char str[20];
+#ifdef DEBUG
+static unsigned int last_RND_value = 0;
 
-  sprintf(str,"%09d",ct);
-  return(&str[strlen(str)-nr]);
+unsigned int last_RND()
+{
+  return last_RND_value;
 }
+#endif
 
 unsigned int RND(unsigned int max)
 {
-  return(rand() % max);
+#ifdef DEBUG
+  return (last_RND_value = random_linux_libc() % max);
+#else
+  return (random_linux_libc() % max);
+#endif
 }
 
 unsigned int InitRND(long seed)
 {
   struct timeval current_time;
 
-  if (seed==NEW_RANDOMIZE)
+  if (seed == NEW_RANDOMIZE)
   {
     gettimeofday(&current_time,NULL);
-    srand((unsigned int) current_time.tv_usec);
-    return((unsigned int) current_time.tv_usec);
+    srandom_linux_libc((unsigned int) current_time.tv_usec);
+    return (unsigned int)current_time.tv_usec;
   }
   else
   {
-    srand((unsigned int) seed);
-    return((unsigned int) seed);
+    srandom_linux_libc((unsigned int) seed);
+    return (unsigned int)seed;
   }
 }
 
-char *GetLoginName()
+char *getLoginName()
 {
   struct passwd *pwd;
 
-  if (!(pwd=getpwuid(getuid())))
-    return("ANONYMOUS");
+  if (!(pwd = getpwuid(getuid())))
+    return "ANONYMOUS";
   else
-    return(pwd->pw_name);
+    return pwd->pw_name;
+}
+
+char *getHomeDir()
+{
+#ifndef MSDOS
+  static char *home_dir = NULL;
+
+  if (!home_dir)
+  {
+    if (!(home_dir = getenv("HOME")))
+    {
+      struct passwd *pwd;
+
+      if ((pwd = getpwuid(getuid())))
+       home_dir = pwd->pw_dir;
+      else
+       home_dir = ".";
+    }
+  }
+
+  return home_dir;
+#else
+  return ".";
+#endif
 }
 
-void InitAnimation()
+char *getPath2(char *path1, char *path2)
 {
-  HandleAnimation(ANIM_START);
+  char *complete_path = checked_malloc(strlen(path1) + 1 +
+                                      strlen(path2) + 1);
+
+  sprintf(complete_path, "%s/%s", path1, path2);
+  return complete_path;
 }
 
-void StopAnimation()
+char *getPath3(char *path1, char *path2, char *path3)
 {
-  HandleAnimation(ANIM_STOP);
+  char *complete_path = checked_malloc(strlen(path1) + 1 +
+                                      strlen(path2) + 1 +
+                                      strlen(path3) + 1);
+
+  sprintf(complete_path, "%s/%s/%s", path1, path2, path3);
+  return complete_path;
 }
 
-void DoAnimation()
+char *getStringCopy(char *s)
 {
-  HandleAnimation(ANIM_CONTINUE);
+  char *s_copy = checked_malloc(strlen(s) + 1);
+
+  strcpy(s_copy, s);
+  return s_copy;
 }
 
-void HandleAnimation(int mode)
+char *getStringToLower(char *s)
 {
-  static long animstart_delay = -1;
-  static long animstart_delay_value = 0;
-  static BOOL anim_restart = TRUE;
-  static BOOL reset_delay = TRUE;
-  static int toon_nr = 0;
+  char *s_copy = checked_malloc(strlen(s) + 1);
+  char *s_ptr = s_copy;
 
-  if (!toons_on || game_status==PLAYING)
-    return;
+  while (*s)
+    *s_ptr++ = tolower(*s++);
 
-  switch(mode)
+  return s_copy;
+}
+
+void MarkTileDirty(int x, int y)
+{
+  int xx = redraw_x1 + x;
+  int yy = redraw_y1 + y;
+
+  if (!redraw[xx][yy])
+    redraw_tiles++;
+
+  redraw[xx][yy] = TRUE;
+  redraw_mask |= REDRAW_TILES;
+}
+
+void GetOptions(char *argv[])
+{
+  char **options_left = &argv[1];
+
+  /* initialize global program options */
+  options.display_name = NULL;
+  options.server_host = NULL;
+  options.server_port = 0;
+  options.base_directory = BASE_PATH;
+  options.level_directory = BASE_PATH "/" LEVELS_DIRECTORY;
+  options.serveronly = FALSE;
+  options.network = FALSE;
+  options.verbose = FALSE;
+
+  while (*options_left)
   {
-    case ANIM_START:
-      anim_restart = TRUE;
-      reset_delay = TRUE;
-      return;
-      break;
-    case ANIM_CONTINUE:
-      break;
-    case ANIM_STOP:
-      redraw_mask |= REDRAW_FIELD;
-      BackToFront();
-      return;
-      break;
-    default:
+    char option_str[MAX_OPTION_LEN];
+    char *option = options_left[0];
+    char *next_option = options_left[1];
+    char *option_arg = NULL;
+    int option_len = strlen(option);
+
+    if (option_len >= MAX_OPTION_LEN)
+      Error(ERR_EXIT_HELP, "unrecognized option '%s'", option);
+
+    strcpy(option_str, option);                        /* copy argument into buffer */
+    option = option_str;
+
+    if (strcmp(option, "--") == 0)             /* stop scanning arguments */
       break;
+
+    if (strncmp(option, "--", 2) == 0)         /* treat '--' like '-' */
+      option++;
+
+    option_arg = strchr(option, '=');
+    if (option_arg == NULL)                    /* no '=' in option */
+      option_arg = next_option;
+    else
+    {
+      *option_arg++ = '\0';                    /* cut argument from option */
+      if (*option_arg == '\0')                 /* no argument after '=' */
+       Error(ERR_EXIT_HELP, "option '%s' has invalid argument", option_str);
+    }
+
+    option_len = strlen(option);
+
+    if (strcmp(option, "-") == 0)
+      Error(ERR_EXIT_HELP, "unrecognized option '%s'", option);
+    else if (strncmp(option, "-help", option_len) == 0)
+    {
+      printf("Usage: %s [options] [server.name [port]]\n"
+            "Options:\n"
+            "  -d, --display machine:0       X server display\n"
+            "  -b, --basepath directory      alternative base directory\n"
+            "  -l, --levels directory        alternative level directory\n"
+            "  -s, --serveronly              only start network server\n"
+            "  -n, --network                 network multiplayer game\n"
+            "  -v, --verbose                 verbose mode\n",
+            program_name);
+      exit(0);
+    }
+    else if (strncmp(option, "-display", option_len) == 0)
+    {
+      if (option_arg == NULL)
+       Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
+
+      options.display_name = option_arg;
+      if (option_arg == next_option)
+       options_left++;
+    }
+    else if (strncmp(option, "-basepath", option_len) == 0)
+    {
+      if (option_arg == NULL)
+       Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
+
+      options.base_directory = option_arg;
+      if (option_arg == next_option)
+       options_left++;
+
+      /* adjust path for level directory accordingly */
+      options.level_directory =
+       getPath2(options.base_directory, LEVELS_DIRECTORY);
+    }
+    else if (strncmp(option, "-levels", option_len) == 0)
+    {
+      if (option_arg == NULL)
+       Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
+
+      options.level_directory = option_arg;
+      if (option_arg == next_option)
+       options_left++;
+    }
+    else if (strncmp(option, "-network", option_len) == 0)
+    {
+      options.network = TRUE;
+    }
+    else if (strncmp(option, "-serveronly", option_len) == 0)
+    {
+      options.serveronly = TRUE;
+    }
+    else if (strncmp(option, "-verbose", option_len) == 0)
+    {
+      options.verbose = TRUE;
+    }
+    else if (*option == '-')
+    {
+      Error(ERR_EXIT_HELP, "unrecognized option '%s'", option_str);
+    }
+    else if (options.server_host == NULL)
+    {
+      options.server_host = *options_left;
+    }
+    else if (options.server_port == 0)
+    {
+      options.server_port = atoi(*options_left);
+      if (options.server_port < 1024)
+       Error(ERR_EXIT_HELP, "bad port number '%d'", options.server_port);
+    }
+    else
+      Error(ERR_EXIT_HELP, "too many arguments");
+
+    options_left++;
   }
+}
+
+void Error(int mode, char *format, ...)
+{
+  char *process_name = "";
+  FILE *error = stderr;
+
+  /* display warnings only when running in verbose mode */
+  if (mode & ERR_WARN && !options.verbose)
+    return;
 
-  if (reset_delay)
+#ifdef MSDOS
+  if ((error = openErrorFile()) == NULL)
   {
-    animstart_delay = Counter();
-    animstart_delay_value = RND(500);
-    reset_delay = FALSE;
+    printf("Cannot write to error output file!\n");
+    CloseAllAndExit(1);
   }
+#endif
 
-  if (anim_restart)
+  if (mode & ERR_SOUND_SERVER)
+    process_name = " sound server";
+  else if (mode & ERR_NETWORK_SERVER)
+    process_name = " network server";
+  else if (mode & ERR_NETWORK_CLIENT)
+    process_name = " network client **";
+
+  if (format)
   {
-    if (!DelayReached(&animstart_delay,animstart_delay_value))
-      return;
+    va_list ap;
+
+    fprintf(error, "%s%s: ", program_name, process_name);
+
+    if (mode & ERR_WARN)
+      fprintf(error, "warning: ");
+
+    va_start(ap, format);
+    vfprintf(error, format, ap);
+    va_end(ap);
+  
+    fprintf(error, "\n");
+  }
+  
+  if (mode & ERR_HELP)
+    fprintf(error, "%s: Try option '--help' for more information.\n",
+           program_name);
+
+  if (mode & ERR_EXIT)
+    fprintf(error, "%s%s: aborting\n", program_name, process_name);
+
+  if (error != stderr)
+    fclose(error);
 
-    toon_nr = RND(NUM_TOONS);
+  if (mode & ERR_EXIT)
+  {
+    if (mode & ERR_FROM_SERVER)
+      exit(1);                         /* child process: normal exit */
+    else
+      CloseAllAndExit(1);              /* main process: clean up stuff */
   }
+}
+
+void *checked_malloc(unsigned long size)
+{
+  void *ptr;
+
+  ptr = malloc(size);
 
-  anim_restart = reset_delay = AnimateToon(toon_nr,anim_restart);
+  if (ptr == NULL)
+    Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
+
+  return ptr;
 }
 
-BOOL AnimateToon(int toon_nr, BOOL restart)
+void *checked_calloc(unsigned long size)
 {
-  static pos_x = 0, pos_y = 0;
-  static delta_x = 0, delta_y = 0;
-  static int frame = 0, frame_step = 1;
-  static BOOL horiz_move, vert_move;
-  static long anim_delay = 0;
-  static int anim_delay_value = 0;
-  static int width,height;
-  static int pad_x,pad_y;
-  static int cut_x,cut_y;
-  static int src_x, src_y;
-  static int dest_x, dest_y;
-  static struct AnimInfo toon[NUM_TOONS] =
+  void *ptr;
+
+  ptr = calloc(1, size);
+
+  if (ptr == NULL)
+    Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
+
+  return ptr;
+}
+
+#define TRANSLATE_KEYSYM_TO_KEYNAME    0
+#define TRANSLATE_KEYSYM_TO_X11KEYNAME 1
+#define TRANSLATE_X11KEYNAME_TO_KEYSYM 2
+
+void translate_keyname(KeySym *keysym, char **x11name, char **name, int mode)
+{
+  static struct
+  {
+    KeySym keysym;
+    char *x11name;
+    char *name;
+  } translate_key[] =
   {
-    DWARF_XSIZE, DWARF_YSIZE,
-    DWARF_X, DWARF_Y,
-    DWARF_FRAMES,
-    DWARF_FPS,
-    DWARF_STEPSIZE,
-    FALSE,
-    ANIMDIR_RIGHT,
-    ANIMPOS_DOWN,
-
-    DWARF_XSIZE, DWARF_YSIZE,
-    DWARF_X, DWARF2_Y,
-    DWARF_FRAMES,
-    DWARF_FPS,
-    DWARF_STEPSIZE,
-    FALSE,
-    ANIMDIR_LEFT,
-    ANIMPOS_DOWN,
-
-    JUMPER_XSIZE, JUMPER_YSIZE,
-    JUMPER_X, JUMPER_Y,
-    JUMPER_FRAMES,
-    JUMPER_FPS,
-    JUMPER_STEPSIZE,
-    FALSE,
-    ANIMDIR_LEFT,
-    ANIMPOS_DOWN,
-
-    CLOWN_XSIZE, CLOWN_YSIZE,
-    CLOWN_X, CLOWN_Y,
-    CLOWN_FRAMES,
-    CLOWN_FPS,
-    CLOWN_STEPSIZE,
-    FALSE,
-    ANIMDIR_UP,
-    ANIMPOS_ANY,
-
-    BIRD_XSIZE, BIRD_YSIZE,
-    BIRD1_X, BIRD1_Y,
-    BIRD_FRAMES,
-    BIRD_FPS,
-    BIRD_STEPSIZE,
-    TRUE,
-    ANIMDIR_RIGHT,
-    ANIMPOS_UPPER,
-
-    BIRD_XSIZE, BIRD_YSIZE,
-    BIRD2_X, BIRD2_Y,
-    BIRD_FRAMES,
-    BIRD_FPS,
-    BIRD_STEPSIZE,
-    TRUE,
-    ANIMDIR_LEFT,
-    ANIMPOS_UPPER
+    /* normal cursor keys */
+    { XK_Left,         "XK_Left",              "cursor left" },
+    { XK_Right,                "XK_Right",             "cursor right" },
+    { XK_Up,           "XK_Up",                "cursor up" },
+    { XK_Down,         "XK_Down",              "cursor down" },
+
+    /* keypad cursor keys */
+#ifdef XK_KP_Left
+    { XK_KP_Left,      "XK_KP_Left",           "keypad left" },
+    { XK_KP_Right,     "XK_KP_Right",          "keypad right" },
+    { XK_KP_Up,                "XK_KP_Up",             "keypad up" },
+    { XK_KP_Down,      "XK_KP_Down",           "keypad down" },
+#endif
+
+    /* other keypad keys */
+#ifdef XK_KP_Enter
+    { XK_KP_Enter,     "XK_KP_Enter",          "keypad enter" },
+    { XK_KP_Add,       "XK_KP_Add",            "keypad +" },
+    { XK_KP_Subtract,  "XK_KP_Subtract",       "keypad -" },
+    { XK_KP_Multiply,  "XK_KP_Multiply",       "keypad mltply" },
+    { XK_KP_Divide,    "XK_KP_Divide",         "keypad /" },
+    { XK_KP_Separator, "XK_KP_Separator",      "keypad ," },
+#endif
+
+    /* modifier keys */
+    { XK_Shift_L,      "XK_Shift_L",           "left shift" },
+    { XK_Shift_R,      "XK_Shift_R",           "right shift" },
+    { XK_Control_L,    "XK_Control_L",         "left control" },
+    { XK_Control_R,    "XK_Control_R",         "right control" },
+    { XK_Meta_L,       "XK_Meta_L",            "left meta" },
+    { XK_Meta_R,       "XK_Meta_R",            "right meta" },
+    { XK_Alt_L,                "XK_Alt_L",             "left alt" },
+    { XK_Alt_R,                "XK_Alt_R",             "right alt" },
+    { XK_Mode_switch,  "XK_Mode_switch",       "mode switch" },
+    { XK_Multi_key,    "XK_Multi_key",         "multi key" },
+
+    /* some special keys */
+    { XK_BackSpace,    "XK_BackSpace",         "backspace" },
+    { XK_Delete,       "XK_Delete",            "delete" },
+    { XK_Insert,       "XK_Insert",            "insert" },
+    { XK_Tab,          "XK_Tab",               "tab" },
+    { XK_Home,         "XK_Home",              "home" },
+    { XK_End,          "XK_End",               "end" },
+    { XK_Page_Up,      "XK_Page_Up",           "page up" },
+    { XK_Page_Down,    "XK_Page_Down",         "page down" },
+    { XK_space,                "XK_space",             "space" },
+
+    /* even more special keys */
+    { XK_adiaeresis,   "XK_adiaeresis",        "ä" },
+    { XK_odiaeresis,   "XK_odiaeresis",        "ö" },
+    { XK_udiaeresis,   "XK_udiaeresis",        "ü" },
+    { XK_apostrophe,   "XK_apostrophe",        "'" },
+    { XK_plus,         "XK_plus",              "+" },
+    { XK_minus,                "XK_minus",             "-" },
+    { XK_equal,                "XK_equal",             "equal" },
+    { XK_comma,                "XK_comma",             "," },
+    { XK_period,       "XK_period",            "." },
+    { XK_colon,                "XK_colon",             ";" },
+    { XK_slash,                "XK_slash",             "/" },
+    { XK_numbersign,   "XK_numbersign",        "#" },
+    { XK_backslash,    "XK_backslash",         "backslash" },
+    { XK_braceleft,    "XK_braceleft",         "brace left" },
+    { XK_braceright,   "XK_braceright",        "brace right" },
+    { XK_less,         "XK_less",              "less" },
+    { XK_greater,      "XK_greater",           "greater" },
+    { XK_asciicircum,  "XK_asciicircum",       "circumflex" },
+    { XK_ssharp,       "XK_ssharp",            "sharp s" },
+
+    /* end-of-array identifier */
+    { 0,                NULL,                  NULL }
   };
-  struct AnimInfo *anim = &toon[toon_nr];
 
-  if (restart)
-  {
-    horiz_move = (anim->direction & (ANIMDIR_LEFT | ANIMDIR_RIGHT));
-    vert_move = (anim->direction & (ANIMDIR_UP | ANIMDIR_DOWN));
-    anim_delay_value = 100/anim->frames_per_second;
-    frame = 0;
+  int i;
 
-    if (horiz_move)
+  if (mode == TRANSLATE_KEYSYM_TO_KEYNAME)
+  {
+    static char name_buffer[30];
+    KeySym key = *keysym;
+
+    if (key >= XK_A && key <= XK_Z)
+      sprintf(name_buffer, "%c", 'A' + (char)(key - XK_A));
+    else if (key >= XK_a && key <= XK_z)
+      sprintf(name_buffer, "%c", 'a' + (char)(key - XK_a));
+    else if (key >= XK_0 && key <= XK_9)
+      sprintf(name_buffer, "%c", '0' + (char)(key - XK_0));
+    else if (key >= XK_KP_0 && key <= XK_KP_9)
+      sprintf(name_buffer, "keypad %c", '0' + (char)(key - XK_KP_0));
+    else if (key >= XK_F1 && key <= XK_F24)
+      sprintf(name_buffer, "function F%d", (int)(key - XK_F1 + 1));
+    else if (key == KEY_UNDEFINDED)
+      strcpy(name_buffer, "(undefined)");
+    else
     {
-      if (anim->position==ANIMPOS_UP)
-       pos_y = 0;
-      else if (anim->position==ANIMPOS_DOWN)
-       pos_y = FULL_SYSIZE-anim->height;
-      else if (anim->position==ANIMPOS_UPPER)
-       pos_y = RND((FULL_SYSIZE-anim->height)/2);
-      else
-       pos_y = RND(FULL_SYSIZE-anim->height);
+      i = 0;
 
-      if (anim->direction==ANIMDIR_RIGHT)
+      do
       {
-       delta_x = anim->stepsize;
-       pos_x = -anim->width+delta_x;
+       if (key == translate_key[i].keysym)
+       {
+         strcpy(name_buffer, translate_key[i].name);
+         break;
+       }
       }
-      else
+      while (translate_key[++i].name);
+
+      if (!translate_key[i].name)
+       strcpy(name_buffer, "(unknown)");
+    }
+
+    *name = name_buffer;
+  }
+  else if (mode == TRANSLATE_KEYSYM_TO_X11KEYNAME)
+  {
+    static char name_buffer[30];
+    KeySym key = *keysym;
+
+    if (key >= XK_A && key <= XK_Z)
+      sprintf(name_buffer, "XK_%c", 'A' + (char)(key - XK_A));
+    else if (key >= XK_a && key <= XK_z)
+      sprintf(name_buffer, "XK_%c", 'a' + (char)(key - XK_a));
+    else if (key >= XK_0 && key <= XK_9)
+      sprintf(name_buffer, "XK_%c", '0' + (char)(key - XK_0));
+    else if (key >= XK_KP_0 && key <= XK_KP_9)
+      sprintf(name_buffer, "XK_KP_%c", '0' + (char)(key - XK_KP_0));
+    else if (key >= XK_F1 && key <= XK_F24)
+      sprintf(name_buffer, "XK_F%d", (int)(key - XK_F1 + 1));
+    else if (key == KEY_UNDEFINDED)
+      strcpy(name_buffer, "[undefined]");
+    else
+    {
+      i = 0;
+
+      do
       {
-       delta_x = -anim->stepsize;
-       pos_x = FULL_SXSIZE+delta_x;
+       if (key == translate_key[i].keysym)
+       {
+         strcpy(name_buffer, translate_key[i].x11name);
+         break;
+       }
       }
-      delta_y = 0;
+      while (translate_key[++i].x11name);
+
+      if (!translate_key[i].x11name)
+       sprintf(name_buffer, "0x%04lx", (unsigned long)key);
     }
-    else
+
+    *x11name = name_buffer;
+  }
+  else if (mode == TRANSLATE_X11KEYNAME_TO_KEYSYM)
+  {
+    KeySym key = XK_VoidSymbol;
+    char *name_ptr = *x11name;
+
+    if (strncmp(name_ptr, "XK_", 3) == 0 && strlen(name_ptr) == 4)
     {
-      if (anim->position==ANIMPOS_LEFT)
-       pos_x = 0;
-      else if (anim->position==ANIMPOS_RIGHT)
-       pos_x = FULL_SXSIZE-anim->width;
-      else
-       pos_x = RND(FULL_SXSIZE-anim->width);
+      char c = name_ptr[3];
+
+      if (c >= 'A' && c <= 'Z')
+       key = XK_A + (KeySym)(c - 'A');
+      else if (c >= 'a' && c <= 'z')
+       key = XK_a + (KeySym)(c - 'a');
+      else if (c >= '0' && c <= '9')
+       key = XK_0 + (KeySym)(c - '0');
+    }
+    else if (strncmp(name_ptr, "XK_KP_", 6) == 0 && strlen(name_ptr) == 7)
+    {
+      char c = name_ptr[6];
+
+      if (c >= '0' && c <= '9')
+       key = XK_0 + (KeySym)(c - '0');
+    }
+    else if (strncmp(name_ptr, "XK_F", 4) == 0 && strlen(name_ptr) <= 6)
+    {
+      char c1 = name_ptr[4];
+      char c2 = name_ptr[5];
+      int d = 0;
+
+      if ((c1 >= '0' && c1 <= '9') &&
+         ((c2 >= '0' && c1 <= '9') || c2 == '\0'))
+       d = atoi(&name_ptr[4]);
 
-      if (anim->direction==ANIMDIR_DOWN)
+      if (d >=1 && d <= 24)
+       key = XK_F1 + (KeySym)(d - 1);
+    }
+    else if (strncmp(name_ptr, "XK_", 3) == 0)
+    {
+      i = 0;
+
+      do
       {
-       delta_y = anim->stepsize;
-       pos_y = -anim->height+delta_y;
+       if (strcmp(name_ptr, translate_key[i].x11name) == 0)
+       {
+         key = translate_key[i].keysym;
+         break;
+       }
       }
-      else
+      while (translate_key[++i].x11name);
+    }
+    else if (strncmp(name_ptr, "0x", 2) == 0)
+    {
+      unsigned long value = 0;
+
+      name_ptr += 2;
+
+      while (name_ptr)
       {
-       delta_y = -anim->stepsize;
-       pos_y = FULL_SYSIZE+delta_y;
+       char c = *name_ptr++;
+       int d = -1;
+
+       if (c >= '0' && c <= '9')
+         d = (int)(c - '0');
+       else if (c >= 'a' && c <= 'f')
+         d = (int)(c - 'a' + 10);
+       else if (c >= 'A' && c <= 'F')
+         d = (int)(c - 'A' + 10);
+
+       if (d == -1)
+       {
+         value = -1;
+         break;
+       }
+
+       value = value * 16 + d;
       }
-      delta_x = 0;
+
+      if (value != -1)
+       key = (KeySym)value;
     }
+
+    *keysym = key;
   }
+}
 
-  if (pos_x <= -anim->width  - anim->stepsize ||
-      pos_x >=  FULL_SXSIZE  + anim->stepsize ||
-      pos_y <= -anim->height - anim->stepsize ||
-      pos_y >=  FULL_SYSIZE  + anim->stepsize)
-    return(TRUE);
+char *getKeyNameFromKeySym(KeySym keysym)
+{
+  char *name;
 
-  if (!DelayReached(&anim_delay,anim_delay_value))
-  {
-    if (game_status==HELPSCREEN && !restart)
-      DrawAnim(src_x+cut_x,src_y+cut_y, width,height,
-              REAL_SX+dest_x,REAL_SY+dest_y, pad_x,pad_y);
+  translate_keyname(&keysym, NULL, &name, TRANSLATE_KEYSYM_TO_KEYNAME);
+  return name;
+}
 
-    return(FALSE);
-  }
+char *getX11KeyNameFromKeySym(KeySym keysym)
+{
+  char *x11name;
 
-  if (pos_x<-anim->width)
-    pos_x = -anim->width;
-  else if (pos_x>FULL_SXSIZE)
-    pos_x = FULL_SXSIZE;
-  if (pos_y<-anim->height)
-    pos_y = -anim->height;
-  else if (pos_y>FULL_SYSIZE)
-    pos_y = FULL_SYSIZE;
-
-  pad_x = (horiz_move ? anim->stepsize : 0);
-  pad_y = (vert_move  ? anim->stepsize : 0);
-  src_x = anim->src_x + frame * anim->width;
-  src_y = anim->src_y;
-  dest_x = pos_x;
-  dest_y = pos_y;
-  cut_x = cut_y = 0;
-  width  = anim->width;
-  height = anim->height;
-
-  if (pos_x<0)
-  {
-    dest_x = 0;
-    width += pos_x;
-    cut_x = -pos_x;
-  }
-  else if (pos_x>FULL_SXSIZE-anim->width)
-    width -= (pos_x - (FULL_SXSIZE-anim->width));
+  translate_keyname(&keysym, &x11name, NULL, TRANSLATE_KEYSYM_TO_X11KEYNAME);
+  return x11name;
+}
+
+KeySym getKeySymFromX11KeyName(char *x11name)
+{
+  KeySym keysym;
 
-  if (pos_y<0)
+  translate_keyname(&keysym, &x11name, NULL, TRANSLATE_X11KEYNAME_TO_KEYSYM);
+  return keysym;
+}
+
+#define TRANSLATE_JOYSYMBOL_TO_JOYNAME 0
+#define TRANSLATE_JOYNAME_TO_JOYSYMBOL 1
+
+void translate_joyname(int *joysymbol, char **name, int mode)
+{
+  static struct
   {
-    dest_y = 0;
-    height += pos_y;
-    cut_y = -pos_y;
-  }
-  else if (pos_y>FULL_SYSIZE-anim->height)
-    height -= (pos_y - (FULL_SYSIZE-anim->height));
+    int joysymbol;
+    char *name;
+  } translate_joy[] =
+  {
+    { JOY_LEFT,                "joystick_left" },
+    { JOY_RIGHT,       "joystick_right" },
+    { JOY_UP,          "joystick_up" },
+    { JOY_DOWN,                "joystick_down" },
+    { JOY_BUTTON_1,    "joystick_button_1" },
+    { JOY_BUTTON_2,    "joystick_button_2" },
+  };
 
-  DrawAnim(src_x+cut_x,src_y+cut_y, width,height,
-          REAL_SX+dest_x,REAL_SY+dest_y, pad_x,pad_y);
+  int i;
 
-  pos_x += delta_x;
-  pos_y += delta_y;
-  frame += frame_step;
+  if (mode == TRANSLATE_JOYSYMBOL_TO_JOYNAME)
+  {
+    *name = "[undefined]";
 
-  if (frame<0 || frame>=anim->frames)
+    for (i=0; i<6; i++)
+    {
+      if (*joysymbol == translate_joy[i].joysymbol)
+      {
+       *name = translate_joy[i].name;
+       break;
+      }
+    }
+  }
+  else if (mode == TRANSLATE_JOYNAME_TO_JOYSYMBOL)
   {
-    if (anim->pingpong)
+    *joysymbol = 0;
+
+    for (i=0; i<6; i++)
     {
-      frame_step *= -1;
-      frame = (frame<0 ? 1 : anim->frames-2);
+      if (strcmp(*name, translate_joy[i].name) == 0)
+      {
+       *joysymbol = translate_joy[i].joysymbol;
+       break;
+      }
     }
-    else
-      frame = (frame<0 ? anim->frames-1 : 0);
   }
+}
+
+char *getJoyNameFromJoySymbol(int joysymbol)
+{
+  char *name;
 
-  return(FALSE);
+  translate_joyname(&joysymbol, &name, TRANSLATE_JOYSYMBOL_TO_JOYNAME);
+  return name;
 }
 
-void DrawAnim(int src_x, int src_y, int width, int height,
-             int dest_x, int dest_y, int pad_x, int pad_y)
+int getJoySymbolFromJoyName(char *name)
 {
-  int buf_x = DOOR_GFX_PAGEX3, buf_y = DOOR_GFX_PAGEY1;
+  int joysymbol;
 
-#if 1
-  /* special method to avoid flickering interference with BackToFront() */
-  XCopyArea(display,backbuffer,pix[PIX_DB_DOOR],gc,dest_x-pad_x,dest_y-pad_y,
-           width+2*pad_x,height+2*pad_y, buf_x,buf_y);
-  XSetClipOrigin(display,clip_gc[PIX_TOONS],dest_x-src_x,dest_y-src_y);
-  XCopyArea(display,pix[PIX_TOONS],backbuffer,clip_gc[PIX_TOONS],
-           src_x,src_y, width,height, dest_x,dest_y);
-  XCopyArea(display,backbuffer,window,gc, dest_x-pad_x,dest_y-pad_y,
-           width+2*pad_x,height+2*pad_y, dest_x-pad_x,dest_y-pad_y);
-  BackToFront();
-  XCopyArea(display,pix[PIX_DB_DOOR],backbuffer,gc, buf_x,buf_y,
-           width+2*pad_x,height+2*pad_y, dest_x-pad_x,dest_y-pad_y);
-#else
-  /* normal method, causing flickering interference with BackToFront() */
-  XCopyArea(display,backbuffer,pix[PIX_DB_DOOR],gc,dest_x-pad_x,dest_y-pad_y,
-           width+2*pad_x,height+2*pad_y, buf_x,buf_y);
-  XSetClipOrigin(display,clip_gc[PIX_TOONS],
-                buf_x-src_x+pad_x,buf_y-src_y+pad_y);
-  XCopyArea(display,pix[PIX_TOONS],pix[PIX_DB_DOOR],clip_gc[PIX_TOONS],
-           src_x,src_y, width,height, buf_x+pad_x,buf_y+pad_y);
-  XCopyArea(display,pix[PIX_DB_DOOR],window,gc, buf_x,buf_y,
-           width+2*pad_x,height+2*pad_y, dest_x-pad_x,dest_y-pad_y);
-#endif
+  translate_joyname(&joysymbol, &name, TRANSLATE_JOYNAME_TO_JOYSYMBOL);
+  return joysymbol;
+}
+
+int getJoystickNrFromDeviceName(char *device_name)
+{
+  char c;
+  int joystick_nr = 0;
+
+  if (device_name == NULL || device_name[0] == '\0')
+    return 0;
+
+  c = device_name[strlen(device_name) - 1];
+
+  if (c >= '0' && c <= '9')
+    joystick_nr = (int)(c - '0');
+
+  if (joystick_nr < 0 || joystick_nr >= MAX_PLAYERS)
+    joystick_nr = 0;
+
+  return joystick_nr;
+}
+
+/* ----------------------------------------------------------------- */
+/* the following is only for debugging purpose and normally not used */
+/* ----------------------------------------------------------------- */
+
+#define DEBUG_NUM_TIMESTAMPS   3
+
+void debug_print_timestamp(int counter_nr, char *message)
+{
+  static long counter[DEBUG_NUM_TIMESTAMPS][2];
+
+  if (counter_nr >= DEBUG_NUM_TIMESTAMPS)
+    Error(ERR_EXIT, "debugging: increase DEBUG_NUM_TIMESTAMPS in misc.c");
+
+  counter[counter_nr][0] = Counter();
+
+  if (message)
+    printf("%s %.2f seconds\n", message,
+          (float)(counter[counter_nr][0] - counter[counter_nr][1]) / 1000);
 
-  XFlush(display);
+  counter[counter_nr][1] = Counter();
 }
index dba380157704facd02366cfabadd863ef3909ed0..ae8ae79e9ad85e266d8778c503a43e25d735d5d7 100644 (file)
@@ -1,13 +1,12 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  misc.h                                                  *
 ***********************************************************/
 
 #include "main.h"
 
-/* values for cartoon figures */
-#define NUM_TOONS      6
+#define INIT_COUNTER           0
+#define READ_COUNTER           1
 
-#define DWARF_XSIZE    40
-#define DWARF_YSIZE    48
-#define DWARF_X                2
-#define DWARF_Y                72
-#define DWARF2_Y       186
-#define DWARF_FRAMES   8
-#define DWARF_FPS      10
-#define DWARF_STEPSIZE 4
-#define JUMPER_XSIZE   48
-#define JUMPER_YSIZE   56
-#define JUMPER_X       2
-#define JUMPER_Y       125
-#define JUMPER_FRAMES  8
-#define JUMPER_FPS     10
-#define JUMPER_STEPSIZE        4
-#define CLOWN_XSIZE    80
-#define CLOWN_YSIZE    110
-#define CLOWN_X                327
-#define CLOWN_Y                10
-#define CLOWN_FRAMES   1
-#define CLOWN_FPS      10
-#define CLOWN_STEPSIZE 4
-#define BIRD_XSIZE     32
-#define BIRD_YSIZE     30
-#define BIRD1_X                2
-#define BIRD1_Y                2
-#define BIRD2_X                2
-#define BIRD2_Y                37
-#define BIRD_FRAMES    8
-#define BIRD_FPS       20
-#define BIRD_STEPSIZE  4
+#define NEW_RANDOMIZE          -1
 
-#define ANIMDIR_LEFT   1
-#define ANIMDIR_RIGHT  2
-#define ANIMDIR_UP     4
-#define ANIMDIR_DOWN   8
+#define ERR_RETURN             0
+#define ERR_WARN               (1 << 0)
+#define ERR_EXIT               (1 << 1)
+#define ERR_HELP               (1 << 2)
+#define ERR_SOUND_SERVER       (1 << 3)
+#define ERR_NETWORK_SERVER     (1 << 4)
+#define ERR_NETWORK_CLIENT     (1 << 5)
+#define ERR_FROM_SERVER                (ERR_SOUND_SERVER | ERR_NETWORK_SERVER)
+#define ERR_EXIT_HELP          (ERR_EXIT | ERR_HELP)
+#define ERR_EXIT_SOUND_SERVER  (ERR_EXIT | ERR_SOUND_SERVER)
+#define ERR_EXIT_NETWORK_SERVER        (ERR_EXIT | ERR_NETWORK_SERVER)
+#define ERR_EXIT_NETWORK_CLIENT        (ERR_EXIT | ERR_NETWORK_CLIENT)
 
-#define ANIMPOS_ANY    0
-#define ANIMPOS_LEFT   1
-#define ANIMPOS_RIGHT  2
-#define ANIMPOS_UP     4
-#define ANIMPOS_DOWN   8
-#define ANIMPOS_UPPER  16
-
-#define ANIM_START     0
-#define ANIM_CONTINUE  1
-#define ANIM_STOP      2
-
-struct AnimInfo
-{
-  int width, height;
-  int src_x, src_y;
-  int frames;
-  int frames_per_second;
-  int stepsize;
-  BOOL pingpong;
-  int direction;
-  int position;
-};
-
-#define NEW_RANDOMIZE  -1
-
-void microsleep(unsigned long);
-long mainCounter(int);
 void InitCounter(void);
-long Counter(void);
-long Counter2(void);
-void WaitCounter(long);
-void WaitCounter2(long);
-void Delay(long);
-BOOL DelayReached(long *, int);
-unsigned long be2long(unsigned long *);
+unsigned long Counter(void);
+void Delay(unsigned long);
+boolean FrameReached(unsigned long *, unsigned long);
+boolean DelayReached(unsigned long *, unsigned long);
+void WaitUntilDelayReached(unsigned long *, unsigned long);
 char *int2str(int, int);
+unsigned int SimpleRND(unsigned int);
 unsigned int RND(unsigned int);
 unsigned int InitRND(long);
-char *GetLoginName(void);
+char *getLoginName(void);
+char *getHomeDir(void);
+char *getPath2(char *, char *);
+char *getPath3(char *, char *, char*);
+char *getStringCopy(char *);
+char *getStringToLower(char *);
+void MarkTileDirty(int, int);
+void GetOptions(char **);
+void Error(int, char *, ...);
+void *checked_malloc(unsigned long);
+void *checked_calloc(unsigned long);
+char *getKeyNameFromKeySym(KeySym);
+char *getX11KeyNameFromKeySym(KeySym);
+KeySym getKeySymFromX11KeyName(char *);
+char *getJoyNameFromJoySymbol(int);
+int getJoySymbolFromJoyName(char *);
+int getJoystickNrFromDeviceName(char *);
 
-void InitAnimation(void);
-void StopAnimation(void);
-void DoAnimation(void);
-void HandleAnimation(int);
-BOOL AnimateToon(int, BOOL);
-void DrawAnim(int, int, int, int, int, int, int, int);
+void debug_print_timestamp(int, char *);
+void print_debug(char *);
 
-#endif
+#endif /* MISC_H */
diff --git a/src/msdos.c b/src/msdos.c
new file mode 100644 (file)
index 0000000..efcd802
--- /dev/null
@@ -0,0 +1,776 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  ©1995 Artsoft Development                               *
+*        Holger Schemel                                    *
+*        33659 Bielefeld-Senne                             *
+*        Telefon: (0521) 493245                            *
+*        eMail: aeglos@valinor.owl.de                      *
+*               aeglos@uni-paderborn.de                    *
+*               q99492@pbhrzx.uni-paderborn.de             *
+*----------------------------------------------------------*
+*  msdos.c                                                 *
+***********************************************************/
+
+#ifdef MSDOS
+
+#include "main.h"
+#include "misc.h"
+#include "tools.h"
+#include "sound.h"
+#include "files.h"
+#include "joystick.h"
+#include "image.h"
+#include "pcx.h"
+
+/* allegro driver declarations */
+DECLARE_GFX_DRIVER_LIST(GFX_DRIVER_VBEAF GFX_DRIVER_VESA2L GFX_DRIVER_VESA1)
+DECLARE_COLOR_DEPTH_LIST(COLOR_DEPTH_8)
+DECLARE_DIGI_DRIVER_LIST(DIGI_DRIVER_SB)
+DECLARE_MIDI_DRIVER_LIST()
+DECLARE_JOYSTICK_DRIVER_LIST(JOYSTICK_DRIVER_STANDARD)
+
+/* allegro global variables */
+extern volatile int key_shifts;
+extern int num_joysticks;
+extern JOYSTICK_INFO joy[];
+extern int i_love_bill;
+
+/* internal variables of msdos.c */
+static int key_press_state[MAX_SCANCODES];
+static XEvent event_buffer[MAX_EVENT_BUFFER];
+static int pending_events;
+static boolean joystick_event;
+static boolean mouse_installed = FALSE;
+static int last_mouse_pos;
+static int last_mouse_b;
+static int last_joystick_state;
+static BITMAP* video_bitmap;
+
+static RGB global_colormap[MAX_COLORS];
+static int global_colormap_entries_used = 0;
+
+boolean wait_for_vsync;
+
+/*
+extern int playing_sounds;
+extern struct SoundControl playlist[MAX_SOUNDS_PLAYING];
+extern struct SoundControl emptySoundControl;
+*/
+
+static BITMAP *Read_PCX_to_AllegroBitmap(char *);
+
+static void allegro_drivers()
+{
+  int i;
+
+  for (i=0; i<MAX_EVENT_BUFFER; i++)
+    event_buffer[i].type = 0;
+
+  for (i=0; i<MAX_SCANCODES; i++)
+    key_press_state[i] = KeyReleaseMask;
+
+  last_mouse_pos = mouse_pos;
+  last_mouse_b = 0;
+
+  pending_events = 0;
+  clear_keybuf();
+
+  /* enable Windows friendly timer mode (already default under Windows) */
+  i_love_bill = TRUE;
+
+  install_keyboard();
+  install_timer();
+  if (install_mouse() > 0)
+    mouse_installed = TRUE;
+
+  last_joystick_state = 0;
+  joystick_event = FALSE;
+
+  reserve_voices(MAX_SOUNDS_PLAYING, 0);
+  if (install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL) == -1)
+    if (install_sound(DIGI_SB, MIDI_NONE, NULL) == -1)
+      sound_status = SOUND_OFF;
+}
+
+static boolean hide_mouse(Display *display, int x, int y,
+                         unsigned int width, unsigned int height)
+{
+  if (mouse_x + display->mouse_ptr->w < x || mouse_x > x + width)
+    return FALSE;
+  if (mouse_y + display->mouse_ptr->h < y || mouse_y > y + height)
+    return FALSE;
+
+  show_mouse(NULL);
+
+  return TRUE;
+}
+
+static void unhide_mouse(Display *display)
+{
+  if (mouse_installed)
+    show_mouse(video_bitmap);
+}
+
+static KeySym ScancodeToKeySym(byte scancode)
+{
+  switch(scancode)
+  {
+    case KEY_ESC:              return XK_Escape;
+    case KEY_1:                        return XK_1;
+    case KEY_2:                        return XK_2;
+    case KEY_3:                        return XK_3;
+    case KEY_4:                        return XK_4;
+    case KEY_5:                        return XK_5;
+    case KEY_6:                        return XK_6;
+    case KEY_7:                        return XK_7;
+    case KEY_8:                        return XK_8;
+    case KEY_9:                        return XK_9;
+    case KEY_0:                        return XK_0;
+    case KEY_MINUS:            return XK_minus;
+    case KEY_EQUALS:           return XK_equal;
+    case KEY_BACKSPACE:                return XK_BackSpace;
+    case KEY_TAB:              return XK_Tab;
+    case KEY_Q:                        return XK_q;
+    case KEY_W:                        return XK_w;
+    case KEY_E:                        return XK_e;
+    case KEY_R:                        return XK_r;
+    case KEY_T:                        return XK_t;
+    case KEY_Y:                        return XK_y;
+    case KEY_U:                        return XK_u;
+    case KEY_I:                        return XK_i;
+    case KEY_O:                        return XK_o;
+    case KEY_P:                        return XK_p;
+    case KEY_OPENBRACE:                return XK_braceleft;
+    case KEY_CLOSEBRACE:       return XK_braceright;
+    case KEY_ENTER:            return XK_Return;
+    case KEY_LCONTROL:         return XK_Control_L;
+    case KEY_A:                        return XK_a;
+    case KEY_S:                        return XK_s;
+    case KEY_D:                        return XK_d;
+    case KEY_F:                        return XK_f;
+    case KEY_G:                        return XK_g;
+    case KEY_H:                        return XK_h;
+    case KEY_J:                        return XK_j;
+    case KEY_K:                        return XK_k;
+    case KEY_L:                        return XK_l;
+    case KEY_COLON:            return XK_colon;
+    case KEY_QUOTE:            return XK_apostrophe;
+    case KEY_TILDE:            return XK_asciitilde;
+    case KEY_LSHIFT:           return XK_Shift_L;
+    case KEY_BACKSLASH:                return XK_backslash;
+    case KEY_Z:                        return XK_z;
+    case KEY_X:                        return XK_x;
+    case KEY_C:                        return XK_c;
+    case KEY_V:                        return XK_v;
+    case KEY_B:                        return XK_b;
+    case KEY_N:                        return XK_n;
+    case KEY_M:                        return XK_m;
+    case KEY_COMMA:            return XK_comma;
+    case KEY_STOP:             return XK_period;
+    case KEY_SLASH:            return XK_slash;
+    case KEY_RSHIFT:           return XK_Shift_R;
+    case KEY_ASTERISK:         return XK_KP_Multiply;
+    case KEY_ALT:              return XK_Alt_L;
+    case KEY_SPACE:            return XK_space;
+    case KEY_CAPSLOCK:         return XK_Caps_Lock;
+    case KEY_F1:               return XK_F1;
+    case KEY_F2:               return XK_F2;
+    case KEY_F3:               return XK_F3;
+    case KEY_F4:               return XK_F4;
+    case KEY_F5:               return XK_F5;
+    case KEY_F6:               return XK_F6;
+    case KEY_F7:               return XK_F7;
+    case KEY_F8:               return XK_F8;
+    case KEY_F9:               return XK_F9;
+    case KEY_F10:              return XK_F10;
+    case KEY_NUMLOCK:          return XK_Num_Lock;
+    case KEY_SCRLOCK:          return XK_Scroll_Lock;
+    case KEY_HOME:             return XK_Home;
+    case KEY_UP:               return XK_Up;
+    case KEY_PGUP:             return XK_Page_Up;
+    case KEY_MINUS_PAD:                return XK_KP_Subtract;
+    case KEY_LEFT:             return XK_Left;
+    case KEY_5_PAD:            return XK_KP_5;
+    case KEY_RIGHT:            return XK_Right;
+    case KEY_PLUS_PAD:         return XK_KP_Add;
+    case KEY_END:              return XK_End;
+    case KEY_DOWN:             return XK_Down;
+    case KEY_PGDN:             return XK_Page_Down;
+    case KEY_INSERT:           return XK_Insert;
+    case KEY_DEL:              return XK_Delete;
+    case KEY_PRTSCR:           return XK_Print;
+    case KEY_F11:              return XK_F11;
+    case KEY_F12:              return XK_F12;
+    case KEY_LWIN:             return XK_Meta_L;
+    case KEY_RWIN:             return XK_Meta_R;
+    case KEY_MENU:             return XK_Menu;
+    case KEY_PAD:              return XK_VoidSymbol;
+    case KEY_RCONTROL:         return XK_Control_R;
+    case KEY_ALTGR:            return XK_Alt_R;
+    case KEY_SLASH2:           return XK_KP_Divide;
+    case KEY_PAUSE:            return XK_Pause;
+
+    case NEW_KEY_BACKSLASH:    return XK_backslash;
+    case NEW_KEY_1_PAD:                return XK_KP_1;
+    case NEW_KEY_2_PAD:                return XK_KP_2;
+    case NEW_KEY_3_PAD:                return XK_KP_3;
+    case NEW_KEY_4_PAD:                return XK_KP_4;
+    case NEW_KEY_5_PAD:                return XK_KP_5;
+    case NEW_KEY_6_PAD:                return XK_KP_6;
+    case NEW_KEY_7_PAD:                return XK_KP_7;
+    case NEW_KEY_8_PAD:                return XK_KP_8;
+    case NEW_KEY_9_PAD:                return XK_KP_9;
+    case NEW_KEY_0_PAD:                return XK_KP_0;
+    case NEW_KEY_STOP_PAD:     return XK_KP_Separator;
+    case NEW_KEY_EQUALS_PAD:   return XK_KP_Equal;
+    case NEW_KEY_SLASH_PAD:    return XK_KP_Divide;
+    case NEW_KEY_ASTERISK_PAD: return XK_KP_Multiply;
+    case NEW_KEY_ENTER_PAD:    return XK_KP_Enter;
+
+    default:                   return XK_VoidSymbol;
+  }
+}
+
+void XMapWindow(Display *display, Window window)
+{
+  int x, y;
+  unsigned int width, height;
+  boolean mouse_off;
+
+  x = display->screens[display->default_screen].x;
+  y = display->screens[display->default_screen].y;
+  width = display->screens[display->default_screen].width;
+  height = display->screens[display->default_screen].height;
+
+  mouse_off = hide_mouse(display, x, y, width, height);
+  blit((BITMAP *)window, video_bitmap, 0, 0, x, y, width, height);
+
+  if (mouse_off)
+    unhide_mouse(display);
+}
+
+Display *XOpenDisplay(char *display_name)
+{
+  Screen *screen;
+  Display *display;
+  BITMAP *mouse_bitmap = NULL;
+  char *filename;
+
+  filename = getPath3(options.base_directory, GRAPHICS_DIRECTORY,
+                     MOUSE_FILENAME);
+
+  mouse_bitmap = Read_PCX_to_AllegroBitmap(filename);
+  free(filename);
+
+  if (mouse_bitmap == NULL)
+    return NULL;
+
+  screen = malloc(sizeof(Screen));
+  display = malloc(sizeof(Display));
+
+  screen[0].cmap = 0;
+  screen[0].root = 0;
+  screen[0].white_pixel = 0xFF;
+  screen[0].black_pixel = 0x00;
+  screen[0].video_bitmap = NULL;
+
+  display->default_screen = 0;
+  display->screens = screen;
+  display->mouse_ptr = mouse_bitmap;
+
+  allegro_init();
+  allegro_drivers();
+  set_color_depth(8);
+
+  /* force Windows 95 to switch to fullscreen mode */
+  set_gfx_mode(GFX_AUTODETECT, 320, 200, 0, 0);
+  rest(200);
+  set_gfx_mode(GFX_AUTODETECT, XRES, YRES, 0, 0);
+
+  return display;
+}
+
+Window XCreateSimpleWindow(Display *display, Window parent, int x, int y,
+                          unsigned int width, unsigned int height,
+                          unsigned int border_width, unsigned long border,
+                          unsigned long background)
+{
+  video_bitmap = create_video_bitmap(XRES, YRES);
+  clear_to_color(video_bitmap, background);
+
+  display->screens[display->default_screen].video_bitmap = video_bitmap;
+  display->screens[display->default_screen].x = x;
+  display->screens[display->default_screen].y = y;
+  display->screens[display->default_screen].width = XRES;
+  display->screens[display->default_screen].height = YRES;
+
+  set_mouse_sprite(display->mouse_ptr);
+  set_mouse_speed(1, 1);
+  set_mouse_range(display->screens[display->default_screen].x + 1,
+                 display->screens[display->default_screen].y + 1,
+                 display->screens[display->default_screen].x + WIN_XSIZE + 1,
+                 display->screens[display->default_screen].y + WIN_YSIZE + 1);
+
+  show_video_bitmap(video_bitmap);
+
+  return (Window)video_bitmap;
+}
+
+Status XStringListToTextProperty(char **list, int count,
+                                XTextProperty *text_prop_return)
+{
+  char *string;
+
+  if (count >= 1)
+  {
+    string = malloc(strlen(list[0] + 1));
+    strcpy(string, list[0]);
+    text_prop_return->value = (unsigned char *)string;
+    return 1;
+  }
+  else
+    text_prop_return = NULL;
+
+  return 0;
+}
+
+void XFree(void *data)
+{
+  if (data)
+    free(data);
+}
+
+GC XCreateGC(Display *display, Drawable d, unsigned long value_mask,
+            XGCValues *values)
+{
+  XGCValues *gcv;
+  gcv = malloc(sizeof(XGCValues));
+  gcv->foreground = values->foreground;
+  gcv->background = values->background;
+  gcv->graphics_exposures = values->graphics_exposures;
+  gcv->clip_mask = values->clip_mask;
+  gcv->clip_x_origin = values->clip_x_origin;
+  gcv->clip_y_origin = values->clip_y_origin;
+  gcv->value_mask = value_mask;
+  return (GC)gcv;
+}
+
+void XSetClipMask(Display *display, GC gc, Pixmap pixmap)
+{
+  XGCValues *gcv = (XGCValues *)gc;
+
+  gcv->clip_mask = pixmap;
+  gcv->value_mask |= GCClipMask;
+}
+
+void XSetClipOrigin(Display *display, GC gc, int x, int y)
+{
+  XGCValues *gcv = (XGCValues *)gc;
+
+  gcv->clip_x_origin = x;
+  gcv->clip_x_origin = y;
+}
+
+void XFillRectangle(Display *display, Drawable d, GC gc, int x, int y,
+                   unsigned int width, unsigned int height)
+{
+  boolean mouse_off = FALSE;
+
+  if ((BITMAP *)d == video_bitmap)
+  {
+    x += display->screens[display->default_screen].x;
+    y += display->screens[display->default_screen].y;
+    freeze_mouse_flag = TRUE;
+    mouse_off = hide_mouse(display, x, y, width, height);
+  }
+
+  rectfill((BITMAP *)d, x, y, x + width, y + height,
+          ((XGCValues *)gc)->foreground);
+
+  if (mouse_off)
+    unhide_mouse(display);
+
+  freeze_mouse_flag = FALSE;
+}
+
+Pixmap XCreatePixmap(Display *display, Drawable d, unsigned int width,
+                    unsigned int height, unsigned int depth)
+{
+  BITMAP *bitmap = NULL;
+
+  if (gfx_capabilities & GFX_HW_VRAM_BLIT &&
+      width == FXSIZE && height == FYSIZE)
+    bitmap = create_video_bitmap(width, height);
+
+  if (bitmap == NULL)
+    bitmap = create_bitmap(width, height);
+
+  return (Pixmap)bitmap;
+}
+
+void XSync(Display *display, Bool discard_events)
+{
+  wait_for_vsync = TRUE;
+}
+
+inline void XCopyArea(Display *display, Drawable src, Drawable dest, GC gc,
+                     int src_x, int src_y,
+                     unsigned int width, unsigned int height,
+                     int dest_x, int dest_y)
+{
+  boolean mouse_off = FALSE;
+
+  if ((BITMAP *)src == video_bitmap)
+  {
+    src_x += display->screens[display->default_screen].x;
+    src_y += display->screens[display->default_screen].y;
+  }
+
+  if ((BITMAP *)dest == video_bitmap)
+  {
+    dest_x += display->screens[display->default_screen].x;
+    dest_y += display->screens[display->default_screen].y;
+    freeze_mouse_flag = TRUE;
+    mouse_off = hide_mouse(display, dest_x, dest_y, width, height);
+  }
+
+  if (wait_for_vsync)
+  {
+    wait_for_vsync = FALSE;
+    vsync();
+  }
+
+  if (((XGCValues *)gc)->value_mask & GCClipMask)
+    masked_blit((BITMAP *)src, (BITMAP *)dest, src_x, src_y, dest_x, dest_y,
+               width, height);
+  else
+    blit((BITMAP *)src, (BITMAP *)dest, src_x, src_y, dest_x, dest_y,
+        width, height);
+
+  if (mouse_off)
+    unhide_mouse(display);
+
+  freeze_mouse_flag = FALSE;
+}
+
+static BITMAP *Image_to_AllegroBitmap(Image *image)
+{
+  BITMAP *bitmap;
+  byte *src_ptr = image->data;
+  byte pixel_mapping[MAX_COLORS];
+  unsigned int depth = 8;
+  int i, j, x, y;
+
+  /* allocate new allegro bitmap structure */
+  if ((bitmap = create_bitmap_ex(depth, image->width, image->height)) == NULL)
+    return NULL;
+
+  clear(bitmap);
+
+  /* try to use existing colors from the global colormap */
+  for (i=0; i<MAX_COLORS; i++)
+  {
+    int r, g, b;
+
+    if (!image->rgb.color_used[i])
+      continue;
+
+    r = image->rgb.red[i] >> 10;
+    g = image->rgb.green[i] >> 10;
+    b = image->rgb.blue[i] >> 10;
+
+    for (j=0; j<global_colormap_entries_used; j++)
+    {
+      if (r == global_colormap[j].r &&
+         g == global_colormap[j].g &&
+         b == global_colormap[j].b)            /* color found */
+      {
+       pixel_mapping[i] = j;
+       break;
+      }
+    }
+
+    if (j == global_colormap_entries_used)     /* color not found */
+    {
+      if (global_colormap_entries_used < MAX_COLORS)
+       global_colormap_entries_used++;
+
+      global_colormap[j].r = r;
+      global_colormap[j].g = g;
+      global_colormap[j].b = b;
+
+      pixel_mapping[i] = j;
+    }
+  }
+
+  /* copy bitmap data */
+  for (y=0; y<image->height; y++)
+    for (x=0; x<image->width; x++)
+      putpixel(bitmap, x, y, pixel_mapping[*src_ptr++]);
+
+  return bitmap;
+}
+
+static BITMAP *Read_PCX_to_AllegroBitmap(char *filename)
+{
+  BITMAP *bitmap;
+  Image *image;
+
+  /* read the graphic file in PCX format to internal image structure */
+  if ((image = Read_PCX_to_Image(filename)) == NULL)
+    return NULL;
+
+  /* convert internal image structure to allegro bitmap structure */
+  if ((bitmap = Image_to_AllegroBitmap(image)) == NULL)
+    return NULL;
+
+  set_pallete(global_colormap);
+
+  return bitmap;
+}
+
+int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
+                      Pixmap *pixmap, Pixmap *pixmap_mask)
+{
+  BITMAP *bitmap;
+
+  if ((bitmap = Read_PCX_to_AllegroBitmap(filename)) == NULL)
+    return PCX_FileInvalid;
+
+  *pixmap = (Pixmap)bitmap;
+  *pixmap_mask = (Pixmap)bitmap;
+
+  return PCX_Success;
+}
+
+int XpmReadFileToPixmap(Display *display, Drawable d, char *filename,
+                       Pixmap *pixmap_return, Pixmap *shapemask_return,
+                       XpmAttributes *attributes)
+{
+  BITMAP *bitmap;
+
+  if ((bitmap = Read_PCX_to_AllegroBitmap(filename)) == NULL)
+    return XpmOpenFailed;
+
+  *pixmap_return = (Pixmap)bitmap;
+
+  return XpmSuccess;
+}
+
+int XReadBitmapFile(Display *display, Drawable d, char *filename,
+                   unsigned int *width_return, unsigned int *height_return,
+                   Pixmap *bitmap_return,
+                   int *x_hot_return, int *y_hot_return)
+{
+  BITMAP *bitmap;
+
+  if ((bitmap = Read_PCX_to_AllegroBitmap(filename)) == NULL)
+    return BitmapOpenFailed;
+
+  *width_return = bitmap->w;
+  *height_return = bitmap->h;
+  *x_hot_return = -1;
+  *y_hot_return = -1;
+  *bitmap_return = (Pixmap)bitmap;
+
+  return BitmapSuccess;
+}
+
+void XFreePixmap(Display *display, Pixmap pixmap)
+{
+  if (pixmap != DUMMY_MASK &&
+      (is_memory_bitmap((BITMAP *)pixmap) ||
+       is_screen_bitmap((BITMAP *)pixmap)))
+    destroy_bitmap((BITMAP *)pixmap);
+}
+
+void XFreeGC(Display *display, GC gc)
+{
+  XGCValues *gcv;
+
+  gcv = (XGCValues *)gc;
+  if (gcv)
+    free(gcv);
+}
+
+void XCloseDisplay(Display *display)
+{
+  BITMAP *bitmap = video_bitmap;
+
+  if (is_screen_bitmap(bitmap))
+    destroy_bitmap(bitmap);
+
+  if (display->screens)
+    free(display->screens);
+
+  if (display)
+    free(display);
+
+  /* return to text mode (or DOS box on Windows screen) */
+  set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
+}
+
+void XNextEvent(Display *display, XEvent *event_return)
+{
+  while (!pending_events)
+    XPending(display);
+
+  memcpy(event_return, &event_buffer[pending_events], sizeof(XEvent));
+  pending_events--;
+}
+
+static void NewKeyEvent(int key_press_state, KeySym keysym)
+{
+  XKeyEvent *xkey;
+
+  if (pending_events >= MAX_EVENT_BUFFER)
+    return;
+
+  pending_events++;
+  xkey = (XKeyEvent *)&event_buffer[pending_events];
+  xkey->type = key_press_state;
+  xkey->state = (unsigned int)keysym;
+}
+
+#define HANDLE_RAW_KB_ALL_KEYS         0
+#define HANDLE_RAW_KB_MODIFIER_KEYS_ONLY       1
+
+static int modifier_scancode[] =
+{
+  KEY_LSHIFT,
+  KEY_RSHIFT,
+  KEY_LCONTROL,
+  KEY_RCONTROL,
+  KEY_ALT,
+  KEY_ALTGR,
+  KEY_LWIN,
+  KEY_RWIN,
+  KEY_CAPSLOCK,
+  KEY_NUMLOCK,
+  KEY_SCRLOCK,
+  -1
+};
+
+static void HandleKeyboardRaw(int mode)
+{
+  int i;
+
+  for (i=0; i<MAX_SCANCODES; i++)
+  {
+    int scancode, new_state, event_type;
+    char key_pressed;
+
+    if (mode == HANDLE_RAW_KB_MODIFIER_KEYS_ONLY)
+    {
+      if ((scancode = modifier_scancode[i]) == -1)
+       return;
+    }
+    else
+      scancode = i;
+
+    key_pressed = key[scancode];
+    new_state = (key_pressed ? KeyPressMask : KeyReleaseMask);
+    event_type = (key_pressed ? KeyPress : KeyRelease);
+
+    if (key_press_state[i] == new_state)       /* state not changed */
+      continue;
+
+    key_press_state[i] = new_state;
+
+    NewKeyEvent(event_type, ScancodeToKeySym(scancode));
+  }
+}
+
+static void HandleKeyboardEvent()
+{
+  if (keypressed())
+  {
+    int key_info = readkey();
+    int scancode = (key_info >> 8);
+    int ascii = (key_info & 0xff);
+    KeySym keysym = ScancodeToKeySym(scancode);
+
+    if (scancode == KEY_PAD)
+    {
+      /* keys on the numeric keypad return just scancode 'KEY_PAD'
+        for some reason, so we must handle them separately */
+
+      if (ascii >= '0' && ascii <= '9')
+       keysym = XK_KP_0 + (KeySym)(ascii - '0');
+      else if (ascii == '.')
+       keysym = XK_KP_Separator;
+    }
+
+    NewKeyEvent(KeyPress, keysym);
+  }
+  else if (key_shifts & (KB_SHIFT_FLAG | KB_CTRL_FLAG | KB_ALT_FLAG))
+  {
+    /* the allegro function keypressed() does not give us single pressed
+       modifier keys, so we must detect them with the internal global
+       allegro variable 'key_shifts' and then handle them separately */
+
+    HandleKeyboardRaw(HANDLE_RAW_KB_MODIFIER_KEYS_ONLY);
+  }
+}
+
+int XPending(Display *display)
+{
+  XButtonEvent *xbutton;
+  XMotionEvent *xmotion;
+  int i;
+
+  /* keyboard event */
+  if (game_status == PLAYING)
+    HandleKeyboardRaw(HANDLE_RAW_KB_ALL_KEYS);
+  else
+    HandleKeyboardEvent();
+
+  /* mouse motion event */
+  /* generate mouse motion event only if any mouse buttons are pressed */
+  if (mouse_pos != last_mouse_pos && mouse_b)
+  {
+    last_mouse_pos = mouse_pos;
+    pending_events++;
+    xmotion = (XMotionEvent *)&event_buffer[pending_events];
+    xmotion->type = MotionNotify;
+    xmotion->x = mouse_x - display->screens[display->default_screen].x;
+    xmotion->y = mouse_y - display->screens[display->default_screen].y;
+  }
+
+  /* mouse button event */
+  if (mouse_b != last_mouse_b)
+  {
+    for (i=0; i<3; i++)                /* check all three mouse buttons */
+    {
+      int bitmask = (1 << i);
+
+      if ((last_mouse_b & bitmask) != (mouse_b & bitmask))
+      {
+       int mapping[3] = { 1, 3, 2 };
+
+       pending_events++;
+        xbutton = (XButtonEvent *)&event_buffer[pending_events];
+        xbutton->type = (mouse_b & bitmask ? ButtonPress : ButtonRelease);
+        xbutton->button = mapping[i];
+       xbutton->x = mouse_x - display->screens[display->default_screen].x;
+       xbutton->y = mouse_y - display->screens[display->default_screen].y;
+      }
+    }
+    last_mouse_b = mouse_b;
+  }
+
+  return pending_events;
+}
+
+KeySym XLookupKeysym(XKeyEvent *key_event, int index)
+{
+  return key_event->state;
+}
+
+void NetworkServer(int port, int serveronly)
+{
+  Error(ERR_WARN, "networking not supported in DOS version");
+}
+
+#endif /* MSDOS */
diff --git a/src/msdos.h b/src/msdos.h
new file mode 100644 (file)
index 0000000..c344414
--- /dev/null
@@ -0,0 +1,709 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  ©1995 Artsoft Development                               *
+*        Holger Schemel                                    *
+*        33659 Bielefeld-Senne                             *
+*        Telefon: (0521) 493245                            *
+*        eMail: aeglos@valinor.owl.de                      *
+*               aeglos@uni-paderborn.de                    *
+*               q99492@pbhrzx.uni-paderborn.de             *
+*----------------------------------------------------------*
+*  msdos.h                                                 *
+***********************************************************/
+
+#include <allegro.h>
+#include <time.h>
+
+/* allegro defines TRUE as -1 */
+#ifdef TRUE
+#undef TRUE
+#undef FALSE
+#endif
+
+#define TRUE   1
+#define FALSE  0
+
+#define XRES   800
+#define YRES   600
+
+/* additional Allegro keyboard mapping */
+
+/* The following are all undefined in Allegro */
+#define NEW_KEY_BACKSLASH      86
+#define NEW_KEY_1_PAD          101
+#define NEW_KEY_2_PAD          102
+#define NEW_KEY_3_PAD          103
+#define NEW_KEY_4_PAD          104
+#define NEW_KEY_5_PAD          105
+#define NEW_KEY_6_PAD          106
+#define NEW_KEY_7_PAD          107
+#define NEW_KEY_8_PAD          108
+#define NEW_KEY_9_PAD          109
+#define NEW_KEY_0_PAD          110
+#define NEW_KEY_STOP_PAD       111
+#define NEW_KEY_EQUALS_PAD     112
+#define NEW_KEY_SLASH_PAD      113
+#define NEW_KEY_ASTERISK_PAD   114
+#define NEW_KEY_ENTER_PAD      115
+
+/* X11 keyboard mapping (from 'keysymdef.h') */
+
+#define XK_VoidSymbol          0xFFFFFF        /* void symbol */
+
+/*
+ * TTY Functions, cleverly chosen to map to ascii, for convenience of
+ * programming, but could have been arbitrary (at the cost of lookup
+ * tables in client code.
+ */
+
+#define XK_BackSpace           0xFF08  /* back space, back char */
+#define XK_Tab                 0xFF09
+#define XK_Linefeed            0xFF0A  /* Linefeed, LF */
+#define XK_Clear               0xFF0B
+#define XK_Return              0xFF0D  /* Return, enter */
+#define XK_Pause               0xFF13  /* Pause, hold */
+#define XK_Scroll_Lock         0xFF14
+#define XK_Sys_Req             0xFF15
+#define XK_Escape              0xFF1B
+#define XK_Delete              0xFFFF  /* Delete, rubout */
+
+/* International & multi-key character composition */
+
+#define XK_Multi_key           0xFF20  /* Multi-key character compose */
+#define XK_SingleCandidate     0xFF3C
+#define XK_MultipleCandidate   0xFF3D
+#define XK_PreviousCandidate   0xFF3E
+
+/* Cursor control & motion */
+
+#define XK_Home                        0xFF50
+#define XK_Left                        0xFF51  /* Move left, left arrow */
+#define XK_Up                  0xFF52  /* Move up, up arrow */
+#define XK_Right               0xFF53  /* Move right, right arrow */
+#define XK_Down                        0xFF54  /* Move down, down arrow */
+#define XK_Prior               0xFF55  /* Prior, previous */
+#define XK_Page_Up             0xFF55
+#define XK_Next                        0xFF56  /* Next */
+#define XK_Page_Down           0xFF56
+#define XK_End                 0xFF57  /* EOL */
+#define XK_Begin               0xFF58  /* BOL */
+
+/* Misc Functions */
+
+#define XK_Select              0xFF60  /* Select, mark */
+#define XK_Print               0xFF61
+#define XK_Execute             0xFF62  /* Execute, run, do */
+#define XK_Insert              0xFF63  /* Insert, insert here */
+#define XK_Undo                        0xFF65  /* Undo, oops */
+#define XK_Redo                        0xFF66  /* redo, again */
+#define XK_Menu                        0xFF67
+#define XK_Find                        0xFF68  /* Find, search */
+#define XK_Cancel              0xFF69  /* Cancel, stop, abort, exit */
+#define XK_Help                        0xFF6A  /* Help */
+#define XK_Break               0xFF6B
+#define XK_Mode_switch         0xFF7E  /* Character set switch */
+#define XK_script_switch        0xFF7E  /* Alias for mode_switch */
+#define XK_Num_Lock            0xFF7F
+
+/* Keypad Functions, keypad numbers cleverly chosen to map to ascii */
+
+#define XK_KP_Space            0xFF80  /* space */
+#define XK_KP_Tab              0xFF89
+#define XK_KP_Enter            0xFF8D  /* enter */
+#define XK_KP_F1               0xFF91  /* PF1, KP_A, ... */
+#define XK_KP_F2               0xFF92
+#define XK_KP_F3               0xFF93
+#define XK_KP_F4               0xFF94
+#define XK_KP_Home             0xFF95
+#define XK_KP_Left             0xFF96
+#define XK_KP_Up               0xFF97
+#define XK_KP_Right            0xFF98
+#define XK_KP_Down             0xFF99
+#define XK_KP_Prior            0xFF9A
+#define XK_KP_Page_Up          0xFF9A
+#define XK_KP_Next             0xFF9B
+#define XK_KP_Page_Down                0xFF9B
+#define XK_KP_End              0xFF9C
+#define XK_KP_Begin            0xFF9D
+#define XK_KP_Insert           0xFF9E
+#define XK_KP_Delete           0xFF9F
+#define XK_KP_Equal            0xFFBD  /* equals */
+#define XK_KP_Multiply         0xFFAA
+#define XK_KP_Add              0xFFAB
+#define XK_KP_Separator                0xFFAC  /* separator, often comma */
+#define XK_KP_Subtract         0xFFAD
+#define XK_KP_Decimal          0xFFAE
+#define XK_KP_Divide           0xFFAF
+
+#define XK_KP_0                        0xFFB0
+#define XK_KP_1                        0xFFB1
+#define XK_KP_2                        0xFFB2
+#define XK_KP_3                        0xFFB3
+#define XK_KP_4                        0xFFB4
+#define XK_KP_5                        0xFFB5
+#define XK_KP_6                        0xFFB6
+#define XK_KP_7                        0xFFB7
+#define XK_KP_8                        0xFFB8
+#define XK_KP_9                        0xFFB9
+
+/*
+ * Auxilliary Functions; note the duplicate definitions for left and right
+ * function keys;  Sun keyboards and a few other manufactures have such
+ * function key groups on the left and/or right sides of the keyboard.
+ * We've not found a keyboard with more than 35 function keys total.
+ */
+
+#define XK_F1                  0xFFBE
+#define XK_F2                  0xFFBF
+#define XK_F3                  0xFFC0
+#define XK_F4                  0xFFC1
+#define XK_F5                  0xFFC2
+#define XK_F6                  0xFFC3
+#define XK_F7                  0xFFC4
+#define XK_F8                  0xFFC5
+#define XK_F9                  0xFFC6
+#define XK_F10                 0xFFC7
+#define XK_F11                 0xFFC8
+#define XK_L1                  0xFFC8
+#define XK_F12                 0xFFC9
+#define XK_L2                  0xFFC9
+#define XK_F13                 0xFFCA
+#define XK_L3                  0xFFCA
+#define XK_F14                 0xFFCB
+#define XK_L4                  0xFFCB
+#define XK_F15                 0xFFCC
+#define XK_L5                  0xFFCC
+#define XK_F16                 0xFFCD
+#define XK_L6                  0xFFCD
+#define XK_F17                 0xFFCE
+#define XK_L7                  0xFFCE
+#define XK_F18                 0xFFCF
+#define XK_L8                  0xFFCF
+#define XK_F19                 0xFFD0
+#define XK_L9                  0xFFD0
+#define XK_F20                 0xFFD1
+#define XK_L10                 0xFFD1
+#define XK_F21                 0xFFD2
+#define XK_R1                  0xFFD2
+#define XK_F22                 0xFFD3
+#define XK_R2                  0xFFD3
+#define XK_F23                 0xFFD4
+#define XK_R3                  0xFFD4
+#define XK_F24                 0xFFD5
+#define XK_R4                  0xFFD5
+#define XK_F25                 0xFFD6
+#define XK_R5                  0xFFD6
+#define XK_F26                 0xFFD7
+#define XK_R6                  0xFFD7
+#define XK_F27                 0xFFD8
+#define XK_R7                  0xFFD8
+#define XK_F28                 0xFFD9
+#define XK_R8                  0xFFD9
+#define XK_F29                 0xFFDA
+#define XK_R9                  0xFFDA
+#define XK_F30                 0xFFDB
+#define XK_R10                 0xFFDB
+#define XK_F31                 0xFFDC
+#define XK_R11                 0xFFDC
+#define XK_F32                 0xFFDD
+#define XK_R12                 0xFFDD
+#define XK_F33                 0xFFDE
+#define XK_R13                 0xFFDE
+#define XK_F34                 0xFFDF
+#define XK_R14                 0xFFDF
+#define XK_F35                 0xFFE0
+#define XK_R15                 0xFFE0
+
+/* Modifiers */
+
+#define XK_Shift_L             0xFFE1  /* Left shift */
+#define XK_Shift_R             0xFFE2  /* Right shift */
+#define XK_Control_L           0xFFE3  /* Left control */
+#define XK_Control_R           0xFFE4  /* Right control */
+#define XK_Caps_Lock           0xFFE5  /* Caps lock */
+#define XK_Shift_Lock          0xFFE6  /* Shift lock */
+
+#define XK_Meta_L              0xFFE7  /* Left meta */
+#define XK_Meta_R              0xFFE8  /* Right meta */
+#define XK_Alt_L               0xFFE9  /* Left alt */
+#define XK_Alt_R               0xFFEA  /* Right alt */
+#define XK_Super_L             0xFFEB  /* Left super */
+#define XK_Super_R             0xFFEC  /* Right super */
+#define XK_Hyper_L             0xFFED  /* Left hyper */
+#define XK_Hyper_R             0xFFEE  /* Right hyper */
+
+/*
+ *  Latin 1
+ *  Byte 3 = 0
+ */
+
+#define XK_space               0x020
+#define XK_exclam              0x021
+#define XK_quotedbl            0x022
+#define XK_numbersign          0x023
+#define XK_dollar              0x024
+#define XK_percent             0x025
+#define XK_ampersand           0x026
+#define XK_apostrophe          0x027
+#define XK_quoteright          0x027   /* deprecated */
+#define XK_parenleft           0x028
+#define XK_parenright          0x029
+#define XK_asterisk            0x02a
+#define XK_plus                0x02b
+#define XK_comma               0x02c
+#define XK_minus               0x02d
+#define XK_period              0x02e
+#define XK_slash               0x02f
+#define XK_0                   0x030
+#define XK_1                   0x031
+#define XK_2                   0x032
+#define XK_3                   0x033
+#define XK_4                   0x034
+#define XK_5                   0x035
+#define XK_6                   0x036
+#define XK_7                   0x037
+#define XK_8                   0x038
+#define XK_9                   0x039
+#define XK_colon               0x03a
+#define XK_semicolon           0x03b
+#define XK_less                0x03c
+#define XK_equal               0x03d
+#define XK_greater             0x03e
+#define XK_question            0x03f
+#define XK_at                  0x040
+#define XK_A                   0x041
+#define XK_B                   0x042
+#define XK_C                   0x043
+#define XK_D                   0x044
+#define XK_E                   0x045
+#define XK_F                   0x046
+#define XK_G                   0x047
+#define XK_H                   0x048
+#define XK_I                   0x049
+#define XK_J                   0x04a
+#define XK_K                   0x04b
+#define XK_L                   0x04c
+#define XK_M                   0x04d
+#define XK_N                   0x04e
+#define XK_O                   0x04f
+#define XK_P                   0x050
+#define XK_Q                   0x051
+#define XK_R                   0x052
+#define XK_S                   0x053
+#define XK_T                   0x054
+#define XK_U                   0x055
+#define XK_V                   0x056
+#define XK_W                   0x057
+#define XK_X                   0x058
+#define XK_Y                   0x059
+#define XK_Z                   0x05a
+#define XK_bracketleft         0x05b
+#define XK_backslash           0x05c
+#define XK_bracketright        0x05d
+#define XK_asciicircum         0x05e
+#define XK_underscore          0x05f
+#define XK_grave               0x060
+#define XK_quoteleft           0x060   /* deprecated */
+#define XK_a                   0x061
+#define XK_b                   0x062
+#define XK_c                   0x063
+#define XK_d                   0x064
+#define XK_e                   0x065
+#define XK_f                   0x066
+#define XK_g                   0x067
+#define XK_h                   0x068
+#define XK_i                   0x069
+#define XK_j                   0x06a
+#define XK_k                   0x06b
+#define XK_l                   0x06c
+#define XK_m                   0x06d
+#define XK_n                   0x06e
+#define XK_o                   0x06f
+#define XK_p                   0x070
+#define XK_q                   0x071
+#define XK_r                   0x072
+#define XK_s                   0x073
+#define XK_t                   0x074
+#define XK_u                   0x075
+#define XK_v                   0x076
+#define XK_w                   0x077
+#define XK_x                   0x078
+#define XK_y                   0x079
+#define XK_z                   0x07a
+#define XK_braceleft           0x07b
+#define XK_bar                 0x07c
+#define XK_braceright          0x07d
+#define XK_asciitilde          0x07e
+
+#define XK_nobreakspace        0x0a0
+#define XK_exclamdown          0x0a1
+#define XK_cent                       0x0a2
+#define XK_sterling            0x0a3
+#define XK_currency            0x0a4
+#define XK_yen                 0x0a5
+#define XK_brokenbar           0x0a6
+#define XK_section             0x0a7
+#define XK_diaeresis           0x0a8
+#define XK_copyright           0x0a9
+#define XK_ordfeminine         0x0aa
+#define XK_guillemotleft       0x0ab   /* left angle quotation mark */
+#define XK_notsign             0x0ac
+#define XK_hyphen              0x0ad
+#define XK_registered          0x0ae
+#define XK_macron              0x0af
+#define XK_degree              0x0b0
+#define XK_plusminus           0x0b1
+#define XK_twosuperior         0x0b2
+#define XK_threesuperior       0x0b3
+#define XK_acute               0x0b4
+#define XK_mu                  0x0b5
+#define XK_paragraph           0x0b6
+#define XK_periodcentered      0x0b7
+#define XK_cedilla             0x0b8
+#define XK_onesuperior         0x0b9
+#define XK_masculine           0x0ba
+#define XK_guillemotright      0x0bb   /* right angle quotation mark */
+#define XK_onequarter          0x0bc
+#define XK_onehalf             0x0bd
+#define XK_threequarters       0x0be
+#define XK_questiondown        0x0bf
+#define XK_Agrave              0x0c0
+#define XK_Aacute              0x0c1
+#define XK_Acircumflex         0x0c2
+#define XK_Atilde              0x0c3
+#define XK_Adiaeresis          0x0c4
+#define XK_Aring               0x0c5
+#define XK_AE                  0x0c6
+#define XK_Ccedilla            0x0c7
+#define XK_Egrave              0x0c8
+#define XK_Eacute              0x0c9
+#define XK_Ecircumflex         0x0ca
+#define XK_Ediaeresis          0x0cb
+#define XK_Igrave              0x0cc
+#define XK_Iacute              0x0cd
+#define XK_Icircumflex         0x0ce
+#define XK_Idiaeresis          0x0cf
+#define XK_ETH                 0x0d0
+#define XK_Eth                 0x0d0   /* deprecated */
+#define XK_Ntilde              0x0d1
+#define XK_Ograve              0x0d2
+#define XK_Oacute              0x0d3
+#define XK_Ocircumflex         0x0d4
+#define XK_Otilde              0x0d5
+#define XK_Odiaeresis          0x0d6
+#define XK_multiply            0x0d7
+#define XK_Ooblique            0x0d8
+#define XK_Ugrave              0x0d9
+#define XK_Uacute              0x0da
+#define XK_Ucircumflex         0x0db
+#define XK_Udiaeresis          0x0dc
+#define XK_Yacute              0x0dd
+#define XK_THORN               0x0de
+#define XK_Thorn               0x0de   /* deprecated */
+#define XK_ssharp              0x0df
+#define XK_agrave              0x0e0
+#define XK_aacute              0x0e1
+#define XK_acircumflex         0x0e2
+#define XK_atilde              0x0e3
+#define XK_adiaeresis          0x0e4
+#define XK_aring               0x0e5
+#define XK_ae                  0x0e6
+#define XK_ccedilla            0x0e7
+#define XK_egrave              0x0e8
+#define XK_eacute              0x0e9
+#define XK_ecircumflex         0x0ea
+#define XK_ediaeresis          0x0eb
+#define XK_igrave              0x0ec
+#define XK_iacute              0x0ed
+#define XK_icircumflex         0x0ee
+#define XK_idiaeresis          0x0ef
+#define XK_eth                 0x0f0
+#define XK_ntilde              0x0f1
+#define XK_ograve              0x0f2
+#define XK_oacute              0x0f3
+#define XK_ocircumflex         0x0f4
+#define XK_otilde              0x0f5
+#define XK_odiaeresis          0x0f6
+#define XK_division            0x0f7
+#define XK_oslash              0x0f8
+#define XK_ugrave              0x0f9
+#define XK_uacute              0x0fa
+#define XK_ucircumflex         0x0fb
+#define XK_udiaeresis          0x0fc
+#define XK_yacute              0x0fd
+#define XK_thorn               0x0fe
+#define XK_ydiaeresis          0x0ff
+
+/* end of X11 keyboard mapping */
+
+#define MOUSE_FILENAME         "mouse.pcx"
+#define JOYSTICK_FILENAME      "joystick.cnf"
+
+#define screen myscreen
+
+#define XFlush(a)
+#define XGetImage(a,b,c,d,e,f,g,h)             ((XImage *) NULL)
+#define XAutoRepeatOn(a)
+#define XAutoRepeatOff(a)
+#define XDisplayName(a)                                ((char *) NULL)
+#define XFreeColors(a,b,c,d,e)
+#define XpmFreeAttributes(a)
+#define XSelectInput(a,b,c)
+#define XDefaultDepth(a,b)                     (8)
+#define XSetWMProperties(a,b,c,d,e,f,g,h,i)
+
+#define MAX_EVENT_BUFFER       256
+#define MAX_SCANCODES          128
+
+#define True                   1
+#define False                  0
+#define None                   0L
+
+#define DUMMY_FILE             ((void *) -1)
+#define DUMMY_MASK             (-1)
+
+#define KeyPressMask           (1L << 0)  
+#define KeyReleaseMask         (1L << 1)  
+#define ButtonPressMask                (1L << 2)  
+#define ButtonReleaseMask      (1L << 3)  
+#define ButtonMotionMask       (1L << 13) 
+#define ExposureMask           (1L << 15) 
+#define StructureNotifyMask    (1L << 17) 
+#define FocusChangeMask                (1L << 21) 
+
+#define KeyPress               2
+#define KeyRelease             3
+#define ButtonPress            4
+#define ButtonRelease          5
+#define MotionNotify           6
+#define FocusIn                        9
+#define FocusOut               10
+#define Expose                 12
+#define UnmapNotify            18
+#define MapNotify              19
+#define ClientMessage          33
+
+#define GCForeground            (1L << 2)
+#define GCBackground            (1L << 3)
+#define GCGraphicsExposures     (1L << 16)
+#define GCClipMask             (1L << 19)
+
+#define NormalState    1       /* most applications want to start this way */
+#define InputHint              (1L << 0)
+#define StateHint              (1L << 1)
+#define IconPixmapHint         (1L << 2)
+#define IconMaskHint           (1L << 5)
+#define PSize                  (1L << 3) /* program specified size */
+#define PMinSize               (1L << 4) /* program specified minimum size */
+#define PMaxSize               (1L << 5) /* program specified maximum size */
+
+#define XpmSuccess              0
+#define XpmOpenFailed          -1
+#define XpmFileInvalid         -2
+#define XpmNoMemory            -3
+#define XpmColorFailed         -4
+
+#define XpmCloseness           (1L << 12)
+
+#define PCX_Success             0
+#define PCX_OpenFailed         -1
+#define PCX_ReadFailed         -2
+#define        PCX_FileInvalid         -3
+#define PCX_NoMemory           -4
+#define PCX_ColorFailed                -5
+
+#define BitmapSuccess          0
+#define BitmapOpenFailed       1
+#define BitmapFileInvalid      2
+#define BitmapNoMemory         3
+
+#define ZPixmap                        2       /* depth == drawable depth */
+
+#define DefaultScreen(dpy)       (((_XPrivDisplay)dpy)->default_screen)
+#define DefaultColormap(dpy, scr) (ScreenOfDisplay(dpy,scr)->cmap)
+#define ScreenOfDisplay(dpy, scr) (&((_XPrivDisplay)dpy)->screens[scr])
+#define BlackPixel(dpy, scr)     (ScreenOfDisplay(dpy,scr)->black_pixel)
+#define WhitePixel(dpy, scr)     (ScreenOfDisplay(dpy,scr)->white_pixel)
+#define RootWindow(dpy, scr)     (ScreenOfDisplay(dpy,scr)->root)
+#define AllPlanes                ((unsigned long)~0L)
+
+#define DefaultVisual(dpy, scr)          (NULL)
+#define DefaultDepth(dpy, scr)   (NULL)
+#define XDisplayWidth(dpy, scr)          (XRES)
+#define XDisplayHeight(dpy, scr)  (YRES)
+
+#define XGetPixel(ximage, x, y) \
+        ((*((ximage)->f.get_pixel))((ximage), (x), (y)))
+
+typedef unsigned long Pixel;   /* Index into colormap */
+typedef unsigned long XID;
+typedef XID Window;
+typedef XID Drawable;
+typedef XID Pixmap;
+typedef XID Colormap;
+typedef XID KeySym;
+typedef XID GContext;
+typedef struct _XDisplay Display;
+typedef long Visual;
+typedef long XVisualInfo;
+typedef long Atom;
+typedef int Status;
+typedef int Bool;
+
+typedef struct _XGC
+{
+  GContext gid;                        /* protocol ID for graphics context */
+} *GC;
+
+typedef struct
+{
+  Colormap cmap;               /* default color map */
+  Window root;                 /* root window id */
+  unsigned long white_pixel;   /* white pixel value */
+  unsigned long black_pixel;   /* black pixel value */
+  int x;
+  int y;
+  unsigned int width;
+  unsigned int height;
+  BITMAP *video_bitmap;
+} Screen;
+
+typedef struct _XDisplay
+{
+  int default_screen;          /* default screen for operations */
+  Screen *screens;             /* pointer to list of screens */
+  BITMAP *mouse_ptr;
+} *_XPrivDisplay;
+
+typedef struct _XImage
+{
+  struct funcs
+  {
+    unsigned long (*get_pixel) (struct _XImage *, int, int);
+  } f;
+} XImage;
+
+typedef struct
+{
+  long flags;          /* marks which fields in this structure are defined */
+  int width, height;   /* should set so old wm's don't mess up */
+  int min_width, min_height;
+  int max_width, max_height;
+} XSizeHints;
+
+typedef struct
+{
+  long flags;          /* marks which fields in this structure are defined */
+  Bool input;          /* does this application rely on the window manager to
+                          get keyboard input? */
+  int initial_state;   /* see below */
+  Pixmap icon_pixmap;  /* pixmap to be used as icon */
+  Pixmap icon_mask;    /* icon mask bitmap */
+} XWMHints;
+
+typedef struct
+{
+  char *res_name;
+  char *res_class;
+} XClassHint;
+
+typedef struct
+{
+  unsigned char *value;                /* same as Property routines */
+} XTextProperty;
+
+typedef struct
+{
+  unsigned long foreground;    /* foreground pixel */
+  unsigned long background;    /* background pixel */
+  Bool graphics_exposures;     /* boolean, should exposures be generated */
+  Pixmap clip_mask;            /* bitmap clipping; other calls for rects */
+  int clip_x_origin;           /* x origin for clipping */
+  int clip_y_origin;           /* y origin for clipping */
+  unsigned long value_mask;
+} XGCValues;
+
+typedef struct
+{
+  unsigned long valuemask;     /* specifies which attributes are */
+  unsigned int closeness;      /* allowable RGB deviation */
+  Pixel *pixels;               /* list of used color pixels */
+  unsigned int npixels;                /* number of used pixels */
+} XpmAttributes;
+
+typedef struct
+{
+  int type;
+  int x, y;
+  int width, height;
+} XExposeEvent;
+
+typedef struct
+{
+  int type;                    /* of event */
+  int x, y;                    /* pointer x, y coordinates in event window */
+  unsigned int button;         /* detail */
+} XButtonEvent;
+
+typedef struct
+{
+  int type;
+  int x, y;                    /* pointer x, y coordinates in event window */
+} XMotionEvent;
+
+typedef struct
+{
+  int type;                    /* of event */
+  unsigned int state;          /* key or button mask */
+} XKeyEvent;
+
+typedef struct
+{
+  int type;                    /* FocusIn or FocusOut */
+} XFocusChangeEvent;
+
+typedef struct
+{
+  int type;                    /* ClientMessage */
+} XClientMessageEvent;
+
+typedef union _XEvent
+{
+  int type;                    /* must not be changed; first element */
+  XExposeEvent xexpose;
+  XButtonEvent xbutton;
+  XMotionEvent xmotion;
+  XKeyEvent xkey;
+} XEvent;
+
+unsigned char get_ascii(KeySym);
+void XMapWindow(Display *, Window);
+Display *XOpenDisplay(char *);
+Window XCreateSimpleWindow(Display *, Window, int, int,
+                          unsigned int, unsigned int, unsigned int,
+                          unsigned long, unsigned long);
+Status XStringListToTextProperty(char **, int, XTextProperty *);
+void XFree(void *);
+GC XCreateGC(Display *, Drawable, unsigned long, XGCValues *);
+void XSetClipMask(Display *, GC, Pixmap);
+void XSetClipOrigin(Display *, GC, int, int);
+void XFillRectangle(Display *, Drawable, GC, int, int,
+                   unsigned int, unsigned int);
+Pixmap XCreatePixmap(Display *, Drawable, unsigned int, unsigned int,
+                    unsigned int);
+void XSync(Display *, Bool);
+inline void XCopyArea(Display *, Drawable, Drawable, GC, int, int,
+                     unsigned int, unsigned int, int, int);
+int Read_PCX_to_Pixmap(Display *, Window, GC, char *, Pixmap *, Pixmap *);
+int XpmReadFileToPixmap(Display *, Drawable, char *, Pixmap *, Pixmap *,
+                       XpmAttributes *);
+int XReadBitmapFile(Display *, Drawable, char *,
+                   unsigned int *, unsigned int *, Pixmap *, int *, int *);
+void XFreePixmap(Display *, Pixmap);
+void XFreeGC(Display *, GC);
+void XCloseDisplay(Display *);
+void XNextEvent(Display *, XEvent *);
+int XPending(Display *);
+KeySym XLookupKeysym(XKeyEvent *, int);
+void NetworkServer(int, int);
diff --git a/src/netserv.c b/src/netserv.c
new file mode 100644 (file)
index 0000000..a9ffa6a
--- /dev/null
@@ -0,0 +1,665 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
+*----------------------------------------------------------*
+*  network.c                                               *
+***********************************************************/
+
+#ifndef MSDOS
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "netserv.h"
+#include "misc.h"
+
+static int clients = 0;
+static int onceonly = 0;
+
+struct NetworkServerPlayerInfo
+{
+  int fd;
+  char player_name[16];
+  unsigned char number;
+  struct NetworkServerPlayerInfo *next;
+  char active;
+  char introduced;
+  unsigned char readbuffer[MAX_BUFFER_SIZE];
+  unsigned char writbuffer[MAX_BUFFER_SIZE];
+  int nread, nwrite;
+  byte action;
+  boolean action_received;
+};
+
+static struct NetworkServerPlayerInfo *first_player = NULL;
+
+#define NEXT(player) ((player)->next ? (player)->next : first_player)
+
+static struct sockaddr_in saddr;
+static int lfd;
+static unsigned char realbuffer[512], *buffer = realbuffer + 4;
+
+static int interrupt;
+static int tcp = -1;
+
+static unsigned long ServerFrameCounter = 0;
+
+static fd_set fds;
+
+static void addtobuffer(struct NetworkServerPlayerInfo *player,
+                       unsigned char *b, int len)
+{
+  if (player->nwrite + len >= MAX_BUFFER_SIZE)
+    Error(ERR_EXIT_NETWORK_SERVER,
+         "internal error: network send buffer overflow");
+
+  memcpy(player->writbuffer + player->nwrite, b, len);
+  player->nwrite += len;
+}
+
+static void flushuser(struct NetworkServerPlayerInfo *player)
+{
+  if (player->nwrite)
+  {
+    write(player->fd, player->writbuffer, player->nwrite);
+    player->nwrite = 0;
+  }
+}
+
+static void broadcast(struct NetworkServerPlayerInfo *except,
+                     int len, int activeonly)
+{
+  struct NetworkServerPlayerInfo *player;
+
+  realbuffer[0] = realbuffer[1] = realbuffer[2] = 0;
+  realbuffer[3] = (unsigned char)len;
+  for (player=first_player; player; player=player->next)
+    if (player != except && (player->active || !activeonly) && player->introduced)
+      addtobuffer(player, realbuffer, 4 + len);
+}
+
+static void sendtoone(struct NetworkServerPlayerInfo *to, int len)
+{
+  realbuffer[0] = realbuffer[1] = realbuffer[2] = 0;
+  realbuffer[3] = (unsigned char)len;
+  addtobuffer(to, realbuffer, 4 + len);
+}
+
+static void RemovePlayer(struct NetworkServerPlayerInfo *player)
+{
+  struct NetworkServerPlayerInfo *v;
+  
+  if (options.verbose)
+    Error(ERR_NETWORK_SERVER, "dropping client %d (%s)",
+         player->number, player->player_name);
+
+  if (player == first_player)
+    first_player = player->next;
+  else
+  {
+    for (v=first_player; v; v=v->next)
+    {
+      if (v->next && v->next == player)
+      {
+       v->next = player->next;
+       break;
+      }
+    }
+  }
+  close(player->fd);
+
+  if (player->introduced)
+  {
+    buffer[0] = player->number;
+    buffer[1] = OP_PLAYER_DISCONNECTED;
+    broadcast(player, 2, 0);
+  }
+
+  free(player);
+  clients--;
+
+  if (onceonly && clients == 0)
+  {
+    if (options.verbose)
+    {
+      Error(ERR_NETWORK_SERVER, "no clients left");
+      Error(ERR_NETWORK_SERVER, "aborting");
+    }
+    exit(0);
+  }
+}
+
+static void AddPlayer(int fd)
+{
+  struct NetworkServerPlayerInfo *player, *v;
+  unsigned char nxn;
+
+  player = checked_malloc(sizeof (struct NetworkServerPlayerInfo));
+
+  player->fd = fd;
+  player->player_name[0] = 0;
+  player->next = first_player;
+  player->active = 0;
+  player->nread = 0;
+  player->nwrite = 0;
+  player->introduced = 0;
+  player->action = 0;
+  player->action_received = FALSE;
+
+  first_player = player;
+
+  nxn = 1;
+
+ again:
+  v = player->next;
+  while(v)
+  {
+    if (v->number == nxn)
+    {
+      nxn++;
+      goto again;
+    }
+    v = v->next;
+  }
+
+  player->number = nxn;
+  if (options.verbose)
+    Error(ERR_NETWORK_SERVER, "client %d connecting from %s",
+         nxn, inet_ntoa(saddr.sin_addr));
+  clients++;
+
+  buffer[0] = 0;
+  buffer[1] = OP_YOUR_NUMBER;
+  buffer[2] = player->number;
+  sendtoone(player, 3);
+}
+
+static void Handle_OP_PROTOCOL_VERSION(struct NetworkServerPlayerInfo *player,
+                                      unsigned int len)
+{
+  if (len != 5 ||
+      buffer[2] != PROTOCOL_VERSION_1 ||
+      buffer[3] != PROTOCOL_VERSION_2)
+  {
+    if (options.verbose)
+      Error(ERR_NETWORK_SERVER,
+           "client %d (%s) has wrong protocol version %d.%d.%d",
+           player->number, player->player_name, buffer[2], buffer[3], buffer[4]);
+
+    buffer[0] = 0;
+    buffer[1] = OP_BAD_PROTOCOL_VERSION;
+    buffer[2] = PROTOCOL_VERSION_1;
+    buffer[3] = PROTOCOL_VERSION_2;
+    buffer[4] = PROTOCOL_VERSION_3;
+    sendtoone(player, 5);
+    flushuser(player);
+
+    RemovePlayer(player);
+    interrupt = 1;
+  }
+  else
+  {
+    if (options.verbose)
+      Error(ERR_NETWORK_SERVER,
+           "client %d (%s) uses protocol version %d.%d.%d",
+           player->number, player->player_name, buffer[2], buffer[3], buffer[4]);
+  }
+}
+
+static void Handle_OP_NUMBER_WANTED(struct NetworkServerPlayerInfo *player)
+{
+  struct NetworkServerPlayerInfo *v;
+  int client_nr = player->number;
+  int nr_wanted = buffer[2];
+  int nr_is_free = 1;
+
+  if (options.verbose)
+      Error(ERR_NETWORK_SERVER, "client %d (%s) wants to switch to # %d",
+           player->number, player->player_name, nr_wanted);
+
+  for (v=first_player; v; v=v->next)
+  {
+    if (v->number == nr_wanted)
+    {
+      nr_is_free = 0;
+      break;
+    }
+  }
+
+  if (options.verbose)
+  {
+    if (nr_is_free)
+      Error(ERR_NETWORK_SERVER, "client %d (%s) switches to # %d",
+           player->number, player->player_name, nr_wanted);
+    else if (player->number == nr_wanted)
+      Error(ERR_NETWORK_SERVER, "client %d (%s) still has # %d",
+           player->number, player->player_name, nr_wanted);
+    else
+      Error(ERR_NETWORK_SERVER,
+           "client %d (%s) cannot switch (client %d still exists)",
+           player->number, player->player_name, nr_wanted);
+  }
+
+  if (nr_is_free)
+    player->number = nr_wanted;
+
+  buffer[0] = client_nr;
+  buffer[1] = OP_NUMBER_WANTED;
+  buffer[2] = nr_wanted;
+  buffer[3] = player->number;
+
+  /*
+  sendtoone(player, 4);
+  */
+
+  broadcast(NULL, 4, 0);
+}
+
+static void Handle_OP_PLAYER_NAME(struct NetworkServerPlayerInfo *player,
+                                 unsigned int len)
+{
+  struct NetworkServerPlayerInfo *v;
+  int i;
+
+  if (len>16)
+    len=16;
+  memcpy(player->player_name, &buffer[2], len-2);
+  player->player_name[len-2] = 0;
+  for (i=0; i<len-2; i++)
+  {
+    if (player->player_name[i] < ' ' || 
+       ((unsigned char)(player->player_name[i]) > 0x7e &&
+        (unsigned char)(player->player_name[i]) <= 0xa0))
+    {
+      player->player_name[i] = 0;
+      break;
+    }
+  }
+
+  if (!player->introduced)
+  {
+    buffer[0] = player->number;
+    buffer[1] = OP_PLAYER_CONNECTED;
+    broadcast(player, 2, 0);
+  }
+             
+  if (options.verbose)
+    Error(ERR_NETWORK_SERVER, "client %d calls itself \"%s\"",
+         player->number, player->player_name);
+  buffer[1] = OP_PLAYER_NAME;
+  broadcast(player, len, 0);
+
+  if (!player->introduced)
+  {
+    for (v=first_player; v; v=v->next)
+    {
+      if (v != player && v->introduced)
+      {
+       buffer[0] = v->number;
+       buffer[1] = OP_PLAYER_CONNECTED;
+       sendtoone(player, 2);
+       buffer[1] = OP_PLAYER_NAME;
+       memcpy(&buffer[2], v->player_name, 14);
+       sendtoone(player, 2+strlen(v->player_name));
+      }
+    }
+  }
+
+  player->introduced = 1;
+}
+
+static void Handle_OP_START_PLAYING(struct NetworkServerPlayerInfo *player)
+{
+  struct NetworkServerPlayerInfo *v, *w;
+
+  if (options.verbose)
+    Error(ERR_NETWORK_SERVER,
+         "client %d (%s) starts game [level %d from levedir %d (%s)]",
+         player->number, player->player_name,
+         (buffer[2] << 8) + buffer[3],
+         (buffer[4] << 8) + buffer[5],
+         &buffer[6]);
+
+  for (w=first_player; w; w=w->next)
+    if (w->introduced)
+      w->active = 1;
+
+  /* reset frame counter */
+  ServerFrameCounter = 0;
+
+  /* reset player actions */
+  for (v=first_player; v; v=v->next)
+  {
+    v->action = 0;
+    v->action_received = FALSE;
+  }
+
+  broadcast(NULL, 10 + strlen((char *)&buffer[10])+1, 0);
+}
+
+static void Handle_OP_PAUSE_PLAYING(struct NetworkServerPlayerInfo *player)
+{
+  if (options.verbose)
+    Error(ERR_NETWORK_SERVER, "client %d (%s) pauses game",
+         player->number, player->player_name);
+  broadcast(NULL, 2, 0);
+}
+
+static void Handle_OP_CONTINUE_PLAYING(struct NetworkServerPlayerInfo *player)
+{
+  if (options.verbose)
+    Error(ERR_NETWORK_SERVER, "client %d (%s) continues game",
+         player->number, player->player_name);
+  broadcast(NULL, 2, 0);
+}
+
+static void Handle_OP_STOP_PLAYING(struct NetworkServerPlayerInfo *player)
+{
+  if (options.verbose)
+    Error(ERR_NETWORK_SERVER, "client %d (%s) stops game",
+         player->number, player->player_name);
+  broadcast(NULL, 2, 0);
+}
+
+static void Handle_OP_MOVE_FIGURE(struct NetworkServerPlayerInfo *player)
+{
+  struct NetworkServerPlayerInfo *v;
+  int last_client_nr = 0;
+  int i;
+
+  /* store player action */
+  for (v=first_player; v; v=v->next)
+  {
+    if (v->number == player->number)
+    {
+      v->action = buffer[2];
+      v->action_received = TRUE;
+    }
+  }
+
+  /* check if server received action from each player */
+  for (v=first_player; v; v=v->next)
+  {
+    if (!v->action_received)
+      return;
+
+    if (v->number > last_client_nr)
+      last_client_nr = v->number;
+  }
+
+  /* initialize all player actions to zero */
+  for (i=0; i<last_client_nr; i++)
+    buffer[6 + i] = 0;
+
+  /* broadcast actions of all players to all players */
+  for (v=first_player; v; v=v->next)
+  {
+    buffer[6 + v->number-1] = v->action;
+    v->action = 0;
+    v->action_received = FALSE;
+  }
+
+  buffer[2] = (unsigned char)((ServerFrameCounter >> 24) & 0xff);
+  buffer[3] = (unsigned char)((ServerFrameCounter >> 16) & 0xff);
+  buffer[4] = (unsigned char)((ServerFrameCounter >>  8) & 0xff);
+  buffer[5] = (unsigned char)((ServerFrameCounter >>  0) & 0xff);
+
+  broadcast(NULL, 6 + last_client_nr, 0);
+
+  ServerFrameCounter++;
+}
+
+void NetworkServer(int port, int serveronly)
+{
+  int i, sl, on;
+  struct NetworkServerPlayerInfo *player;
+  int mfd;
+  int r; 
+  unsigned int len;
+  struct protoent *tcpproto;
+  struct timeval tv;
+  int is_daemon = 0;
+
+#ifndef NeXT
+  struct sigaction sact;
+#endif
+
+  if (port == 0)
+    port = DEFAULT_SERVER_PORT;
+
+  if (!serveronly)
+    onceonly = 1;
+
+  if ((tcpproto = getprotobyname("tcp")) != NULL)
+    tcp = tcpproto->p_proto;
+
+#ifdef NeXT
+  signal(SIGPIPE, SIG_IGN);
+#else
+  sact.sa_handler = SIG_IGN;
+  sigemptyset(&sact.sa_mask);
+  sact.sa_flags = 0;
+  sigaction(SIGPIPE, &sact, NULL);
+#endif
+
+
+  if ((lfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
+    Error(ERR_EXIT_NETWORK_SERVER, "socket() failed");
+
+  saddr.sin_family = AF_INET;
+  saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+  saddr.sin_port = htons(port);
+
+  on = 1;
+
+  setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(int));
+  if (bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
+    Error(ERR_EXIT_NETWORK_SERVER, "bind() failed");
+
+  listen(lfd, 5);
+
+  if (is_daemon)
+  {
+    /* become a daemon, breaking all ties with the controlling terminal */
+    options.verbose = 0;
+    for (i=0; i<255; i++)
+    {
+      if (i != lfd)
+       close(i);
+    }
+
+    if (fork())
+      exit(0);
+    setsid();
+    if (fork())
+      exit(0);
+    chdir("/");
+
+    /* open a fake stdin, stdout, stderr, just in case */
+    open("/dev/null", O_RDONLY);
+    open("/dev/null", O_WRONLY);
+    open("/dev/null", O_WRONLY);
+  }
+
+  if (options.verbose)
+  {
+    Error(ERR_NETWORK_SERVER, "started up, listening on port %d", port);
+    Error(ERR_NETWORK_SERVER, "using protocol version %d.%d.%d",
+         PROTOCOL_VERSION_1, PROTOCOL_VERSION_2, PROTOCOL_VERSION_3);
+  }
+
+  while(1)
+  {
+    interrupt = 0;
+
+    for (player=first_player; player; player=player->next)
+      flushuser(player);
+
+    FD_ZERO(&fds);
+    mfd = lfd;
+    player = first_player;
+    while (player)
+    {
+      FD_SET(player->fd, &fds);
+      if (player->fd > mfd)
+       mfd = player->fd;
+      player = player->next;
+    }
+    FD_SET(lfd, &fds);
+    tv.tv_sec = 0;
+    tv.tv_usec = 500000;
+    if ((sl = select(mfd + 1, &fds, NULL, NULL, &tv)) < 0)
+    {
+      if (errno != EINTR)
+       Error(ERR_EXIT_NETWORK_SERVER, "select() failed");
+      else
+       continue;
+    }
+
+    if (sl < 0)
+      continue;
+    
+    if (sl == 0)
+      continue;
+
+    if (FD_ISSET(lfd, &fds))
+    {
+      int newfd, slen;
+
+      slen = sizeof(saddr);
+      newfd = accept(lfd, (struct sockaddr *)&saddr, &slen);
+      if (newfd < 0)
+      {
+       if (errno != EINTR)
+         Error(ERR_EXIT_NETWORK_SERVER, "accept() failed");
+      }
+      else
+      {
+       if (tcp != -1)
+       {
+         on = 1;
+         setsockopt(newfd, tcp, TCP_NODELAY, (char *)&on, sizeof(int));
+       }
+       AddPlayer(newfd);
+      }
+      continue;
+    }
+
+    player = first_player;
+
+    do
+    {
+      if (FD_ISSET(player->fd, &fds))
+      {
+       r = read(player->fd, player->readbuffer + player->nread, MAX_BUFFER_SIZE - player->nread);
+       if (r <= 0)
+       {
+         if (options.verbose)
+           Error(ERR_NETWORK_SERVER, "EOF from client %d (%s)",
+                 player->number, player->player_name);
+         RemovePlayer(player);
+         interrupt = 1;
+         break;
+       }
+       player->nread += r;
+       while (player->nread >= 4 && player->nread >= 4 + player->readbuffer[3])
+       {
+         len = player->readbuffer[3];
+         if (player->readbuffer[0] || player->readbuffer[1] || player->readbuffer[2])
+         {
+           if (options.verbose)
+             Error(ERR_NETWORK_SERVER, "crap from client %d (%s)",
+                   player->number, player->player_name);
+           RemovePlayer(player);
+           interrupt = 1;
+           break;
+         }
+         memcpy(buffer, &player->readbuffer[4], len);
+         player->nread -= 4 + len;
+         memmove(player->readbuffer, player->readbuffer + 4 + len, player->nread);
+
+         buffer[0] = player->number;
+         if (!player->introduced && buffer[1] != OP_PLAYER_NAME)
+         {
+           if (options.verbose)
+             Error(ERR_NETWORK_SERVER, "!(client %d)->introduced && buffer[1]==%d (expected OP_PLAYER_NAME)", buffer[0], buffer[1]);
+
+           RemovePlayer(player);
+           interrupt = 1;
+           break;
+         }
+
+         switch(buffer[1])
+         {
+           case OP_PLAYER_NAME:
+             Handle_OP_PLAYER_NAME(player, len);
+             break;
+
+           case OP_PROTOCOL_VERSION:
+             Handle_OP_PROTOCOL_VERSION(player, len);
+             break;
+
+           case OP_NUMBER_WANTED:
+             Handle_OP_NUMBER_WANTED(player);
+             break;
+
+           case OP_START_PLAYING:
+             Handle_OP_START_PLAYING(player);
+             break;
+
+           case OP_PAUSE_PLAYING:
+             Handle_OP_PAUSE_PLAYING(player);
+             break;
+
+           case OP_CONTINUE_PLAYING:
+             Handle_OP_CONTINUE_PLAYING(player);
+             break;
+
+           case OP_STOP_PLAYING:
+             Handle_OP_STOP_PLAYING(player);
+             break;
+
+           case OP_MOVE_FIGURE:
+             Handle_OP_MOVE_FIGURE(player);
+             break;
+
+           case OP_BROADCAST_MESSAGE:
+             buffer[len] = '\0';
+             if (options.verbose)
+               Error(ERR_NETWORK_SERVER, "client %d (%s) sends message: %s",
+                     player->number, player->player_name, &buffer[2]);
+             broadcast(player, len, 0);
+             break;
+           
+           default:
+             if (options.verbose)
+               Error(ERR_NETWORK_SERVER,
+                     "unknown opcode %d from client %d (%s)",
+                     buffer[0], player->number, player->player_name);
+         }
+       }
+      }
+
+      if (player && !interrupt)
+       player = player->next;
+    }
+    while (player && !interrupt);
+  }
+}
+
+#endif /* !MSDOS */
diff --git a/src/netserv.h b/src/netserv.h
new file mode 100644 (file)
index 0000000..5177fb4
--- /dev/null
@@ -0,0 +1,43 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
+*----------------------------------------------------------*
+*  netserv.h                                               *
+***********************************************************/
+
+#ifndef NETSERV_H
+#define NETSERV_H
+
+#include "main.h"
+
+#define DEFAULT_SERVER_PORT    19504
+
+#define PROTOCOL_VERSION_1     1
+#define PROTOCOL_VERSION_2     2
+#define PROTOCOL_VERSION_3     0
+
+#define OP_PROTOCOL_VERSION    1
+#define OP_BAD_PROTOCOL_VERSION        2
+#define OP_YOUR_NUMBER         3
+#define OP_NUMBER_WANTED       4
+#define OP_PLAYER_NAME         5
+#define OP_PLAYER_CONNECTED    6
+#define OP_PLAYER_DISCONNECTED 7
+#define OP_START_PLAYING       8
+#define OP_PAUSE_PLAYING       9
+#define OP_CONTINUE_PLAYING    10
+#define OP_STOP_PLAYING                11
+#define OP_MOVE_FIGURE         12
+#define OP_BROADCAST_MESSAGE   13
+
+#define MAX_BUFFER_SIZE                4096
+
+void NetworkServer(int, int);
+
+#endif
diff --git a/src/network.c b/src/network.c
new file mode 100644 (file)
index 0000000..5d137f0
--- /dev/null
@@ -0,0 +1,610 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
+*----------------------------------------------------------*
+*  network.c                                               *
+***********************************************************/
+
+#ifndef MSDOS
+
+#include <unistd.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "network.h"
+#include "netserv.h"
+#include "game.h"
+#include "tape.h"
+#include "files.h"
+#include "tools.h"
+#include "buttons.h"
+#include "screens.h"
+#include "misc.h"
+
+#define MAX_PLAYER_NAME_LEN 14
+
+struct NetworkClientPlayerInfo
+{
+  byte nr;
+  char name[MAX_PLAYER_NAME_LEN + 2];
+  struct NetworkClientPlayerInfo *next;
+};
+
+static struct NetworkClientPlayerInfo first_player =
+{
+  0,
+  EMPTY_PLAYER_NAME,
+  NULL
+};
+
+/* server stuff */
+
+static int sfd;
+static byte realbuffer[512];
+static byte readbuffer[MAX_BUFFER_SIZE], writbuffer[MAX_BUFFER_SIZE];
+static byte *buffer = realbuffer + 4;
+static int nread = 0, nwrite = 0;
+
+static void SendBufferToServer(int size)
+{
+  if (!options.network)
+    return;
+
+  realbuffer[0] = realbuffer[1] = realbuffer[2] = 0;
+  realbuffer[3] = (byte)size;
+  buffer[0] = 0;
+
+  if (nwrite + 4 + size >= MAX_BUFFER_SIZE)
+    Error(ERR_EXIT, "internal error: network send buffer overflow");
+
+  memcpy(writbuffer + nwrite, realbuffer, 4 + size);
+  nwrite += 4 + size;
+
+  /* directly send the buffer to the network server */
+  write(sfd, writbuffer, nwrite);
+  nwrite = 0;
+}
+
+struct NetworkClientPlayerInfo *getNetworkPlayer(int player_nr)
+{
+  struct NetworkClientPlayerInfo *player = NULL;
+
+  for (player = &first_player; player; player = player->next)
+    if (player->nr == player_nr)
+      break;
+
+  if (player == NULL)  /* should not happen */
+    Error(ERR_EXIT, "protocol error: reference to non-existing player %d",
+         player_nr);
+
+  return player;
+}
+
+char *getNetworkPlayerName(int player_nr)
+{
+  struct NetworkClientPlayerInfo *player;
+
+  if (player_nr == 0)
+    return("the network game server");
+  else if (player_nr == first_player.nr)
+    return("you");
+  else
+    for (player=&first_player; player; player=player->next)
+      if (player->nr == player_nr && player->name && strlen(player->name))
+       return(player->name);
+
+  return(EMPTY_PLAYER_NAME);
+}
+
+static void StartNetworkServer(int port)
+{
+  switch (fork())
+  {
+    case 0:
+      NetworkServer(port, options.serveronly);
+
+      /* never reached */
+      exit(0);
+
+    case -1:
+      Error(ERR_WARN,
+           "cannot create network server process - no network playing");
+      options.network = FALSE;
+      return;
+
+    default:
+      /* we are parent process -- resume normal operation */
+      return;
+  }
+}
+
+boolean ConnectToServer(char *hostname, int port)
+{
+  struct sockaddr_in s;
+  struct protoent *tcpproto;
+  int on = 1, i;
+
+  if (hostname)
+  {
+    if ((s.sin_addr.s_addr = inet_addr(hostname)) == -1)
+    {
+      struct hostent *host;
+
+      if ((host = gethostbyname(hostname)) == NULL)
+       Error(ERR_EXIT, "cannot locate host '%s'", hostname);
+
+      s.sin_addr = *(struct in_addr *)(host->h_addr_list[0]);
+    }
+  }
+  else
+    s.sin_addr.s_addr = inet_addr("127.0.0.1");                /* localhost */
+
+  if (port == 0)
+    port = DEFAULT_SERVER_PORT;
+
+  s.sin_port = htons(port);
+  s.sin_family = AF_INET;
+
+  sfd = socket(PF_INET, SOCK_STREAM, 0);
+  if (sfd < 0)
+    Error(ERR_EXIT, "out of file descriptors");
+
+  if ((tcpproto = getprotobyname("tcp")) != NULL)
+    setsockopt(sfd, tcpproto->p_proto, TCP_NODELAY, (char *)&on, sizeof(int));
+
+  if (connect(sfd, (struct sockaddr *)&s, sizeof(s)) == 0)     /* connected */
+    return TRUE;
+
+  if (hostname)        /* connect to specified server failed */
+    return FALSE;
+
+  printf("No rocksndiamonds server on localhost - starting up one ...\n");
+  StartNetworkServer(port);
+
+  /* wait for server to start up and try connecting several times */
+  for (i=0; i<6; i++)
+  {
+    Delay(500);                /* wait 500 ms == 0.5 seconds */
+    close(sfd);
+
+    sfd = socket(PF_INET, SOCK_STREAM, 0);
+    if (sfd < 0)
+      Error(ERR_EXIT, "out of file descriptors");
+
+    setsockopt(sfd, tcpproto->p_proto, TCP_NODELAY, (char *)&on, sizeof(int));
+
+    if (connect(sfd, (struct sockaddr *)&s, sizeof(s)) >= 0)   /* connected */
+      return TRUE;
+  }
+
+  /* when reaching this point, connect to newly started server has failed */
+  return FALSE;
+}
+
+void SendToServer_PlayerName(char *player_name)
+{
+  int len_player_name = strlen(player_name);
+
+  buffer[1] = OP_PLAYER_NAME;
+  memcpy(&buffer[2], player_name, len_player_name);
+  SendBufferToServer(2 + len_player_name);
+  Error(ERR_NETWORK_CLIENT, "you set your player name to \"%s\"", player_name);
+}
+
+void SendToServer_ProtocolVersion()
+{
+  buffer[1] = OP_PROTOCOL_VERSION;
+  buffer[2] = PROTOCOL_VERSION_1;
+  buffer[3] = PROTOCOL_VERSION_2;
+  buffer[4] = PROTOCOL_VERSION_3;
+
+  SendBufferToServer(5);
+}
+
+void SendToServer_NrWanted(int nr_wanted)
+{
+  buffer[1] = OP_NUMBER_WANTED;
+  buffer[2] = nr_wanted;
+
+  SendBufferToServer(3);
+}
+
+void SendToServer_StartPlaying()
+{
+  unsigned long new_random_seed = InitRND(NEW_RANDOMIZE);
+
+  buffer[1] = OP_START_PLAYING;
+  buffer[2] = (byte)(level_nr >> 8);
+  buffer[3] = (byte)(level_nr & 0xff);
+  buffer[4] = (byte)(leveldir_nr >> 8);
+  buffer[5] = (byte)(leveldir_nr & 0xff);
+
+  buffer[6] = (unsigned char)((new_random_seed >> 24) & 0xff);
+  buffer[7] = (unsigned char)((new_random_seed >> 16) & 0xff);
+  buffer[8] = (unsigned char)((new_random_seed >>  8) & 0xff);
+  buffer[9] = (unsigned char)((new_random_seed >>  0) & 0xff);
+
+  strcpy((char *)&buffer[10], leveldir[leveldir_nr].name);
+
+  SendBufferToServer(10 + strlen(leveldir[leveldir_nr].name)+1);
+}
+
+void SendToServer_PausePlaying()
+{
+  buffer[1] = OP_PAUSE_PLAYING;
+
+  SendBufferToServer(2);
+}
+
+void SendToServer_ContinuePlaying()
+{
+  buffer[1] = OP_CONTINUE_PLAYING;
+
+  SendBufferToServer(2);
+}
+
+void SendToServer_StopPlaying()
+{
+  buffer[1] = OP_STOP_PLAYING;
+
+  SendBufferToServer(2);
+}
+
+void SendToServer_MovePlayer(byte player_action)
+{
+  buffer[1] = OP_MOVE_FIGURE;
+  buffer[2] = player_action;
+
+  SendBufferToServer(3);
+}
+
+static void Handle_OP_BAD_PROTOCOL_VERSION()
+{
+  Error(ERR_WARN, "protocol version mismatch");
+  Error(ERR_EXIT, "server expects %d.%d.x instead of %d.%d.%d",
+       buffer[2], buffer[3],
+       PROTOCOL_VERSION_1, PROTOCOL_VERSION_2, PROTOCOL_VERSION_3);
+}
+
+static void Handle_OP_YOUR_NUMBER()
+{
+  int new_client_nr = buffer[2];
+  int new_index_nr = new_client_nr - 1;
+  struct PlayerInfo *old_local_player = local_player;
+  struct PlayerInfo *new_local_player = &stored_player[new_index_nr];
+
+  printf("OP_YOUR_NUMBER: %d\n", buffer[0]);
+  first_player.nr = new_client_nr;
+
+  if (old_local_player != new_local_player)
+  {
+    /* copy existing player settings and change to new player */
+
+    *new_local_player = *old_local_player;
+    old_local_player->connected = FALSE;
+    local_player = new_local_player;
+  }
+
+  if (first_player.nr > MAX_PLAYERS)
+    Error(ERR_EXIT, "sorry - no more than %d players", MAX_PLAYERS);
+
+  Error(ERR_NETWORK_CLIENT, "you get client # %d", new_client_nr);
+}
+
+static void Handle_OP_NUMBER_WANTED()
+{
+  int client_nr_wanted = buffer[2];
+  int old_client_nr = buffer[0];
+  int new_client_nr = buffer[3];
+  int old_index_nr = old_client_nr - 1;
+  int new_index_nr = new_client_nr - 1;
+  int index_nr_wanted = client_nr_wanted - 1;
+  struct PlayerInfo *old_player = &stored_player[old_index_nr];
+  struct PlayerInfo *new_player = &stored_player[new_index_nr];
+
+  printf("OP_NUMBER_WANTED: %d\n", buffer[0]);
+
+  if (new_client_nr == client_nr_wanted)       /* switching succeeded */
+  {
+    struct NetworkClientPlayerInfo *player;
+
+    if (old_client_nr != client_nr_wanted)     /* client's nr has changed */
+      Error(ERR_NETWORK_CLIENT, "client %d switches to # %d",
+           old_client_nr, new_client_nr);
+    else if (old_client_nr == first_player.nr) /* local player keeps his nr */
+      Error(ERR_NETWORK_CLIENT, "keeping client # %d", new_client_nr);
+
+    if (old_client_nr != new_client_nr)
+    {
+      /* copy existing player settings and change to new player */
+
+      *new_player = *old_player;
+      old_player->connected = FALSE;
+    }
+
+    player = getNetworkPlayer(old_client_nr);
+    player->nr = new_client_nr;
+
+    if (old_player == local_player)            /* local player switched */
+      local_player = new_player;
+  }
+  else if (old_client_nr == first_player.nr)   /* failed -- local player? */
+  {
+    char *color[] = { "yellow", "red", "green", "blue" };
+    char request[100];
+
+    sprintf(request, "Sorry ! %s player still exists ! You are %s player !",
+           color[index_nr_wanted], color[new_index_nr]);
+    Request(request, REQ_CONFIRM);
+
+    Error(ERR_NETWORK_CLIENT, "cannot switch -- you keep client # %d",
+         new_client_nr);
+  }
+}
+
+static void Handle_OP_PLAYER_NAME(unsigned int len)
+{
+  struct NetworkClientPlayerInfo *player;
+  int player_nr = (int)buffer[0];
+
+  printf("OP_PLAYER_NAME: %d\n", player_nr);
+  player = getNetworkPlayer(player_nr);
+  buffer[len] = 0;
+  Error(ERR_NETWORK_CLIENT, "client %d calls itself \"%s\"",
+       buffer[0], &buffer[2]);
+  strncpy(player->name, (char *)&buffer[2], MAX_PLAYER_NAME_LEN);
+}
+
+static void Handle_OP_PLAYER_CONNECTED()
+{
+  struct NetworkClientPlayerInfo *player, *last_player = NULL;
+  int new_client_nr = (int)buffer[0];
+  int new_index_nr = new_client_nr - 1;
+
+  printf("OP_PLAYER_CONNECTED: %d\n", new_client_nr);
+  Error(ERR_NETWORK_CLIENT, "new client %d connected", new_client_nr);
+
+  for (player=&first_player; player; player=player->next)
+  {
+    if (player->nr == new_client_nr)
+      Error(ERR_EXIT, "multiplayer server sent duplicate player id");
+
+    last_player = player;
+  }
+
+  last_player->next = player =
+    checked_malloc(sizeof(struct NetworkClientPlayerInfo));
+  player->nr = new_client_nr;
+  player->name[0] = '\0';
+  player->next = NULL;
+
+  stored_player[new_index_nr].connected = TRUE;
+}
+
+static void Handle_OP_PLAYER_DISCONNECTED()
+{
+  struct NetworkClientPlayerInfo *player, *player_disconnected;
+  int player_nr = (int)buffer[0];
+
+  printf("OP_PLAYER_DISCONNECTED: %d\n", player_nr);
+  player_disconnected = getNetworkPlayer(player_nr);
+  Error(ERR_NETWORK_CLIENT, "client %d (%s) disconnected",
+       player_nr, getNetworkPlayerName(buffer[0]));
+
+  for (player=&first_player; player; player=player->next)
+    if (player->next == player_disconnected)
+      player->next = player_disconnected->next;
+  free(player_disconnected);
+}
+
+static void Handle_OP_START_PLAYING()
+{
+  int new_level_nr, new_leveldir_nr;
+  unsigned long new_random_seed;
+  char *new_leveldir_name;
+
+  new_level_nr = (buffer[2] << 8) + buffer[3];
+  new_leveldir_nr = (buffer[4] << 8) + buffer[5];
+  new_random_seed =
+    (buffer[6] << 24) | (buffer[7] << 16) | (buffer[8] << 8) | (buffer[9]);
+  new_leveldir_name = (char *)&buffer[10];
+
+  printf("OP_START_PLAYING: %d\n", buffer[0]);
+  Error(ERR_NETWORK_CLIENT,
+       "client %d starts game [level %d from levedir %d (%s)]\n",
+       buffer[0], new_level_nr, new_leveldir_nr, new_leveldir_name);
+
+  if (strcmp(leveldir[new_leveldir_nr].name, new_leveldir_name) != 0)
+    Error(ERR_WARN, "no such level directory: '%s'",new_leveldir_name);
+
+  leveldir_nr = new_leveldir_nr;
+  level_nr = new_level_nr;
+
+  TapeErase();
+  LoadTape(level_nr);
+  LoadLevel(level_nr);
+
+  if (setup.autorecord)
+    TapeStartRecording();
+
+  if (tape.recording)
+    tape.random_seed = new_random_seed;
+
+  InitRND(new_random_seed);
+
+  game_status = PLAYING;
+  InitGame();
+}
+
+static void Handle_OP_PAUSE_PLAYING()
+{
+  printf("OP_PAUSE_PLAYING: %d\n", buffer[0]);
+  Error(ERR_NETWORK_CLIENT, "client %d pauses game", buffer[0]);
+
+  tape.pausing = TRUE;
+  DrawVideoDisplay(VIDEO_STATE_PAUSE_ON,0);
+}
+
+static void Handle_OP_CONTINUE_PLAYING()
+{
+  printf("OP_CONTINUE_PLAYING: %d\n", buffer[0]);
+  Error(ERR_NETWORK_CLIENT, "client %d continues game", buffer[0]);
+
+  tape.pausing = FALSE;
+  DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
+}
+
+static void Handle_OP_STOP_PLAYING()
+{
+  printf("OP_STOP_PLAYING: %d\n", buffer[0]);
+  Error(ERR_NETWORK_CLIENT, "client %d stops game", buffer[0]);
+
+  game_status = MAINMENU;
+  DrawMainMenu();
+}
+
+static void Handle_OP_MOVE_FIGURE(unsigned int len)
+{
+  int server_frame_counter;
+  int i;
+
+  if (!network_playing)
+    return;
+
+  server_frame_counter =
+    (buffer[2] << 24) | (buffer[3] << 16) | (buffer[4] << 8) | (buffer[5]);
+
+  if (server_frame_counter != FrameCounter)
+  {
+    Error(ERR_RETURN, "client and servers frame counters out of sync");
+    Error(ERR_RETURN, "frame counter of client is %d", FrameCounter);
+    Error(ERR_RETURN, "frame counter of server is %d", server_frame_counter);
+    Error(ERR_EXIT,   "this should not happen -- please debug");
+  }
+
+  /* copy valid player actions */
+  for (i=0; i<MAX_PLAYERS; i++)
+    stored_player[i].effective_action =
+      (i < len - 6 && stored_player[i].active ? buffer[6 + i] : 0);
+
+  network_player_action_received = TRUE;
+}
+
+static void HandleNetworkingMessages()
+{
+  unsigned int message_length;
+
+  while (nread >= 4 && nread >= 4 + readbuffer[3])
+  {
+    message_length = readbuffer[3];
+    if (readbuffer[0] || readbuffer[1] || readbuffer[2])
+      Error(ERR_EXIT, "wrong network server line length");
+
+    memcpy(buffer, &readbuffer[4], message_length);
+    nread -= 4 + message_length;
+    memmove(readbuffer, readbuffer + 4 + message_length, nread);
+
+    switch(buffer[1])
+    {
+      case OP_BAD_PROTOCOL_VERSION:
+       Handle_OP_BAD_PROTOCOL_VERSION();
+       break;
+
+      case OP_YOUR_NUMBER:
+       Handle_OP_YOUR_NUMBER();
+       break;
+
+      case OP_NUMBER_WANTED:
+       Handle_OP_NUMBER_WANTED();
+       break;
+
+      case OP_PLAYER_NAME:
+       Handle_OP_PLAYER_NAME(message_length);
+       break;
+
+      case OP_PLAYER_CONNECTED:
+       Handle_OP_PLAYER_CONNECTED();
+       break;
+      
+      case OP_PLAYER_DISCONNECTED:
+       Handle_OP_PLAYER_DISCONNECTED();
+       break;
+
+      case OP_START_PLAYING:
+       Handle_OP_START_PLAYING();
+       break;
+
+      case OP_PAUSE_PLAYING:
+       Handle_OP_PAUSE_PLAYING();
+       break;
+
+      case OP_CONTINUE_PLAYING:
+       Handle_OP_CONTINUE_PLAYING();
+       break;
+
+      case OP_STOP_PLAYING:
+       Handle_OP_STOP_PLAYING();
+       break;
+
+      case OP_MOVE_FIGURE:
+       Handle_OP_MOVE_FIGURE(message_length);
+       break;
+
+      case OP_BROADCAST_MESSAGE:
+       printf("OP_BROADCAST_MESSAGE: %d\n", buffer[0]);
+       Error(ERR_NETWORK_CLIENT, "client %d sends message", buffer[0]);
+       break;
+    }
+  }
+
+  fflush(stdout);
+}
+
+void HandleNetworking()
+{
+  static struct timeval tv = { 0, 0 };
+  fd_set rfds;
+  int r = 0;
+
+  FD_ZERO(&rfds);
+  FD_SET(sfd, &rfds);
+
+  r = select(sfd + 1, &rfds, NULL, NULL, &tv);
+
+  if (r < 0 && errno != EINTR)
+    Error(ERR_EXIT, "HandleNetworking(): select() failed");
+
+  if (r < 0)
+    FD_ZERO(&rfds);
+
+  if (FD_ISSET(sfd, &rfds))
+  {
+    int r;
+
+    r = read(sfd, readbuffer + nread, MAX_BUFFER_SIZE - nread);
+
+    if (r < 0)
+      Error(ERR_EXIT, "error reading from network server");
+
+    if (r == 0)
+      Error(ERR_EXIT, "connection to network server lost");
+
+    nread += r;
+
+    HandleNetworkingMessages();
+  }
+}
+
+#endif /* !MSDOS */
diff --git a/src/network.h b/src/network.h
new file mode 100644 (file)
index 0000000..162f0f7
--- /dev/null
@@ -0,0 +1,30 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
+*----------------------------------------------------------*
+*  network.h                                               *
+***********************************************************/
+
+#ifndef NETWORK_H
+#define NETWORK_H
+
+#include "main.h"
+
+boolean ConnectToServer(char *, int);
+void SendToServer_PlayerName(char *);
+void SendToServer_ProtocolVersion(void);
+void SendToServer_NrWanted(int);
+void SendToServer_StartPlaying(void);
+void SendToServer_PausePlaying(void);
+void SendToServer_ContinuePlaying(void);
+void SendToServer_StopPlaying(void);
+void SendToServer_MovePlayer(byte);
+void HandleNetworking(void);
+
+#endif
diff --git a/src/pcx.c b/src/pcx.c
new file mode 100644 (file)
index 0000000..514d30d
--- /dev/null
+++ b/src/pcx.c
@@ -0,0 +1,228 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
+*----------------------------------------------------------*
+*  pcx.c                                                   *
+***********************************************************/
+
+#include "pcx.h"
+#include "image.h"
+#include "misc.h"
+
+#define PCX_MAGIC              0x0a    /* first byte in a PCX image file    */
+#define PCX_LAST_VERSION       5       /* last acceptable version number    */
+#define PCX_ENCODING           1       /* PCX encoding method               */
+#define PCX_256COLORS_MAGIC    0x0c    /* first byte of a PCX 256 color map */
+#define PCX_MAXDEPTH           8       /* supports up to 8 bits per pixel   */
+#define PCX_MAXCOLORS          256     /* maximum number of colors          */
+
+#define PCX_HEADER_SIZE                128
+#define PCX_COLORMAP_SIZE      (3 * PCX_MAXCOLORS)
+
+struct PCX_Header
+{
+  unsigned char signature;     /* PCX file identifier                 */
+  unsigned char version;       /* version compatibility level         */
+  unsigned char encoding;      /* encoding method                     */
+  unsigned char bits_per_pixel;        /* bits per pixel, or depth            */
+  unsigned short xmin;         /* X position of left edge             */
+  unsigned short ymin;         /* Y position of top edge              */
+  unsigned short xmax;         /* X position of right edge            */
+  unsigned short ymax;         /* Y position of bottom edge           */
+  unsigned short hres;         /* X screen resolution of source image */
+  unsigned short vres;         /* Y screen resolution of source image */
+  unsigned char palette[16][3];        /* PCX color map                       */
+  unsigned char reserved;      /* should be 0, 1 if std res fax       */
+  unsigned char color_planes;  /* bit planes in image                 */
+  unsigned short bytes_per_line;/* byte delta between scanlines        */
+  unsigned short palette_type; /* 0 = undef, 1 = color, 2 = grayscale */
+  unsigned char filler[58];    /* fill to struct size of 128          */
+};
+
+static byte *PCX_ReadBitmap(Image *image, byte *buffer_ptr, byte *buffer_last)
+{
+  /* Run Length Encoding: If the two high bits are set,
+   * then the low 6 bits contain a repeat count, and the byte to
+   * repeat is the next byte in the file.  If the two high bits are
+   * not set, then this is the byte to write.
+   */
+
+  unsigned int bytes_per_pixel = (image->depth + 7) / 8;
+  register byte *bitmap_ptr, *bitmap_last;
+  register byte value, count;
+
+  bitmap_ptr = image->data;
+  bitmap_last = bitmap_ptr + (image->width * image->height * bytes_per_pixel);
+
+  while (bitmap_ptr < bitmap_last && buffer_ptr < buffer_last)
+  {
+    value = *buffer_ptr++;
+
+    if ((value & 0xc0) == 0xc0)                /* this is a repeat count byte */
+    {
+      count = value & 0x3f;            /* extract repeat count from byte */
+      value = *buffer_ptr++;           /* next byte is value to repeat */
+
+      for (; count && bitmap_ptr < bitmap_last; count--)
+       *bitmap_ptr++ = value;
+
+      if (count)                       /* repeat count spans end of bitmap */
+       return NULL;
+    }
+    else
+      *bitmap_ptr++ = value;
+
+    image->rgb.color_used[value] = TRUE;
+  }
+
+  /* check if end of buffer was reached before end of bitmap */
+  if (bitmap_ptr < bitmap_last)
+    return NULL;
+
+  /* return current buffer position for next decoding function */
+  return buffer_ptr;
+}
+
+static byte *PCX_ReadColormap(Image *image,byte *buffer_ptr, byte *buffer_last)
+{
+  int i, magic;
+
+  /* read colormap magic byte */
+  magic = *buffer_ptr++;
+
+  /* check magic colormap header byte */
+  if (magic != PCX_256COLORS_MAGIC)
+    return NULL;
+
+  /* check if enough bytes left for a complete colormap */
+  if (buffer_ptr + PCX_COLORMAP_SIZE > buffer_last)
+    return NULL;
+
+  /* read 256 colors from PCX colormap */
+  for (i=0; i<PCX_MAXCOLORS; i++)
+  {
+    image->rgb.red[i]   = *buffer_ptr++ << 8;
+    image->rgb.green[i] = *buffer_ptr++ << 8;
+    image->rgb.blue[i]  = *buffer_ptr++ << 8;
+  }
+
+  /* return current buffer position for next decoding function */
+  return buffer_ptr;
+}
+
+Image *Read_PCX_to_Image(char *filename)
+{
+  FILE *file;
+  byte *file_buffer;
+  byte *buffer_ptr, *buffer_last;
+  unsigned int file_length;
+  struct PCX_Header pcx;
+  Image *image;
+  int width, height, depth;
+  int i;
+
+  if (!(file = fopen(filename, "r")))
+    return NULL;
+
+  if (fseek(file, 0, SEEK_END) == -1)
+  {
+    fclose(file);
+    return NULL;
+  }
+
+  file_length = ftell(file);
+  rewind(file);
+
+  if (file_length < PCX_HEADER_SIZE + PCX_COLORMAP_SIZE)
+  {
+    fclose(file);
+    return NULL;
+  }
+
+  file_buffer = checked_malloc(file_length);
+
+  if (fread(file_buffer, 1, file_length, file) != file_length)
+  {
+    fclose(file);
+    return NULL;
+  }
+
+  fclose(file);
+
+  pcx.signature      = file_buffer[0];
+  pcx.version        = file_buffer[1];
+  pcx.encoding       = file_buffer[2];
+  pcx.bits_per_pixel = file_buffer[3];
+  pcx.xmin           = file_buffer[4]  + 256 * file_buffer[5];
+  pcx.ymin           = file_buffer[6]  + 256 * file_buffer[7];
+  pcx.xmax           = file_buffer[8]  + 256 * file_buffer[9];
+  pcx.ymax           = file_buffer[10] + 256 * file_buffer[11];
+  pcx.color_planes   = file_buffer[65];
+  pcx.bytes_per_line = file_buffer[66] + 256 * file_buffer[67];
+  pcx.palette_type   = file_buffer[68] + 256 * file_buffer[69];
+
+  width  = pcx.xmax - pcx.xmin + 1;
+  height = pcx.ymax - pcx.ymin + 1;
+  depth  = pcx.bits_per_pixel;
+
+  if (pcx.signature != PCX_MAGIC || pcx.version > PCX_LAST_VERSION ||
+      pcx.encoding != PCX_ENCODING || pcx.color_planes > PCX_MAXDEPTH ||
+      width < 0 || height < 0)
+  {
+    free(file_buffer);
+    return NULL;
+  }
+
+  if (options.verbose)
+  {
+    printf("%s is a %dx%d PC Paintbrush image with %d bitplanes\n",
+          filename, pcx.xmax, pcx.ymax,
+          pcx.color_planes);
+    printf("depth: %d\n", pcx.bits_per_pixel);
+    printf("bytes_per_line: %d\n", pcx.bytes_per_line);
+    printf("palette type: %s\n",
+          (pcx.palette_type == 1 ? "color" :
+           pcx.palette_type == 2 ? "grayscale" : "undefined"));
+  }
+
+  /* allocate new image structure */
+  image = newImage(width, height, depth);
+
+  buffer_ptr  = file_buffer + PCX_HEADER_SIZE;
+  buffer_last = file_buffer + file_length;
+
+  /* read compressed bitmap data */
+  if ((buffer_ptr = PCX_ReadBitmap(image, buffer_ptr, buffer_last)) == NULL)
+  {
+    free(file_buffer);
+    freeImage(image);
+    return NULL;
+  }
+
+  /* read colormap data */
+  if (!PCX_ReadColormap(image, buffer_ptr, buffer_last))
+  {
+    free(file_buffer);
+    freeImage(image);
+    return NULL;
+  }
+
+  free(file_buffer);
+
+  /* determine number of used colormap entries */
+  image->rgb.used = 0;
+  for (i=0; i<PCX_MAXCOLORS; i++)
+    if (image->rgb.color_used[i])
+      image->rgb.used++;
+
+  if (options.verbose)
+    printf("Read_PCX_to_Image: %d colors found\n", image->rgb.used);
+
+  return image;
+}
diff --git a/src/pcx.h b/src/pcx.h
new file mode 100644 (file)
index 0000000..b1fa1c6
--- /dev/null
+++ b/src/pcx.h
@@ -0,0 +1,29 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
+*----------------------------------------------------------*
+*  pcx.h                                                   *
+***********************************************************/
+
+#ifndef PCX_H
+#define PCX_H
+
+#include "main.h"
+#include "image.h"
+
+#define PCX_Success             0
+#define PCX_OpenFailed         -1
+#define PCX_ReadFailed         -2
+#define        PCX_FileInvalid         -3
+#define PCX_NoMemory           -4
+#define PCX_ColorFailed                -5
+
+Image *Read_PCX_to_Image(char *);
+
+#endif /* PCX_H */
diff --git a/src/random.c b/src/random.c
new file mode 100644 (file)
index 0000000..864f3dc
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * This is derived from the Berkeley source:
+ *     @(#)random.c    5.5 (Berkeley) 7/6/88
+ * It was reworked for the GNU C Library by Roland McGrath.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "random.h"
+
+
+/* An improved random number generation package.  In addition to the standard
+   rand()/srand() like interface, this package also has a special state info
+   interface.  The initstate() routine is called with a seed, an array of
+   bytes, and a count of how many bytes are being passed in; this array is
+   then initialized to contain information for random number generation with
+   that much state information.  Good sizes for the amount of state
+   information are 32, 64, 128, and 256 bytes.  The state can be switched by
+   calling the setstate() function with the same array as was initiallized
+   with initstate().  By default, the package runs with 128 bytes of state
+   information and generates far better random numbers than a linear
+   congruential generator.  If the amount of state information is less than
+   32 bytes, a simple linear congruential R.N.G. is used.  Internally, the
+   state information is treated as an array of longs; the zeroeth element of
+   the array is the type of R.N.G. being used (small integer); the remainder
+   of the array is the state information for the R.N.G.  Thus, 32 bytes of
+   state information will give 7 longs worth of state information, which will
+   allow a degree seven polynomial.  (Note: The zeroeth word of state
+   information also has some other information stored in it; see setstate
+   for details).  The random number generation technique is a linear feedback
+   shift register approach, employing trinomials (since there are fewer terms
+   to sum up that way).  In this approach, the least significant bit of all
+   the numbers in the state table will act as a linear feedback shift register,
+   and will have period 2^deg - 1 (where deg is the degree of the polynomial
+   being used, assuming that the polynomial is irreducible and primitive).
+   The higher order bits will have longer periods, since their values are
+   also influenced by pseudo-random carries out of the lower bits.  The
+   total period of the generator is approximately deg*(2**deg - 1); thus
+   doubling the amount of state information has a vast influence on the
+   period of the generator.  Note: The deg*(2**deg - 1) is an approximation
+   only good for large deg, when the period of the shift register is the
+   dominant factor.  With deg equal to seven, the period is actually much
+   longer than the 7*(2**7 - 1) predicted by this formula.  */
+
+
+
+/* For each of the currently supported random number generators, we have a
+   break value on the amount of state information (you need at least thi
+   bytes of state info to support this random number generator), a degree for
+   the polynomial (actually a trinomial) that the R.N.G. is based on, and
+   separation between the two lower order coefficients of the trinomial.  */
+
+/* Linear congruential.  */
+#define        TYPE_0          0
+#define        BREAK_0         8
+#define        DEG_0           0
+#define        SEP_0           0
+
+/* x**7 + x**3 + 1.  */
+#define        TYPE_1          1
+#define        BREAK_1         32
+#define        DEG_1           7
+#define        SEP_1           3
+
+/* x**15 + x + 1.  */
+#define        TYPE_2          2
+#define        BREAK_2         64
+#define        DEG_2           15
+#define        SEP_2           1
+
+/* x**31 + x**3 + 1.  */
+#define        TYPE_3          3
+#define        BREAK_3         128
+#define        DEG_3           31
+#define        SEP_3           3
+
+/* x**63 + x + 1.  */
+#define        TYPE_4          4
+#define        BREAK_4         256
+#define        DEG_4           63
+#define        SEP_4           1
+
+
+/* Array versions of the above information to make code run faster.
+   Relies on fact that TYPE_i == i.  */
+
+#define        MAX_TYPES       5       /* Max number of types above.  */
+
+
+
+/* Initially, everything is set up as if from:
+       initstate(1, randtbl, 128);
+   Note that this initialization takes advantage of the fact that srandom
+   advances the front and rear pointers 10*rand_deg times, and hence the
+   rear pointer which starts at 0 will also end up at zero; thus the zeroeth
+   element of the state information, which contains info about the current
+   position of the rear pointer is just
+       (MAX_TYPES * (rptr - state)) + TYPE_3 == TYPE_3.  */
+
+static long int randtbl[DEG_3 + 1] =
+{
+  TYPE_3,
+  -851904987, -43806228, -2029755270, 1390239686, -1912102820,
+  -485608943, 1969813258, -1590463333, -1944053249, 455935928, 508023712,
+  -1714531963, 1800685987, -2015299881, 654595283, -1149023258,
+  -1470005550, -1143256056, -1325577603, -1568001885, 1275120390,
+  -607508183, -205999574, -1696891592, 1492211999, -1528267240,
+  -952028296, -189082757, 362343714, 1424981831, 2039449641,
+};
+
+/* FPTR and RPTR are two pointers into the state info, a front and a rear
+   pointer.  These two pointers are always rand_sep places aparts, as they
+   cycle through the state information.  (Yes, this does mean we could get
+   away with just one pointer, but the code for random is more efficient
+   this way).  The pointers are left positioned as they would be from the call:
+       initstate(1, randtbl, 128);
+   (The position of the rear pointer, rptr, is really 0 (as explained above
+   in the initialization of randtbl) because the state table pointer is set
+   to point to randtbl[1] (as explained below).)  */
+
+static long int *fptr = &randtbl[SEP_3 + 1];
+static long int *rptr = &randtbl[1];
+
+
+
+/* The following things are the pointer to the state information table,
+   the type of the current generator, the degree of the current polynomial
+   being used, and the separation between the two pointers.
+   Note that for efficiency of random, we remember the first location of
+   the state information, not the zeroeth.  Hence it is valid to access
+   state[-1], which is used to store the type of the R.N.G.
+   Also, we remember the last location, since this is more efficient than
+   indexing every time to find the address of the last element to see if
+   the front and rear pointers have wrapped.  */
+
+static long int *state = &randtbl[1];
+
+static int rand_type = TYPE_3;
+static int rand_deg = DEG_3;
+static int rand_sep = SEP_3;
+
+static long int *end_ptr = &randtbl[sizeof(randtbl) / sizeof(randtbl[0])];
+
+/* Initialize the random number generator based on the given seed.  If the
+   type is the trivial no-state-information type, just remember the seed.
+   Otherwise, initializes state[] based on the given "seed" via a linear
+   congruential generator.  Then, the pointers are set to known locations
+   that are exactly rand_sep places apart.  Lastly, it cycles the state
+   information a given number of times to get rid of any initial dependencies
+   introduced by the L.C.R.N.G.  Note that the initialization of randtbl[]
+   for default usage relies on values produced by this routine.  */
+
+void srandom_linux_libc(unsigned int x)
+{
+  state[0] = x;
+  if (rand_type != TYPE_0)
+  {
+    register long int i;
+    for (i = 1; i < rand_deg; ++i)
+      state[i] = (1103515145 * state[i - 1]) + 12345;
+    fptr = &state[rand_sep];
+    rptr = &state[0];
+    for (i = 0; i < 10 * rand_deg; ++i)
+      (void) random_linux_libc();
+  }
+}
+
+/* If we are using the trivial TYPE_0 R.N.G., just do the old linear
+   congruential bit.  Otherwise, we do our fancy trinomial stuff, which is the
+   same in all ther other cases due to all the global variables that have been
+   set up.  The basic operation is to add the number at the rear pointer into
+   the one at the front pointer.  Then both pointers are advanced to the next
+   location cyclically in the table.  The value returned is the sum generated,
+   reduced to 31 bits by throwing away the "least random" low bit.
+   Note: The code takes advantage of the fact that both the front and
+   rear pointers can't wrap on the same call by not testing the rear
+   pointer if the front one has wrapped.  Returns a 31-bit random number.  */
+
+long int random_linux_libc()
+{
+  if (rand_type == TYPE_0)
+  {
+    state[0] = ((state[0] * 1103515245) + 12345) & LONG_MAX;
+    return state[0];
+  }
+  else
+  {
+    long int i;
+    *fptr += *rptr;
+    /* Chucking least random bit.  */
+    i = (*fptr >> 1) & LONG_MAX;
+    ++fptr;
+    if (fptr >= end_ptr)
+    {
+      fptr = state;
+      ++rptr;
+    }
+    else
+    {
+      ++rptr;
+      if (rptr >= end_ptr)
+       rptr = state;
+    }
+    return i;
+  }
+}
diff --git a/src/random.h b/src/random.h
new file mode 100644 (file)
index 0000000..366d1ff
--- /dev/null
@@ -0,0 +1,20 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
+*----------------------------------------------------------*
+*  random.h                                                *
+***********************************************************/
+
+#ifndef RANDOM_H
+#define RANDOM_H
+
+void srandom_linux_libc(unsigned int);
+long int random_linux_libc(void);
+
+#endif
index 5246fa82a76ce42a2bf6cde9990c401802621ba7..c0a3b8c64a93b851a9b24380d3cde2c3786cc0b5 100644 (file)
@@ -1,13 +1,12 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  screens.c                                               *
 ***********************************************************/
 #include "misc.h"
 #include "files.h"
 #include "buttons.h"
+#include "tape.h"
+#include "joystick.h"
+#include "cartoons.h"
+#include "network.h"
+#include "init.h"
+
+/* for DrawSetupScreen(), HandleSetupScreen() */
+#define SETUP_SCREEN_POS_START         2
+#define SETUP_SCREEN_POS_END           16
+#define SETUP_SCREEN_POS_EMPTY1                (SETUP_SCREEN_POS_END - 2)
+#define SETUP_SCREEN_POS_EMPTY2                (SETUP_SCREEN_POS_END - 2)
+
+/* for HandleSetupInputScreen() */
+#define SETUPINPUT_SCREEN_POS_START    2
+#define SETUPINPUT_SCREEN_POS_END      15
+#define SETUPINPUT_SCREEN_POS_EMPTY1   (SETUPINPUT_SCREEN_POS_START + 3)
+#define SETUPINPUT_SCREEN_POS_EMPTY2   (SETUPINPUT_SCREEN_POS_END - 1)
+
+/* for HandleChooseLevel() */
+#define MAX_LEVEL_SERIES_ON_SCREEN     15
+
+#ifdef MSDOS
+extern unsigned char get_ascii(KeySym);
+#endif
+
+void DrawHeadline()
+{
+  int x = SX + (SXSIZE - strlen(GAMETITLE_STRING) * FONT1_XSIZE) / 2;
+
+  DrawText(x, SY + 8, GAMETITLE_STRING, FS_BIG, FC_YELLOW);
+  DrawTextFCentered(46, FC_RED, COPYRIGHT_STRING);
+}
 
 void DrawMainMenu()
 {
   int i;
+  char *name_text = (!options.network && setup.team_mode ? "Team:" : "Name:");
 
   FadeSounds();
   GetPlayerConfig();
   LoadLevel(level_nr);
 
   ClearWindow();
-  DrawText(SX+16, SY+8,  "ROCKS'N'DIAMONDS",FS_BIG,FC_YELLOW);
-  DrawText(SX+25+16, SY+46, "Copyright ^1995 by Holger Schemel",
-          FS_SMALL,FC_RED);
-  DrawText(SX+32, SY+64, "Name:",FS_BIG,FC_GREEN);
-  DrawText(SX+192,SY+64, player.alias_name,FS_BIG,FC_RED);
-  DrawText(SX+32, SY+96, "Level:",FS_BIG,FC_GREEN);
-  DrawText(SX+352,SY+96, int2str(level_nr,3),FS_BIG,
-          (level_nr<leveldir[leveldir_nr].num_ready ? FC_RED : FC_YELLOW));
-  DrawText(SX+32, SY+128,"Hall Of Fame",FS_BIG,FC_GREEN);
-  DrawText(SX+32, SY+160,"Level Creator",FS_BIG,FC_GREEN);
-  DrawText(SY+32, SY+192,"Info Screen",FS_BIG,FC_GREEN);
-  DrawText(SX+32, SY+224,"Start Game",FS_BIG,FC_GREEN);
-  DrawText(SX+32, SY+256,"Setup",FS_BIG,FC_GREEN);
-  DrawText(SX+32, SY+288,"Quit",FS_BIG,FC_GREEN);
+  DrawHeadline();
+  DrawText(SX + 32,    SY + 2*32, name_text, FS_BIG, FC_GREEN);
+  DrawText(SX + 6*32,  SY + 2*32, setup.player_name, FS_BIG, FC_RED);
+  DrawText(SX + 32,    SY + 3*32, "Level:", FS_BIG, FC_GREEN);
+  DrawText(SX + 11*32, SY + 3*32, int2str(level_nr,3), FS_BIG,
+          (leveldir[leveldir_nr].readonly ? FC_RED : FC_YELLOW));
+  DrawText(SX + 32,    SY + 4*32, "Hall Of Fame", FS_BIG, FC_GREEN);
+  DrawText(SX + 32,    SY + 5*32, "Level Creator", FS_BIG, FC_GREEN);
+  DrawText(SY + 32,    SY + 6*32, "Info Screen", FS_BIG, FC_GREEN);
+  DrawText(SX + 32,    SY + 7*32, "Start Game", FS_BIG, FC_GREEN);
+  DrawText(SX + 32,    SY + 8*32, "Setup", FS_BIG, FC_GREEN);
+  DrawText(SX + 32,    SY + 9*32, "Quit", FS_BIG, FC_GREEN);
 
   DrawMicroLevel(MICROLEV_XPOS,MICROLEV_YPOS);
 
-  for(i=2;i<10;i++)
-    DrawGraphic(0,i,GFX_KUGEL_BLAU);
-  DrawGraphic(10,3,GFX_KUGEL_BLAU);
-  DrawGraphic(14,3,GFX_KUGEL_BLAU);
+  for(i=2; i<10; i++)
+    DrawGraphic(0, i, GFX_KUGEL_BLAU);
+  DrawGraphic(10, 3, GFX_PFEIL_L);
+  DrawGraphic(14, 3, GFX_PFEIL_R);
+
+  DrawTextF(15*32 + 6, 3*32 + 9, FC_RED, "%d", leveldir[leveldir_nr].levels);
+
+  DrawText(SX + 56, SY + 326, "A Game by Artsoft Entertainment",
+          FS_SMALL, FC_RED);
+
+  if (leveldir[leveldir_nr].name)
+  {
+    int len = strlen(leveldir[leveldir_nr].name);
+    int lxpos = SX + (SXSIZE - len * FONT4_XSIZE) / 2;
+    int lypos = SY + 352;
 
-  DrawText(SX+54+16,SY+326,"A Game by Artsoft Development",FS_SMALL,FC_BLUE);
-  DrawText(SX+40+16,SY+344,"Graphics: Deluxe Paint IV Amiga",
-          FS_SMALL,FC_BLUE);
-  DrawText(SX+60+16,SY+362,"Sounds: AudioMaster IV Amiga",
-          FS_SMALL,FC_BLUE);
+    DrawText(lxpos, lypos, leveldir[leveldir_nr].name, FS_SMALL, FC_SPECIAL2);
+  }
 
   FadeToFront();
   InitAnimation();
-  HandleMainMenu(0,0,0,0,MB_MENU_MARK);
+  HandleMainMenu(0,0, 0,0, MB_MENU_INITIALIZE);
 
   TapeStop();
   if (TAPE_IS_EMPTY(tape))
-    LoadLevelTape(level_nr);
+    LoadTape(level_nr);
   DrawCompleteVideoDisplay();
 
   OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2);
 
+  ClearEventQueue();
   XAutoRepeatOn(display);
 }
 
@@ -77,32 +116,35 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
 {
   static int choice = 3;
   static int redraw = TRUE;
-  int x = (mx+32-SX)/32, y = (my+32-SY)/32;
+  int x = (mx + 32 - SX) / 32, y = (my + 32 - SY) / 32;
 
-  if (redraw)
+  if (redraw || button == MB_MENU_INITIALIZE)
   {
-    DrawGraphic(0,choice-1,GFX_KUGEL_ROT);
+    DrawGraphic(0, choice - 1, GFX_KUGEL_ROT);
     redraw = FALSE;
   }
 
+  if (button == MB_MENU_INITIALIZE)
+    return;
+
   if (dx || dy)
   {
-    if (dx && choice==4)
+    if (dx && choice == 4)
     {
-      x = (dx<0 ? 11 : 15);
+      x = (dx < 0 ? 11 : 15);
       y = 4;
     }
     else if (dy)
     {
       x = 1;
-      y = choice+dy;
+      y = choice + dy;
     }
     else
       x = y = 0;
 
-    if (y<3)
+    if (y < 3)
       y = 3;
-    else if (y>10)
+    else if (y > 10)
       y = 10;
   }
 
@@ -112,114 +154,122 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
     y = choice;
   }
 
-  if (y==4 && ((x==11 && level_nr>0) ||
-              (x==15 && level_nr<LEVELDIR_SIZE(leveldir[leveldir_nr]))) &&
+  if (y == 4 && ((x == 11 && level_nr > 0) ||
+                (x == 15 && level_nr < leveldir[leveldir_nr].levels - 1)) &&
       button)
   {
-    static long level_delay = 0;
-    int step = (button==1 ? 1 : button==2 ? 5 : 10);
-
-    if (!DelayReached(&level_delay,20))
+    static unsigned long level_delay = 0;
+    int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
+    int new_level_nr, old_level_nr = level_nr;
+    int font_color = (leveldir[leveldir_nr].readonly ? FC_RED : FC_YELLOW);
+
+    new_level_nr = level_nr + (x == 11 ? -step : +step);
+    if (new_level_nr < 0)
+      new_level_nr = 0;
+    if (new_level_nr > leveldir[leveldir_nr].levels - 1)
+      new_level_nr = leveldir[leveldir_nr].levels - 1;
+
+    if (old_level_nr == new_level_nr || !DelayReached(&level_delay, 150))
       goto out;
 
-    level_nr += (x==11 ? -step : +step);
-    if (level_nr<0)
-      level_nr = 0;
-    if (level_nr>LEVELDIR_SIZE(leveldir[leveldir_nr])-1)
-      level_nr = LEVELDIR_SIZE(leveldir[leveldir_nr])-1;
-
-    if (level_nr>player.handicap && level_nr<leveldir[leveldir_nr].num_ready)
-    {
-      if (x==11 || leveldir[leveldir_nr].num_free==0)
-       level_nr = player.handicap;
-      else
-       level_nr = leveldir[leveldir_nr].num_ready;
-    }
+    level_nr = new_level_nr;
 
-    DrawTextExt(drawto,gc,SX+352,SY+96, int2str(level_nr,3),FS_BIG,
-               (level_nr<leveldir[leveldir_nr].num_ready ?FC_RED :FC_YELLOW));
-    DrawTextExt(window,gc,SX+352,SY+96,        int2str(level_nr,3),FS_BIG,
-               (level_nr<leveldir[leveldir_nr].num_ready ?FC_RED :FC_YELLOW));
+    DrawTextExt(drawto, gc, SX + 11 * 32, SY + 3 * 32,
+               int2str(level_nr, 3), FS_BIG, font_color);
+    DrawTextExt(window, gc, SX + 11 * 32, SY + 3 * 32,
+               int2str(level_nr, 3), FS_BIG, font_color);
 
     LoadLevel(level_nr);
-    DrawMicroLevel(MICROLEV_XPOS,MICROLEV_YPOS);
+    DrawMicroLevel(MICROLEV_XPOS, MICROLEV_YPOS);
 
     TapeErase();
-    LoadLevelTape(level_nr);
+    LoadTape(level_nr);
     DrawCompleteVideoDisplay();
+
+    /* needed because DrawMicroLevel() takes some time */
+    BackToFront();
+    XSync(display, FALSE);
+    DelayReached(&level_delay, 0);     /* reset delay counter */
   }
-  else if (x==1 && y>=3 && y<=10)
+  else if (x == 1 && y >= 3 && y <= 10)
   {
     if (button)
     {
-      if (y!=choice)
+      if (y != choice)
       {
-       DrawGraphic(0,y-1,GFX_KUGEL_ROT);
-       DrawGraphic(0,choice-1,GFX_KUGEL_BLAU);
+       DrawGraphic(0, y-1, GFX_KUGEL_ROT);
+       DrawGraphic(0, choice - 1, GFX_KUGEL_BLAU);
+       choice = y;
       }
-      choice = y;
     }
     else
     {
-      if (y==3)
+      if (y == 3)
       {
        game_status = TYPENAME;
-       HandleTypeName(strlen(player.alias_name),0);
+       HandleTypeName(strlen(setup.player_name), 0);
       }
-      else if (y==4)
+      else if (y == 4)
       {
        if (num_leveldirs)
        {
          game_status = CHOOSELEVEL;
+         SaveLevelSetup();
          DrawChooseLevel();
-         redraw = TRUE;
        }
       }
-      else if (y==5)
+      else if (y == 5)
       {
        game_status = HALLOFFAME;
        DrawHallOfFame(-1);
-       redraw = TRUE;
       }
-      else if (y==6)
+      else if (y == 6)
       {
+       if (leveldir[leveldir_nr].readonly)
+         Request("This level is read only !", REQ_CONFIRM);
        game_status = LEVELED;
        DrawLevelEd();
-       redraw = TRUE;
       }
-      else if (y==7)
+      else if (y == 7)
       {
        game_status = HELPSCREEN;
        DrawHelpScreen();
-       redraw = TRUE;
       }
-      else if (y==8)
+      else if (y == 8)
       {
-       if (autorecord_on && !tape.playing)
-         TapeInitRecording();
+       if (setup.autorecord)
+         TapeStartRecording();
 
-       game_status = PLAYING;
-       InitGame();
-       redraw = TRUE;
+#ifndef MSDOS
+       if (options.network)
+         SendToServer_StartPlaying();
+       else
+#endif
+       {
+         game_status = PLAYING;
+         InitGame();
+       }
       }
-      else if (y==9)
+      else if (y == 9)
       {
        game_status = SETUP;
        DrawSetupScreen();
-       redraw = TRUE;
       }
-      else if (y==10)
+      else if (y == 10)
       {
-        if (AreYouSure("Do you really want to quit ?",AYS_ASK|AYS_STAY_CLOSED))
+       SaveLevelSetup();
+        if (Request("Do you really want to quit ?", REQ_ASK | REQ_STAY_CLOSED))
          game_status = EXITGAME;
       }
+
+      redraw = TRUE;
     }
   }
   BackToFront();
 
   out:
 
-  if (game_status==MAINMENU)
+  if (game_status == MAINMENU)
     DoAnimation();
 }
 
@@ -233,25 +283,49 @@ static int helpscreen_frame[MAX_HELPSCREEN_ELS];
 static int helpscreen_delay[MAX_HELPSCREEN_ELS];
 static int helpscreen_action[] =
 {
+  GFX_SPIELER1_DOWN,4,2,
+  GFX_SPIELER1_UP,4,2,
+  GFX_SPIELER1_LEFT,4,2,
+  GFX_SPIELER1_RIGHT,4,2,
+  GFX_SPIELER1_PUSH_LEFT,4,2,
+  GFX_SPIELER1_PUSH_RIGHT,4,2,                                 HA_NEXT,
   GFX_ERDREICH,1,100,                                          HA_NEXT,
   GFX_LEERRAUM,1,100,                                          HA_NEXT,
   GFX_MORAST_LEER,1,100,                                       HA_NEXT,
   GFX_BETON,1,100,                                             HA_NEXT,
   GFX_MAUERWERK,1,100,                                         HA_NEXT,
+  GFX_MAUER_R1,3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10,
+  GFX_MAUER_L1,3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10,     HA_NEXT,
+  GFX_UNSICHTBAR,1,100,                                                HA_NEXT,
   GFX_FELSBODEN,1,100,                                         HA_NEXT,
+  GFX_CHAR_A,30,4, GFX_CHAR_AUSRUF,32,4,                       HA_NEXT,
   GFX_EDELSTEIN,2,5,                                           HA_NEXT,
   GFX_DIAMANT,2,5,                                             HA_NEXT,
+  GFX_EDELSTEIN_BD,2,5,                                                HA_NEXT,
+  GFX_EDELSTEIN_GELB,2,5, GFX_EDELSTEIN_ROT,2,5,
+  GFX_EDELSTEIN_LILA,2,5,                                      HA_NEXT,
   GFX_FELSBROCKEN,4,5,                                         HA_NEXT,
   GFX_BOMBE,1,50, GFX_EXPLOSION,8,1, GFX_LEERRAUM,1,10,                HA_NEXT,
   GFX_KOKOSNUSS,1,50, GFX_CRACKINGNUT,3,1, GFX_EDELSTEIN,1,10, HA_NEXT,
   GFX_ERZ_EDEL,1,50, GFX_EXPLOSION,8,1, GFX_EDELSTEIN,1,10,    HA_NEXT,
   GFX_ERZ_DIAM,1,50, GFX_EXPLOSION,8,1, GFX_DIAMANT,1,10,      HA_NEXT,
+  GFX_ERZ_EDEL_BD,1,50, GFX_EXPLOSION,8,1,GFX_EDELSTEIN_BD,1,10,HA_NEXT,
+  GFX_ERZ_EDEL_GELB,1,50, GFX_EXPLOSION,8,1,
+  GFX_EDELSTEIN_GELB,1,10, GFX_ERZ_EDEL_ROT,1,50,
+  GFX_EXPLOSION,8,1, GFX_EDELSTEIN_ROT,1,10,
+  GFX_ERZ_EDEL_LILA,1,50, GFX_EXPLOSION,8,1,
+  GFX_EDELSTEIN_LILA,1,10,                                     HA_NEXT,
   GFX_GEBLUBBER,4,4,                                           HA_NEXT,
-  GFX_SCHLUESSEL1,4,33,                                                HA_NEXT,
-  GFX_PFORTE1,4,33,                                            HA_NEXT,
-  GFX_PFORTE1X,4,33,                                           HA_NEXT,
+  GFX_SCHLUESSEL1,4,25,                                                HA_NEXT,
+  GFX_PFORTE1,4,25,                                            HA_NEXT,
+  GFX_PFORTE1X,4,25,                                           HA_NEXT,
   GFX_DYNAMIT_AUS,1,100,                                       HA_NEXT,
   GFX_DYNAMIT,7,6, GFX_EXPLOSION,8,1, GFX_LEERRAUM,1,10,       HA_NEXT,
+  GFX_DYNABOMB+0,4,3, GFX_DYNABOMB+3,1,3, GFX_DYNABOMB+2,1,3,
+  GFX_DYNABOMB+1,1,3, GFX_DYNABOMB+0,1,3, GFX_EXPLOSION,8,1,
+  GFX_LEERRAUM,1,10,                                           HA_NEXT,
+  GFX_DYNABOMB_NR,1,100,                                       HA_NEXT,
+  GFX_DYNABOMB_SZ,1,100,                                       HA_NEXT,
   GFX_FLIEGER+4,1,3, GFX_FLIEGER+0,1,3, GFX_FLIEGER+4,1,3,
   GFX_FLIEGER+5,1,3, GFX_FLIEGER+1,1,3, GFX_FLIEGER+5,1,3,
   GFX_FLIEGER+6,1,3, GFX_FLIEGER+2,1,3, GFX_FLIEGER+6,1,3,
@@ -260,69 +334,133 @@ static int helpscreen_action[] =
   GFX_KAEFER+5,1,1, GFX_KAEFER+1,1,1, GFX_KAEFER+5,1,1,
   GFX_KAEFER+6,1,1, GFX_KAEFER+2,1,1, GFX_KAEFER+6,1,1,
   GFX_KAEFER+7,1,1, GFX_KAEFER+3,1,1, GFX_KAEFER+7,1,1,                HA_NEXT,
+  GFX_BUTTERFLY,2,2,                                           HA_NEXT,
+  GFX_FIREFLY,2,2,                                             HA_NEXT,
   GFX_PACMAN+0,1,3, GFX_PACMAN+4,1,2, GFX_PACMAN+0,1,3,
   GFX_PACMAN+1,1,3, GFX_PACMAN+5,1,2, GFX_PACMAN+1,1,3,
   GFX_PACMAN+2,1,3, GFX_PACMAN+6,1,2, GFX_PACMAN+2,1,3,
   GFX_PACMAN+3,1,3, GFX_PACMAN+7,1,2, GFX_PACMAN+3,1,3,                HA_NEXT,
-  GFX_MAMPFER+0,4,0, GFX_MAMPFER+3,1,0, GFX_MAMPFER+2,1,0,
-  GFX_MAMPFER+1,1,0,                                           HA_NEXT,
-  GFX_ZOMBIE+0,4,0, GFX_ZOMBIE+3,1,0, GFX_ZOMBIE+2,1,0,
-  GFX_ZOMBIE+1,1,0,                                            HA_NEXT,
+  GFX_MAMPFER+0,4,1, GFX_MAMPFER+3,1,1, GFX_MAMPFER+2,1,1,
+  GFX_MAMPFER+1,1,1, GFX_MAMPFER+0,1,1,                                HA_NEXT,
+  GFX_MAMPFER2+0,4,1, GFX_MAMPFER2+3,1,1, GFX_MAMPFER2+2,1,1,
+  GFX_MAMPFER2+1,1,1, GFX_MAMPFER2+0,1,1,                      HA_NEXT,
+  GFX_ROBOT+0,4,1, GFX_ROBOT+3,1,1, GFX_ROBOT+2,1,1,
+  GFX_ROBOT+1,1,1, GFX_ROBOT+0,1,1,                            HA_NEXT,
+  GFX_MAULWURF_DOWN,4,2,
+  GFX_MAULWURF_UP,4,2,
+  GFX_MAULWURF_LEFT,4,2,
+  GFX_MAULWURF_RIGHT,4,2,                                      HA_NEXT,
+  GFX_PINGUIN_DOWN,4,2,
+  GFX_PINGUIN_UP,4,2,
+  GFX_PINGUIN_LEFT,4,2,
+  GFX_PINGUIN_RIGHT,4,2,                                       HA_NEXT,
+  GFX_SCHWEIN_DOWN,4,2,
+  GFX_SCHWEIN_UP,4,2,
+  GFX_SCHWEIN_LEFT,4,2,
+  GFX_SCHWEIN_RIGHT,4,2,                                       HA_NEXT,
+  GFX_DRACHE_DOWN,4,2,
+  GFX_DRACHE_UP,4,2,
+  GFX_DRACHE_LEFT,4,2,
+  GFX_DRACHE_RIGHT,4,2,                                                HA_NEXT,
+  GFX_SONDE_START,8,1,                                         HA_NEXT,
   GFX_ABLENK,4,1,                                              HA_NEXT,
-  GFX_AMOEBE_LEBT,4,40,                                                HA_NEXT,
+  GFX_BIRNE_AUS,1,25, GFX_BIRNE_EIN,1,25,                      HA_NEXT,
+  GFX_ZEIT_VOLL,1,25, GFX_ZEIT_LEER,1,25,                      HA_NEXT,
+  GFX_TROPFEN,1,25, GFX_AMOEBING,4,1, GFX_AMOEBE_LEBT,1,10,    HA_NEXT,
   GFX_AMOEBE_TOT+2,2,50, GFX_AMOEBE_TOT,2,50,                  HA_NEXT,
+  GFX_AMOEBE_LEBT,4,40,                                                HA_NEXT,
+  GFX_AMOEBE_LEBT,1,10,        GFX_AMOEBING,4,2,                       HA_NEXT,
+  GFX_AMOEBE_LEBT,1,25, GFX_AMOEBE_TOT,1,25, GFX_EXPLOSION,8,1,
+  GFX_DIAMANT,1,10,                                            HA_NEXT,
+  GFX_LIFE,1,100,                                              HA_NEXT,
+  GFX_LIFE_ASYNC,1,100,                                                HA_NEXT,
   GFX_SIEB_LEER,4,2,                                           HA_NEXT,
+  GFX_SIEB2_LEER,4,2,                                          HA_NEXT,
+  GFX_AUSGANG_ZU,1,100, GFX_AUSGANG_ACT,4,2,
+  GFX_AUSGANG_AUF+0,4,2, GFX_AUSGANG_AUF+3,1,2,
+  GFX_AUSGANG_AUF+2,1,2, GFX_AUSGANG_AUF+1,1,2,                        HA_NEXT,
+  GFX_AUSGANG_AUF+0,4,2, GFX_AUSGANG_AUF+3,1,2,
+  GFX_AUSGANG_AUF+2,1,2, GFX_AUSGANG_AUF+1,1,2,                        HA_NEXT,
   HA_END
 };
 static char *helpscreen_eltext[][2] =
 {
-  "Normal sand:", "You can dig through it",
-  "Empty field:", "You can walk through it",
-  "Quicksand: You cannot pass it,", "but rocks can fall though it",
-  "Massive Wall:", "Nothing can go through it",
-  "Normal Wall: You can't go through", "it, but you can bomb it away",
-  "Old Wall: Like normal wall, but", "some things can fall down from it",
-  "Emerald: You must collect enough of", "them to finish a level",
-  "Diamond: Counts as 3 emeralds;", "Can be destroyed by rocks",
-  "Rock: Smashes several things;", "Can be moved by the player",
-  "Bomb: You can move it, but be", "careful when dropping it",
-  "Nut: Throw a rock on it to open it;", "Each nut contains an emerald",
-  "Wall with an Emerald inside:", "Bomb the wall away to get it",
-  "Wall with a Diamond inside:", "Bomb the wall away to get it",
-  "Acid: Destroys everything that", "falls or walks into it",
-  "Key: Opens the door that has the", "same color (red/yellow/green/blue)",
-  "Door: Can be opened by the key", "with the same color",
-  "Door: You have to find out the", "right color of the key for it",
-  "Dynamite: Collect it and use it to", "destroy walls or kill enemies",
-  "Dynamite: This one explodes after", "a few seconds",
-  "Spaceship: Moves at the left side", "of walls; don't touch it!",
-  "Bug: Moves at the right side of", "walls; don't touch it!",
-  "Pacman: Eats the amoeba and you,", "if you're not careful",
-  "Cruncher: Eats diamonds and you,", "if you're not careful",
-  "Robot: Tries to kill the player", "",
-  "Magic Wheel: Touch it to get rid of", "the robots for some seconds",
-  "Living Amoeba: Grows through empty", "fields, sand and quicksand",
-  "Dead Amoeba: Does not grow, but", "can still kill bugs and spaceships",
-  "Magic Wall: Changes rocks, emeralds", "and diamonds when they pass it",
+ {"THE HERO:",                         "(Is _this_ guy good old Rockford?)"},
+ {"Normal sand:",                      "You can dig through it"},
+ {"Empty field:",                      "You can walk through it"},
+ {"Quicksand: You cannot pass it,",    "but rocks can fall though it"},
+ {"Massive Wall:",                     "Nothing can go through it"},
+ {"Normal Wall: You can't go through", "it, but you can bomb it away"},
+ {"Growing Wall: Grows to the left or",        "right if there is an empty field"},
+ {"Invisible Wall: Behaves like normal","wall, but is invisible"},
+ {"Old Wall: Like normal wall, but",   "some things can fall down from it"},
+ {"Letter Wall: Looks like a letter,", "behaves like a normal wall"},
+ {"Emerald: You must collect enough of","them to finish a level"},
+ {"Diamond: Counts as 3 emeralds, but",        "can be destroyed by rocks"},
+ {"Diamond (BD style): Counts like one","emerald and behaves a bit different"},
+ {"Colorful Gems:",                    "Seem to behave like Emeralds"},
+ {"Rock: Smashes several things;",     "Can be moved by the player"},
+ {"Bomb: You can move it, but be",     "careful when dropping it"},
+ {"Nut: Throw a rock on it to open it;","Each nut contains an emerald"},
+ {"Wall with an emerald inside:",      "Bomb the wall away to get it"},
+ {"Wall with a diamond inside:",       "Bomb the wall away to get it"},
+ {"Wall with BD style diamond inside:",        "Bomb the wall away to get it"},
+ {"Wall with colorful gem inside:",    "Bomb the wall away to get it"},
+ {"Acid: Things that fall in are gone",        "forever (including our hero)"},
+ {"Key: Opens the door that has the",  "same color (red/yellow/green/blue)"},
+ {"Door: Can be opened by the key",    "with the same color"},
+ {"Door: You have to find out the",    "right color of the key for it"},
+ {"Dynamite: Collect it and use it to",        "destroy walls or kill enemies"},
+ {"Dynamite: This one explodes after", "a few seconds"},
+ {"Dyna Bomb: Explodes in 4 directions","with variable explosion size"},
+ {"Dyna Bomb: Increases the number of",        "dyna bombs available at a time"},
+ {"Dyna Bomb: Increases the size of",  "explosion of dyna bombs"},
+ {"Spaceship: Moves at the left side", "of walls; don't touch it!"},
+ {"Bug: Moves at the right side",      "of walls; don't touch it!"},
+ {"Butterfly: Moves at the right side",        "of walls; don't touch it!"},
+ {"Firefly: Moves at the left side",   "of walls; don't touch it!"},
+ {"Pacman: Eats the amoeba and you,",  "if you're not careful"},
+ {"Cruncher: Eats diamonds and you,",  "if you're not careful"},
+ {"Cruncher (BD style):",              "Eats almost everything"},
+ {"Robot: Tries to kill the player",   ""},
+ {"The mole: You must guide him savely","to the exit; he will follow you"},
+ {"The penguin: Guide him to the exit,","but keep him away from monsters!"},
+ {"The Pig: Harmless, but eats all",   "gems it can get"},
+ {"The Dragon: Breathes fire,",                "especially to some monsters"},
+ {"Sonde: Follows you everywhere;",    "harmless, but may block your way"},
+ {"Magic Wheel: Touch it to get rid of","the robots for some seconds"},
+ {"Light Bulb: All of them must be",   "switched on to finish a level"},
+ {"Extra Time Orb: Adds some seconds", "to the time available for the level"},
+ {"Amoeba Drop: Grows to an amoeba on",        "the ground - don't touch it"},
+ {"Dead Amoeba: Does not grow, but",   "can still kill bugs and spaceships"},
+ {"Normal Amoeba: Grows through empty",        "fields, sand and quicksand"},
+ {"Dropping Amoeba: This one makes",   "drops that grow to a new amoeba"},
+ {"Living Amoeba (BD style): Contains",        "other element, when surrounded"},
+ {"Game Of Life: Behaves like the well","known 'Game Of Life' (2333 style)"},
+ {"Biomaze: A bit like the 'Game Of",  "Life', but builds crazy mazes"},
+ {"Magic Wall: Changes rocks, emeralds","and diamonds when they pass it"},
+ {"Magic Wall (BD style):",            "Changes rocks and BD style diamonds"},
+ {"Exit door: Opens if you have enough","emeralds to finish the level"},
+ {"Open exit door: Enter here to leave","the level and exit the actual game"},
 };
 static int num_helpscreen_els = sizeof(helpscreen_eltext)/(2*sizeof(char *));
 
 static char *helpscreen_music[][3] =
 {
-  "Alchemy",                   "Ian Boddy",            "Drive",
-  "The Chase",                 "Propaganda",           "A Secret Wish",
-  "Network 23",                        "Tangerine Dream",      "Exit",
-  "Czardasz",                  "Robert Pieculewicz",   "Czardasz",
-  "21st Century Common Man",   "Tangerine Dream",      "Tyger",
-  "Voyager",                   "The Alan Parsons Project","Pyramid",
-  "Twilight Painter",          "Tangerine Dream",      "Heartbreakers"
+  { "Alchemy",                 "Ian Boddy",            "Drive" },
+  { "The Chase",               "Propaganda",           "A Secret Wish" },
+  { "Network 23",              "Tangerine Dream",      "Exit" },
+  { "Czardasz",                        "Robert Pieculewicz",   "Czardasz" },
+  { "21st Century Common Man", "Tangerine Dream",      "Tyger" },
+  { "Voyager",                 "The Alan Parsons Project","Pyramid" },
+  { "Twilight Painter",                "Tangerine Dream",      "Heartbreakers" }
 };
 static int helpscreen_musicpos;
 
 void DrawHelpScreenElAction(int start)
 {
   int i = 0, j = 0;
-  int frame, delay, graphic;
+  int frame, graphic;
   int xstart = SX+16, ystart = SY+64+2*32, ystep = TILEY+4;
 
   while(helpscreen_action[j] != HA_END)
@@ -355,8 +493,7 @@ void DrawHelpScreenElAction(int start)
       helpscreen_frame[i-start] = helpscreen_action[j++]-1;
     }
 
-    delay = helpscreen_action[j++];
-    helpscreen_delay[i-start] = delay;
+    helpscreen_delay[i-start] = helpscreen_action[j++] - 1;
 
     if (helpscreen_action[j] == HA_NEXT)
     {
@@ -372,82 +509,65 @@ void DrawHelpScreenElAction(int start)
     }
     j++;
 
-    DrawGraphicExtHiRes(drawto,gc,xstart,ystart+(i-start)*ystep,
-                       graphic+frame);
+    DrawGraphicExt(drawto, gc, xstart, ystart+(i-start)*ystep, graphic+frame);
     i++;
   }
 
-  redraw_tiles += 28;
   for(i=2;i<16;i++)
-    redraw[0][i] = redraw[1][i] = TRUE;
-  redraw_mask |= REDRAW_TILES;
+  {
+    MarkTileDirty(0,i);
+    MarkTileDirty(1,i);
+  }
 }
 
 void DrawHelpScreenElText(int start)
 {
   int i;
-  int xstart = SX+56, ystart = SY+65+2*32, ystep = TILEY+4;
-  char text[FULL_SXSIZE/FONT2_XSIZE+10];
+  int xstart = SX + 56, ystart = SY + 65 + 2 * 32, ystep = TILEY + 4;
+  int ybottom = SYSIZE - 20;
 
   ClearWindow();
-  DrawText(SX+16, SY+8,  "ROCKS'N'DIAMONDS",FS_BIG,FC_YELLOW);
-  DrawText(SX+25+16, SY+46, "Copyright ^1995 by Holger Schemel",
-          FS_SMALL,FC_RED);
+  DrawHeadline();
 
-  sprintf(text,"The game elements:");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+100,
-          text,FS_SMALL,FC_GREEN);
+  DrawTextFCentered(100, FC_GREEN, "The game elements:");
 
-  for(i=start;i<start+MAX_HELPSCREEN_ELS && i<num_helpscreen_els;i++)
+  for(i=start; i < start + MAX_HELPSCREEN_ELS && i < num_helpscreen_els; i++)
   {
-    DrawText(xstart,ystart+(i-start)*ystep+(*helpscreen_eltext[i][1] ? 0 : 8),
-            helpscreen_eltext[i][0],FS_SMALL,FC_YELLOW);
-    DrawText(xstart,ystart+(i-start)*ystep+16,
-            helpscreen_eltext[i][1],FS_SMALL,FC_YELLOW);
+    DrawText(xstart,
+            ystart + (i - start) * ystep + (*helpscreen_eltext[i][1] ? 0 : 8),
+            helpscreen_eltext[i][0], FS_SMALL, FC_YELLOW);
+    DrawText(xstart, ystart + (i - start) * ystep + 16,
+            helpscreen_eltext[i][1], FS_SMALL, FC_YELLOW);
   }
 
-  sprintf(text,"Press any key or button for next page");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+SYSIZE-20,
-          text,FS_SMALL,FC_BLUE);
+  DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for next page");
 }
 
 void DrawHelpScreenMusicText(int num)
 {
   int ystart = 150, ystep = 30;
-  char text[FULL_SXSIZE/FONT2_XSIZE+10];
+  int ybottom = SYSIZE - 20;
 
   FadeSounds();
   ClearWindow();
-  DrawText(SX+16, SY+8,  "ROCKS'N'DIAMONDS",FS_BIG,FC_YELLOW);
-  DrawText(SX+25+16, SY+46, "Copyright ^1995 by Holger Schemel",
-          FS_SMALL,FC_RED);
-
-  sprintf(text,"The game background music loops:");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+100,
-          text,FS_SMALL,FC_GREEN);
-
-  sprintf(text,"Excerpt from");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+ystart+0*ystep,
-          text,FS_SMALL,FC_YELLOW);
-  sprintf(text,"%c%s%c",'\"',helpscreen_music[num][0],'\"');
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+ystart+1*ystep,
-          text,FS_SMALL,FC_RED);
-  sprintf(text,"by");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+ystart+2*ystep,
-          text,FS_SMALL,FC_YELLOW);
-  sprintf(text,"%s",helpscreen_music[num][1]);
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+ystart+3*ystep,
-          text,FS_SMALL,FC_RED);
-  sprintf(text,"from the album");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+ystart+4*ystep,
-          text,FS_SMALL,FC_YELLOW);
-  sprintf(text,"%c%s%c",'\"',helpscreen_music[num][2],'\"');
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+ystart+5*ystep,
-          text,FS_SMALL,FC_RED);
-
-  sprintf(text,"Press any key or button for next page");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+SYSIZE-20,
-          text,FS_SMALL,FC_BLUE);
+  DrawHeadline();
+
+  DrawTextFCentered(100, FC_GREEN, "The game background music loops:");
+
+  DrawTextFCentered(ystart + 0 * ystep, FC_YELLOW,
+                   "Excerpt from");
+  DrawTextFCentered(ystart + 1 * ystep, FC_RED, "\"%s\"",
+                   helpscreen_music[num][0]);
+  DrawTextFCentered(ystart + 2 * ystep, FC_YELLOW,
+                   "by");
+  DrawTextFCentered(ystart + 3 * ystep, FC_RED,
+                   "%s", helpscreen_music[num][1]);
+  DrawTextFCentered(ystart + 4 * ystep, FC_YELLOW,
+                   "from the album");
+  DrawTextFCentered(ystart + 5 * ystep, FC_RED, "\"%s\"",
+                   helpscreen_music[num][2]);
+
+  DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for next page");
 
   PlaySoundLoop(background_loop[num]);
 }
@@ -455,46 +575,65 @@ void DrawHelpScreenMusicText(int num)
 void DrawHelpScreenCreditsText()
 {
   int ystart = 150, ystep = 30;
-  char text[FULL_SXSIZE/FONT2_XSIZE+10];
+  int ybottom = SYSIZE - 20;
 
   FadeSounds();
   ClearWindow();
-  DrawText(SX+16, SY+8,  "ROCKS'N'DIAMONDS",FS_BIG,FC_YELLOW);
-  DrawText(SX+25+16, SY+46, "Copyright ^1995 by Holger Schemel",
-          FS_SMALL,FC_RED);
-
-  sprintf(text,"Program information:");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+100,
-          text,FS_SMALL,FC_GREEN);
-
-  sprintf(text,"This game is Freeware!");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+ystart+0*ystep,
-          text,FS_SMALL,FC_YELLOW);
-  sprintf(text,"If you like it, send e-mail to:");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+ystart+1*ystep,
-          text,FS_SMALL,FC_YELLOW);
-  sprintf(text,"aeglos@valinor.owl.de");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+ystart+2*ystep,
-          text,FS_SMALL,FC_RED);
-  sprintf(text,"or SnailMail to:");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+ystart+3*ystep,
-          text,FS_SMALL,FC_YELLOW);
-  sprintf(text,"Holger Schemel");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+ystart+4*ystep,
-          text,FS_SMALL,FC_RED);
-  sprintf(text,"Sennehof 28");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+ystart+4*ystep+20,
-          text,FS_SMALL,FC_RED);
-  sprintf(text,"33659 Bielefeld");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+ystart+4*ystep+40,
-          text,FS_SMALL,FC_RED);
-  sprintf(text,"Germany");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+ystart+4*ystep+60,
-          text,FS_SMALL,FC_RED);
-
-  sprintf(text,"Press any key or button for main menu");
-  DrawText(SX+(SXSIZE-strlen(text)*FONT2_XSIZE)/2,SY+SYSIZE-20,
-          text,FS_SMALL,FC_BLUE);
+  DrawHeadline();
+
+  DrawTextFCentered(100, FC_GREEN,
+                   "Credits:");
+  DrawTextFCentered(ystart + 0 * ystep, FC_YELLOW,
+                   "DOS/Windows port of the game:");
+  DrawTextFCentered(ystart + 1 * ystep, FC_RED,
+                   "Guido Schulz");
+  DrawTextFCentered(ystart + 2 * ystep, FC_YELLOW,
+                   "Additional toons:");
+  DrawTextFCentered(ystart + 3 * ystep, FC_RED,
+                   "Karl Hörnell");
+  DrawTextFCentered(ystart + 5 * ystep, FC_YELLOW,
+                   "...and many thanks to all contributors");
+  DrawTextFCentered(ystart + 6 * ystep, FC_YELLOW,
+                   "of new levels!");
+
+  DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for next page");
+}
+
+void DrawHelpScreenContactText()
+{
+  int ystart = 150, ystep = 30;
+  int ybottom = SYSIZE - 20;
+
+  ClearWindow();
+  DrawHeadline();
+
+  DrawTextFCentered(100, FC_GREEN, "Program information:");
+
+  DrawTextFCentered(ystart + 0 * ystep, FC_YELLOW,
+                   "This game is Freeware!");
+  DrawTextFCentered(ystart + 1 * ystep, FC_YELLOW,
+                   "If you like it, send e-mail to:");
+  DrawTextFCentered(ystart + 2 * ystep, FC_RED,
+                   "aeglos@valinor.owl.de");
+  DrawTextFCentered(ystart + 3 * ystep, FC_YELLOW,
+                   "or SnailMail to:");
+  DrawTextFCentered(ystart + 4 * ystep + 0, FC_RED,
+                   "Holger Schemel");
+  DrawTextFCentered(ystart + 4 * ystep + 20, FC_RED,
+                   "Oststrasse 11a");
+  DrawTextFCentered(ystart + 4 * ystep + 40, FC_RED,
+                   "33604 Bielefeld");
+  DrawTextFCentered(ystart + 4 * ystep + 60, FC_RED,
+                   "Germany");
+
+  DrawTextFCentered(ystart + 7 * ystep, FC_YELLOW,
+                   "If you have created new levels,");
+  DrawTextFCentered(ystart + 8 * ystep, FC_YELLOW,
+                   "send them to me to include them!");
+  DrawTextFCentered(ystart + 9 * ystep, FC_YELLOW,
+                   ":-)");
+
+  DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for main menu");
 }
 
 void DrawHelpScreen()
@@ -517,7 +656,7 @@ void DrawHelpScreen()
 
 void HandleHelpScreen(int button)
 {
-  static long hs_delay = 0;
+  static unsigned long hs_delay = 0;
   int num_helpscreen_els_pages =
     (num_helpscreen_els + MAX_HELPSCREEN_ELS-1) / MAX_HELPSCREEN_ELS;
   int button_released = !button;
@@ -525,7 +664,7 @@ void HandleHelpScreen(int button)
 
   if (button_released)
   {
-    if (helpscreen_state<num_helpscreen_els_pages-1)
+    if (helpscreen_state < num_helpscreen_els_pages - 1)
     {
       for(i=0;i<MAX_HELPSCREEN_ELS;i++)
        helpscreen_step[i] = helpscreen_frame[i] = helpscreen_delay[i] = 0;
@@ -533,16 +672,21 @@ void HandleHelpScreen(int button)
       DrawHelpScreenElText(helpscreen_state*MAX_HELPSCREEN_ELS);
       DrawHelpScreenElAction(helpscreen_state*MAX_HELPSCREEN_ELS);
     }
-    else if (helpscreen_state<num_helpscreen_els_pages+num_bg_loops-1)
+    else if (helpscreen_state < num_helpscreen_els_pages + num_bg_loops - 1)
     {
       helpscreen_state++;
-      DrawHelpScreenMusicText(helpscreen_state-num_helpscreen_els_pages);
+      DrawHelpScreenMusicText(helpscreen_state - num_helpscreen_els_pages);
     }
-    else if (helpscreen_state==num_helpscreen_els_pages+num_bg_loops-1)
+    else if (helpscreen_state == num_helpscreen_els_pages + num_bg_loops - 1)
     {
       helpscreen_state++;
       DrawHelpScreenCreditsText();
     }
+    else if (helpscreen_state == num_helpscreen_els_pages + num_bg_loops)
+    {
+      helpscreen_state++;
+      DrawHelpScreenContactText();
+    }
     else
     {
       FadeSounds();
@@ -552,7 +696,7 @@ void HandleHelpScreen(int button)
   }
   else
   {
-    if (DelayReached(&hs_delay,3))
+    if (DelayReached(&hs_delay,GAME_FRAME_DELAY * 2))
     {
       if (helpscreen_state<num_helpscreen_els_pages)
        DrawHelpScreenElAction(helpscreen_state*MAX_HELPSCREEN_ELS);
@@ -563,114 +707,181 @@ void HandleHelpScreen(int button)
   BackToFront();
 }
 
-void CheckCheat()
-{
-  int old_handicap = player.handicap;
-
-  if (!strcmp(player.alias_name,"Artsoft"))
-    player.handicap = MAX(0,leveldir[leveldir_nr].num_ready-1);
-
-  if (player.handicap != old_handicap)
-  {
-    SavePlayerInfo(PLAYER_LEVEL);
-    level_nr = player.handicap;
-  }
-}
-
 void HandleTypeName(int newxpos, KeySym key)
 {
   static int xpos = 0, ypos = 2;
-  unsigned char ascii;
 
   if (newxpos)
   {
     xpos = newxpos;
-    DrawText(SX+6*32,SY+ypos*32,player.alias_name,FS_BIG,FC_YELLOW);
-    DrawGraphic(xpos+6,ypos,GFX_KUGEL_ROT);
+    DrawText(SX + 6*32, SY + ypos*32, setup.player_name, FS_BIG, FC_YELLOW);
+    DrawGraphic(xpos + 6, ypos, GFX_KUGEL_ROT);
     return;
   }
 
-  if ((key>=XK_A && key<=XK_Z) || (key>=XK_a && key<=XK_z && 
-      xpos<MAX_NAMELEN-1))
+  if (((key >= XK_A && key <= XK_Z) || (key >= XK_a && key <= XK_z)) && 
+      xpos < MAX_NAMELEN - 1)
   {
-    if (key>=XK_A && key<=XK_Z)
-      ascii = 'A'+(char)(key-XK_A);
-    if (key>=XK_a && key<=XK_z)
-      ascii = 'a'+(char)(key-XK_a);
-    player.alias_name[xpos] = ascii;
-    player.alias_name[xpos+1] = 0;
+    char ascii;
+
+    if (key >= XK_A && key <= XK_Z)
+      ascii = 'A' + (char)(key - XK_A);
+    else
+      ascii = 'a' + (char)(key - XK_a);
+
+    setup.player_name[xpos] = ascii;
+    setup.player_name[xpos + 1] = 0;
     xpos++;
-    DrawTextExt(drawto,gc,SX+6*32,SY+ypos*32,
-               player.alias_name,FS_BIG,FC_YELLOW);
-    DrawTextExt(window,gc,SX+6*32,SY+ypos*32,
-               player.alias_name,FS_BIG,FC_YELLOW);
-    DrawGraphic(xpos+6,ypos,GFX_KUGEL_ROT);
+    DrawTextExt(drawto, gc, SX + 6*32, SY + ypos*32,
+               setup.player_name, FS_BIG, FC_YELLOW);
+    DrawTextExt(window, gc, SX + 6*32, SY + ypos*32,
+               setup.player_name, FS_BIG, FC_YELLOW);
+    DrawGraphic(xpos + 6, ypos, GFX_KUGEL_ROT);
   }
-  else if (key==XK_Delete && xpos>0)
+  else if ((key == XK_Delete || key == XK_BackSpace) && xpos > 0)
   {
-    player.alias_name[xpos] = 0;
     xpos--;
-    DrawGraphic(xpos+6,ypos,GFX_KUGEL_ROT);
-    DrawGraphic(xpos+7,ypos,GFX_LEERRAUM);
+    setup.player_name[xpos] = 0;
+    DrawGraphic(xpos + 6, ypos, GFX_KUGEL_ROT);
+    DrawGraphic(xpos + 7, ypos, GFX_LEERRAUM);
   }
-  else if (key==XK_Return && xpos>0)
+  else if (key == XK_Return && xpos > 0)
   {
-    DrawText(SX+6*32,SY+ypos*32,player.alias_name,FS_BIG,FC_RED);
-    DrawGraphic(xpos+6,ypos,GFX_LEERRAUM);
-    SavePlayerInfo(PLAYER_SETUP);
-    CheckCheat();
+    DrawText(SX + 6*32, SY + ypos*32, setup.player_name, FS_BIG, FC_RED);
+    DrawGraphic(xpos + 6, ypos, GFX_LEERRAUM);
 
+    SaveSetup();
     game_status = MAINMENU;
-    DrawMainMenu();
   }
+
   BackToFront();
 }
 
 void DrawChooseLevel()
 {
-  int i;
-
   CloseDoor(DOOR_CLOSE_2);
 
+  FadeToFront();
+  InitAnimation();
+  HandleChooseLevel(0,0, 0,0, MB_MENU_INITIALIZE);
+}
+
+static void drawChooseLevelList(int first_entry, int num_page_entries)
+{
+  int i;
+  char buffer[SCR_FIELDX];
+
   ClearWindow();
-  DrawText(SX,SY,"Level Directories",FS_BIG,FC_GREEN);
-  for(i=0;i<num_leveldirs;i++)
+  DrawText(SX, SY, "Level Directories", FS_BIG, FC_GREEN);
+
+  for(i=0; i<num_page_entries; i++)
   {
-    DrawText(SX+32,SY+(i+2)*32,leveldir[i].name,FS_BIG,FC_YELLOW);
-    DrawGraphic(0,i+2,GFX_KUGEL_BLAU);
+    strncpy(buffer, leveldir[first_entry + i].name , SCR_FIELDX - 1);
+    buffer[SCR_FIELDX - 1] = '\0';
+    DrawText(SX + 32, SY + (i + 2) * 32, buffer, FS_BIG, FC_YELLOW);
+    DrawGraphic(0, i + 2, GFX_KUGEL_BLAU);
   }
 
-  FadeToFront();
-  InitAnimation();
-  HandleChooseLevel(0,0,0,0,MB_MENU_MARK);
+  if (first_entry > 0)
+    DrawGraphic(0, 1, GFX_PFEIL_O);
+
+  if (first_entry + num_page_entries < num_leveldirs)
+    DrawGraphic(0, MAX_LEVEL_SERIES_ON_SCREEN + 1, GFX_PFEIL_U);
+}
+
+static void drawChooseLevelInfo(int leveldir_nr)
+{
+  XFillRectangle(display, drawto, gc, SX + 32, SY + 32, SXSIZE - 32, 32);
+  DrawTextFCentered(40, FC_RED, "%3d levels (%s)",
+                   leveldir[leveldir_nr].levels,
+                   leveldir[leveldir_nr].readonly ? "readonly" : "writable");
 }
 
 void HandleChooseLevel(int mx, int my, int dx, int dy, int button)
 {
   static int choice = 3;
+  static int first_entry = 0;
+  static unsigned long choose_delay = 0;
   static int redraw = TRUE;
-  int x = (mx+32-SX)/32, y = (my+32-SY)/32;
+  int x = (mx + 32 - SX) / 32, y = (my + 32 - SY) / 32;
+  int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
+  int num_page_entries;
+
+  if (num_leveldirs <= MAX_LEVEL_SERIES_ON_SCREEN)
+    num_page_entries = num_leveldirs;
+  else
+    num_page_entries = MAX_LEVEL_SERIES_ON_SCREEN - 1;
+
+  if (button == MB_MENU_INITIALIZE)
+  {
+    redraw = TRUE;
+    choice = leveldir_nr + 3 - first_entry;
+
+    if (choice > num_page_entries + 2)
+    {
+      choice = num_page_entries + 2;
+      first_entry = num_leveldirs - num_page_entries;
+    }
+
+    drawChooseLevelList(first_entry, num_page_entries);
+    drawChooseLevelInfo(leveldir_nr);
+  }
 
   if (redraw)
   {
-    DrawGraphic(0,choice-1,GFX_KUGEL_ROT);
+    DrawGraphic(0, choice - 1, GFX_KUGEL_ROT);
     redraw = FALSE;
   }
 
+  if (button == MB_MENU_INITIALIZE)
+    return;
+
   if (dx || dy)
   {
     if (dy)
     {
       x = 1;
-      y = choice+dy;
+      y = choice + dy;
     }
     else
       x = y = 0;
+  }
 
-    if (y<3)
-      y = 3;
-    else if (y>num_leveldirs+2)
-      y = num_leveldirs+2;
+  if (x == 1 && y == 2)
+  {
+    if (first_entry > 0 &&
+       (dy || DelayReached(&choose_delay, 150)))
+    {
+#if 0
+      first_entry--;
+#else
+      first_entry -= step;
+      if (first_entry < 0)
+       first_entry = 0;
+#endif
+      drawChooseLevelList(first_entry, num_page_entries);
+      drawChooseLevelInfo(first_entry);
+      DrawGraphic(0, choice - 1, GFX_KUGEL_ROT);
+      return;
+    }
+  }
+  else if (x == 1 && y > num_page_entries + 2)
+  {
+    if (first_entry + num_page_entries < num_leveldirs &&
+       (dy || DelayReached(&choose_delay, 150)))
+    {
+#if 0
+      first_entry++;
+#else
+      first_entry += step;
+      if (first_entry + num_page_entries > num_leveldirs)
+       first_entry = num_leveldirs - num_page_entries;
+#endif
+      drawChooseLevelList(first_entry, num_page_entries);
+      drawChooseLevelInfo(first_entry + num_page_entries - 1);
+      DrawGraphic(0, choice - 1, GFX_KUGEL_ROT);
+      return;
+    }
   }
 
   if (!mx && !my && !dx && !dy)
@@ -679,57 +890,62 @@ void HandleChooseLevel(int mx, int my, int dx, int dy, int button)
     y = choice;
   }
 
-  if (x==1 && y>=3 && y<=num_leveldirs+2)
+  if (x == 1 && y >= 3 && y <= num_page_entries + 2)
   {
     if (button)
     {
-      if (y!=choice)
+      if (y != choice)
       {
-        DrawGraphic(0,y-1,GFX_KUGEL_ROT);
-        DrawGraphic(0,choice-1,GFX_KUGEL_BLAU);
+        DrawGraphic(0, y - 1, GFX_KUGEL_ROT);
+        DrawGraphic(0, choice - 1, GFX_KUGEL_BLAU);
+       drawChooseLevelInfo(first_entry + y - 3);
+       choice = y;
       }
-      choice = y;
     }
     else
     {
-      player.leveldir_nr = leveldir_nr = y-3;
-      LoadPlayerInfo(PLAYER_LEVEL);
-      SavePlayerInfo(PLAYER_SETUP);
-      CheckCheat();
+      leveldir_nr = first_entry + y - 3;
+      level_nr =
+       getLastPlayedLevelOfLevelSeries(leveldir[leveldir_nr].filename);
+
+      SaveLevelSetup();
+      TapeErase();
 
       game_status = MAINMENU;
       DrawMainMenu();
       redraw = TRUE;
     }
   }
+
   BackToFront();
 
-  if (game_status==CHOOSELEVEL)
+  if (game_status == CHOOSELEVEL)
     DoAnimation();
 }
 
-void DrawHallOfFame(int pos)
+void DrawHallOfFame(int highlight_position)
 {
-  int y;
-  char txt[40];
+  int i;
 
   CloseDoor(DOOR_CLOSE_2);
 
-  if (pos<0) 
+  if (highlight_position < 0) 
     LoadScore(level_nr);
+
   ClearWindow();
-  DrawText(SX+64,SY+10,"Hall Of Fame",FS_BIG,FC_YELLOW);
-  sprintf(txt,"HighScores of Level %d",level_nr);
-  DrawText(SX+256-strlen(txt)*7,SY+48,txt,FS_SMALL,FC_RED);
-  for(y=0;y<MAX_SCORE_ENTRIES;y++)
+
+  DrawText(SX + 80, SY + 8, "Hall Of Fame", FS_BIG, FC_YELLOW);
+  DrawTextFCentered(46, FC_RED, "HighScores of Level %d", level_nr);
+
+  for(i=0; i<MAX_LEVEL_SERIES_ON_SCREEN; i++)
   {
-    DrawText(SX,SY+64+y*32,".................",FS_BIG,
-            (y==pos ? FC_RED : FC_GREEN));
-    DrawText(SX,SY+64+y*32,highscore[y].Name,FS_BIG,
-            (y==pos ? FC_RED : FC_GREEN));
-    DrawText(SX+12*32,SY+64+y*32,
-            int2str(highscore[y].Score,5),FS_BIG,
-            (y==pos ? FC_RED : FC_GREEN));
+    DrawText(SX, SY + 64 + i * 32, ".................", FS_BIG,
+            (i == highlight_position ? FC_RED : FC_GREEN));
+    DrawText(SX, SY + 64 + i * 32, highscore[i].Name, FS_BIG,
+            (i == highlight_position ? FC_RED : FC_GREEN));
+    DrawText(SX + 12 * 32, SY + 64 + i * 32,
+            int2str(highscore[i].Score, 5), FS_BIG,
+            (i == highlight_position ? FC_RED : FC_GREEN));
   }
 
   FadeToFront();
@@ -755,77 +971,55 @@ void HandleHallOfFame(int button)
 void DrawSetupScreen()
 {
   int i;
+  static struct setup
+  {
+    boolean *value;
+    char *text;
+  } setup_info[] =
+  {
+    { &setup.sound,            "Sound:",       },
+    { &setup.sound_loops,      " Sound Loops:" },
+    { &setup.sound_music,      " Game Music:"  },
+    { &setup.toons,            "Toons:"        },
+    { &setup.double_buffering, "Buffered gfx:" },
+    { &setup.scroll_delay,     "Scroll Delay:" },
+    { &setup.soft_scrolling,   "Soft Scroll.:" },
+    { &setup.fading,           "Fading:"       },
+    { &setup.quick_doors,      "Quick Doors:"  },
+    { &setup.autorecord,       "Auto-Record:"  },
+    { &setup.team_mode,                "Team-Mode:"    },
+    { NULL,                    "Input Devices" },
+    { NULL,                    ""              },
+    { NULL,                    "Exit"          },
+    { NULL,                    "Save and exit" }
+  };
 
   CloseDoor(DOOR_CLOSE_2);
-
   ClearWindow();
-  DrawText(SX+16, SY+16,  "SETUP",FS_BIG,FC_YELLOW);
-  DrawText(SX+32, SY+2*32,"Sound:",FS_BIG,FC_GREEN);
-  DrawText(SX+32, SY+3*32,"Sound loops:",FS_BIG,FC_GREEN);
-  DrawText(SX+32, SY+4*32,"Game music:",FS_BIG,FC_GREEN);
-  DrawText(SX+32, SY+5*32,"Toons:",FS_BIG,FC_GREEN);
-  DrawText(SX+32, SY+6*32,"Buffered gfx:",FS_BIG,FC_GREEN);
-  DrawText(SX+32, SY+7*32,"Fading:",FS_BIG,FC_GREEN);
-  DrawText(SX+32, SY+8*32,"Quick Doors:",FS_BIG,FC_GREEN);
-  DrawText(SX+32, SY+9*32,"Auto-Record:",FS_BIG,FC_GREEN);
-  DrawText(SX+32, SY+10*32,"Joystick:",FS_BIG,FC_GREEN);
-  DrawText(SX+32, SY+11*32,"Cal. Joystick",FS_BIG,FC_GREEN);
-
-  DrawText(SX+32, SY+13*32,"Exit",FS_BIG,FC_GREEN);
-  DrawText(SX+32, SY+14*32,"Save and exit",FS_BIG,FC_GREEN);
-
-  if (SETUP_SOUND_ON(player.setup))
-    DrawText(SX+14*32, SY+2*32,"on",FS_BIG,FC_YELLOW);
-  else
-    DrawText(SX+14*32, SY+2*32,"off",FS_BIG,FC_BLUE);
+  DrawText(SX+16, SY+16, "SETUP",FS_BIG,FC_YELLOW);
 
-  if (SETUP_SOUND_LOOPS_ON(player.setup))
-    DrawText(SX+14*32, SY+3*32,"on",FS_BIG,FC_YELLOW);
-  else
-    DrawText(SX+14*32, SY+3*32,"off",FS_BIG,FC_BLUE);
-
-  if (SETUP_SOUND_MUSIC_ON(player.setup))
-    DrawText(SX+14*32, SY+4*32,"on",FS_BIG,FC_YELLOW);
-  else
-    DrawText(SX+14*32, SY+4*32,"off",FS_BIG,FC_BLUE);
-
-  if (SETUP_TOONS_ON(player.setup))
-    DrawText(SX+14*32, SY+5*32,"on",FS_BIG,FC_YELLOW);
-  else
-    DrawText(SX+14*32, SY+5*32,"off",FS_BIG,FC_BLUE);
-
-  if (!SETUP_DIRECT_DRAW_ON(player.setup))
-    DrawText(SX+14*32, SY+6*32,"on",FS_BIG,FC_YELLOW);
-  else
-    DrawText(SX+14*32, SY+6*32,"off",FS_BIG,FC_BLUE);
-
-  if (SETUP_FADING_ON(player.setup))
-    DrawText(SX+14*32, SY+7*32,"on",FS_BIG,FC_YELLOW);
-  else
-    DrawText(SX+14*32, SY+7*32,"off",FS_BIG,FC_BLUE);
-
-  if (SETUP_QUICK_DOORS_ON(player.setup))
-    DrawText(SX+14*32, SY+8*32,"on",FS_BIG,FC_YELLOW);
-  else
-    DrawText(SX+14*32, SY+8*32,"off",FS_BIG,FC_BLUE);
+  for(i=SETUP_SCREEN_POS_START;i<=SETUP_SCREEN_POS_END;i++)
+  {
+    int base = i - SETUP_SCREEN_POS_START;
 
-  if (SETUP_RECORD_EACH_GAME_ON(player.setup))
-    DrawText(SX+14*32, SY+9*32,"on",FS_BIG,FC_YELLOW);
-  else
-    DrawText(SX+14*32, SY+9*32,"off",FS_BIG,FC_BLUE);
+    if (!(i >= SETUP_SCREEN_POS_EMPTY1 && i <= SETUP_SCREEN_POS_EMPTY2))
+    {
+      DrawGraphic(0,i,GFX_KUGEL_BLAU);
+      DrawText(SX+32,SY+i*32, setup_info[base].text, FS_BIG,FC_GREEN);
+    }
 
-  if (SETUP_2ND_JOYSTICK_ON(player.setup))
-    DrawText(SX+14*32, SY+10*32,"2nd",FS_BIG,FC_YELLOW);
-  else
-    DrawText(SX+14*32, SY+10*32,"1st",FS_BIG,FC_YELLOW);
+    if (setup_info[base].value)
+    {
+      int setting_value = *setup_info[base].value;
 
-  for(i=2;i<15;i++)
-    if (i!=12)
-      DrawGraphic(0,i,GFX_KUGEL_BLAU);
+      DrawText(SX+14*32, SY+i*32, (setting_value ? "on" : "off"),
+              FS_BIG, (setting_value ? FC_YELLOW : FC_BLUE));
+    }
+  }
 
   FadeToFront();
   InitAnimation();
-  HandleSetupScreen(0,0,0,0,MB_MENU_MARK);
+  HandleSetupScreen(0,0,0,0,MB_MENU_INITIALIZE);
 }
 
 void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
@@ -833,6 +1027,13 @@ void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
   static int choice = 3;
   static int redraw = TRUE;
   int x = (mx+32-SX)/32, y = (my+32-SY)/32;
+  int pos_start  = SETUP_SCREEN_POS_START  + 1;
+  int pos_empty1 = SETUP_SCREEN_POS_EMPTY1 + 1;
+  int pos_empty2 = SETUP_SCREEN_POS_EMPTY2 + 1;
+  int pos_end    = SETUP_SCREEN_POS_END    + 1;
+
+  if (button == MB_MENU_INITIALIZE)
+    redraw = TRUE;
 
   if (redraw)
   {
@@ -840,6 +1041,9 @@ void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
     redraw = FALSE;
   }
 
+  if (button == MB_MENU_INITIALIZE)
+    return;
+
   if (dx || dy)
   {
     if (dy)
@@ -850,13 +1054,13 @@ void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
     else
       x = y = 0;
 
-    if (y==13)
-      y = (dy>0 ? 14 : 12);
+    if (y >= pos_empty1 && y <= pos_empty2)
+      y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
 
-    if (y<3)
-      y = 3;
-    else if (y>15)
-      y = 15;
+    if (y < pos_start)
+      y = pos_start;
+    else if (y > pos_end)
+      y = pos_end;
   }
 
   if (!mx && !my && !dx && !dy)
@@ -865,7 +1069,8 @@ void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
     y = choice;
   }
 
-  if (x==1 && y>=3 && y<=15 && y!=13)
+  if (x==1 && y >= pos_start && y <= pos_end &&
+      !(y >= pos_empty1 && y <= pos_empty2))
   {
     if (button)
     {
@@ -882,87 +1087,134 @@ void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
 
       if (y==3 && sound_status==SOUND_AVAILABLE)
       {
-       if (SETUP_SOUND_ON(player.setup))
+       if (setup.sound)
+       {
          DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
+         DrawText(SX+14*32, SY+(yy+1)*32,"off",FS_BIG,FC_BLUE);
+         DrawText(SX+14*32, SY+(yy+2)*32,"off",FS_BIG,FC_BLUE);
+         setup.sound_loops = FALSE;
+         setup.sound_music = FALSE;
+       }
        else
          DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       player.setup ^= SETUP_SOUND;
+       setup.sound = !setup.sound;
       }
       else if (y==4 && sound_loops_allowed)
       {
-       if (SETUP_SOUND_LOOPS_ON(player.setup))
+       if (setup.sound_loops)
          DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
        else
+       {
          DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       player.setup ^= SETUP_SOUND_LOOPS;
+         DrawText(SX+14*32, SY+(yy-1)*32,"on ",FS_BIG,FC_YELLOW);
+         setup.sound = TRUE;
+       }
+       setup.sound_loops = !setup.sound_loops;
       }
       else if (y==5 && sound_loops_allowed)
       {
-       if (SETUP_SOUND_MUSIC_ON(player.setup))
+       if (setup.sound_music)
          DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
        else
+       {
          DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       player.setup ^= SETUP_SOUND_MUSIC;
+         DrawText(SX+14*32, SY+(yy-2)*32,"on ",FS_BIG,FC_YELLOW);
+         setup.sound = TRUE;
+       }
+       setup.sound_music = !setup.sound_music;
       }
       else if (y==6)
       {
-       if (SETUP_TOONS_ON(player.setup))
+       if (setup.toons)
          DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
        else
          DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       player.setup ^= SETUP_TOONS;
+       setup.toons = !setup.toons;
       }
       else if (y==7)
       {
-       if (!SETUP_DIRECT_DRAW_ON(player.setup))
+#if 0
+       if (setup.double_buffering)
          DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
        else
          DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       player.setup ^= SETUP_DIRECT_DRAW;
+       setup.double_buffering = !setup.double_buffering;
+       setup.direct_draw = !setup.double_buffering;
+#else
+       DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
+       setup.double_buffering = TRUE;
+       setup.direct_draw = !setup.double_buffering;
+#endif
       }
       else if (y==8)
       {
-       if (SETUP_FADING_ON(player.setup))
+       if (setup.scroll_delay)
          DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
        else
          DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       player.setup ^= SETUP_FADING;
+       setup.scroll_delay = !setup.scroll_delay;
       }
       else if (y==9)
       {
-       if (SETUP_QUICK_DOORS_ON(player.setup))
+       if (setup.soft_scrolling)
          DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
        else
          DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       player.setup ^= SETUP_QUICK_DOORS;
+       setup.soft_scrolling = !setup.soft_scrolling;
       }
       else if (y==10)
       {
-       if (SETUP_RECORD_EACH_GAME_ON(player.setup))
+       if (setup.fading)
          DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
        else
          DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       player.setup ^= SETUP_RECORD_EACH_GAME;
+       setup.fading = !setup.fading;
       }
       else if (y==11)
       {
-       if (SETUP_2ND_JOYSTICK_ON(player.setup))
-         DrawText(SX+14*32, SY+yy*32,"1st",FS_BIG,FC_YELLOW);
+       if (setup.quick_doors)
+         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
        else
-         DrawText(SX+14*32, SY+yy*32,"2nd",FS_BIG,FC_YELLOW);
-       player.setup ^= SETUP_2ND_JOYSTICK;
+         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
+       setup.quick_doors = !setup.quick_doors;
       }
       else if (y==12)
       {
-       CalibrateJoystick();
+       if (setup.autorecord)
+         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
+       else
+         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
+       setup.autorecord = !setup.autorecord;
+      }
+      else if (y==13)
+      {
+       if (setup.team_mode)
+         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
+       else
+         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
+       setup.team_mode = !setup.team_mode;
+      }
+      else if (y==14)
+      {
+       game_status = SETUPINPUT;
+       DrawSetupInputScreen();
        redraw = TRUE;
       }
-      else if (y==14 || y==15)
+      else if (y==pos_end-1 || y==pos_end)
       {
-        if (y==15)
+        if (y==pos_end)
        {
-         SavePlayerInfo(PLAYER_SETUP);
+         SaveSetup();
+
+         /*
          SaveJoystickData();
+         */
+
+#ifdef MSDOS
+         save_joystick_data(JOYSTICK_FILENAME);
+#endif
+
+
        }
 
        game_status = MAINMENU;
@@ -977,96 +1229,756 @@ void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
     DoAnimation();
 }
 
-void CalibrateJoystick()
+void DrawSetupInputScreen()
 {
-#ifdef __FreeBSD__
-  struct joystick joy_ctrl;
-#else
-  struct joystick_control
-  {
-    int buttons;
-    int x;
-    int y;
-  } joy_ctrl;
-#endif
+  ClearWindow();
+  DrawText(SX+16, SY+16, "SETUP INPUT", FS_BIG, FC_YELLOW);
 
-  int new_joystick_xleft, new_joystick_xright, new_joystick_xmiddle;
-  int new_joystick_yupper, new_joystick_ylower, new_joystick_ymiddle;
+  DrawGraphic(0, 2, GFX_KUGEL_BLAU);
+  DrawGraphic(0, 3, GFX_KUGEL_BLAU);
+  DrawGraphic(0, 4, GFX_KUGEL_BLAU);
+  DrawGraphic(0, 15, GFX_KUGEL_BLAU);
+  DrawGraphic(10, 2, GFX_PFEIL_L);
+  DrawGraphic(12, 2, GFX_PFEIL_R);
 
-  if (joystick_status==JOYSTICK_OFF)
-    goto error_out;
+  DrawText(SX+32, SY+2*32, "Player:", FS_BIG, FC_GREEN);
+  DrawText(SX+32, SY+3*32, "Device:", FS_BIG, FC_GREEN);
+  DrawText(SX+32, SY+15*32, "Exit", FS_BIG, FC_GREEN);
 
-  ClearWindow();
-  DrawText(SX+16, SY+7*32, "MOVE JOYSTICK TO",FS_BIG,FC_YELLOW);
-  DrawText(SX+16, SY+8*32, " THE UPPER LEFT ",FS_BIG,FC_YELLOW);
-  DrawText(SX+16, SY+9*32, "AND PRESS BUTTON",FS_BIG,FC_YELLOW);
-  BackToFront();
+  DrawTextFCentered(SYSIZE - 20, FC_BLUE,
+                   "Joysticks deactivated on this screen");
 
-#ifdef __FreeBSD__
-  joy_ctrl.b1 = joy_ctrl.b2 = 0;
-#else
-  joy_ctrl.buttons = 0;
-#endif
-  while(Joystick() & JOY_BUTTON);
-#ifdef __FreeBSD__
-  while(!(joy_ctrl.b1||joy_ctrl.b2))
-#else
-  while(!joy_ctrl.buttons)
-#endif
-  {
-    if (read(joystick_device, &joy_ctrl, sizeof(joy_ctrl)) != sizeof(joy_ctrl))
-    {
-      joystick_status=JOYSTICK_OFF;
-      goto error_out;
-    }
-    Delay(10000);
-  }
+  HandleSetupInputScreen(0,0, 0,0, MB_MENU_INITIALIZE);
+  FadeToFront();
+  InitAnimation();
+}
 
-  new_joystick_xleft = joy_ctrl.x;
-  new_joystick_yupper = joy_ctrl.y;
+static void setJoystickDeviceToNr(char *device_name, int device_nr)
+{
+  if (device_name == NULL)
+    return;
 
-  ClearWindow();
-  DrawText(SX+16, SY+7*32, "MOVE JOYSTICK TO",FS_BIG,FC_YELLOW);
-  DrawText(SX+32, SY+8*32, "THE LOWER RIGHT",FS_BIG,FC_YELLOW);
-  DrawText(SX+16, SY+9*32, "AND PRESS BUTTON",FS_BIG,FC_YELLOW);
-  BackToFront();
+  if (device_nr < 0 || device_nr >= MAX_PLAYERS)
+    device_nr = 0;
 
-#ifdef __FreeBSD__
-  joy_ctrl.b1 = joy_ctrl.b2 = 0;
-#else
-  joy_ctrl.buttons = 0;
-#endif
-  while(Joystick() & JOY_BUTTON);
-#ifdef __FreeBSD__
-  while(!(joy_ctrl.b1||joy_ctrl.b2))
-#else
-  while(!joy_ctrl.buttons)
-#endif
+  if (strlen(device_name) > 1)
   {
-    if (read(joystick_device, &joy_ctrl, sizeof(joy_ctrl)) != sizeof(joy_ctrl))
-    {
-      joystick_status=JOYSTICK_OFF;
-      goto error_out;
-    }
-    Delay(10000);
+    char c1 = device_name[strlen(device_name) - 1];
+    char c2 = device_name[strlen(device_name) - 2];
+
+    if (c1 >= '0' && c1 <= '9' && !(c2 >= '0' && c2 <= '9'))
+      device_name[strlen(device_name) - 1] = '0' + (char)(device_nr % 10);
   }
+  else
+    strncpy(device_name, joystick_device_name[device_nr], strlen(device_name));
+}
 
-  new_joystick_xright = joy_ctrl.x;
-  new_joystick_ylower = joy_ctrl.y;
+static void drawPlayerSetupInputInfo(int player_nr)
+{
+  int i;
+  static struct SetupKeyboardInfo custom_key;
+  static struct
+  {
+    KeySym *keysym;
+    char *text;
+  } custom[] =
+  {
+    { &custom_key.left,  "Joystick Left"  },
+    { &custom_key.right, "Joystick Right" },
+    { &custom_key.up,    "Joystick Up"    },
+    { &custom_key.down,  "Joystick Down"  },
+    { &custom_key.snap,  "Button 1"       },
+    { &custom_key.bomb,  "Button 2"       }
+  };
+  static char *joystick_name[MAX_PLAYERS] =
+  {
+    "Joystick1",
+    "Joystick2",
+    "Joystick3",
+    "Joystick4"
+  };
 
-  ClearWindow();
-  DrawText(SX+32, SY+16+7*32, "CENTER JOYSTICK",FS_BIG,FC_YELLOW);
-  DrawText(SX+16, SY+16+8*32, "AND PRESS BUTTON",FS_BIG,FC_YELLOW);
-  BackToFront();
+  custom_key = setup.input[player_nr].key;
 
-#ifdef __FreeBSD__
+  DrawText(SX+11*32, SY+2*32, int2str(player_nr + 1, 1), FS_BIG, FC_RED);
+  DrawGraphic(8, 2, GFX_SPIELER1 + player_nr);
+
+  if (setup.input[player_nr].use_joystick)
+  {
+    char *device_name = setup.input[player_nr].joy.device_name;
+
+    DrawText(SX+8*32, SY+3*32,
+            joystick_name[getJoystickNrFromDeviceName(device_name)],
+            FS_BIG, FC_YELLOW);
+    DrawText(SX+32, SY+4*32, "Calibrate", FS_BIG, FC_GREEN);
+  }
+  else
+  {
+    DrawText(SX+8*32, SY+3*32, "Keyboard ", FS_BIG, FC_YELLOW);
+    DrawText(SX+32, SY+4*32, "Customize", FS_BIG, FC_GREEN);
+  }
+
+  DrawText(SX+32, SY+5*32, "Actual Settings:", FS_BIG, FC_GREEN);
+  DrawGraphic(1, 6, GFX_PFEIL_L);
+  DrawGraphic(1, 7, GFX_PFEIL_R);
+  DrawGraphic(1, 8, GFX_PFEIL_O);
+  DrawGraphic(1, 9, GFX_PFEIL_U);
+  DrawText(SX+2*32, SY+6*32, ":", FS_BIG, FC_BLUE);
+  DrawText(SX+2*32, SY+7*32, ":", FS_BIG, FC_BLUE);
+  DrawText(SX+2*32, SY+8*32, ":", FS_BIG, FC_BLUE);
+  DrawText(SX+2*32, SY+9*32, ":", FS_BIG, FC_BLUE);
+  DrawText(SX+32, SY+10*32, "Snap Field:", FS_BIG, FC_BLUE);
+  DrawText(SX+32, SY+12*32, "Place Bomb:", FS_BIG, FC_BLUE);
+
+  for (i=0; i<6; i++)
+  {
+    int ypos = 6 + i + (i > 3 ? i-3 : 0);
+
+    DrawText(SX + 3*32, SY + ypos*32,
+            "              ", FS_BIG, FC_YELLOW);
+    DrawText(SX + 3*32, SY + ypos*32,
+            (setup.input[player_nr].use_joystick ?
+             custom[i].text :
+             getKeyNameFromKeySym(*custom[i].keysym)),
+            FS_BIG, FC_YELLOW);
+  }
+}
+
+void HandleSetupInputScreen(int mx, int my, int dx, int dy, int button)
+{
+  static int choice = 3;
+  static int player_nr = 0;
+  static int redraw = TRUE;
+  int x = (mx+32-SX)/32, y = (my+32-SY)/32;
+  int pos_start  = SETUPINPUT_SCREEN_POS_START  + 1;
+  int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1 + 1;
+  int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2 + 1;
+  int pos_end    = SETUPINPUT_SCREEN_POS_END    + 1;
+
+  if (button == MB_MENU_INITIALIZE)
+  {
+    drawPlayerSetupInputInfo(player_nr);
+    redraw = TRUE;
+  }
+
+  if (redraw)
+  {
+    DrawGraphic(0,choice-1,GFX_KUGEL_ROT);
+    redraw = FALSE;
+  }
+
+  if (button == MB_MENU_INITIALIZE)
+    return;
+
+  if (dx || dy)
+  {
+    if (dx && choice == 3)
+    {
+      x = (dx < 0 ? 11 : 13);
+      y = 3;
+    }
+    else if (dx && choice == 4)
+    {
+      button = MB_MENU_CHOICE;
+      x = 1;
+      y = 4;
+    }
+    else if (dy)
+    {
+      x = 1;
+      y = choice + dy;
+    }
+    else
+      x = y = 0;
+
+    if (y >= pos_empty1 && y <= pos_empty2)
+      y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
+
+    if (y < pos_start)
+      y = pos_start;
+    else if (y > pos_end)
+      y = pos_end;
+  }
+
+  if (!mx && !my && !dx && !dy)
+  {
+    x = 1;
+    y = choice;
+  }
+
+  if (y == 3 && ((x == 1 && !button) || ((x == 11 || x == 13) && button)))
+  {
+    static unsigned long delay = 0;
+
+    if (!DelayReached(&delay, 150))
+      goto out;
+
+    player_nr = (player_nr + (x == 11 ? -1 : +1) + MAX_PLAYERS) % MAX_PLAYERS;
+
+    drawPlayerSetupInputInfo(player_nr);
+  }
+  else if (x==1 && y >= pos_start && y <= pos_end &&
+          !(y >= pos_empty1 && y <= pos_empty2))
+  {
+    if (button)
+    {
+      if (y != choice)
+      {
+       DrawGraphic(0, y-1, GFX_KUGEL_ROT);
+       DrawGraphic(0, choice-1, GFX_KUGEL_BLAU);
+      }
+      choice = y;
+    }
+    else
+    {
+      if (y == 4)
+      {
+       char *device_name = setup.input[player_nr].joy.device_name;
+
+       if (!setup.input[player_nr].use_joystick)
+       {
+         int new_device_nr = (dx >= 0 ? 0 : MAX_PLAYERS - 1);
+
+         setJoystickDeviceToNr(device_name, new_device_nr);
+         setup.input[player_nr].use_joystick = TRUE;
+       }
+       else
+       {
+         int device_nr = getJoystickNrFromDeviceName(device_name);
+         int new_device_nr = device_nr + (dx >= 0 ? +1 : -1);
+
+         if (new_device_nr < 0 || new_device_nr >= MAX_PLAYERS)
+           setup.input[player_nr].use_joystick = FALSE;
+         else
+           setJoystickDeviceToNr(device_name, new_device_nr);
+       }
+
+
+       /*
+       InitJoysticks();
+       */
+
+
+#if 0
+       int one_joystick_nr       = (dx >= 0 ? 0 : 1);
+       int the_other_joystick_nr = (dx >= 0 ? 1 : 0);
+
+       if (setup.input[player_nr].use_joystick)
+       {
+         if (setup.input[player_nr].joystick_nr == one_joystick_nr)
+           setup.input[player_nr].joystick_nr = the_other_joystick_nr;
+         else
+           setup.input[player_nr].use_joystick = FALSE;
+       }
+       else
+       {
+         setup.input[player_nr].use_joystick = TRUE;
+         setup.input[player_nr].joystick_nr = one_joystick_nr;
+       }
+#endif
+
+       drawPlayerSetupInputInfo(player_nr);
+      }
+      else if (y == 5)
+      {
+       if (setup.input[player_nr].use_joystick)
+       {
+         InitJoysticks();
+         game_status = CALIBRATION;
+         CalibrateJoystick(player_nr);
+         game_status = SETUPINPUT;
+       }
+       else
+         CustomizeKeyboard(player_nr);
+
+       redraw = TRUE;
+      }
+      else if (y == pos_end)
+      {
+       InitJoysticks();
+
+       game_status = SETUP;
+       DrawSetupScreen();
+       redraw = TRUE;
+      }
+    }
+  }
+  BackToFront();
+
+  out:
+
+  if (game_status == SETUPINPUT)
+    DoAnimation();
+}
+
+void CustomizeKeyboard(int player_nr)
+{
+  int i;
+  int step_nr;
+  boolean finished = FALSE;
+  static struct SetupKeyboardInfo custom_key;
+  static struct
+  {
+    KeySym *keysym;
+    char *text;
+  } customize_step[] =
+  {
+    { &custom_key.left,  "Move Left"  },
+    { &custom_key.right, "Move Right" },
+    { &custom_key.up,    "Move Up"    },
+    { &custom_key.down,  "Move Down"  },
+    { &custom_key.snap,  "Snap Field" },
+    { &custom_key.bomb,  "Place Bomb" }
+  };
+
+  /* read existing key bindings from player setup */
+  custom_key = setup.input[player_nr].key;
+
+  ClearWindow();
+  DrawText(SX + 16, SY + 16, "Keyboard Input", FS_BIG, FC_YELLOW);
+
+  BackToFront();
+  InitAnimation();
+
+  step_nr = 0;
+  DrawText(SX, SY + (2+2*step_nr)*32,
+          customize_step[step_nr].text, FS_BIG, FC_RED);
+  DrawText(SX, SY + (2+2*step_nr+1)*32,
+          "Key:", FS_BIG, FC_RED);
+  DrawText(SX + 4*32, SY + (2+2*step_nr+1)*32,
+          getKeyNameFromKeySym(*customize_step[step_nr].keysym),
+          FS_BIG, FC_BLUE);
+
+  while(!finished)
+  {
+    if (XPending(display))     /* got event from X server */
+    {
+      XEvent event;
+
+      XNextEvent(display, &event);
+
+      switch(event.type)
+      {
+        case KeyPress:
+         {
+           KeySym key = XLookupKeysym((XKeyEvent *)&event,
+                                      ((XKeyEvent *)&event)->state);
+
+           if (key == XK_Escape || (key == XK_Return && step_nr == 6))
+           {
+             finished = TRUE;
+             break;
+           }
+
+           /* press 'Enter' to keep the existing key binding */
+           if (key == XK_Return || step_nr == 6)
+             key = *customize_step[step_nr].keysym;
+
+           /* check if key already used */
+           for (i=0; i<step_nr; i++)
+             if (*customize_step[i].keysym == key)
+               break;
+           if (i < step_nr)
+             break;
+
+           /* got new key binding */
+           *customize_step[step_nr].keysym = key;
+           DrawText(SX + 4*32, SY + (2+2*step_nr+1)*32,
+                    "             ", FS_BIG, FC_YELLOW);
+           DrawText(SX + 4*32, SY + (2+2*step_nr+1)*32,
+                    getKeyNameFromKeySym(key), FS_BIG, FC_YELLOW);
+           step_nr++;
+
+           /* un-highlight last query */
+           DrawText(SX, SY+(2+2*(step_nr-1))*32,
+                    customize_step[step_nr-1].text, FS_BIG, FC_GREEN);
+           DrawText(SX, SY+(2+2*(step_nr-1)+1)*32,
+                    "Key:", FS_BIG, FC_GREEN);
+
+           /* press 'Enter' to leave */
+           if (step_nr == 6)
+           {
+             DrawText(SX + 16, SY + 15*32+16,
+                      "Press Enter", FS_BIG, FC_YELLOW);
+             break;
+           }
+
+           /* query next key binding */
+           DrawText(SX, SY+(2+2*step_nr)*32,
+                    customize_step[step_nr].text, FS_BIG, FC_RED);
+           DrawText(SX, SY+(2+2*step_nr+1)*32,
+                    "Key:", FS_BIG, FC_RED);
+           DrawText(SX + 4*32, SY+(2+2*step_nr+1)*32,
+                    getKeyNameFromKeySym(*customize_step[step_nr].keysym),
+                    FS_BIG, FC_BLUE);
+         }
+         break;
+
+        case KeyRelease:
+         key_joystick_mapping = 0;
+         break;
+
+        default:
+         HandleOtherEvents(&event);
+         break;
+      }
+    }
+
+    BackToFront();
+    DoAnimation();
+
+    /* don't eat all CPU time */
+    Delay(10);
+  }
+
+  /* write new key bindings back to player setup */
+  setup.input[player_nr].key = custom_key;
+
+  StopAnimation();
+  DrawSetupInputScreen();
+}
+
+void CalibrateJoystick(int player_nr)
+{
+#ifdef __FreeBSD__
+  struct joystick joy_ctrl;
+#else
+  struct joystick_control
+  {
+    int buttons;
+    int x;
+    int y;
+  } joy_ctrl;
+#endif
+
+#ifndef MSDOS
+  int new_joystick_xleft = 128, new_joystick_xright = 128;
+  int new_joystick_yupper = 128, new_joystick_ylower = 128;
+  int new_joystick_xmiddle, new_joystick_ymiddle;
+#else
+  int calibration_step = 1;
+#endif
+
+  int joystick_fd = stored_player[player_nr].joystick_fd;
+  int x, y, last_x, last_y, xpos = 8, ypos = 3;
+  boolean check[3][3];
+  int check_remaining;
+  int joy_value;
+  int result = -1;
+
+  if (joystick_status == JOYSTICK_OFF ||
+      joystick_fd < 0 ||
+      !setup.input[player_nr].use_joystick)
+    goto error_out;
+
+  ClearWindow();
+
+#ifndef MSDOS
+  DrawText(SX,      SY +  6*32, " ROTATE JOYSTICK ", FS_BIG, FC_YELLOW);
+  DrawText(SX,      SY +  7*32, "IN ALL DIRECTIONS", FS_BIG, FC_YELLOW);
+  DrawText(SX + 16, SY +  9*32, "  IF ALL BALLS  ",  FS_BIG, FC_YELLOW);
+  DrawText(SX,      SY + 10*32, "   ARE YELLOW,   ", FS_BIG, FC_YELLOW);
+  DrawText(SX,      SY + 11*32, "PRESS ANY BUTTON!", FS_BIG, FC_YELLOW);
+  check_remaining = 3 * 3;
+#else
+  DrawText(SX,      SY +  7*32, "  MOVE JOYSTICK  ", FS_BIG, FC_YELLOW);
+  DrawText(SX + 16, SY +  8*32, "       TO       ",  FS_BIG, FC_YELLOW);
+  DrawText(SX,      SY +  9*32, " CENTER POSITION ",  FS_BIG, FC_YELLOW);
+  DrawText(SX,      SY + 10*32, "       AND       ", FS_BIG, FC_YELLOW);
+  DrawText(SX,      SY + 11*32, "PRESS ANY BUTTON!", FS_BIG, FC_YELLOW);
+  check_remaining = 0;
+#endif
+
+  for(y=0; y<3; y++)
+  {
+    for(x=0; x<3; x++)
+    {
+      check[x][y] = FALSE;
+      DrawGraphic(xpos + x - 1, ypos + y - 1, GFX_KUGEL_BLAU);
+    }
+  }
+
+  joy_value = Joystick(player_nr);
+  last_x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
+  last_y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
+  DrawGraphic(xpos + last_x, ypos + last_y, GFX_KUGEL_ROT);
+
+  BackToFront();
+
+#ifdef __FreeBSD__
+  joy_ctrl.b1 = joy_ctrl.b2 = 0;
+#else
+  joy_ctrl.buttons = 0;
+#endif
+
+  while(Joystick(player_nr) & JOY_BUTTON);
+
+  InitAnimation();
+
+  while(result < 0)
+  {
+    if (XPending(display))     /* got event from X server */
+    {
+      XEvent event;
+
+      XNextEvent(display, &event);
+
+      switch(event.type)
+      {
+       case KeyPress:
+         switch(XLookupKeysym((XKeyEvent *)&event,
+                              ((XKeyEvent *)&event)->state))
+         {
+           case XK_Return:
+             if (check_remaining == 0)
+               result = 1;
+             break;
+
+           case XK_Escape:
+             result = 0;
+             break;
+
+           default:
+             break;
+         }
+         break;
+
+       case KeyRelease:
+         key_joystick_mapping = 0;
+         break;
+
+       default:
+         HandleOtherEvents(&event);
+         break;
+      }
+    }
+
+#ifndef MSDOS
+    if (read(joystick_fd, &joy_ctrl, sizeof(joy_ctrl)) != sizeof(joy_ctrl))
+    {
+      joystick_status = JOYSTICK_OFF;
+      goto error_out;
+    }
+
+    new_joystick_xleft  = MIN(new_joystick_xleft,  joy_ctrl.x);
+    new_joystick_xright = MAX(new_joystick_xright, joy_ctrl.x);
+    new_joystick_yupper = MIN(new_joystick_yupper, joy_ctrl.y);
+    new_joystick_ylower = MAX(new_joystick_ylower, joy_ctrl.y);
+
+    new_joystick_xmiddle =
+      new_joystick_xleft + (new_joystick_xright - new_joystick_xleft) / 2;
+    new_joystick_ymiddle =
+      new_joystick_yupper + (new_joystick_ylower - new_joystick_yupper) / 2;
+
+    setup.input[player_nr].joy.xleft = new_joystick_xleft;
+    setup.input[player_nr].joy.yupper = new_joystick_yupper;
+    setup.input[player_nr].joy.xright = new_joystick_xright;
+    setup.input[player_nr].joy.ylower = new_joystick_ylower;
+    setup.input[player_nr].joy.xmiddle = new_joystick_xmiddle;
+    setup.input[player_nr].joy.ymiddle = new_joystick_ymiddle;
+
+    CheckJoystickData();
+#endif
+
+    joy_value = Joystick(player_nr);
+
+    if (joy_value & JOY_BUTTON && check_remaining == 0)
+    {
+      result = 1;
+
+#ifdef MSDOS
+      if (calibration_step == 1)
+      {
+       remove_joystick();
+       InitJoysticks();
+      }
+      else if (calibrate_joystick(joystick_fd) != 0)
+      {
+       joystick_status = JOYSTICK_OFF;
+       goto error_out;
+      }
+
+      if (joy[joystick_fd].flags & JOYFLAG_CALIBRATE)
+      {
+       calibration_step++;
+       result = -1;
+
+       DrawText(SX,      SY +  7*32, "  MOVE JOYSTICK  ", FS_BIG, FC_YELLOW);
+       DrawText(SX + 16, SY +  8*32, "       TO       ",  FS_BIG, FC_YELLOW);
+
+       if (calibration_step == 2)
+         DrawText(SX + 16, SY + 9*32," THE UPPER LEFT ",  FS_BIG, FC_YELLOW);
+       else
+         DrawText(SX,      SY + 9*32," THE LOWER RIGHT ", FS_BIG, FC_YELLOW);
+
+       DrawText(SX,      SY + 10*32, "       AND       ", FS_BIG, FC_YELLOW);
+       DrawText(SX,      SY + 11*32, "PRESS ANY BUTTON!", FS_BIG, FC_YELLOW);
+
+       BackToFront();
+
+       while(Joystick(player_nr) & JOY_BUTTON)
+         DoAnimation();
+      }
+#endif
+    }
+
+    x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
+    y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
+
+    if (x != last_x || y != last_y)
+    {
+#ifndef MSDOS
+      DrawGraphic(xpos + last_x, ypos + last_y, GFX_KUGEL_GELB);
+#else
+      DrawGraphic(xpos + last_x, ypos + last_y, GFX_KUGEL_BLAU);
+#endif
+      DrawGraphic(xpos + x,      ypos + y,      GFX_KUGEL_ROT);
+
+      last_x = x;
+      last_y = y;
+
+      if (check_remaining > 0 && !check[x+1][y+1])
+      {
+       check[x+1][y+1] = TRUE;
+       check_remaining--;
+      }
+
+#if 0
+      printf("LEFT / MIDDLE / RIGHT == %d / %d / %d\n",
+            setup.input[player_nr].joy.xleft,
+            setup.input[player_nr].joy.xmiddle,
+            setup.input[player_nr].joy.xright);
+      printf("UP / MIDDLE / DOWN == %d / %d / %d\n",
+            setup.input[player_nr].joy.yupper,
+            setup.input[player_nr].joy.ymiddle,
+            setup.input[player_nr].joy.ylower);
+#endif
+
+    }
+
+    BackToFront();
+    DoAnimation();
+
+    /* don't eat all CPU time */
+    Delay(10);
+  }
+
+  StopAnimation();
+
+  DrawSetupInputScreen();
+  while(Joystick(player_nr) & JOY_BUTTON);
+  return;
+
+  error_out:
+
+  ClearWindow();
+  DrawText(SX + 16, SY + 6*32, "  JOYSTICK NOT  ", FS_BIG, FC_YELLOW);
+  DrawText(SX,      SY + 7*32, "    AVAILABLE    ", FS_BIG, FC_YELLOW);
+  BackToFront();
+  Delay(2000);
+  DrawSetupInputScreen();
+}
+
+
+
+#if 0
+
+void CalibrateJoystick_OLD()
+{
+#ifdef __FreeBSD__
+  struct joystick joy_ctrl;
+#else
+  struct joystick_control
+  {
+    int buttons;
+    int x;
+    int y;
+  } joy_ctrl;
+#endif
+
+#ifdef MSDOS
+  char joy_nr[4];
+#endif
+
+  int joystick_nr = setup.input[0].joystick_nr;
+  int new_joystick_xleft, new_joystick_xright, new_joystick_xmiddle;
+  int new_joystick_yupper, new_joystick_ylower, new_joystick_ymiddle;
+
+  if (joystick_status == JOYSTICK_OFF)
+    goto error_out;
+
+#ifndef MSDOS
+  ClearWindow();
+  DrawText(SX+16, SY+7*32, "MOVE JOYSTICK TO",FS_BIG,FC_YELLOW);
+  DrawText(SX+16, SY+8*32, " THE UPPER LEFT ",FS_BIG,FC_YELLOW);
+  DrawText(SX+16, SY+9*32, "AND PRESS BUTTON",FS_BIG,FC_YELLOW);
+  BackToFront();
+
+#ifdef __FreeBSD__
+  joy_ctrl.b1 = joy_ctrl.b2 = 0;
+#else
+  joy_ctrl.buttons = 0;
+#endif
+  while(Joystick() & JOY_BUTTON);
+#ifdef __FreeBSD__
+  while(!(joy_ctrl.b1 || joy_ctrl.b2))
+#else
+  while(!joy_ctrl.buttons)
+#endif
+  {
+    if (read(joystick_device, &joy_ctrl, sizeof(joy_ctrl)) != sizeof(joy_ctrl))
+    {
+      joystick_status=JOYSTICK_OFF;
+      goto error_out;
+    }
+    Delay(10);
+  }
+
+  new_joystick_xleft = joy_ctrl.x;
+  new_joystick_yupper = joy_ctrl.y;
+
+  ClearWindow();
+  DrawText(SX+16, SY+7*32, "MOVE JOYSTICK TO",FS_BIG,FC_YELLOW);
+  DrawText(SX+32, SY+8*32, "THE LOWER RIGHT",FS_BIG,FC_YELLOW);
+  DrawText(SX+16, SY+9*32, "AND PRESS BUTTON",FS_BIG,FC_YELLOW);
+  BackToFront();
+
+#ifdef __FreeBSD__
+  joy_ctrl.b1 = joy_ctrl.b2 = 0;
+#else
+  joy_ctrl.buttons = 0;
+#endif
+  while(Joystick() & JOY_BUTTON);
+#ifdef __FreeBSD__
+  while(!(joy_ctrl.b1 || joy_ctrl.b2))
+#else
+  while(!joy_ctrl.buttons)
+#endif
+  {
+    if (read(joystick_device, &joy_ctrl, sizeof(joy_ctrl)) != sizeof(joy_ctrl))
+    {
+      joystick_status=JOYSTICK_OFF;
+      goto error_out;
+    }
+    Delay(10);
+  }
+
+  new_joystick_xright = joy_ctrl.x;
+  new_joystick_ylower = joy_ctrl.y;
+
+  ClearWindow();
+  DrawText(SX+32, SY+16+7*32, "CENTER JOYSTICK",FS_BIG,FC_YELLOW);
+  DrawText(SX+16, SY+16+8*32, "AND PRESS BUTTON",FS_BIG,FC_YELLOW);
+  BackToFront();
+
+#ifdef __FreeBSD__
   joy_ctrl.b1 = joy_ctrl.b2 = 0;
 #else
   joy_ctrl.buttons = 0;
 #endif
   while(Joystick() & JOY_BUTTON);
 #ifdef __FreeBSD__
-  while(!(joy_ctrl.b1||joy_ctrl.b2))
+  while(!(joy_ctrl.b1 || joy_ctrl.b2))
 #else
   while(!joy_ctrl.buttons)
 #endif
@@ -1076,18 +1988,18 @@ void CalibrateJoystick()
       joystick_status=JOYSTICK_OFF;
       goto error_out;
     }
-    Delay(10000);
+    Delay(10);
   }
 
   new_joystick_xmiddle = joy_ctrl.x;
   new_joystick_ymiddle = joy_ctrl.y;
 
-  joystick[joystick_nr].xleft = new_joystick_xleft;
-  joystick[joystick_nr].yupper = new_joystick_yupper;
-  joystick[joystick_nr].xright = new_joystick_xright;
-  joystick[joystick_nr].ylower = new_joystick_ylower;
-  joystick[joystick_nr].xmiddle = new_joystick_xmiddle;
-  joystick[joystick_nr].ymiddle = new_joystick_ymiddle;
+  setup.input[player_nr].joy.xleft = new_joystick_xleft;
+  setup.input[player_nr].joy.yupper = new_joystick_yupper;
+  setup.input[player_nr].joy.xright = new_joystick_xright;
+  setup.input[player_nr].joy.ylower = new_joystick_ylower;
+  setup.input[player_nr].joy.xmiddle = new_joystick_xmiddle;
+  setup.input[player_nr].joy.ymiddle = new_joystick_ymiddle;
 
   CheckJoystickData();
 
@@ -1095,152 +2007,304 @@ void CalibrateJoystick()
   while(Joystick() & JOY_BUTTON);
   return;
 
+#endif
   error_out:
 
+#ifdef MSDOS
+  joy_nr[0] = '#';
+  joy_nr[1] = SETUP_2ND_JOYSTICK_ON(local_player->setup)+49;
+  joy_nr[2] = '\0';
+
+  remove_joystick();
+  ClearWindow();
+  DrawText(SX+32, SY+7*32, "CENTER JOYSTICK",FS_BIG,FC_YELLOW);
+  DrawText(SX+16+7*32, SY+8*32, joy_nr, FS_BIG,FC_YELLOW);
+  DrawText(SX+32, SY+9*32, "AND PRESS A KEY",FS_BIG,FC_YELLOW);
+  BackToFront();
+
+  for(clear_keybuf();!keypressed(););
+  install_joystick(JOY_TYPE_2PADS);
+
+  ClearWindow();
+  DrawText(SX+16, SY+7*32, "MOVE JOYSTICK TO",FS_BIG,FC_YELLOW);
+  DrawText(SX+16, SY+8*32, " THE UPPER LEFT ",FS_BIG,FC_YELLOW);
+  DrawText(SX+32, SY+9*32, "AND PRESS A KEY",FS_BIG,FC_YELLOW);
+  BackToFront();
+
+  for(clear_keybuf();!keypressed(););
+  calibrate_joystick(SETUP_2ND_JOYSTICK_ON(local_player->setup));
+
+  ClearWindow();
+  DrawText(SX+16, SY+7*32, "MOVE JOYSTICK TO",FS_BIG,FC_YELLOW);
+  DrawText(SX+32, SY+8*32, "THE LOWER RIGHT",FS_BIG,FC_YELLOW);
+  DrawText(SX+32, SY+9*32, "AND PRESS A KEY",FS_BIG,FC_YELLOW);
+  BackToFront();
+
+  for(clear_keybuf();!keypressed(););
+  calibrate_joystick(SETUP_2ND_JOYSTICK_ON(local_player->setup));
+
+  DrawSetupScreen();
+  return;
+#endif
+
   ClearWindow();
   DrawText(SX+16, SY+16, "NO JOYSTICK",FS_BIG,FC_YELLOW);
   DrawText(SX+16, SY+48, " AVAILABLE ",FS_BIG,FC_YELLOW);
-  Delay(3000000);
+  BackToFront();
+  Delay(3000);
   DrawSetupScreen();
 }
 
+#endif
+
+
+
+void HandleGameActions()
+{
+  if (game_status != PLAYING)
+    return;
+
+  if (local_player->LevelSolved)
+    GameWon();
+
+  if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
+    TapeStop();
+
+  GameActions();
+
+  BackToFront();
+}
+
 void HandleVideoButtons(int mx, int my, int button)
 {
-  if (game_status!=MAINMENU && game_status!=PLAYING)
+  if (game_status != MAINMENU && game_status != PLAYING)
     return;
 
   switch(CheckVideoButtons(mx,my,button))
   {
     case BUTTON_VIDEO_EJECT:
       TapeStop();
-      if (!TAPE_IS_EMPTY(tape))
-       SaveLevelTape(tape.level_nr);
+      if (TAPE_IS_EMPTY(tape))
+      {
+       LoadTape(level_nr);
+       if (TAPE_IS_EMPTY(tape))
+         Request("No tape for this level !",REQ_CONFIRM);
+      }
       else
-       AreYouSure("Tape is empty !",AYS_CONFIRM);
+      {
+       if (tape.changed)
+         SaveTape(tape.level_nr);
+       TapeErase();
+      }
       DrawCompleteVideoDisplay();
       break;
+
     case BUTTON_VIDEO_STOP:
       TapeStop();
       break;
+
     case BUTTON_VIDEO_PAUSE:
       TapeTogglePause();
       break;
+
     case BUTTON_VIDEO_REC:
-      if (tape.pausing)
-       TapeTogglePause();
-      else if (game_status==MAINMENU)
-       TapeInitRecording();
+      if (TAPE_IS_STOPPED(tape))
+      {
+       TapeStartRecording();
+
+#ifndef MSDOS
+       if (options.network)
+         SendToServer_StartPlaying();
+       else
+#endif
+       {
+         game_status = PLAYING;
+         InitGame();
+       }
+      }
+      else if (tape.pausing)
+      {
+       if (tape.playing)       /* PLAYING -> PAUSING -> RECORDING */
+       {
+         tape.pos[tape.counter].delay = tape.delay_played;
+         tape.playing = FALSE;
+         tape.recording = TRUE;
+         tape.changed = TRUE;
+
+         DrawVideoDisplay(VIDEO_STATE_PLAY_OFF | VIDEO_STATE_REC_ON,0);
+       }
+       else
+         TapeTogglePause();
+      }
       break;
+
     case BUTTON_VIDEO_PLAY:
-      if (tape.pausing)
-       TapeTogglePause();
-      else if (game_status==MAINMENU)
-       TapeInitPlaying();
+      if (TAPE_IS_EMPTY(tape))
+       break;
+
+      if (TAPE_IS_STOPPED(tape))
+      {
+       TapeStartPlaying();
+
+       game_status = PLAYING;
+       InitGame();
+      }
+      else if (tape.playing)
+      {
+       if (tape.pausing)                       /* PAUSE -> PLAY */
+         TapeTogglePause();
+       else if (!tape.fast_forward)            /* PLAY -> FAST FORWARD PLAY */
+       {
+         tape.fast_forward = TRUE;
+         DrawVideoDisplay(VIDEO_STATE_FFWD_ON, 0);
+       }
+       else if (!tape.pause_before_death)      /* FFWD PLAY -> + AUTO PAUSE */
+       {
+         tape.pause_before_death = TRUE;
+         DrawVideoDisplay(VIDEO_STATE_PBEND_ON, VIDEO_DISPLAY_LABEL_ONLY);
+       }
+       else                                    /* -> NORMAL PLAY */
+       {
+         tape.fast_forward = FALSE;
+         tape.pause_before_death = FALSE;
+         DrawVideoDisplay(VIDEO_STATE_FFWD_OFF | VIDEO_STATE_PBEND_OFF, 0);
+       }
+      }
       break;
+
     default:
       break;
   }
+
+  BackToFront();
 }
 
 void HandleSoundButtons(int mx, int my, int button)
 {
-  if (game_status!=PLAYING)
+  if (game_status != PLAYING)
     return;
 
   switch(CheckSoundButtons(mx,my,button))
   {
     case BUTTON_SOUND_MUSIC:
-      if (sound_music_on)
+      if (setup.sound_music)
       { 
-       sound_music_on = FALSE;
-       player.setup &= ~SETUP_SOUND_MUSIC;
+       setup.sound_music = FALSE;
        FadeSound(background_loop[level_nr % num_bg_loops]);
        DrawSoundDisplay(BUTTON_SOUND_MUSIC_OFF);
       }
       else if (sound_loops_allowed)
       { 
-       sound_music_on = TRUE;
-       player.setup |= SETUP_SOUND_MUSIC;
+       setup.sound = setup.sound_music = TRUE;
        PlaySoundLoop(background_loop[level_nr % num_bg_loops]);
        DrawSoundDisplay(BUTTON_SOUND_MUSIC_ON);
       }
       else
        DrawSoundDisplay(BUTTON_SOUND_MUSIC_OFF);
       break;
+
     case BUTTON_SOUND_LOOPS:
-      if (sound_loops_on)
+      if (setup.sound_loops)
       { 
-       sound_loops_on = FALSE;
-       player.setup &= ~SETUP_SOUND_LOOPS;
+       setup.sound_loops = FALSE;
        DrawSoundDisplay(BUTTON_SOUND_LOOPS_OFF);
       }
       else if (sound_loops_allowed)
       { 
-       sound_loops_on = TRUE;
-       player.setup |= SETUP_SOUND_LOOPS;
+       setup.sound = setup.sound_loops = TRUE;
        DrawSoundDisplay(BUTTON_SOUND_LOOPS_ON);
       }
       else
        DrawSoundDisplay(BUTTON_SOUND_LOOPS_OFF);
       break;
-    case BUTTON_SOUND_SOUND:
-      if (sound_on)
+
+    case BUTTON_SOUND_SIMPLE:
+      if (setup.sound_simple)
       { 
-       sound_on = FALSE;
-       player.setup &= ~SETUP_SOUND;
-       DrawSoundDisplay(BUTTON_SOUND_SOUND_OFF);
+       setup.sound_simple = FALSE;
+       DrawSoundDisplay(BUTTON_SOUND_SIMPLE_OFF);
       }
       else if (sound_status==SOUND_AVAILABLE)
       { 
-       sound_on = TRUE;
-       player.setup |= SETUP_SOUND;
-       DrawSoundDisplay(BUTTON_SOUND_SOUND_ON);
+       setup.sound = setup.sound_simple = TRUE;
+       DrawSoundDisplay(BUTTON_SOUND_SIMPLE_ON);
       }
       else
-       DrawSoundDisplay(BUTTON_SOUND_SOUND_OFF);
+       DrawSoundDisplay(BUTTON_SOUND_SIMPLE_OFF);
       break;
+
     default:
       break;
   }
+
+  BackToFront();
 }
 
 void HandleGameButtons(int mx, int my, int button)
 {
-  if (game_status!=PLAYING)
+  if (game_status != PLAYING)
     return;
 
   switch(CheckGameButtons(mx,my,button))
   {
     case BUTTON_GAME_STOP:
-      if (AreYouSure("Do you really want to quit the game ?",
-                     AYS_ASK | AYS_STAY_CLOSED))
-      { 
+      if (AllPlayersGone)
+      {
+       CloseDoor(DOOR_CLOSE_1);
        game_status = MAINMENU;
        DrawMainMenu();
+       break;
+      }
+
+      if (Request("Do you really want to quit the game ?",
+                 REQ_ASK | REQ_STAY_CLOSED))
+      { 
+#ifndef MSDOS
+       if (options.network)
+         SendToServer_StopPlaying();
+       else
+#endif
+       {
+         game_status = MAINMENU;
+         DrawMainMenu();
+       }
       }
       else
        OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
       break;
+
     case BUTTON_GAME_PAUSE:
-      if (tape.pausing)
+      if (options.network)
       {
-       tape.pausing = FALSE;
-       DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
+#ifndef MSDOS
+       if (tape.pausing)
+         SendToServer_ContinuePlaying();
+       else
+         SendToServer_PausePlaying();
+#endif
       }
       else
-      {
-       tape.pausing = TRUE;
-       DrawVideoDisplay(VIDEO_STATE_PAUSE_ON,0);
-      }
+       TapeTogglePause();
       break;
+
     case BUTTON_GAME_PLAY:
       if (tape.pausing)
       {
-       tape.pausing = FALSE;
-       DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
+#ifndef MSDOS
+       if (options.network)
+         SendToServer_ContinuePlaying();
+       else
+#endif
+       {
+         tape.pausing = FALSE;
+         DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
+       }
       }
       break;
+
     default:
       break;
   }
+
+  BackToFront();
 }
index 32a58401c399f31e2dff6acb8668b6a29c134924..39e301538a40d1e97bad2979932d154f29bf8266 100644 (file)
@@ -1,13 +1,12 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  screens.h                                               *
 ***********************************************************/
 
 #include "main.h"
 
-void DrawMainMenu();
+void DrawHeadline(void);
+void DrawMainMenu(void);
 void HandleMainMenu(int, int, int, int, int);
 void DrawHelpScreenElAction(int);
 void DrawHelpScreenElText(int);
 void DrawHelpScreenMusicText(int);
 void DrawHelpScreenCreditsText(void);
-void DrawHelpScreen();
+void DrawHelpScreen(void);
 void HandleHelpScreen(int);
 void HandleTypeName(int, KeySym);
 void DrawChooseLevel(void);
 void HandleChooseLevel(int, int, int, int, int);
 void DrawHallOfFame(int);
 void HandleHallOfFame(int);
-void DrawSetupScreen();
+void DrawSetupScreen(void);
 void HandleSetupScreen(int, int, int, int, int);
-void CalibrateJoystick(void);
+void DrawSetupInputScreen(void);
+void HandleSetupInputScreen(int, int, int, int, int);
+void CustomizeKeyboard(int);
+void CalibrateJoystick(int);
+void HandleGameActions(void);
 void HandleVideoButtons(int, int, int);
 void HandleSoundButtons(int, int, int);
 void HandleGameButtons(int, int, int);
 
-#endif
+#endif /* SCREENS_H */
index dc14ad55349e9792a17d121d38b14e48d735402f..340ec942b8ebc30d25e62107bdf02a28e30498c5 100644 (file)
@@ -1,45 +1,71 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  sound.c                                                 *
 ***********************************************************/
 
 #include "sound.h"
+#include "misc.h"
 
 /*** THE STUFF BELOW IS ONLY USED BY THE SOUND SERVER CHILD PROCESS ***/
 
+static int playing_sounds = 0;
 static struct SoundControl playlist[MAX_SOUNDS_PLAYING];
 static struct SoundControl emptySoundControl =
 {
   -1,0,0, FALSE,FALSE,FALSE,FALSE,FALSE, 0,0L,0L,NULL
 };
+
+#ifndef MSDOS
 static int stereo_volume[PSND_MAX_LEFT2RIGHT+1];
 static char premix_first_buffer[SND_BLOCKSIZE];
+#ifdef VOXWARE
 static char premix_left_buffer[SND_BLOCKSIZE];
 static char premix_right_buffer[SND_BLOCKSIZE];
 static int premix_last_buffer[SND_BLOCKSIZE];
+#endif /* VOXWARE */
 static unsigned char playing_buffer[SND_BLOCKSIZE];
-static int playing_sounds = 0;
+#endif /* MSDOS */
+
+/* forward declaration of internal functions */
+#ifdef VOXWARE
+static void SoundServer_InsertNewSound(struct SoundControl);
+#endif
+#ifndef VOXWARE
+static unsigned char linear_to_ulaw(int);
+static int ulaw_to_linear(unsigned char);
+#endif
+#ifdef HPUX_AUDIO
+static void HPUX_Audio_Control();
+#endif
+#ifdef MSDOS
+static void SoundServer_InsertNewSound(struct SoundControl);
+static void SoundServer_StopSound(int);
+static void SoundServer_StopAllSounds();
+#endif
 
 void SoundServer()
 {
+  int i;
+#ifndef MSDOS
   struct SoundControl snd_ctrl;
   fd_set sound_fdset;
-  int i;
 
   close(sound_pipe[1]);                /* no writing into pipe needed */
+#endif
 
   for(i=0;i<MAX_SOUNDS_PLAYING;i++)
     playlist[i] = emptySoundControl;
+  playing_sounds = 0;
 
+#ifndef MSDOS
   stereo_volume[PSND_MAX_LEFT2RIGHT] = 0;
   for(i=0;i<PSND_MAX_LEFT2RIGHT;i++)
     stereo_volume[i] =
@@ -52,17 +78,14 @@ void SoundServer()
   FD_ZERO(&sound_fdset); 
   FD_SET(sound_pipe[0], &sound_fdset);
 
-  for(;;)      /* wait for calls from PlaySound(), StopSound(), ... */
+  while(1)     /* wait for sound playing commands from client */
   {
     FD_SET(sound_pipe[0], &sound_fdset);
     select(sound_pipe[0]+1, &sound_fdset, NULL, NULL, NULL);
     if (!FD_ISSET(sound_pipe[0], &sound_fdset))
       continue;
     if (read(sound_pipe[0], &snd_ctrl, sizeof(snd_ctrl)) != sizeof(snd_ctrl))
-    {
-      fprintf(stderr,"%s: broken pipe - no sounds\n",progname);
-      exit(0);
-    }
+      Error(ERR_EXIT_SOUND_SERVER, "broken pipe - no sounds");
 
 #ifdef VOXWARE
 
@@ -105,10 +128,11 @@ void SoundServer()
     if (playing_sounds || snd_ctrl.active)
     {
       struct timeval delay = { 0, 0 };
-      char *sample_ptr;
-      long sample_size, max_sample_size;
-      long fragment_size;
-      BOOL stereo;
+      byte *sample_ptr;
+      long sample_size;
+      static long max_sample_size = 0;
+      static long fragment_size = 0;
+      boolean stereo;
 
       if (playing_sounds || (sound_device=open(sound_device_name,O_WRONLY))>=0)
       {
@@ -238,12 +262,12 @@ void SoundServer()
       }
     }
 
-#else  /* von '#ifdef VOXWARE' */
+#else /* !VOXWARE */
 
     if (snd_ctrl.active && !snd_ctrl.loop)
     {
       struct timeval delay = { 0, 0 };
-      char *sample_ptr;
+      byte *sample_ptr;
       long sample_size, max_sample_size = SND_BLOCKSIZE;
       long sample_rate = 8000; /* standard "/dev/audio" sampling rate */
       int wait_percent = 90;   /* wait 90% of the real playing time */
@@ -259,9 +283,9 @@ void SoundServer()
          FD_SET(sound_pipe[0], &sound_fdset);
 
          /* get pointer and size of the actual sound sample */
-         sample_ptr = snd_ctrl.data_ptr+snd_ctrl.playingpos;
+         sample_ptr = snd_ctrl.data_ptr + snd_ctrl.playingpos;
          sample_size =
-           MIN(max_sample_size,snd_ctrl.data_len-snd_ctrl.playingpos);
+           MIN(max_sample_size, snd_ctrl.data_len - snd_ctrl.playingpos);
          snd_ctrl.playingpos += sample_size;
 
          /* fill the first mixing buffer with original sample */
@@ -292,24 +316,81 @@ void SoundServer()
       }
     }
 
-#endif /* von '#ifdef VOXWARE' */
+#endif /* !VOXWARE */
 
   }
+#endif /* !MSDOS */
 }
 
-void SoundServer_InsertNewSound(struct SoundControl snd_ctrl)
+#ifdef MSDOS
+static void sound_handler(struct SoundControl snd_ctrl)
 {
-  int i,k;
+  int i;
+
+  if (snd_ctrl.fade_sound)
+  {
+    if (!playing_sounds)
+      return;
 
-  /* wenn voll, ältesten Sound 'rauswerfen */
+    for (i=0; i<MAX_SOUNDS_PLAYING; i++)
+      if ((snd_ctrl.stop_all_sounds || playlist[i].nr == snd_ctrl.nr) &&
+         !playlist[i].fade_sound)
+      {
+       playlist[i].fade_sound = TRUE;
+       if (voice_check(playlist[i].voice))
+         voice_ramp_volume(playlist[i].voice, 1000, 0);
+       playlist[i].loop = PSND_NO_LOOP;
+      }
+  }
+  else if (snd_ctrl.stop_all_sounds)
+  {
+    if (!playing_sounds)
+      return;
+    SoundServer_StopAllSounds();
+  }
+  else if (snd_ctrl.stop_sound)
+  {
+    if (!playing_sounds)
+      return;
+    SoundServer_StopSound(snd_ctrl.nr);
+  }
+
+  for (i=0; i<MAX_SOUNDS_PLAYING; i++)
+  {
+    if (!playlist[i].active || playlist[i].loop)
+      continue;
+
+    playlist[i].playingpos = voice_get_position(playlist[i].voice);
+    playlist[i].volume = voice_get_volume(playlist[i].voice);
+    if (playlist[i].playingpos == -1 || !playlist[i].volume)
+    {
+      deallocate_voice(playlist[i].voice);
+      playlist[i] = emptySoundControl;
+      playing_sounds--;
+    }
+  }
+
+  if (snd_ctrl.active)
+    SoundServer_InsertNewSound(snd_ctrl);
+}
+#endif /* MSDOS */
+
+static void SoundServer_InsertNewSound(struct SoundControl snd_ctrl)
+{
+  int i, k;
+
+  /* if playlist is full, remove oldest sound */
   if (playing_sounds==MAX_SOUNDS_PLAYING)
   {
     int longest=0, longest_nr=0;
 
     for(i=0;i<MAX_SOUNDS_PLAYING;i++)
     {
-      int actual =
-       100 * playlist[i].playingpos / playlist[i].data_len;
+#ifndef MSDOS
+      int actual = 100 * playlist[i].playingpos / playlist[i].data_len;
+#else
+      int actual = playlist[i].playingpos;
+#endif
 
       if (!playlist[i].loop && actual>longest)
       {
@@ -317,18 +398,22 @@ void SoundServer_InsertNewSound(struct SoundControl snd_ctrl)
        longest_nr=i;
       }
     }
+#ifdef MSDOS
+    voice_set_volume(playlist[longest_nr].voice, 0);
+    deallocate_voice(playlist[longest_nr].voice);
+#endif
     playlist[longest_nr] = emptySoundControl;
     playing_sounds--;
   }
 
-  /* nachsehen, ob (und ggf. wie oft) Sound bereits gespielt wird */
+  /* check if sound is already being played (and how often) */
   for(k=0,i=0;i<MAX_SOUNDS_PLAYING;i++)
   {
     if (playlist[i].nr == snd_ctrl.nr)
       k++;
   }
 
-  /* falls Sound-Loop: nur neu beginnen, wenn Sound gerade ausklingt */
+  /* restart loop sounds only if they are just fading out */
   if (k>=1 && snd_ctrl.loop)
   {
     for(i=0;i<MAX_SOUNDS_PLAYING;i++)
@@ -337,17 +422,22 @@ void SoundServer_InsertNewSound(struct SoundControl snd_ctrl)
       {
        playlist[i].fade_sound = FALSE;
        playlist[i].volume = PSND_MAX_VOLUME;
+#ifdef MSDOS
+        playlist[i].loop = PSND_LOOP;
+        voice_stop_volumeramp(playlist[i].voice);
+        voice_ramp_volume(playlist[i].voice, playlist[i].volume, 1000);
+#endif
       }
     }
     return;
   }
 
-  /* keinen Sound mehr als n mal gleichzeitig spielen (momentan n==2) */
+  /* don't play sound more than n times simultaneously (with n == 2 for now) */
   if (k>=2)
   {
     int longest=0, longest_nr=0;
 
-    /* den bereits am längsten gespielten (gleichen) Sound suchen */
+    /* look for oldest equal sound */
     for(i=0;i<MAX_SOUNDS_PLAYING;i++)
     {
       int actual;
@@ -355,13 +445,21 @@ void SoundServer_InsertNewSound(struct SoundControl snd_ctrl)
       if (!playlist[i].active || playlist[i].nr != snd_ctrl.nr)
        continue;
 
+#ifndef MSDOS
       actual = 100 * playlist[i].playingpos / playlist[i].data_len;
+#else
+      actual = playlist[i].playingpos;
+#endif
       if (actual>=longest)
       {
        longest=actual;
        longest_nr=i;
       }
     }
+#ifdef MSDOS
+    voice_set_volume(playlist[longest_nr].voice, 0);
+    deallocate_voice(playlist[longest_nr].voice);
+#endif
     playlist[longest_nr] = emptySoundControl;
     playing_sounds--;
   }
@@ -373,6 +471,14 @@ void SoundServer_InsertNewSound(struct SoundControl snd_ctrl)
     {
       playlist[i] = snd_ctrl;
       playing_sounds++;
+#ifdef MSDOS
+      playlist[i].voice = allocate_voice(Sound[snd_ctrl.nr].sample_ptr);
+      if(snd_ctrl.loop)
+        voice_set_playmode(playlist[i].voice, PLAYMODE_LOOP);
+      voice_set_volume(playlist[i].voice, snd_ctrl.volume);
+      voice_set_pan(playlist[i].voice, snd_ctrl.stereo);
+      voice_start(playlist[i].voice);       
+#endif
       break;
     }
   }
@@ -392,7 +498,7 @@ void SoundServer_FadeSound(int nr)
 }
 */
 
-void SoundServer_StopSound(int nr)
+static void SoundServer_StopSound(int nr)
 {
   int i;
 
@@ -402,49 +508,54 @@ void SoundServer_StopSound(int nr)
   for(i=0;i<MAX_SOUNDS_PLAYING;i++)
     if (playlist[i].nr == nr)
     {
+#ifdef MSDOS
+      voice_set_volume(playlist[i].voice, 0);
+      deallocate_voice(playlist[i].voice);
+#endif
       playlist[i] = emptySoundControl;
       playing_sounds--;
     }
 
+#ifndef MSDOS
   if (!playing_sounds)
     close(sound_device);
+#endif
 }
 
-void SoundServer_StopAllSounds()
+static void SoundServer_StopAllSounds()
 {
   int i;
 
   for(i=0;i<MAX_SOUNDS_PLAYING;i++)
+  {
+#ifdef MSDOS
+    voice_set_volume(playlist[i].voice, 0);
+    deallocate_voice(playlist[i].voice);
+#endif
     playlist[i]=emptySoundControl;
-  playing_sounds=0;
+  }
+  playing_sounds = 0;
 
+#ifndef MSDOS
   close(sound_device);
+#endif
 }
 
 #ifdef HPUX_AUDIO
-void HPUX_Audio_Control()
+static void HPUX_Audio_Control()
 {
   struct audio_describe ainfo;
   int audio_ctl;
 
   audio_ctl = open("/dev/audioCtl", O_WRONLY | O_NDELAY);
   if (audio_ctl == -1)
-  {
-    fprintf(stderr,"%s: cannot open /dev/audioCtl - no sounds\n",progname);
-    exit(0);
-  }
+    Error(ERR_EXIT_SOUND_SERVER, "cannot open /dev/audioCtl - no sounds");
 
   if (ioctl(audio_ctl, AUDIO_DESCRIBE, &ainfo) == -1)
-  {
-    fprintf(stderr,"%s: no audio info - no sounds\n",progname);
-    exit(0);
-  }
+    Error(ERR_EXIT_SOUND_SERVER, "no audio info - no sounds");
 
   if (ioctl(audio_ctl, AUDIO_SET_DATA_FORMAT, AUDIO_FORMAT_ULAW) == -1)
-  {
-    fprintf(stderr,"%s: ulaw audio not available - no sounds\n",progname);
-    exit(0);
-  }
+    Error(ERR_EXIT_SOUND_SERVER, "ulaw audio not available - no sounds");
 
   ioctl(audio_ctl, AUDIO_SET_CHANNELS, 1);
   ioctl(audio_ctl, AUDIO_SET_SAMPLE_RATE, 8000);
@@ -480,7 +591,7 @@ void HPUX_Audio_Control()
 #define BIAS 0x84   /* define the add-in bias for 16 bit samples */
 #define CLIP 32635
 
-unsigned char linear_to_ulaw(int sample)
+static unsigned char linear_to_ulaw(int sample)
 {
   static int exp_lut[256] =
   {
@@ -541,7 +652,7 @@ unsigned char linear_to_ulaw(int sample)
 ** Output: signed 16 bit linear sample
 */
 
-int ulaw_to_linear(unsigned char ulawbyte)
+static int ulaw_to_linear(unsigned char ulawbyte)
 {
   static int exp_lut[8] = { 0, 132, 396, 924, 1980, 4092, 8316, 16764 };
   int sign, exponent, mantissa, sample;
@@ -563,27 +674,123 @@ int ulaw_to_linear(unsigned char ulawbyte)
 
 /*** THE STUFF BELOW IS ONLY USED BY THE MAIN PROCESS ***/
 
-BOOL LoadSound(struct SoundInfo *snd_info)
+#ifndef MSDOS
+static unsigned long be2long(unsigned long *be)        /* big-endian -> longword */
+{
+  unsigned char *ptr = (unsigned char *)be;
+
+  return(ptr[0]<<24 | ptr[1]<<16 | ptr[2]<<8 | ptr[3]);
+}
+
+static unsigned long le2long(unsigned long *be)        /* little-endian -> longword */
+{
+  unsigned char *ptr = (unsigned char *)be;
+
+  return(ptr[3]<<24 | ptr[2]<<16 | ptr[1]<<8 | ptr[0]);
+}
+#endif /* !MSDOS */
+
+boolean LoadSound(struct SoundInfo *snd_info)
 {
+  char filename[256];
+  char *sound_ext = "wav";
+#ifndef MSDOS
+  struct SoundHeader_WAV *sound_header;
   FILE *file;
+  int i;
+#endif
+
+  sprintf(filename, "%s/%s/%s.%s",
+         options.base_directory, SOUNDS_DIRECTORY, snd_info->name, sound_ext);
+
+#ifndef MSDOS
+
+  if ((file = fopen(filename, "r")) == NULL)
+  {
+    Error(ERR_WARN, "cannot open sound file '%s' - no sounds", filename);
+    return(FALSE);
+  }
+
+  if (fseek(file, 0, SEEK_END) < 0)
+  {
+    Error(ERR_WARN, "cannot read sound file '%s' - no sounds", filename);
+    fclose(file);
+    return(FALSE);
+  }
+
+  snd_info->file_len = ftell(file);
+  rewind(file);
+
+  snd_info->file_ptr = checked_malloc(snd_info->file_len);
+
+  if (fread(snd_info->file_ptr, 1, snd_info->file_len, file) !=
+      snd_info->file_len)
+  {
+    Error(ERR_WARN, "cannot read sound file '%s' - no sounds", filename);
+    fclose(file);
+    return(FALSE);
+  }
+
+  fclose(file);
+
+  sound_header = (struct SoundHeader_WAV *)snd_info->file_ptr;
+
+  if (strncmp(sound_header->magic_RIFF, "RIFF", 4) ||
+      snd_info->file_len != le2long(&sound_header->header_size) + 8 ||
+      strncmp(sound_header->magic_WAVE, "WAVE", 4) ||
+      strncmp(sound_header->magic_DATA, "data", 4) ||
+      snd_info->file_len != le2long(&sound_header->data_size) + 44)
+  {
+    Error(ERR_WARN, "'%s' is not a RIFF/WAVE file or broken - no sounds",
+         filename);
+    return(FALSE);
+  }
+
+  snd_info->data_ptr = snd_info->file_ptr + 44;
+  snd_info->data_len = le2long(&sound_header->data_size);
+
+  for (i=0; i<snd_info->data_len; i++)
+    snd_info->data_ptr[i] = snd_info->data_ptr[i]^0x80;
+
+#else /* MSDOS */
+
+  snd_info->sample_ptr = load_sample(filename);
+  if (!snd_info->sample_ptr)
+  {
+    Error(ERR_WARN, "cannot read sound file '%s' - no sounds", filename);
+    return(FALSE);
+  }
+
+#endif /* MSDOS */
+
+  return(TRUE);
+}
+
+boolean LoadSound_8SVX(struct SoundInfo *snd_info)
+{
   char filename[256];
+#ifndef MSDOS
+  struct SoundHeader_8SVX *sound_header;
+  FILE *file;
+  char *ptr;
   char *sound_ext = "8svx";
-  struct SoundHeader_8SVX *snd_hdr;
-  unsigned char *ptr;
+#else
+  char *sound_ext = "wav";
+#endif
 
-  sprintf(filename,"%s/%s.%s",SND_PATH,snd_info->name,sound_ext);
+  sprintf(filename, "%s/%s/%s.%s",
+         options.base_directory, SOUNDS_DIRECTORY, snd_info->name, sound_ext);
 
+#ifndef MSDOS
   if (!(file=fopen(filename,"r")))
   {
-    fprintf(stderr,"%s: cannot open sound file '%s' - no sounds\n",
-           progname,filename);
+    Error(ERR_WARN, "cannot open sound file '%s' - no sounds", filename);
     return(FALSE);
   }
 
   if (fseek(file,0,SEEK_END)<0)
   {
-    fprintf(stderr,"%s: cannot read sound file '%s' - no sounds\n",
-           progname,filename);
+    Error(ERR_WARN, "cannot read sound file '%s' - no sounds", filename);
     fclose(file);
     return(FALSE);
   }
@@ -593,59 +800,74 @@ BOOL LoadSound(struct SoundInfo *snd_info)
 
   if (!(snd_info->file_ptr=malloc(snd_info->file_len)))
   {
-    fprintf(stderr,"%s: out of memory (this shouldn't happen :) - no sounds\n",
-           progname);
+    Error(ERR_WARN, "out of memory (this shouldn't happen :) - no sounds");
     fclose(file);
     return(FALSE);
   }
 
   if (fread(snd_info->file_ptr,1,snd_info->file_len,file)!=snd_info->file_len)
   {
-    fprintf(stderr,"%s: cannot read sound file '%s' - no sounds\n",
-           progname,filename);
+    Error(ERR_WARN, "cannot read sound file '%s' - no sounds", filename);
     fclose(file);
     return(FALSE);
   }
 
   fclose(file);
 
-  snd_hdr = (struct SoundHeader_8SVX *)snd_info->file_ptr;
+  sound_header = (struct SoundHeader_8SVX *)snd_info->file_ptr;
 
-  if (strncmp(snd_hdr->magic_FORM,"FORM",4) ||
-      snd_info->file_len!=be2long(&snd_hdr->chunk_size)+8 ||
-      strncmp(snd_hdr->magic_8SVX,"8SVX",4))
+  if (strncmp(sound_header->magic_FORM,"FORM",4) ||
+      snd_info->file_len != be2long(&sound_header->chunk_size)+8 ||
+      strncmp(sound_header->magic_8SVX,"8SVX",4))
   {
-    fprintf(stderr,"%s: '%s' is not an IFF/8SVX file or broken- no sounds\n",
-           progname,filename);
+    Error(ERR_WARN, "'%s' is not an IFF/8SVX file or broken - no sounds",
+         filename);
     return(FALSE);
   }
 
-  ptr = (unsigned char *)snd_info->file_ptr;
+  ptr = (char *)snd_info->file_ptr + 12;
 
-  while(ptr<(unsigned char *)snd_info->file_ptr+snd_info->file_len)
+  while(ptr < (char *)(snd_info->file_ptr + snd_info->file_len))
   {
     if (!strncmp(ptr,"VHDR",4))
     {
-      ptr+=be2long((unsigned long *)(ptr+4));
+      ptr += be2long((unsigned long *)(ptr + 4)) + 8;
+      continue;
     }
-    if (!strncmp(ptr,"ANNO",4))
+    else if (!strncmp(ptr,"ANNO",4))
     {
-      ptr+=be2long((unsigned long *)(ptr+4));
+      ptr += be2long((unsigned long *)(ptr + 4)) + 8;
+      continue;
     }
-    if (!strncmp(ptr,"CHAN",4))
+    else if (!strncmp(ptr,"CHAN",4))
     {
-      ptr+=be2long((unsigned long *)(ptr+4));
+      ptr += be2long((unsigned long *)(ptr + 4)) + 8;
+      continue;
     }
-    if (!strncmp(ptr,"BODY",4))
+    else if (!strncmp(ptr,"BODY",4))
     {
-      snd_info->data_ptr = ptr+8;
-      snd_info->data_len = be2long((unsigned long *)(ptr+4));
+      snd_info->data_ptr = (byte *)ptr + 8;
+      snd_info->data_len = be2long((unsigned long *)(ptr + 4));
       return(TRUE);
     }
-    ptr++;
+    else
+    {
+      /* other chunk not recognized here */
+      ptr += be2long((unsigned long *)(ptr + 4)) + 8;
+      continue;
+    }
   }
 
   return(FALSE);
+#else /* MSDOS */
+  snd_info->sample_ptr = load_sample(filename);
+  if(!snd_info->sample_ptr)
+  {
+    Error(ERR_WARN, "cannot read sound file '%s' - no sounds", filename);
+    return(FALSE);
+  }
+  return(TRUE);
+#endif /* MSDOS */
 }
 
 void PlaySound(int nr)
@@ -663,11 +885,11 @@ void PlaySoundLoop(int nr)
   PlaySoundExt(nr, PSND_MAX_VOLUME, PSND_MIDDLE, PSND_LOOP);
 }
 
-void PlaySoundExt(int nr, int volume, int stereo, BOOL loop)
+void PlaySoundExt(int nr, int volume, int stereo, boolean loop)
 {
   struct SoundControl snd_ctrl = emptySoundControl;
 
-  if (sound_status==SOUND_OFF || !sound_on)
+  if (sound_status==SOUND_OFF || !setup.sound)
     return;
 
   if (volume<PSND_MIN_VOLUME)
@@ -688,12 +910,16 @@ void PlaySoundExt(int nr, int volume, int stereo, BOOL loop)
   snd_ctrl.data_ptr    = Sound[nr].data_ptr;
   snd_ctrl.data_len    = Sound[nr].data_len;
 
+#ifndef MSDOS
   if (write(sound_pipe[1], &snd_ctrl, sizeof(snd_ctrl))<0)
   {
-    fprintf(stderr,"%s: cannot pipe to child process - no sounds\n",progname);
-    sound_status=SOUND_OFF;
+    Error(ERR_WARN, "cannot pipe to child process - no sounds");
+    sound_status = SOUND_OFF;
     return;
   }
+#else
+  sound_handler(snd_ctrl);
+#endif
 }
 
 void FadeSound(int nr)
@@ -734,12 +960,16 @@ void StopSoundExt(int nr, int method)
     snd_ctrl.stop_sound = TRUE;
   }
 
+#ifndef MSDOS
   if (write(sound_pipe[1], &snd_ctrl, sizeof(snd_ctrl))<0)
   {
-    fprintf(stderr,"%s: cannot pipe to child process - no sounds\n",progname);
-    sound_status=SOUND_OFF;
+    Error(ERR_WARN, "cannot pipe to child process - no sounds");
+    sound_status = SOUND_OFF;
     return;
   }
+#else
+  sound_handler(snd_ctrl);
+#endif
 }
 
 void FreeSounds(int max)
@@ -750,7 +980,11 @@ void FreeSounds(int max)
     return;
 
   for(i=0;i<max;i++)
+#ifndef MSDOS
     free(Sound[i].file_ptr);
+#else
+    destroy_sample(Sound[i].sample_ptr);
+#endif
 }
 
 /*** THE STUFF ABOVE IS ONLY USED BY THE MAIN PROCESS ***/
index 384f77cf7b6bda5de951ffa1b60de579977d7476..ca97ff0a39f735a7cccb6ce700b8ee949cb20f71 100644 (file)
@@ -1,13 +1,12 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  sound.c                                                 *
 ***********************************************************/
@@ -15,8 +14,8 @@
 #ifndef SOUND_H
 #define SOUND_H
 
-#include "main.h"
 #include <math.h>
+#include "main.h"
 
 #ifdef linux
 #include <linux/soundcard.h>
@@ -40,9 +39,14 @@ extern void ioctl(long, long, void *);
 #define HPUX_AUDIO
 #endif /* _HPUX_SOURCE */
 
+#ifndef MSDOS
 #define MAX_SOUNDS_PLAYING     16
+#else
+#define MAX_SOUNDS_PLAYING     8
+#endif
 
 /* some values for PlaySound(), StopSound() and friends */
+#ifndef MSDOS
 #define PSND_SILENCE           0
 #define PSND_MAX_VOLUME_BITS   7
 #define PSND_MIN_VOLUME                0
@@ -56,6 +60,16 @@ extern void ioctl(long, long, void *);
 #define PSND_MAX_RIGHT         (+PSND_MAX_STEREO)
 #define PSND_MAX_LEFT2RIGHT_BITS (PSND_MAX_STEREO_BITS+1)
 #define PSND_MAX_LEFT2RIGHT    (1 << PSND_MAX_LEFT2RIGHT_BITS)
+#else
+#define PSND_SILENCE           0
+#define PSND_MIN_VOLUME                0
+#define PSND_MAX_VOLUME                255
+#define PSND_NO_LOOP           0
+#define PSND_LOOP              1
+#define PSND_MAX_LEFT          0
+#define PSND_MAX_RIGHT         255
+#define PSND_MIDDLE            128
+#endif
 
 #define SSND_FADE_SOUND                (1<<0)
 #define SSND_FADE_ALL_SOUNDS   (1<<1)
@@ -65,9 +79,6 @@ extern void ioctl(long, long, void *);
 #define SSND_STOPPING(x)       (x & (SSND_STOP_SOUND | SSND_STOP_ALL_SOUNDS))
 #define SSND_ALL(x)            (x&(SSND_FADE_ALL_SOUNDS|SSND_STOP_ALL_SOUNDS))
 
-#define        TRUE    1
-#define FALSE  0
-
 /* settings for sound path, sound device, etc. */
 #ifndef SND_PATH
 #define SND_PATH       "./sounds"
@@ -108,11 +119,25 @@ struct SoundHeader_8SVX
   char magic_8SVX[4];
 };
 
+struct SoundHeader_WAV
+{
+  char magic_RIFF[4];
+  unsigned long header_size;
+  char magic_WAVE[4];
+  char some_stuff[24];
+  char magic_DATA[4];
+  unsigned long data_size;
+};
+
 struct SoundInfo
 { 
   char *name;
-  char *file_ptr, *data_ptr;
+  byte *file_ptr;
+  byte *data_ptr;
   long file_len, data_len;
+#ifdef MSDOS
+  SAMPLE *sample_ptr;
+#endif
 };
 
 struct SoundControl
@@ -120,35 +145,29 @@ struct SoundControl
   int nr;
   int volume;
   int stereo;
-  BOOL active;
-  BOOL loop;
-  BOOL fade_sound;
-  BOOL stop_sound;
-  BOOL stop_all_sounds;
+  boolean active;
+  boolean loop;
+  boolean fade_sound;
+  boolean stop_sound;
+  boolean stop_all_sounds;
   int playingtime;
   long playingpos;
   long data_len;
-  char *data_ptr;
+  byte *data_ptr;
+#ifdef MSDOS
+  int voice;
+#endif
 };
 
-/* function from "misc.c" */
-unsigned long be2long(unsigned long *);
-
-/* sound server functions */
+/* start sound server */
 void SoundServer(void);
-void SoundServer_InsertNewSound(struct SoundControl);
-void SoundServer_StopSound(int);
-void SoundServer_StopAllSounds(void);
-void HPUX_Audio_Control(void);
-unsigned char linear_to_ulaw(int);
-int ulaw_to_linear(unsigned char);
-
-/* application functions */
-BOOL LoadSound(struct SoundInfo *);
+
+/* client functions */
+boolean LoadSound(struct SoundInfo *);
 void PlaySound(int);
 void PlaySoundStereo(int, int);
 void PlaySoundLoop(int);
-void PlaySoundExt(int, int, int, BOOL);
+void PlaySoundExt(int, int, int, boolean);
 void FadeSound(int);
 void FadeSounds(void);
 void StopSound(int);
diff --git a/src/tape.c b/src/tape.c
new file mode 100644 (file)
index 0000000..2cc0b39
--- /dev/null
@@ -0,0 +1,254 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
+*----------------------------------------------------------*
+*  tape.c                                                  *
+***********************************************************/
+
+#include "tape.h"
+#include "misc.h"
+#include "game.h"
+#include "buttons.h"
+
+void TapeStartRecording()
+{
+  time_t zeit1 = time(NULL);
+  struct tm *zeit2 = localtime(&zeit1);
+  int i;
+
+  if (!TAPE_IS_STOPPED(tape))
+    TapeStop();
+
+  tape.level_nr = level_nr;
+  tape.length = 0;
+  tape.counter = 0;
+  tape.pos[tape.counter].delay = 0;
+  tape.recording = TRUE;
+  tape.playing = FALSE;
+  tape.pausing = FALSE;
+  tape.changed = TRUE;
+  tape.date = 10000*(zeit2->tm_year%100) + 100*zeit2->tm_mon + zeit2->tm_mday;
+  tape.random_seed = InitRND(NEW_RANDOMIZE);
+
+  for(i=0; i<MAX_PLAYERS; i++)
+    tape.player_participates[i] = FALSE;
+
+  DrawVideoDisplay(VIDEO_STATE_REC_ON, 0);
+  DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
+  DrawVideoDisplay(VIDEO_STATE_TIME_ON, 0);
+}
+
+void TapeStopRecording()
+{
+  int i;
+
+  if (!tape.recording)
+    return;
+
+  for(i=0; i<MAX_PLAYERS; i++)
+    tape.pos[tape.counter].action[i] = 0;
+
+  tape.counter++;
+  tape.length = tape.counter;
+  tape.length_seconds = GetTapeLength();
+  tape.recording = FALSE;
+  tape.pausing = FALSE;
+  DrawVideoDisplay(VIDEO_STATE_REC_OFF, 0);
+}
+
+void TapeRecordAction(byte joy[MAX_PLAYERS])
+{
+  int i;
+
+  if (!tape.recording || tape.pausing)
+    return;
+
+  if (tape.counter >= MAX_TAPELEN-1)
+  {
+    TapeStopRecording();
+    return;
+  }
+
+  for(i=0; i<MAX_PLAYERS; i++)
+    tape.pos[tape.counter].action[i] = joy[i];
+
+  tape.counter++;
+  tape.pos[tape.counter].delay = 0;
+}
+
+void TapeRecordDelay()
+{
+  int i;
+
+  if (!tape.recording || tape.pausing)
+    return;
+
+  if (tape.counter >= MAX_TAPELEN)
+  {
+    TapeStopRecording();
+    return;
+  }
+
+  tape.pos[tape.counter].delay++;
+
+  if (tape.pos[tape.counter].delay >= 255)
+  {
+    for(i=0; i<MAX_PLAYERS; i++)
+      tape.pos[tape.counter].action[i] = 0;
+
+    tape.counter++;
+    tape.pos[tape.counter].delay = 0;
+  }
+}
+
+void TapeTogglePause()
+{
+  if (!tape.recording && !tape.playing)
+    return;
+
+  tape.pausing = !tape.pausing;
+  tape.fast_forward = FALSE;
+  tape.pause_before_death = FALSE;
+  DrawVideoDisplay((tape.pausing ?
+                   VIDEO_STATE_PAUSE_ON :
+                   VIDEO_STATE_PAUSE_OFF) | VIDEO_STATE_PBEND_OFF,
+                  0);
+}
+
+void TapeStartPlaying()
+{
+  if (TAPE_IS_EMPTY(tape))
+    return;
+
+  if (!TAPE_IS_STOPPED(tape))
+    TapeStop();
+
+  tape.counter = 0;
+  tape.delay_played = 0;
+  tape.pause_before_death = FALSE;
+  tape.recording = FALSE;
+  tape.playing = TRUE;
+  tape.pausing = FALSE;
+  tape.fast_forward = FALSE;
+  InitRND(tape.random_seed);
+
+  DrawVideoDisplay(VIDEO_STATE_PLAY_ON, 0);
+  DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
+  DrawVideoDisplay(VIDEO_STATE_TIME_ON, 0);
+}
+
+void TapeStopPlaying()
+{
+  if (!tape.playing)
+    return;
+
+  tape.playing = FALSE;
+  tape.pausing = FALSE;
+  DrawVideoDisplay(VIDEO_STATE_PLAY_OFF, 0);
+}
+
+byte *TapePlayAction()
+{
+  static byte joy[MAX_PLAYERS];
+  int i;
+
+  if (!tape.playing || tape.pausing)
+    return(NULL);
+
+  if (tape.counter >= tape.length)
+  {
+    TapeStop();
+    return(NULL);
+  }
+
+  if (tape.delay_played == tape.pos[tape.counter].delay)
+  {
+    tape.delay_played = 0;
+    tape.counter++;
+
+    for(i=0; i<MAX_PLAYERS; i++)
+      joy[i] = tape.pos[tape.counter-1].action[i];
+  }
+  else
+  {
+    for(i=0; i<MAX_PLAYERS; i++)
+      joy[i] = 0;
+  }
+
+  return(joy);
+}
+
+boolean TapePlayDelay()
+{
+  if (!tape.playing || tape.pausing)
+    return(FALSE);
+
+  if (tape.pause_before_death) /* STOP 10s BEFORE PLAYER GETS KILLED... */
+  {
+    if (!(FrameCounter % 20))
+    {
+      if ((FrameCounter / 20) % 2)
+       DrawVideoDisplay(VIDEO_STATE_PBEND_ON, VIDEO_DISPLAY_LABEL_ONLY);
+      else
+       DrawVideoDisplay(VIDEO_STATE_PBEND_OFF, VIDEO_DISPLAY_LABEL_ONLY);
+    }
+
+    if (level.time-TimeLeft > tape.length_seconds - PAUSE_SECONDS_BEFORE_DEATH)
+    {
+      TapeTogglePause();
+      return(FALSE);
+    }
+  }
+
+  if (tape.counter >= tape.length)
+  {
+    TapeStop();
+    return(TRUE);
+  }
+
+  if (tape.delay_played < tape.pos[tape.counter].delay)
+  {
+    tape.delay_played++;
+    return(TRUE);
+  }
+  else
+    return(FALSE);
+}
+
+void TapeStop()
+{
+  TapeStopRecording();
+  TapeStopPlaying();
+
+  DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
+  if (tape.date && tape.length)
+  {
+    DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
+    DrawVideoDisplay(VIDEO_STATE_TIME_ON, tape.length_seconds);
+  }
+}
+
+void TapeErase()
+{
+  tape.length = 0;
+}
+
+unsigned int GetTapeLength()
+{
+  unsigned int tape_length = 0;
+  int i;
+
+  if (TAPE_IS_EMPTY(tape))
+    return(0);
+
+  for(i=0;i<tape.length;i++)
+    tape_length += tape.pos[i].delay;
+
+  return(tape_length * GAME_FRAME_DELAY / 1000);
+}
diff --git a/src/tape.h b/src/tape.h
new file mode 100644 (file)
index 0000000..3115108
--- /dev/null
@@ -0,0 +1,34 @@
+/***********************************************************
+*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+*----------------------------------------------------------*
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
+*----------------------------------------------------------*
+*  tape.c                                                  *
+***********************************************************/
+
+#ifndef TAPE_H
+#define TAPE_H
+
+#include "main.h"
+
+#define PAUSE_SECONDS_BEFORE_DEATH     3
+
+void TapeStartRecording(void);
+void TapeStopRecording(void);
+void TapeRecordAction(byte *);
+void TapeRecordDelay(void);
+void TapeTogglePause(void);
+void TapeStartPlaying(void);
+void TapeStopPlaying(void);
+byte *TapePlayAction(void);
+boolean TapePlayDelay(void);
+void TapeStop(void);
+void TapeErase(void);
+unsigned int GetTapeLength(void);
+
+#endif
index 625159f316b2366220597d9b4bb945a401aceffd..d2645960674a93a8112d1c9b9db28fe0bf379cbe 100644 (file)
@@ -1,17 +1,18 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  tools.c                                                 *
 ***********************************************************/
 
+#include <stdarg.h>
+
 #ifdef __FreeBSD__
 #include <machine/joystick.h>
 #endif
 #include "sound.h"
 #include "misc.h"
 #include "buttons.h"
-#include <math.h>
+#include "joystick.h"
+#include "cartoons.h"
+#include "network.h"
+
+#ifdef MSDOS
+extern boolean wait_for_vsync;
+#endif
+
+void SetDrawtoField(int mode)
+{
+  if (mode == DRAW_BUFFERED && setup.soft_scrolling)
+  {
+    FX = TILEX;
+    FY = TILEY;
+    BX1 = -1;
+    BY1 = -1;
+    BX2 = SCR_FIELDX;
+    BY2 = SCR_FIELDY;
+    redraw_x1 = 1;
+    redraw_y1 = 1;
+
+    drawto_field = fieldbuffer;
+  }
+  else /* DRAW_DIRECT, DRAW_BACKBUFFER */
+  {
+    FX = SX;
+    FY = SY;
+    BX1 = 0;
+    BY1 = 0;
+    BX2 = SCR_FIELDX - 1;
+    BY2 = SCR_FIELDY - 1;
+    redraw_x1 = 0;
+    redraw_y1 = 0;
+
+    drawto_field = (mode == DRAW_DIRECT ? window :  backbuffer);
+  }
+}
 
 void BackToFront()
 {
   int x,y;
+  Drawable buffer = (drawto_field == window ? backbuffer : drawto_field);
 
-  if (direct_draw_on && game_status==PLAYING)
+  if (setup.direct_draw && game_status == PLAYING)
     redraw_mask &= ~REDRAW_MAIN;
 
+  if (redraw_mask & REDRAW_TILES && redraw_tiles > REDRAWTILES_THRESHOLD)
+    redraw_mask |= REDRAW_FIELD;
+
+  if (redraw_mask & REDRAW_FIELD)
+    redraw_mask &= ~REDRAW_TILES;
+
+  /*
+  if (redraw_mask & REDRAW_FIELD ||
+      (ScreenGfxPos && setup.soft_scrolling && game_status == PLAYING))
+    redraw_mask &= ~REDRAW_TILES;
+  */
+
   if (!redraw_mask)
     return;
 
-  if (redraw_mask & REDRAW_ALL ||
-      (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS))
+  /* synchronize X11 graphics at this point; if we would synchronize the
+     display immediately after the buffer switching (after the XFlush),
+     this could mean that we have to wait for the graphics to complete,
+     although we could go on doing calculations for the next frame */
+
+  XSync(display, FALSE);
+
+  /*
+#ifdef MSDOS
+  wait_for_vsync = TRUE;
+#endif
+  */
+
+  if (redraw_mask & REDRAW_ALL)
   {
     XCopyArea(display,backbuffer,window,gc,
              0,0, WIN_XSIZE,WIN_YSIZE,
              0,0);
     redraw_mask = 0;
   }
-  else if (redraw_mask & REDRAW_FIELD)
+
+  if (redraw_mask & REDRAW_FIELD)
   {
-    XCopyArea(display,backbuffer,window,gc,
-             REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
-             REAL_SX,REAL_SY);
+    if (game_status != PLAYING || redraw_mask & REDRAW_FROM_BACKBUFFER)
+      XCopyArea(display,backbuffer,window,gc,
+               REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
+               REAL_SX,REAL_SY);
+    else
+    {
+      int fx = FX, fy = FY;
+
+      if (setup.soft_scrolling)
+      {
+       fx += (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
+       fy += (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
+      }
+
+      if (setup.soft_scrolling ||
+         ABS(ScreenGfxPos) + ScrollStepSize == TILEX ||
+         ABS(ScreenGfxPos) == ScrollStepSize ||
+         redraw_tiles > REDRAWTILES_THRESHOLD)
+       XCopyArea(display, buffer, window, gc, fx, fy, SXSIZE, SYSIZE, SX, SY);
+    }
     redraw_mask &= ~REDRAW_MAIN;
   }
-  else if (redraw_mask & REDRAW_DOORS)
+
+  if (redraw_mask & REDRAW_DOORS)
   {
     if (redraw_mask & REDRAW_DOOR_1)
       XCopyArea(display,backbuffer,window,gc,
-               DX,DY, DXSIZE,DYSIZE, DX,DY);
+               DX,DY, DXSIZE,DYSIZE,
+               DX,DY);
     if (redraw_mask & REDRAW_DOOR_2)
     {
       if ((redraw_mask & REDRAW_DOOR_2) == REDRAW_DOOR_2)
        XCopyArea(display,backbuffer,window,gc,
-                 VX,VY, VXSIZE,VYSIZE, VX,VY);
+                 VX,VY, VXSIZE,VYSIZE,
+                 VX,VY);
       else
       {
        if (redraw_mask & REDRAW_VIDEO_1)
@@ -94,31 +177,31 @@ void BackToFront()
 
   if (redraw_mask & REDRAW_TILES)
   {
-    if (redraw_tiles>REDRAWTILES_TH)
-      XCopyArea(display,backbuffer,window,gc,SX,SY,SXSIZE,SYSIZE,SX,SY);
-    else
-      for(x=0;x<SCR_FIELDX;x++)
-       for(y=0;y<SCR_FIELDY;y++)
-         if (redraw[x][y])
-           XCopyArea(display,backbuffer,window,gc,
-                     SX+x*TILEX,SY+y*TILEY,TILEX,TILEY,SX+x*TILEX,SY+y*TILEY);
+    for(x=0; x<SCR_FIELDX; x++)
+      for(y=0; y<SCR_FIELDY; y++)
+       if (redraw[redraw_x1 + x][redraw_y1 + y])
+         XCopyArea(display,buffer,window,gc,
+                   FX+x*TILEX,FX+y*TILEY, TILEX,TILEY,
+                   SX+x*TILEX,SY+y*TILEY);
   }
 
   XFlush(display);
 
-  for(x=0;x<SCR_FIELDX;x++)
-    for(y=0;y<SCR_FIELDY;y++)
-      redraw[x][y]=0;
-  redraw_tiles=0;
-  redraw_mask=0;
+  for(x=0; x<MAX_BUF_XSIZE; x++)
+    for(y=0; y<MAX_BUF_YSIZE; y++)
+      redraw[x][y] = 0;
+  redraw_tiles = 0;
+  redraw_mask = 0;
 }
 
 void FadeToFront()
 {
-  long fading_delay = 300000;
+/*
+  long fading_delay = 300;
 
-  if (fading_on && (redraw_mask & REDRAW_FIELD))
+  if (setup.fading && (redraw_mask & REDRAW_FIELD))
   {
+*/
 
 /*
     int x,y;
@@ -135,7 +218,7 @@ void FadeToFront()
                  REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
       }
       XFlush(display);
-      Delay(10000);
+      Delay(10);
     }
 */
 
@@ -147,7 +230,7 @@ void FadeToFront()
     Delay(fading_delay);
 */
 
-
+/*
     XSetClipOrigin(display,clip_gc[PIX_FADEMASK],0,0);
     XCopyArea(display,backbuffer,window,clip_gc[PIX_FADEMASK],
              REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE, REAL_SX,REAL_SY);
@@ -174,358 +257,801 @@ void FadeToFront()
 
     redraw_mask &= ~REDRAW_MAIN;
   }
+*/
 
   BackToFront();
 }
 
 void ClearWindow()
 {
-  drawto_field = backbuffer;
-  XFillRectangle(display,drawto_field,gc,
-                REAL_SX,REAL_SY,FULL_SXSIZE,FULL_SYSIZE);
-  redraw_mask|=REDRAW_FIELD;
+  XFillRectangle(display, backbuffer, gc,
+                REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
 
-  if (game_status==PLAYING && direct_draw_on)
+  if (setup.soft_scrolling && game_status == PLAYING)
   {
-    drawto_field = window;
-    XFillRectangle(display,drawto_field,gc,
-                  REAL_SX,REAL_SY,FULL_SXSIZE,FULL_SYSIZE);
+    XFillRectangle(display, fieldbuffer, gc, 0, 0, FXSIZE, FYSIZE);
+    SetDrawtoField(DRAW_BUFFERED);
+  }
+  else
+    SetDrawtoField(DRAW_BACKBUFFER);
+
+  if (setup.direct_draw && game_status == PLAYING)
+  {
+    XFillRectangle(display, window, gc,
+                  REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
+    SetDrawtoField(DRAW_DIRECT);
   }
+
+  redraw_mask |= REDRAW_FIELD;
 }
 
-void DrawText(int x, int y, char *text, int font, int col)
+void DrawTextFCentered(int y, int font_type, char *format, ...)
 {
-  DrawTextExt(drawto, gc, x, y, text, font, col);
-  if (x<DX)
-    redraw_mask|=REDRAW_FIELD;
-  else if (y<VY)
-    redraw_mask|=REDRAW_DOOR_1;
+  char buffer[FULL_SXSIZE / FONT3_XSIZE + 10];
+  int font_xsize;
+  va_list ap;
+
+  font_xsize = (font_type < FC_SPECIAL1 ? FONT2_XSIZE :
+               font_type < FC_SPECIAL2 ? FONT3_XSIZE : FONT4_XSIZE);
+
+  va_start(ap, format);
+  vsprintf(buffer, format, ap);
+  va_end(ap);
+
+  DrawText(SX + (SXSIZE - strlen(buffer) * font_xsize) / 2, SY + y,
+          buffer, FS_SMALL, font_type);
+}
+
+void DrawTextF(int x, int y, int font_type, char *format, ...)
+{
+  char buffer[FULL_SXSIZE / FONT3_XSIZE + 10];
+  va_list ap;
+
+  va_start(ap, format);
+  vsprintf(buffer, format, ap);
+  va_end(ap);
+
+  DrawText(SX + x, SY + y, buffer, FS_SMALL, font_type);
+}
+
+void DrawText(int x, int y, char *text, int font_size, int font_type)
+{
+  DrawTextExt(drawto, gc, x, y, text, font_size, font_type);
+
+  if (x < DX)
+    redraw_mask |= REDRAW_FIELD;
+  else if (y < VY)
+    redraw_mask |= REDRAW_DOOR_1;
 }
 
 void DrawTextExt(Drawable d, GC gc, int x, int y,
-                char *text, int font, int font_color)
+                char *text, int font_size, int font_type)
 {
   int font_width, font_height, font_start;
   int font_pixmap;
 
-  if (font!=FS_SMALL && font!=FS_BIG)
-    font = FS_SMALL;
-  if (font_color<FC_RED || font_color>FC_SPECIAL2)
-    font_color = FC_RED;
-
-  font_width =
-    (font==FS_BIG ? FONT1_XSIZE :
-     font_color<FC_SPECIAL1 ? FONT2_XSIZE :
-     font_color<FC_SPECIAL2 ? FONT3_XSIZE : FONT4_XSIZE);
-  font_height =
-    (font==FS_BIG ? FONT1_XSIZE :
-     font_color<FC_SPECIAL2 ? FONT2_XSIZE: FONT4_XSIZE);
-  font_pixmap = (font==FS_BIG ? PIX_BIGFONT : PIX_SMALLFONT);
-  font_start =
-    font_color*(font==FS_BIG ? FONT1_YSIZE : FONT2_YSIZE)*FONT_LINES_PER_FONT;
+  if (font_size != FS_SMALL && font_size != FS_BIG)
+    font_size = FS_SMALL;
+  if (font_type < FC_RED || font_type > FC_SPECIAL2)
+    font_type = FC_RED;
+
+  font_width = (font_size == FS_BIG ? FONT1_XSIZE :
+               font_type < FC_SPECIAL1 ? FONT2_XSIZE :
+               font_type < FC_SPECIAL2 ? FONT3_XSIZE : FONT4_XSIZE);
+  font_height = (font_size == FS_BIG ? FONT1_XSIZE :
+                font_type < FC_SPECIAL2 ? FONT2_XSIZE : FONT4_XSIZE);
+  font_pixmap = (font_size == FS_BIG ? PIX_BIGFONT : PIX_SMALLFONT);
+  font_start = (font_type * (font_size == FS_BIG ? FONT1_YSIZE : FONT2_YSIZE) *
+               FONT_LINES_PER_FONT);
 
   while(*text)
   {
     char c = *text++;
 
-    if (c>='a' && c<='z')
+    if (c >= 'a' && c <= 'z')
       c = 'A' + (c - 'a');
-    else if (c=='ä' || c=='Ä')
+    else if (c == 'ä' || c == 'Ä')
       c = 91;
-    else if (c=='ö' || c=='Ö')
+    else if (c == 'ö' || c == 'Ö')
       c = 92;
-    else if (c=='ü' || c=='Ü')
+    else if (c == 'ü' || c == 'Ü')
       c = 93;
 
-    if (c>=32 && c<=95)
-      XCopyArea(display,pix[font_pixmap],d,gc,
-               ((c-32) % FONT_CHARS_PER_LINE)*font_width,
-               ((c-32) / FONT_CHARS_PER_LINE)*font_height + font_start,
-               font_width,font_height, x,y);
+    if (c >= 32 && c <= 95)
+      XCopyArea(display, pix[font_pixmap], d, gc,
+               ((c - 32) % FONT_CHARS_PER_LINE) * font_width,
+               ((c - 32) / FONT_CHARS_PER_LINE) * font_height + font_start,
+               font_width, font_height, x, y);
 
     x += font_width;
   }
 }
 
-void DrawGraphic(int x, int y, int graphic)
+void DrawAllPlayers()
 {
-  DrawGraphicExt(drawto_field, gc, x, y, graphic);
-  redraw_tiles++;
-  redraw[x][y] = TRUE;
-  redraw_mask |= REDRAW_TILES;
+  int i;
+
+  for(i=0; i<MAX_PLAYERS; i++)
+    if (stored_player[i].active)
+      DrawPlayer(&stored_player[i]);
 }
 
-void DrawGraphicExt(Drawable d, GC gc, int x, int y, int graphic)
+void DrawPlayerField(int x, int y)
 {
-  DrawGraphicExtHiRes(d, gc, SX+x*TILEX, SY+y*TILEY, graphic);
+  if (!IS_PLAYER(x,y))
+    return;
+
+  DrawPlayer(PLAYERINFO(x,y));
 }
 
-void DrawGraphicExtHiRes(Drawable d, GC gc, int x, int y, int graphic)
+void DrawPlayer(struct PlayerInfo *player)
 {
-  if (graphic<0)
-    XFillRectangle(display,d,gc, x,y, TILEX,TILEY);
-  else if (graphic<256)
-    XCopyArea(display,pix[PIX_BACK],d,gc,
-             SX+(graphic % GFX_PER_LINE)*TILEX,
-             SY+(graphic / GFX_PER_LINE)*TILEY,
-             TILEX,TILEY, x,y);
-  else
+  int jx = player->jx, jy = player->jy;
+  int last_jx = player->last_jx, last_jy = player->last_jy;
+  int next_jx = jx + (jx - last_jx), next_jy = jy + (jy - last_jy);
+  int sx = SCREENX(jx), sy = SCREENY(jy);
+  int sxx = 0, syy = 0;
+  int element = Feld[jx][jy];
+  int graphic, phase;
+
+  if (!player->active || player->gone ||
+      !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
+    return;
+
+#if DEBUG
+  if (!IN_LEV_FIELD(jx,jy))
+  {
+    printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
+    printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
+    printf("DrawPlayerField(): This should never happen!\n");
+    return;
+  }
+#endif
+
+  if (element == EL_EXPLODING)
+    return;
+
+  /* draw things in the field the player is leaving, if needed */
+
+  if (last_jx != jx || last_jy != jy)
+  {
+    if (Store[last_jx][last_jy])
+    {
+      DrawLevelElement(last_jx, last_jy, Store[last_jx][last_jy]);
+      DrawLevelFieldThruMask(last_jx, last_jy);
+    }
+    else if (Feld[last_jx][last_jy] == EL_DYNAMIT)
+      DrawDynamite(last_jx, last_jy);
+    else
+      DrawLevelField(last_jx, last_jy);
+
+    if (player->Pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
+    {
+      if (player->GfxPos)
+      {
+       if (Feld[next_jx][next_jy] == EL_SOKOBAN_FELD_VOLL)
+         DrawLevelElement(next_jx, next_jy, EL_SOKOBAN_FELD_LEER);
+       else
+         DrawLevelElement(next_jx, next_jy, EL_LEERRAUM);
+      }
+      else
+       DrawLevelField(next_jx, next_jy);
+    }
+  }
+
+  if (!IN_SCR_FIELD(sx, sy))
+    return;
+
+  if (setup.direct_draw)
+    SetDrawtoField(DRAW_BUFFERED);
+
+  /* draw things behind the player, if needed */
+
+  if (Store[jx][jy])
+    DrawLevelElement(jx, jy, Store[jx][jy]);
+  else if (element != EL_DYNAMIT && element != EL_DYNABOMB)
+    DrawLevelField(jx, jy);
+
+  /* draw player himself */
+
+  if (player->MovDir == MV_LEFT)
+    graphic = (player->Pushing ? GFX_SPIELER1_PUSH_LEFT : GFX_SPIELER1_LEFT);
+  else if (player->MovDir == MV_RIGHT)
+    graphic = (player->Pushing ? GFX_SPIELER1_PUSH_RIGHT : GFX_SPIELER1_RIGHT);
+  else if (player->MovDir == MV_UP)
+    graphic = GFX_SPIELER1_UP;
+  else /* MV_DOWN || MV_NO_MOVING */
+    graphic = GFX_SPIELER1_DOWN;
+
+  graphic += player->index_nr * 3*HEROES_PER_LINE;
+  graphic += player->Frame;
+
+  if (player->GfxPos)
   {
-    graphic -= 256;
-    XCopyArea(display,pix[PIX_BIGFONT],d,gc,
-             (graphic % FONT_CHARS_PER_LINE)*TILEX,
-             (graphic / FONT_CHARS_PER_LINE)*TILEY +
-             FC_SPECIAL1*TILEY*FONT_LINES_PER_FONT,
-             TILEX,TILEY, x,y);
+    if (player->MovDir == MV_LEFT || player->MovDir == MV_RIGHT)
+      sxx = player->GfxPos;
+    else
+      syy = player->GfxPos;
   }
+
+  if (!setup.soft_scrolling && ScreenMovPos)
+    sxx = syy = 0;
+
+  DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, NO_CUTTING);
+
+  if (player->Pushing && player->GfxPos)
+  {
+    int px = SCREENX(next_jx), py = SCREENY(next_jy);
+
+    if (Feld[jx][jy] == EL_SOKOBAN_FELD_LEER ||
+       Feld[next_jx][next_jy] == EL_SOKOBAN_FELD_VOLL)
+      DrawGraphicShiftedThruMask(px, py, sxx, syy, GFX_SOKOBAN_OBJEKT,
+                                NO_CUTTING);
+    else
+    {
+      int element = Feld[next_jx][next_jy];
+      int graphic = el2gfx(element);
+
+      if (element == EL_FELSBROCKEN && sxx)
+      {
+       int phase = (player->GfxPos / (TILEX/4));
+
+       if (player->MovDir == MV_LEFT)
+         graphic += phase;
+       else
+         graphic += (phase+4)%4;
+      }
+
+      DrawGraphicShifted(px, py, sxx, syy, graphic, NO_CUTTING, NO_MASKING);
+    }
+  }
+
+  /* draw things in front of player (EL_DYNAMIT || EL_DYNABOMB) */
+
+  if (element == EL_DYNAMIT || element == EL_DYNABOMB)
+  {
+    graphic = el2gfx(element);
+
+    if (element == EL_DYNAMIT)
+    {
+      if ((phase = (96 - MovDelay[jx][jy]) / 12) > 6)
+       phase = 6;
+    }
+    else
+    {
+      if ((phase = ((96 - MovDelay[jx][jy]) / 6) % 8) > 3)
+       phase = 7 - phase;
+    }
+
+    DrawGraphicThruMask(sx, sy, graphic + phase);
+  }
+
+  if ((last_jx != jx || last_jy != jy) &&
+      Feld[last_jx][last_jy] == EL_EXPLODING)
+  {
+    int phase = Frame[last_jx][last_jy];
+    int delay = 2;
+
+    if (phase > 2)
+      DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy),
+                         GFX_EXPLOSION + ((phase - 1) / delay - 1));
+  }
+
+  if (setup.direct_draw)
+  {
+    int dest_x = SX + SCREENX(MIN(jx, last_jx)) * TILEX;
+    int dest_y = SY + SCREENY(MIN(jy, last_jy)) * TILEY;
+    int x_size = TILEX * (1 + ABS(jx - last_jx));
+    int y_size = TILEY * (1 + ABS(jy - last_jy));
+
+    XCopyArea(display, drawto_field, window, gc,
+             dest_x, dest_y, x_size, y_size, dest_x, dest_y);
+    SetDrawtoField(DRAW_DIRECT);
+  }
+
+  MarkTileDirty(sx,sy);
 }
 
-void DrawGraphicThruMask(int x, int y, int graphic)
+static int getGraphicAnimationPhase(int frames, int delay, int mode)
 {
-  int src_x,src_y, dest_x,dest_y;
+  int phase;
 
-  if (graphic<0 || graphic>255)
+  if (mode == ANIM_OSCILLATE)
   {
-    DrawGraphic(x,y,graphic);
-    return;
+    int max_anim_frames = 2 * frames - 2;
+    phase = (FrameCounter % (delay * max_anim_frames)) / delay;
+    phase = (phase < frames ? phase : max_anim_frames - phase);
   }
+  else
+    phase = (FrameCounter % (delay * frames)) / delay;
 
-  src_x  = SX+(graphic % GFX_PER_LINE)*TILEX;
-  src_y  = SY+(graphic / GFX_PER_LINE)*TILEY;
-  dest_x = SX+x*TILEX;
-  dest_y = SY+y*TILEY;
+  if (mode == ANIM_REVERSE)
+    phase = -phase;
 
-  XSetClipOrigin(display,clip_gc[PIX_BACK],dest_x-src_x,dest_y-src_y);
-  XCopyArea(display,pix[PIX_BACK],drawto_field,clip_gc[PIX_BACK], src_x,src_y,
-           TILEX,TILEY, dest_x,dest_y);
+  return(phase);
+}
 
-  redraw_tiles++;
-  redraw[x][y]=TRUE;
-  redraw_mask|=REDRAW_TILES;
+void DrawGraphicAnimationExt(int x, int y, int graphic,
+                            int frames, int delay, int mode, int mask_mode)
+{
+  int phase = getGraphicAnimationPhase(frames, delay, mode);
+
+  if (!(FrameCounter % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+  {
+    if (mask_mode == USE_MASKING)
+      DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic + phase);
+    else
+      DrawGraphic(SCREENX(x), SCREENY(y), graphic + phase);
+  }
 }
 
-void DrawElementThruMask(int x, int y, int element)
+void DrawGraphicAnimation(int x, int y, int graphic,
+                         int frames, int delay, int mode)
 {
-  DrawGraphicThruMask(x,y,el2gfx(element));
+  DrawGraphicAnimationExt(x, y, graphic, frames, delay, mode, NO_MASKING);
 }
 
-void DrawMiniGraphic(int x, int y, int graphic)
+void DrawGraphicAnimationThruMask(int x, int y, int graphic,
+                                 int frames, int delay, int mode)
 {
-  DrawMiniGraphicExt(drawto, gc, x, y, graphic);
-  redraw_tiles++;
-  redraw[x/2][y/2]=TRUE;
-  redraw_mask|=REDRAW_TILES;
+  DrawGraphicAnimationExt(x, y, graphic, frames, delay, mode, USE_MASKING);
 }
 
-void DrawMiniGraphicExt(Drawable d, GC gc, int x, int y, int graphic)
+void DrawGraphic(int x, int y, int graphic)
+{
+#if DEBUG
+  if (!IN_SCR_FIELD(x,y))
+  {
+    printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
+    printf("DrawGraphic(): This should never happen!\n");
+    return;
+  }
+#endif
+
+  DrawGraphicExt(drawto_field, gc, FX + x*TILEX, FY + y*TILEY, graphic);
+  MarkTileDirty(x,y);
+}
+
+void DrawGraphicExt(Drawable d, GC gc, int x, int y, int graphic)
+{
+  if (graphic >= GFX_START_ROCKSSCREEN && graphic <= GFX_END_ROCKSSCREEN)
+  {
+    graphic -= GFX_START_ROCKSSCREEN;
+    XCopyArea(display, pix[PIX_BACK], d, gc,
+             SX + (graphic % GFX_PER_LINE) * TILEX,
+             SY + (graphic / GFX_PER_LINE) * TILEY,
+             TILEX, TILEY, x, y);
+  }
+  else if (graphic >= GFX_START_ROCKSHEROES && graphic <= GFX_END_ROCKSHEROES)
+  {
+    graphic -= GFX_START_ROCKSHEROES;
+    XCopyArea(display, pix[PIX_HEROES], d, gc,
+             (graphic % HEROES_PER_LINE) * TILEX,
+             (graphic / HEROES_PER_LINE) * TILEY,
+             TILEX, TILEY, x, y);
+  }
+  else if (graphic >= GFX_START_ROCKSFONT && graphic <= GFX_END_ROCKSFONT)
+  {
+    graphic -= GFX_START_ROCKSFONT;
+    XCopyArea(display, pix[PIX_BIGFONT], d, gc,
+             (graphic % FONT_CHARS_PER_LINE) * TILEX,
+             (graphic / FONT_CHARS_PER_LINE) * TILEY +
+             FC_SPECIAL1 * FONT_LINES_PER_FONT * TILEY,
+             TILEX, TILEY, x, y);
+  }
+  else
+    XFillRectangle(display, d, gc, x, y, TILEX, TILEY);
+}
+
+void DrawGraphicThruMask(int x, int y, int graphic)
 {
-  DrawMiniGraphicExtHiRes(d,gc, SX+x*MINI_TILEX,SY+y*MINI_TILEY, graphic);
+#if DEBUG
+  if (!IN_SCR_FIELD(x,y))
+  {
+    printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
+    printf("DrawGraphicThruMask(): This should never happen!\n");
+    return;
+  }
+#endif
+
+  DrawGraphicThruMaskExt(drawto_field, FX + x*TILEX, FY + y*TILEY, graphic);
+  MarkTileDirty(x,y);
 }
 
-void DrawMiniGraphicExtHiRes(Drawable d, GC gc, int x, int y, int graphic)
+void DrawGraphicThruMaskExt(Drawable d, int dest_x, int dest_y, int graphic)
 {
-  if (graphic<0)
-    XFillRectangle(display,d,gc, x,y, MINI_TILEX,MINI_TILEY);
-  else if (graphic<256)
-    XCopyArea(display,pix[PIX_BACK],d,gc,
-             MINI_GFX_STARTX+(graphic % MINI_GFX_PER_LINE)*MINI_TILEX,
-             MINI_GFX_STARTY+(graphic / MINI_GFX_PER_LINE)*MINI_TILEY,
-             MINI_TILEX,MINI_TILEY, x,y);
+  int src_x, src_y;
+  int tile = graphic;
+  Pixmap src_pixmap;
+  GC drawing_gc;
+
+  if (graphic >= GFX_START_ROCKSSCREEN && graphic <= GFX_END_ROCKSSCREEN)
+  {
+    src_pixmap = pix[PIX_BACK];
+    drawing_gc = clip_gc[PIX_BACK];
+    graphic -= GFX_START_ROCKSSCREEN;
+    src_x  = SX + (graphic % GFX_PER_LINE) * TILEX;
+    src_y  = SY + (graphic / GFX_PER_LINE) * TILEY;
+  }
+  else if (graphic >= GFX_START_ROCKSHEROES && graphic <= GFX_END_ROCKSHEROES)
+  {
+    src_pixmap = pix[PIX_HEROES];
+    drawing_gc = clip_gc[PIX_HEROES];
+    graphic -= GFX_START_ROCKSHEROES;
+    src_x  = (graphic % HEROES_PER_LINE) * TILEX;
+    src_y  = (graphic / HEROES_PER_LINE) * TILEY;
+  }
   else
   {
-    graphic -= 256;
-    XCopyArea(display,pix[PIX_SMALLFONT],d,gc,
-             (graphic % FONT_CHARS_PER_LINE)*FONT4_XSIZE,
-             (graphic / FONT_CHARS_PER_LINE)*FONT4_YSIZE +
-             FC_SPECIAL2*FONT2_YSIZE*FONT_LINES_PER_FONT,
-             MINI_TILEX,MINI_TILEY, x,y);
+    DrawGraphicExt(d, gc, dest_x,dest_y, graphic);
+    return;
+  }
+
+  if (tile_clipmask[tile] != None)
+  {
+    XSetClipMask(display, tile_clip_gc, tile_clipmask[tile]);
+    XSetClipOrigin(display, tile_clip_gc, dest_x, dest_y);
+    XCopyArea(display, src_pixmap, drawto_field, tile_clip_gc,
+             src_x, src_y, TILEX, TILEY, dest_x, dest_y);
+  }
+  else
+  {
+#if DEBUG
+    printf("DrawGraphicThruMask(): tile '%d' needs clipping!\n", tile);
+#endif
+
+    XSetClipOrigin(display, drawing_gc, dest_x-src_x, dest_y-src_y);
+    XCopyArea(display, src_pixmap, drawto_field, drawing_gc,
+             src_x, src_y, TILEX, TILEY, dest_x, dest_y);
+  }
+}
+
+void DrawMiniGraphic(int x, int y, int graphic)
+{
+  DrawMiniGraphicExt(drawto,gc, SX + x*MINI_TILEX, SY + y*MINI_TILEY, graphic);
+  MarkTileDirty(x/2, y/2);
+}
+
+void DrawMiniGraphicExt(Drawable d, GC gc, int x, int y, int graphic)
+{
+  if (graphic >= GFX_START_ROCKSSCREEN && graphic <= GFX_END_ROCKSSCREEN)
+  {
+    graphic -= GFX_START_ROCKSSCREEN;
+    XCopyArea(display, pix[PIX_BACK], d, gc,
+             MINI_GFX_STARTX + (graphic % MINI_GFX_PER_LINE) * MINI_TILEX,
+             MINI_GFX_STARTY + (graphic / MINI_GFX_PER_LINE) * MINI_TILEY,
+             MINI_TILEX, MINI_TILEY, x, y);
+  }
+  else if (graphic >= GFX_START_ROCKSFONT && graphic <= GFX_END_ROCKSFONT)
+  {
+    graphic -= GFX_START_ROCKSFONT;
+    XCopyArea(display, pix[PIX_SMALLFONT], d, gc,
+             (graphic % FONT_CHARS_PER_LINE) * FONT4_XSIZE,
+             (graphic / FONT_CHARS_PER_LINE) * FONT4_YSIZE +
+             FC_SPECIAL2 * FONT2_YSIZE * FONT_LINES_PER_FONT,
+             MINI_TILEX, MINI_TILEY, x, y);
   }
+  else
+    XFillRectangle(display, d, gc, x, y, MINI_TILEX, MINI_TILEY);
 }
 
-void DrawGraphicShifted(int x,int y, int dx,int dy, int graphic, int cut_mode)
+void DrawGraphicShifted(int x,int y, int dx,int dy, int graphic,
+                       int cut_mode, int mask_mode)
 {
   int width = TILEX, height = TILEY;
   int cx = 0, cy = 0;
+  int src_x, src_y, dest_x, dest_y;
+  int tile = graphic;
+  Pixmap src_pixmap;
+  GC drawing_gc;
 
-  if (graphic<0)
+  if (graphic < 0)
   {
-    DrawGraphic(x,y,graphic);
+    DrawGraphic(x, y, graphic);
     return;
   }
 
   if (dx || dy)                        /* Verschiebung der Grafik? */
   {
-    if (x<0)                   /* Element kommt von links ins Bild */
+    if (x < BX1)               /* Element kommt von links ins Bild */
     {
-      x=0;
-      width=dx;
-      cx=TILEX-dx;
-      dx=0;
+      x = BX1;
+      width = dx;
+      cx = TILEX - dx;
+      dx = 0;
     }
-    else if (x==SCR_FIELDX)    /* Element kommt von rechts ins Bild */
+    else if (x > BX2)          /* Element kommt von rechts ins Bild */
     {
-      x=SCR_FIELDX-1;
-      width=-dx;
-      dx=TILEX+dx;
+      x = BX2;
+      width = -dx;
+      dx = TILEX + dx;
     }
-    else if (x==0 && dx<0)     /* Element verläßt links das Bild */
+    else if (x==BX1 && dx < 0) /* Element verläßt links das Bild */
     {
-      width+=dx;
-      cx=-dx;
-      dx=0;
+      width += dx;
+      cx = -dx;
+      dx = 0;
     }
-    else if (x==SCR_FIELDX-1 && dx>0)  /* El. verläßt rechts das Bild */
-      width-=dx;
+    else if (x==BX2 && dx > 0) /* Element verläßt rechts das Bild */
+      width -= dx;
     else if (dx)               /* allg. Bewegung in x-Richtung */
-      redraw[x+SIGN(dx)][y]=TRUE;
+      MarkTileDirty(x + SIGN(dx), y);
 
-    if (y<0)                   /* Element kommt von oben ins Bild */
+    if (y < BY1)               /* Element kommt von oben ins Bild */
     {
       if (cut_mode==CUT_BELOW) /* Element oberhalb des Bildes */
        return;
 
-      y=0;
-      height=dy;
-      cy=TILEY-dy;
-      dy=0;
+      y = BY1;
+      height = dy;
+      cy = TILEY - dy;
+      dy = 0;
     }
-    else if (y==SCR_FIELDY)    /* Element kommt von unten ins Bild */
+    else if (y > BY2)          /* Element kommt von unten ins Bild */
     {
-      y=SCR_FIELDY-1;
-      height=-dy;
-      dy=TILEY+dy;
+      y = BY2;
+      height = -dy;
+      dy = TILEY + dy;
     }
-    else if (y==0 && dy<0)     /* Element verläßt oben das Bild */
+    else if (y==BY1 && dy < 0) /* Element verläßt oben das Bild */
     {
-      height+=dy;
-      cy=-dy;
-      dy=0;
+      height += dy;
+      cy = -dy;
+      dy = 0;
     }
-    else if (dy>0 && cut_mode==CUT_ABOVE)
+    else if (dy > 0 && cut_mode == CUT_ABOVE)
     {
-      if (y==SCR_FIELDY-1)     /* Element unterhalb des Bildes */
+      if (y == BY2)            /* Element unterhalb des Bildes */
        return;
 
-      height=dy;
-      cy=TILEY-dy;
-      dy=TILEY;
-      redraw[x][y+1]=TRUE;
+      height = dy;
+      cy = TILEY - dy;
+      dy = TILEY;
+      MarkTileDirty(x, y + 1);
     }                          /* Element verläßt unten das Bild */
-    else if (dy>0 && (y==SCR_FIELDY-1 || cut_mode==CUT_BELOW))
-      height-=dy;
+    else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
+      height -= dy;
     else if (dy)               /* allg. Bewegung in y-Richtung */
-      redraw[x][y+SIGN(dy)]=TRUE;
+      MarkTileDirty(x, y + SIGN(dy));
   }
 
-  XCopyArea(display,pix[PIX_BACK],drawto_field,gc,
-           SX+(graphic % GFX_PER_LINE)*TILEX+cx,
-           SY+(graphic / GFX_PER_LINE)*TILEY+cy,
-           width,height, SX+x*TILEX+dx,SY+y*TILEY+dy);
+  if (graphic >= GFX_START_ROCKSSCREEN && graphic <= GFX_END_ROCKSSCREEN)
+  {
+    src_pixmap = pix[PIX_BACK];
+    drawing_gc = clip_gc[PIX_BACK];
+    graphic -= GFX_START_ROCKSSCREEN;
+    src_x  = SX + (graphic % GFX_PER_LINE) * TILEX + cx;
+    src_y  = SY + (graphic / GFX_PER_LINE) * TILEY + cy;
+  }
+  else if (graphic >= GFX_START_ROCKSHEROES && graphic <= GFX_END_ROCKSHEROES)
+  {
+    src_pixmap = pix[PIX_HEROES];
+    drawing_gc = clip_gc[PIX_HEROES];
+    graphic -= GFX_START_ROCKSHEROES;
+    src_x  = (graphic % HEROES_PER_LINE) * TILEX + cx;
+    src_y  = (graphic / HEROES_PER_LINE) * TILEY + cy;
+  }
+  else /* big font graphics currently not allowed (and not needed) */
+    return;
+
+  dest_x = FX + x * TILEX + dx;
+  dest_y = FY + y * TILEY + dy;
 
-  redraw_tiles++;
-  redraw[x][y]=TRUE;
-  redraw_mask|=REDRAW_TILES;
+#if DEBUG
+  if (!IN_SCR_FIELD(x,y))
+  {
+    printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
+    printf("DrawGraphicShifted(): This should never happen!\n");
+    return;
+  }
+#endif
+
+  if (mask_mode == USE_MASKING)
+  {
+    if (tile_clipmask[tile] != None)
+    {
+      XSetClipMask(display, tile_clip_gc, tile_clipmask[tile]);
+      XSetClipOrigin(display, tile_clip_gc, dest_x, dest_y);
+      XCopyArea(display, src_pixmap, drawto_field, tile_clip_gc,
+               src_x, src_y, TILEX, TILEY, dest_x, dest_y);
+    }
+    else
+    {
+#if DEBUG
+      printf("DrawGraphicShifted(): tile '%d' needs clipping!\n", tile);
+#endif
+
+      XSetClipOrigin(display, drawing_gc, dest_x - src_x, dest_y - src_y);
+      XCopyArea(display, src_pixmap, drawto_field, drawing_gc,
+               src_x, src_y, width, height, dest_x, dest_y);
+    }
+  }
+  else
+    XCopyArea(display, src_pixmap, drawto_field, gc,
+             src_x, src_y, width, height, dest_x, dest_y);
+
+  MarkTileDirty(x,y);
+}
+
+void DrawGraphicShiftedThruMask(int x,int y, int dx,int dy, int graphic,
+                               int cut_mode)
+{
+  DrawGraphicShifted(x,y, dx,dy, graphic, cut_mode, USE_MASKING);
 }
 
-void DrawElementShifted(int x, int y, int dx, int dy, int element,int cut_mode)
+void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
+                         int cut_mode, int mask_mode)
 {
-  int ux = UNSCROLLX(x), uy = UNSCROLLY(y);
+  int ux = LEVELX(x), uy = LEVELY(y);
   int graphic = el2gfx(element);
-  int phase = ABS(MovPos[ux][uy])/(TILEX/2);
+  int phase4 = ABS(MovPos[ux][uy]) / (TILEX / 4);
+  int phase  = phase4 / 2;
   int dir = MovDir[ux][uy];
-  int horiz_move = (dir==MV_LEFT || dir==MV_RIGHT);
 
-  if (element==EL_PACMAN ||
-      element==EL_KAEFER ||
-      element==EL_FLIEGER)
+  if (element == EL_PACMAN || element == EL_KAEFER || element == EL_FLIEGER)
   {
-    if (element==EL_PACMAN)
-      graphic = GFX_PACMAN + 4*!phase;
-    else
-      graphic += 4*!phase;
+    graphic += 4*!phase;
 
-    if (dir==MV_UP)
+    if (dir == MV_UP)
       graphic += 1;
-    else if (dir==MV_LEFT)
+    else if (dir == MV_LEFT)
       graphic += 2;
-    else if (dir==MV_DOWN)
+    else if (dir == MV_DOWN)
       graphic += 3;
   }
-  else if ((element==EL_FELSBROCKEN ||
-           element==EL_EDELSTEIN ||
-           element==EL_DIAMANT) && horiz_move && phase)
+  else if (element == EL_MAULWURF || element == EL_PINGUIN ||
+          element == EL_SCHWEIN || element == EL_DRACHE)
   {
-    if (element==EL_FELSBROCKEN)
-      graphic += 2;
+    if (dir == MV_LEFT)
+      graphic = (element == EL_MAULWURF ? GFX_MAULWURF_LEFT :
+                element == EL_PINGUIN ? GFX_PINGUIN_LEFT :
+                element == EL_SCHWEIN ? GFX_SCHWEIN_LEFT : GFX_DRACHE_LEFT);
+    else if (dir == MV_RIGHT)
+      graphic = (element == EL_MAULWURF ? GFX_MAULWURF_RIGHT :
+                element == EL_PINGUIN ? GFX_PINGUIN_RIGHT :
+                element == EL_SCHWEIN ? GFX_SCHWEIN_RIGHT : GFX_DRACHE_RIGHT);
+    else if (dir == MV_UP)
+      graphic = (element == EL_MAULWURF ? GFX_MAULWURF_UP :
+                element == EL_PINGUIN ? GFX_PINGUIN_UP :
+                element == EL_SCHWEIN ? GFX_SCHWEIN_UP : GFX_DRACHE_UP);
     else
-      graphic += 1;
+      graphic = (element == EL_MAULWURF ? GFX_MAULWURF_DOWN :
+                element == EL_PINGUIN ? GFX_PINGUIN_DOWN :
+                element == EL_SCHWEIN ? GFX_SCHWEIN_DOWN : GFX_DRACHE_DOWN);
+
+    graphic += phase4;
   }
-  else if ((element==EL_SIEB_LEER ||
-           element==EL_SIEB_VOLL) && SiebAktiv)
+  else if (element == EL_SONDE)
   {
-    graphic += 3-(SiebAktiv%8)/2;
+    graphic = GFX_SONDE_START + getGraphicAnimationPhase(8, 2, ANIM_NORMAL);
+  }
+  else if (element == EL_SALZSAEURE)
+  {
+    graphic = GFX_GEBLUBBER + getGraphicAnimationPhase(4, 10, ANIM_NORMAL);
+  }
+  else if (element == EL_BUTTERFLY || element == EL_FIREFLY)
+  {
+    graphic += !phase;
+  }
+  else if ((element == EL_FELSBROCKEN || IS_GEM(element)) && !cut_mode)
+  {
+    graphic += phase * (element == EL_FELSBROCKEN ? 2 : 1);
+  }
+  else if ((element == EL_SIEB_LEER || element == EL_SIEB2_LEER ||
+           element == EL_SIEB_VOLL || element == EL_SIEB2_VOLL) && SiebAktiv)
+  {
+    graphic += 3 - (SiebAktiv % 8) / 2;
   }
   else if (IS_AMOEBOID(element))
   {
-    graphic = (element==EL_AMOEBE_TOT ? GFX_AMOEBE_TOT : GFX_AMOEBE_LEBT);
-    graphic += (x+2*y) % 4;
+    graphic = (element == EL_AMOEBE_TOT ? GFX_AMOEBE_TOT : GFX_AMOEBE_LEBT);
+    graphic += (x + 2 * y + 4) % 4;
+  }
+  else if (element == EL_MAUER_LEBT)
+  {
+    boolean links_massiv = FALSE, rechts_massiv = FALSE;
+
+    if (!IN_LEV_FIELD(ux-1, uy) || IS_MAUER(Feld[ux-1][uy]))
+      links_massiv = TRUE;
+    if (!IN_LEV_FIELD(ux+1, uy) || IS_MAUER(Feld[ux+1][uy]))
+      rechts_massiv = TRUE;
+
+    if (links_massiv && rechts_massiv)
+      graphic = GFX_MAUERWERK;
+    else if (links_massiv)
+      graphic = GFX_MAUER_R;
+    else if (rechts_massiv)
+      graphic = GFX_MAUER_L;
   }
 
   if (dx || dy)
-    DrawGraphicShifted(x,y, dx,dy, graphic, cut_mode);
+    DrawGraphicShifted(x, y, dx, dy, graphic, cut_mode, mask_mode);
+  else if (mask_mode == USE_MASKING)
+    DrawGraphicThruMask(x, y, graphic);
   else
-    DrawGraphic(x,y, graphic);
+    DrawGraphic(x, y, graphic);
+}
+
+void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
+                        int cut_mode, int mask_mode)
+{
+  if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+    DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
+                        cut_mode, mask_mode);
+}
+
+void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
+                             int cut_mode)
+{
+  DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
+}
+
+void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
+                            int cut_mode)
+{
+  DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
+}
+
+void DrawScreenElementThruMask(int x, int y, int element)
+{
+  DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
+}
+
+void DrawLevelElementThruMask(int x, int y, int element)
+{
+  DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
+}
+
+void DrawLevelFieldThruMask(int x, int y)
+{
+  DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
 }
 
 void ErdreichAnbroeckeln(int x, int y)
 {
   int i, width, height, cx,cy;
-  int ux = UNSCROLLX(x), uy = UNSCROLLY(y);
+  int ux = LEVELX(x), uy = LEVELY(y);
   int element, graphic;
   int snip = 4;
   static int xy[4][2] =
   {
-    0,-1,
-    -1,0,
-    +1,0,
-    0,+1
+    { 0, -1 },
+    { -1, 0 },
+    { +1, 0 },
+    { 0, +1 }
   };
 
-  if (!IN_LEV_FIELD(ux,uy))
+  if (!IN_LEV_FIELD(ux, uy))
     return;
 
   element = Feld[ux][uy];
 
-  if (element==EL_ERDREICH)
+  if (element == EL_ERDREICH)
   {
-    if (!IN_SCR_FIELD(x,y))
+    if (!IN_SCR_FIELD(x, y))
       return;
 
     graphic = GFX_ERDENRAND;
 
-    for(i=0;i<4;i++)
+    for(i=0; i<4; i++)
     {
-      int uxx,uyy;
+      int uxx, uyy;
 
-      uxx = ux+xy[i][0];
-      uyy = uy+xy[i][1];
-      if (!IN_LEV_FIELD(uxx,uyy))
+      uxx = ux + xy[i][0];
+      uyy = uy + xy[i][1];
+      if (!IN_LEV_FIELD(uxx, uyy))
        element = EL_BETON;
       else
        element = Feld[uxx][uyy];
 
-/*
-      if (element==EL_ERDREICH || IS_SOLID(element))
-       continue;
-*/
-      if (element==EL_ERDREICH)
+      if (element == EL_ERDREICH)
        continue;
 
-      if (i==1 || i==2)
+      if (i == 1 || i == 2)
       {
        width = snip;
        height = TILEY;
-       cx = (i==2 ? TILEX-snip : 0);
+       cx = (i == 2 ? TILEX - snip : 0);
        cy = 0;
       }
       else
@@ -533,45 +1059,39 @@ void ErdreichAnbroeckeln(int x, int y)
        width = TILEX;
        height = snip;
        cx = 0;
-       cy = (i==3 ? TILEY-snip : 0);
+       cy = (i == 3 ? TILEY - snip : 0);
       }
 
-      XCopyArea(display,pix[PIX_BACK],drawto_field,gc,
-               SX+(graphic % GFX_PER_LINE)*TILEX+cx,
-               SY+(graphic / GFX_PER_LINE)*TILEY+cy,
-               width,height, SX+x*TILEX+cx,SY+y*TILEY+cy);
+      XCopyArea(display, pix[PIX_BACK], drawto_field, gc,
+               SX + (graphic % GFX_PER_LINE) * TILEX + cx,
+               SY + (graphic / GFX_PER_LINE) * TILEY + cy,
+               width, height, FX + x * TILEX + cx, FY + y * TILEY + cy);
     }
 
-    redraw_tiles++;
-    redraw[x][y]=TRUE;
+    MarkTileDirty(x, y);
   }
   else
   {
     graphic = GFX_ERDENRAND;
 
-    for(i=0;i<4;i++)
+    for(i=0; i<4; i++)
     {
-      int xx,yy,uxx,uyy;
+      int xx, yy, uxx, uyy;
 
-      xx = x+xy[i][0];
-      yy = y+xy[i][1];
-      uxx = ux+xy[i][0];
-      uyy = uy+xy[i][1];
-/*
-      if (!IN_LEV_FIELD(uxx,uyy) || Feld[uxx][uyy]!=EL_ERDREICH ||
-         !IN_SCR_FIELD(xx,yy) || IS_SOLID(element))
-       continue;
-*/
+      xx = x + xy[i][0];
+      yy = y + xy[i][1];
+      uxx = ux + xy[i][0];
+      uyy = uy + xy[i][1];
 
-      if (!IN_LEV_FIELD(uxx,uyy) || Feld[uxx][uyy]!=EL_ERDREICH ||
-         !IN_SCR_FIELD(xx,yy))
+      if (!IN_LEV_FIELD(uxx, uyy) || Feld[uxx][uyy] != EL_ERDREICH ||
+         !IN_SCR_FIELD(xx, yy))
        continue;
 
-      if (i==1 || i==2)
+      if (i == 1 || i == 2)
       {
        width = snip;
        height = TILEY;
-       cx = (i==1 ? TILEX-snip : 0);
+       cx = (i == 1 ? TILEX - snip : 0);
        cy = 0;
       }
       else
@@ -582,116 +1102,121 @@ void ErdreichAnbroeckeln(int x, int y)
        cy = (i==0 ? TILEY-snip : 0);
       }
 
-      XCopyArea(display,pix[PIX_BACK],drawto_field,gc,
-               SX+(graphic % GFX_PER_LINE)*TILEX+cx,
-               SY+(graphic / GFX_PER_LINE)*TILEY+cy,
-               width,height, SX+xx*TILEX+cx,SY+yy*TILEY+cy);
+      XCopyArea(display, pix[PIX_BACK], drawto_field, gc,
+               SX + (graphic % GFX_PER_LINE) * TILEX + cx,
+               SY + (graphic / GFX_PER_LINE) * TILEY + cy,
+               width, height, FX + xx * TILEX + cx, FY + yy * TILEY + cy);
 
-      redraw_tiles++;
-      redraw[xx][yy]=TRUE;
+      MarkTileDirty(xx, yy);
     }
   }
 }
 
 void DrawScreenElement(int x, int y, int element)
 {
-  DrawElementShifted(x,y,0,0,element,CUT_NO_CUTTING);
-  ErdreichAnbroeckeln(x,y);
+  DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
+  ErdreichAnbroeckeln(x, y);
 }
 
 void DrawLevelElement(int x, int y, int element)
 {
-  if (IN_LEV_FIELD(x,y) && IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-    DrawScreenElement(SCROLLX(x),SCROLLY(y),element);
+  if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+    DrawScreenElement(SCREENX(x), SCREENY(y), element);
 }
 
 void DrawScreenField(int x, int y)
 {
-  int ux = UNSCROLLX(x), uy = UNSCROLLY(y);
+  int ux = LEVELX(x), uy = LEVELY(y);
   int element;
 
-  if (!IN_LEV_FIELD(ux,uy))
+  if (!IN_LEV_FIELD(ux, uy))
   {
-    DrawScreenElement(x,y,EL_BETON);
+    DrawScreenElement(x, y, EL_BETON);
     return;
   }
 
   element = Feld[ux][uy];
 
-  if (IS_MOVING(ux,uy))
+  if (IS_MOVING(ux, uy))
   {
-    int horiz_move = (MovDir[ux][uy]==MV_LEFT || MovDir[ux][uy]==MV_RIGHT);
-    BOOL cut_mode = CUT_NO_CUTTING;
+    int horiz_move = (MovDir[ux][uy] == MV_LEFT || MovDir[ux][uy] == MV_RIGHT);
+    boolean cut_mode = NO_CUTTING;
 
-    if (Store[ux][uy]==EL_MORAST_LEER ||
-       Store[ux][uy]==EL_SIEB_LEER ||
-       Store[ux][uy]==EL_AMOEBE_NASS)
+    if (Store[ux][uy] == EL_MORAST_LEER ||
+       Store[ux][uy] == EL_SIEB_LEER ||
+       Store[ux][uy] == EL_SIEB2_LEER ||
+       Store[ux][uy] == EL_AMOEBE_NASS)
       cut_mode = CUT_ABOVE;
-    else if (Store[ux][uy]==EL_MORAST_VOLL ||
-       Store[ux][uy]==EL_SIEB_VOLL ||
-       Store[ux][uy]==EL_SALZSAEURE)
+    else if (Store[ux][uy] == EL_MORAST_VOLL ||
+            Store[ux][uy] == EL_SIEB_VOLL ||
+            Store[ux][uy] == EL_SIEB2_VOLL)
       cut_mode = CUT_BELOW;
 
-    if (cut_mode==CUT_ABOVE)
-      DrawElementShifted(x,y,0,0,Store[ux][uy],CUT_NO_CUTTING);
+    if (cut_mode == CUT_ABOVE)
+      DrawScreenElementShifted(x, y, 0, 0, Store[ux][uy], NO_CUTTING);
     else
-      DrawScreenElement(x,y,EL_LEERRAUM);
+      DrawScreenElement(x, y, EL_LEERRAUM);
 
     if (horiz_move)
-      DrawElementShifted(x,y,MovPos[ux][uy],0,element,CUT_NO_CUTTING);
+      DrawScreenElementShifted(x, y, MovPos[ux][uy], 0, element, NO_CUTTING);
     else
-      DrawElementShifted(x,y,0,MovPos[ux][uy],element,cut_mode);
+      DrawScreenElementShifted(x, y, 0, MovPos[ux][uy], element, cut_mode);
+
+    if (Store[ux][uy] == EL_SALZSAEURE)
+      DrawLevelElementThruMask(ux, uy + 1, EL_SALZSAEURE);
   }
-  else if (IS_BLOCKED(ux,uy))
+  else if (IS_BLOCKED(ux, uy))
   {
-    int oldx,oldy;
+    int oldx, oldy;
     int sx, sy;
     int horiz_move;
-    BOOL cut_mode = CUT_NO_CUTTING;
-
-    Blocked2Moving(ux,uy,&oldx,&oldy);
-    sx = SCROLLX(oldx);
-    sy = SCROLLY(oldy);
-    horiz_move = (MovDir[oldx][oldy]==MV_LEFT || MovDir[oldx][oldy]==MV_RIGHT);
-
-    if (Store[oldx][oldy]==EL_MORAST_LEER ||
-       Store[oldx][oldy]==EL_SIEB_LEER ||
-       Store[oldx][oldy]==EL_AMOEBE_NASS)
+    boolean cut_mode = NO_CUTTING;
+
+    Blocked2Moving(ux, uy, &oldx, &oldy);
+    sx = SCREENX(oldx);
+    sy = SCREENY(oldy);
+    horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
+                 MovDir[oldx][oldy] == MV_RIGHT);
+
+    if (Store[oldx][oldy] == EL_MORAST_LEER ||
+       Store[oldx][oldy] == EL_SIEB_LEER ||
+       Store[oldx][oldy] == EL_SIEB2_LEER ||
+       Store[oldx][oldy] == EL_AMOEBE_NASS)
       cut_mode = CUT_ABOVE;
 
-    DrawScreenElement(x,y,EL_LEERRAUM);
+    DrawScreenElement(x, y, EL_LEERRAUM);
     element = Feld[oldx][oldy];
 
     if (horiz_move)
-      DrawElementShifted(sx,sy,MovPos[oldx][oldy],0,element,CUT_NO_CUTTING);
+      DrawScreenElementShifted(sx,sy, MovPos[oldx][oldy],0,element,NO_CUTTING);
     else
-      DrawElementShifted(sx,sy,0,MovPos[oldx][oldy],element,cut_mode);
+      DrawScreenElementShifted(sx,sy, 0,MovPos[oldx][oldy],element,cut_mode);
   }
   else if (IS_DRAWABLE(element))
-    DrawScreenElement(x,y,element);
+    DrawScreenElement(x, y, element);
   else
-    DrawScreenElement(x,y,EL_LEERRAUM);
+    DrawScreenElement(x, y, EL_LEERRAUM);
 }
 
 void DrawLevelField(int x, int y)
 {
-  if (IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-    DrawScreenField(SCROLLX(x),SCROLLY(y));
-  else if (IS_MOVING(x,y))
+  if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+    DrawScreenField(SCREENX(x), SCREENY(y));
+  else if (IS_MOVING(x, y))
   {
     int newx,newy;
 
-    Moving2Blocked(x,y,&newx,&newy);
-    if (IN_SCR_FIELD(SCROLLX(newx),SCROLLY(newy)))
-      DrawScreenField(SCROLLX(newx),SCROLLY(newy));
+    Moving2Blocked(x, y, &newx, &newy);
+    if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
+      DrawScreenField(SCREENX(newx), SCREENY(newy));
   }
-  else if (IS_BLOCKED(x,y))
+  else if (IS_BLOCKED(x, y))
   {
-    int oldx,oldy;
+    int oldx, oldy;
 
-    Blocked2Moving(x,y,&oldx,&oldy);
-    if (IN_SCR_FIELD(SCROLLX(oldx),SCROLLY(oldy)))
-      DrawScreenField(SCROLLX(oldx),SCROLLY(oldy));
+    Blocked2Moving(x, y, &oldx, &oldy);
+    if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
+      DrawScreenField(SCREENX(oldx), SCREENY(oldy));
   }
 }
 
@@ -701,43 +1226,39 @@ void DrawMiniElement(int x, int y, int element)
 
   if (!element)
   {
-    DrawMiniGraphic(x,y,-1);
+    DrawMiniGraphic(x, y, -1);
     return;
   }
 
   graphic = el2gfx(element);
-  DrawMiniGraphic(x,y,graphic);
-
-  redraw_tiles++;
-  redraw[x/2][y/2]=TRUE;
-  redraw_mask|=REDRAW_TILES;
+  DrawMiniGraphic(x, y, graphic);
 }
 
-void DrawMiniElementOrWall(int x, int y, int scroll_x, int scroll_y)
+void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
 {
-  if (x+scroll_x<-1 || x+scroll_x>lev_fieldx ||
-      y+scroll_y<-1 || y+scroll_y>lev_fieldy)
-    DrawMiniElement(x,y,EL_LEERRAUM);
-  else if (x+scroll_x==-1 || x+scroll_x==lev_fieldx ||
-          y+scroll_y==-1 || y+scroll_y==lev_fieldy)
-    DrawMiniElement(x,y,EL_BETON);
+  int x = sx + scroll_x, y = sy + scroll_y;
+
+  if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
+    DrawMiniElement(sx, sy, EL_LEERRAUM);
+  else if (x == -1 || x == lev_fieldx || y == -1 || y == lev_fieldy)
+    DrawMiniElement(sx, sy, EL_BETON);
   else
-    DrawMiniElement(x,y,Feld[x+scroll_x][y+scroll_y]);
+    DrawMiniElement(sx, sy, Feld[x][y]);
 }
 
 void DrawMicroElement(int xpos, int ypos, int element)
 {
   int graphic;
 
-  if (element==EL_LEERRAUM)
+  if (element == EL_LEERRAUM)
     return;
 
   graphic = el2gfx(element);
 
-  XCopyArea(display,pix[PIX_BACK],drawto,gc,
-           MICRO_GFX_STARTX+(graphic % MICRO_GFX_PER_LINE)*MICRO_TILEX,
-           MICRO_GFX_STARTY+(graphic / MICRO_GFX_PER_LINE)*MICRO_TILEY,
-           MICRO_TILEX,MICRO_TILEY, xpos,ypos);
+  XCopyArea(display, pix[PIX_BACK], drawto, gc,
+           MICRO_GFX_STARTX + (graphic % MICRO_GFX_PER_LINE) * MICRO_TILEX,
+           MICRO_GFX_STARTY + (graphic / MICRO_GFX_PER_LINE) * MICRO_TILEY,
+           MICRO_TILEX, MICRO_TILEY, xpos, ypos);
 }
 
 void DrawLevel()
@@ -746,11 +1267,15 @@ void DrawLevel()
 
   ClearWindow();
 
-  for(x=0;x<SCR_FIELDX;x++)
-    for(y=0;y<SCR_FIELDY;y++)
-      DrawScreenField(x,y);
+  for(x=BX1; x<=BX2; x++)
+    for(y=BY1; y<=BY2; y++)
+      DrawScreenField(x, y);
 
-  redraw_mask |= REDRAW_FIELD;
+  if (setup.soft_scrolling)
+    XCopyArea(display, fieldbuffer, backbuffer, gc,
+             FX, FY, SXSIZE, SYSIZE, SX, SY);
+
+  redraw_mask |= (REDRAW_FIELD | REDRAW_FROM_BACKBUFFER);
 }
 
 void DrawMiniLevel(int scroll_x, int scroll_y)
@@ -759,9 +1284,9 @@ void DrawMiniLevel(int scroll_x, int scroll_y)
 
   ClearWindow();
 
-  for(x=0;x<2*SCR_FIELDX;x++)
-    for(y=0;y<2*SCR_FIELDY;y++)
-      DrawMiniElementOrWall(x,y,scroll_x,scroll_y);
+  for(x=0; x<2*SCR_FIELDX; x++)
+    for(y=0; y<2*SCR_FIELDY; y++)
+      DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
 
   redraw_mask |= REDRAW_FIELD;
 }
@@ -770,72 +1295,88 @@ void DrawMicroLevel(int xpos, int ypos)
 {
   int x,y;
 
-  XFillRectangle(display,drawto,gc,
-                xpos-MICRO_TILEX,ypos-MICRO_TILEY,
-                MICRO_TILEX*(STD_LEV_FIELDX+2),
-                MICRO_TILEY*(STD_LEV_FIELDY+2));
-
-  for(x=-1;x<=STD_LEV_FIELDX;x++)
-    for(y=-1;y<=STD_LEV_FIELDY;y++)
-      if (x>=0 && x<lev_fieldx && y>=0 && y<lev_fieldy)
-       DrawMicroElement(xpos+MICRO_TILEX*x,ypos+MICRO_TILEY*y,
-                        Feld[x][y]=Ur[x][y]);
-      else if (x>=-1 && x<lev_fieldx+1 && y>=-1 && y<lev_fieldy+1)
-       DrawMicroElement(xpos+MICRO_TILEX*x,ypos+MICRO_TILEY*y,
+  XFillRectangle(display, drawto, gc,
+                xpos - MICRO_TILEX, ypos - MICRO_TILEY,
+                MICRO_TILEX * (STD_LEV_FIELDX + 2),
+                MICRO_TILEY * (STD_LEV_FIELDY + 2));
+  if (lev_fieldx < STD_LEV_FIELDX)
+    xpos += (STD_LEV_FIELDX - lev_fieldx)/2 * MICRO_TILEX;
+  if (lev_fieldy < STD_LEV_FIELDY)
+    ypos += (STD_LEV_FIELDY - lev_fieldy)/2 * MICRO_TILEY;
+
+  for(x=-1; x<=STD_LEV_FIELDX; x++)
+    for(y=-1; y<=STD_LEV_FIELDY; y++)
+      if (x >= 0 && x < lev_fieldx && y >= 0 && y < lev_fieldy)
+       DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
+                        Ur[x][y]);
+      else if (x >= -1 && x < lev_fieldx+1 && y >= -1 && y < lev_fieldy+1)
+       DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
                         EL_BETON);
 
-  XFillRectangle(display,drawto,gc, SX,MICROLABEL_YPOS, SXSIZE,FONT4_YSIZE);
+  XFillRectangle(display, drawto,gc, SX, MICROLABEL_YPOS, SXSIZE, FONT4_YSIZE);
 
   if (level.name)
   {
     int len = strlen(level.name);
-    int lxpos = SX+(SXSIZE-len*FONT4_XSIZE)/2;
+    int lxpos = SX + (SXSIZE - len * FONT4_XSIZE) / 2;
     int lypos = MICROLABEL_YPOS;
 
-    DrawText(lxpos,lypos,level.name,FS_SMALL,FC_SPECIAL2);
+    DrawText(lxpos, lypos, level.name, FS_SMALL, FC_SPECIAL2);
   }
 
   redraw_mask |= REDRAW_MICROLEV;
 }
 
-int AYS_in_range(int x, int y)
+int REQ_in_range(int x, int y)
 {
-  if (y>DY+249 && y<DY+278)
+  if (y > DY+249 && y < DY+278)
   {
-    if (x>DX+1 && x<DX+48)
-      return(1);
-    else if (x>DX+51 && x<DX+98) 
-      return(2);
+    if (x > DX+1 && x < DX+48)
+      return 1;
+    else if (x > DX+51 && x < DX+98) 
+      return 2;
   }
-  return(0);
+  return 0;
 }
 
-BOOL AreYouSure(char *text, unsigned int ays_state)
+boolean Request(char *text, unsigned int req_state)
 {
-  int mx,my, ty, result = -1;
+  int mx, my, ty, result = -1;
+  unsigned int old_door_state;
+
+#ifndef MSDOS
+  /* pause network game while waiting for request to answer */
+  if (options.network &&
+      game_status == PLAYING &&
+      req_state & REQUEST_WAIT_FOR)
+    SendToServer_PausePlaying();
+#endif
+
+  old_door_state = GetDoorState();
 
   CloseDoor(DOOR_CLOSE_1);
 
   /* Alten Türinhalt sichern */
-  XCopyArea(display,pix[PIX_DB_DOOR],pix[PIX_DB_DOOR],gc,
-           DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
-           DOOR_GFX_PAGEX2,DOOR_GFX_PAGEY1);
+  XCopyArea(display, pix[PIX_DB_DOOR], pix[PIX_DB_DOOR], gc,
+           DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
+           DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
 
   /* Fragetext schreiben */
-  XFillRectangle(display,pix[PIX_DB_DOOR],gc,
-                DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY1,DXSIZE,DYSIZE);
+  XFillRectangle(display, pix[PIX_DB_DOOR], gc,
+                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE);
 
-  for(ty=0;ty<13;ty++)
+  for(ty=0; ty<13; ty++)
   {
-    int tx,tl,tc;
+    int tx, tl, tc;
     char txt[256];
 
-    if (!(*text))
+    if (!*text)
       break;
-    for(tl=0,tx=0;tx<7;tl++,tx++)
+
+    for(tl=0,tx=0; tx<7; tl++,tx++)
     {
-      tc=*(text+tx);
-      if (!tc || tc==32)
+      tc = *(text + tx);
+      if (!tc || tc == 32)
        break;
     }
     if (!tl)
@@ -844,59 +1385,59 @@ BOOL AreYouSure(char *text, unsigned int ays_state)
       ty--; 
       continue; 
     }
-    sprintf(txt,text); 
-    txt[tl]=0;
-    DrawTextExt(pix[PIX_DB_DOOR],gc,
-               DOOR_GFX_PAGEX1+51-(tl*14)/2,SY+ty*16,txt,FS_SMALL,FC_YELLOW);
-    text+=(tl+(tc==32));
-  }
-
-  if (ays_state & AYS_ASK)
-    XCopyArea(display,pix[PIX_DOOR],pix[PIX_DB_DOOR],gc,
-             DOOR_GFX_PAGEX4,OK_BUTTON_GFX_YPOS,
-             DXSIZE,OK_BUTTON_YSIZE,
-             DOOR_GFX_PAGEX1,OK_BUTTON_YPOS);
-  else if (ays_state & AYS_CONFIRM)
-    XCopyArea(display,pix[PIX_DOOR],pix[PIX_DB_DOOR],gc,
-             DOOR_GFX_PAGEX4,CONFIRM_BUTTON_GFX_YPOS,
-             DXSIZE,CONFIRM_BUTTON_YSIZE,
-             DOOR_GFX_PAGEX1,CONFIRM_BUTTON_YPOS);
+    sprintf(txt, text); 
+    txt[tl] = 0;
+    DrawTextExt(pix[PIX_DB_DOOR], gc,
+               DOOR_GFX_PAGEX1 + 51 - (tl * 14)/2, SY + ty * 16,
+               txt, FS_SMALL, FC_YELLOW);
+    text += tl + (tc == 32 ? 1 : 0);
+  }
+
+  if (req_state & REQ_ASK)
+  {
+    DrawYesNoButton(BUTTON_OK, DB_INIT);
+    DrawYesNoButton(BUTTON_NO, DB_INIT);
+  }
+  else if (req_state & REQ_CONFIRM)
+  {
+    DrawConfirmButton(BUTTON_CONFIRM, DB_INIT);
+  }
+  else if (req_state & REQ_PLAYER)
+  {
+    DrawPlayerButton(BUTTON_PLAYER_1, DB_INIT);
+    DrawPlayerButton(BUTTON_PLAYER_2, DB_INIT);
+    DrawPlayerButton(BUTTON_PLAYER_3, DB_INIT);
+    DrawPlayerButton(BUTTON_PLAYER_4, DB_INIT);
+  }
 
   OpenDoor(DOOR_OPEN_1);
+  ClearEventQueue();
 
-  if (!(ays_state & AYS_ASK) && !(ays_state & AYS_CONFIRM))
+  if (!(req_state & REQUEST_WAIT_FOR))
     return(FALSE);
 
-  if (game_status!=MAINMENU)
+  if (game_status != MAINMENU)
     InitAnimation();
 
   button_status = MB_RELEASED;
 
-  while(result<0)
+  while(result < 0)
   {
-    DoAnimation();
-    Delay(10000);
-
     if (XPending(display))
     {
       XEvent event;
 
       XNextEvent(display, &event);
+
       switch(event.type)
       {
-       case Expose:
-         HandleExposeEvent((XExposeEvent *) &event);
-         break;
-       case UnmapNotify:
-         SleepWhileUnmapped();
-         break;
        case ButtonPress:
        case ButtonRelease:
        case MotionNotify:
        {
          int choice;
 
-         if (event.type==MotionNotify)
+         if (event.type == MotionNotify)
          {
            motion_status = TRUE;
            mx = ((XMotionEvent *) &event)->x;
@@ -913,10 +1454,12 @@ BOOL AreYouSure(char *text, unsigned int ays_state)
              button_status = MB_RELEASED;
          }
 
-         if (ays_state & AYS_ASK)
-           choice = CheckChooseButtons(mx,my,button_status);
-         else
+         if (req_state & REQ_ASK)
+           choice = CheckYesNoButtons(mx,my,button_status);
+         else if (req_state & REQ_CONFIRM)
            choice = CheckConfirmButton(mx,my,button_status);
+         else
+           choice = CheckPlayerButtons(mx,my,button_status);
 
          switch(choice)
          {
@@ -927,56 +1470,80 @@ BOOL AreYouSure(char *text, unsigned int ays_state)
              result = FALSE;
              break;
            case BUTTON_CONFIRM:
-             result = TRUE|FALSE;
+             result = TRUE | FALSE;
              break;
+
+           case BUTTON_PLAYER_1:
+             result = 1;
+             break;
+           case BUTTON_PLAYER_2:
+             result = 2;
+             break;
+           case BUTTON_PLAYER_3:
+             result = 3;
+             break;
+           case BUTTON_PLAYER_4:
+             result = 4;
+             break;
+
            default:
              break;
          }
          break;
        }
+
        case KeyPress:
-         key_status = KEY_PRESSED;
          switch(XLookupKeysym((XKeyEvent *)&event,
                               ((XKeyEvent *)&event)->state))
          {
            case XK_Return:
              result = 1;
              break;
+
            case XK_Escape:
              result = 0;
              break;
+
+           default:
+             break;
          }
+         if (req_state & REQ_PLAYER)
+           result = 0;
          break;
-       case FocusIn:
-         HandleFocusEvent(FOCUS_IN);
-         break;
-       case FocusOut:
-         HandleFocusEvent(FOCUS_OUT);
+
+       case KeyRelease:
+         key_joystick_mapping = 0;
          break;
+
        default:
+         HandleOtherEvents(&event);
          break;
       }
     }
-    else if (JoystickButton()==JOY_BUTTON_NEW_PRESSED)
+    else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
     {
-      int joy=Joystick();
+      int joy = AnyJoystick();
 
       if (joy & JOY_BUTTON_1)
        result = 1;
       else if (joy & JOY_BUTTON_2)
        result = 0;
     }
+
+    DoAnimation();
+
+    /* don't eat all CPU time */
+    Delay(10);
   }
 
-  if (game_status!=MAINMENU)
+  if (game_status != MAINMENU)
     StopAnimation();
 
-  if (!(ays_state & AYS_STAY_OPEN))
+  if (!(req_state & REQ_STAY_OPEN))
   {
     CloseDoor(DOOR_CLOSE_1);
 
-    if (!(ays_state & AYS_STAY_CLOSED) &&
-       (game_status==PLAYING || game_status==LEVELED))
+    if (!(req_state & REQ_STAY_CLOSED) && (old_door_state & DOOR_OPEN_1))
     {
       XCopyArea(display,pix[PIX_DB_DOOR],pix[PIX_DB_DOOR],gc,
                DOOR_GFX_PAGEX2,DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
@@ -985,381 +1552,324 @@ BOOL AreYouSure(char *text, unsigned int ays_state)
     }
   }
 
+#ifndef MSDOS
+  /* continue network game after request */
+  if (options.network &&
+      game_status == PLAYING &&
+      req_state & REQUEST_WAIT_FOR)
+    SendToServer_ContinuePlaying();
+#endif
+
   return(result);
 }
 
-void OpenDoor(unsigned int door_state)
+unsigned int OpenDoor(unsigned int door_state)
 {
+  unsigned int new_door_state;
+
   if (door_state & DOOR_COPY_BACK)
   {
-    XCopyArea(display,pix[PIX_DB_DOOR],pix[PIX_DB_DOOR],gc,
-             DOOR_GFX_PAGEX2,DOOR_GFX_PAGEY1, DXSIZE,DYSIZE+VYSIZE,
-             DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY1);
+    XCopyArea(display, pix[PIX_DB_DOOR], pix[PIX_DB_DOOR], gc,
+             DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE + VYSIZE,
+             DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
     door_state &= ~DOOR_COPY_BACK;
   }
 
-  MoveDoor(door_state);
-  ClearEventQueue();
+  new_door_state = MoveDoor(door_state);
+
+  return(new_door_state);
 }
 
-void CloseDoor(unsigned int door_state)
+unsigned int CloseDoor(unsigned int door_state)
 {
-  XCopyArea(display,backbuffer,pix[PIX_DB_DOOR],gc,
-           DX,DY, DXSIZE,DYSIZE, DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY1);
-  XCopyArea(display,backbuffer,pix[PIX_DB_DOOR],gc,
-           VX,VY, VXSIZE,VYSIZE, DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY2);
+  unsigned int new_door_state;
 
-  MoveDoor(door_state);
-  ClearEventQueue();
+  XCopyArea(display, backbuffer, pix[PIX_DB_DOOR], gc,
+           DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+  XCopyArea(display, backbuffer, pix[PIX_DB_DOOR], gc,
+           VX, VY, VXSIZE, VYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
+
+  new_door_state = MoveDoor(door_state);
+
+  return(new_door_state);
+}
+
+unsigned int GetDoorState()
+{
+  return(MoveDoor(DOOR_GET_STATE));
 }
 
-void MoveDoor(unsigned int door_state)
+unsigned int MoveDoor(unsigned int door_state)
 {
   static int door1 = DOOR_OPEN_1;
   static int door2 = DOOR_CLOSE_2;
-  int x, start, stepsize = 4, door_anim_delay = stepsize*5000;
+  static unsigned long door_delay = 0;
+  int x, start, stepsize = 2;
+  unsigned long door_delay_value = stepsize * 5;
 
-  if (door1==DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
+  if (door_state == DOOR_GET_STATE)
+    return(door1 | door2);
+
+  if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
     door_state &= ~DOOR_OPEN_1;
-  else if (door1==DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
+  else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
     door_state &= ~DOOR_CLOSE_1;
-  if (door2==DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
+  if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
     door_state &= ~DOOR_OPEN_2;
-  else if (door2==DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
+  else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
     door_state &= ~DOOR_CLOSE_2;
 
-  if (quick_doors)
+  if (setup.quick_doors)
   {
     stepsize = 20;
-    door_anim_delay = 0;
+    door_delay_value = 0;
     StopSound(SND_OEFFNEN);
   }
 
   if (door_state & DOOR_ACTION)
   {
     if (!(door_state & DOOR_NO_DELAY))
-      PlaySoundStereo(SND_OEFFNEN,PSND_MAX_RIGHT);
+      PlaySoundStereo(SND_OEFFNEN, PSND_MAX_RIGHT);
 
     start = ((door_state & DOOR_NO_DELAY) ? DXSIZE : 0);
 
-    for(x=start;x<=DXSIZE;x+=stepsize)
+    for(x=start; x<=DXSIZE; x+=stepsize)
     {
+      WaitUntilDelayReached(&door_delay, door_delay_value);
+
       if (door_state & DOOR_ACTION_1)
       {
        int i = (door_state & DOOR_OPEN_1 ? DXSIZE-x : x);
-       int j = (DXSIZE - i)/3;
-
-       XCopyArea(display,pix[PIX_DB_DOOR],drawto,gc,
-                 DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY1+i/2,
-                 DXSIZE,DYSIZE-i/2, DX,DY);
-
-       XFillRectangle(display,drawto,gc,DX,DY+DYSIZE-i/2,DXSIZE,i/2);
-
-       XSetClipOrigin(display,clip_gc[PIX_DOOR],
-                      DX-i,(DY+j)-DOOR_GFX_PAGEY1);
-       XCopyArea(display,pix[PIX_DOOR],drawto,clip_gc[PIX_DOOR],
-                 DXSIZE,DOOR_GFX_PAGEY1, i,77, DX+DXSIZE-i,DY+j);
-       XCopyArea(display,pix[PIX_DOOR],drawto,clip_gc[PIX_DOOR],
-                 DXSIZE,DOOR_GFX_PAGEY1+140, i,63, DX+DXSIZE-i,DY+140+j);
-       XSetClipOrigin(display,clip_gc[PIX_DOOR],
-                      DX-DXSIZE+i,DY-(DOOR_GFX_PAGEY1+j));
-       XCopyArea(display,pix[PIX_DOOR],drawto,clip_gc[PIX_DOOR],
-                 DXSIZE-i,DOOR_GFX_PAGEY1+j, i,77-j, DX,DY);
-       XCopyArea(display,pix[PIX_DOOR],drawto,clip_gc[PIX_DOOR],
-                 DXSIZE-i,DOOR_GFX_PAGEY1+140, i,63, DX,DY+140-j);
-
-       XCopyArea(display,pix[PIX_DOOR],drawto,clip_gc[PIX_DOOR],
-                 DXSIZE-i,DOOR_GFX_PAGEY1+77, i,63,
-                 DX,DY+77-j);
-       XCopyArea(display,pix[PIX_DOOR],drawto,clip_gc[PIX_DOOR],
-                 DXSIZE-i,DOOR_GFX_PAGEY1+203, i,77,
-                 DX,DY+203-j);
-       XSetClipOrigin(display,clip_gc[PIX_DOOR],
-                      DX-i,(DY+j)-DOOR_GFX_PAGEY1);
-       XCopyArea(display,pix[PIX_DOOR],drawto,clip_gc[PIX_DOOR],
-                 DXSIZE,DOOR_GFX_PAGEY1+77, i,63,
-                 DX+DXSIZE-i,DY+77+j);
-       XCopyArea(display,pix[PIX_DOOR],drawto,clip_gc[PIX_DOOR],
-                 DXSIZE,DOOR_GFX_PAGEY1+203, i,77-j,
-                 DX+DXSIZE-i,DY+203+j);
+       int j = (DXSIZE - i) / 3;
+
+       XCopyArea(display, pix[PIX_DB_DOOR], drawto, gc,
+                 DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1 + i/2,
+                 DXSIZE,DYSIZE - i/2, DX, DY);
+
+       XFillRectangle(display, drawto, gc, DX, DY + DYSIZE - i/2, DXSIZE,i/2);
+
+       XSetClipOrigin(display, clip_gc[PIX_DOOR],
+                      DX - i, (DY + j) - DOOR_GFX_PAGEY1);
+       XCopyArea(display, pix[PIX_DOOR], drawto,clip_gc[PIX_DOOR],
+                 DXSIZE, DOOR_GFX_PAGEY1, i, 77, DX + DXSIZE - i, DY + j);
+       XCopyArea(display, pix[PIX_DOOR], drawto, clip_gc[PIX_DOOR],
+                 DXSIZE, DOOR_GFX_PAGEY1 + 140, i, 63, DX + DXSIZE - i,
+                 DY + 140 + j);
+       XSetClipOrigin(display, clip_gc[PIX_DOOR],
+                      DX - DXSIZE + i, DY - (DOOR_GFX_PAGEY1 + j));
+       XCopyArea(display, pix[PIX_DOOR], drawto, clip_gc[PIX_DOOR],
+                 DXSIZE - i, DOOR_GFX_PAGEY1 + j, i, 77 - j, DX, DY);
+       XCopyArea(display, pix[PIX_DOOR], drawto, clip_gc[PIX_DOOR],
+                 DXSIZE-i, DOOR_GFX_PAGEY1 + 140, i, 63, DX, DY + 140 - j);
+
+       XCopyArea(display, pix[PIX_DOOR], drawto, clip_gc[PIX_DOOR],
+                 DXSIZE - i, DOOR_GFX_PAGEY1 + 77, i, 63,
+                 DX, DY + 77 - j);
+       XCopyArea(display, pix[PIX_DOOR], drawto, clip_gc[PIX_DOOR],
+                 DXSIZE - i, DOOR_GFX_PAGEY1 + 203, i, 77,
+                 DX, DY + 203 - j);
+       XSetClipOrigin(display, clip_gc[PIX_DOOR],
+                      DX - i, (DY + j) - DOOR_GFX_PAGEY1);
+       XCopyArea(display, pix[PIX_DOOR], drawto, clip_gc[PIX_DOOR],
+                 DXSIZE, DOOR_GFX_PAGEY1 + 77, i, 63,
+                 DX + DXSIZE - i, DY + 77 + j);
+       XCopyArea(display, pix[PIX_DOOR], drawto, clip_gc[PIX_DOOR],
+                 DXSIZE, DOOR_GFX_PAGEY1 + 203, i, 77 - j,
+                 DX + DXSIZE - i, DY + 203 + j);
 
        redraw_mask |= REDRAW_DOOR_1;
       }
 
       if (door_state & DOOR_ACTION_2)
       {
-       int i = (door_state & DOOR_OPEN_2 ? VXSIZE-x : x);
-       int j = (VXSIZE - i)/3;
-
-       XCopyArea(display,pix[PIX_DB_DOOR],drawto,gc,
-                 DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY2+i/2,
-                 VXSIZE,VYSIZE-i/2, VX,VY);
-
-       XFillRectangle(display,drawto,gc,VX,VY+VYSIZE-i/2,VXSIZE,i/2);
-
-       XSetClipOrigin(display,clip_gc[PIX_DOOR],
-                      VX-i,(VY+j)-DOOR_GFX_PAGEY2);
-       XCopyArea(display,pix[PIX_DOOR],drawto,clip_gc[PIX_DOOR],
-                 VXSIZE,DOOR_GFX_PAGEY2, i,VYSIZE/2, VX+VXSIZE-i,VY+j);
-       XSetClipOrigin(display,clip_gc[PIX_DOOR],
-                      VX-VXSIZE+i,VY-(DOOR_GFX_PAGEY2+j));
-       XCopyArea(display,pix[PIX_DOOR],drawto,clip_gc[PIX_DOOR],
-                 VXSIZE-i,DOOR_GFX_PAGEY2+j, i,VYSIZE/2-j, VX,VY);
-
-       XCopyArea(display,pix[PIX_DOOR],drawto,clip_gc[PIX_DOOR],
-                 VXSIZE-i,DOOR_GFX_PAGEY2+VYSIZE/2, i,VYSIZE/2,
-                 VX,VY+VYSIZE/2-j);
-       XSetClipOrigin(display,clip_gc[PIX_DOOR],
-                      VX-i,(VY+j)-DOOR_GFX_PAGEY2);
-       XCopyArea(display,pix[PIX_DOOR],drawto,clip_gc[PIX_DOOR],
-                 VXSIZE,DOOR_GFX_PAGEY2+VYSIZE/2, i,VYSIZE/2-j,
-                 VX+VXSIZE-i,VY+VYSIZE/2+j);
+       int i = (door_state & DOOR_OPEN_2 ? VXSIZE - x : x);
+       int j = (VXSIZE - i) / 3;
+
+       XCopyArea(display, pix[PIX_DB_DOOR], drawto, gc,
+                 DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2 + i/2,
+                 VXSIZE, VYSIZE - i/2, VX, VY);
+
+       XFillRectangle(display, drawto, gc, VX, VY + VYSIZE-i/2, VXSIZE, i/2);
+
+       XSetClipOrigin(display, clip_gc[PIX_DOOR],
+                      VX - i, (VY + j) - DOOR_GFX_PAGEY2);
+       XCopyArea(display, pix[PIX_DOOR], drawto, clip_gc[PIX_DOOR],
+                 VXSIZE, DOOR_GFX_PAGEY2, i, VYSIZE / 2, VX + VXSIZE-i, VY+j);
+       XSetClipOrigin(display, clip_gc[PIX_DOOR],
+                      VX - VXSIZE + i, VY - (DOOR_GFX_PAGEY2 + j));
+       XCopyArea(display, pix[PIX_DOOR], drawto,clip_gc[PIX_DOOR],
+                 VXSIZE - i, DOOR_GFX_PAGEY2 + j, i, VYSIZE / 2 - j, VX, VY);
+
+       XCopyArea(display, pix[PIX_DOOR], drawto, clip_gc[PIX_DOOR],
+                 VXSIZE - i, DOOR_GFX_PAGEY2 + VYSIZE / 2, i, VYSIZE / 2,
+                 VX, VY + VYSIZE / 2 - j);
+       XSetClipOrigin(display, clip_gc[PIX_DOOR],
+                      VX - i, (VY + j) - DOOR_GFX_PAGEY2);
+       XCopyArea(display, pix[PIX_DOOR], drawto, clip_gc[PIX_DOOR],
+                 VXSIZE, DOOR_GFX_PAGEY2 + VYSIZE / 2, i, VYSIZE / 2 - j,
+                 VX + VXSIZE - i, VY + VYSIZE / 2 + j);
 
        redraw_mask |= REDRAW_DOOR_2;
       }
 
       BackToFront();
-      Delay(door_anim_delay);
 
-      if (game_status==MAINMENU)
+      if (game_status == MAINMENU)
        DoAnimation();
     }
   }
 
+  if (setup.quick_doors)
+    StopSound(SND_OEFFNEN);
+
   if (door_state & DOOR_ACTION_1)
     door1 = door_state & DOOR_ACTION_1;
   if (door_state & DOOR_ACTION_2)
     door2 = door_state & DOOR_ACTION_2;
+
+  return(door1 | door2);
 }
 
 int ReadPixel(Drawable d, int x, int y)
 {
   static XImage *pixelimage;
 
-  pixelimage = XGetImage(display, d, x,y, 1,1, AllPlanes, ZPixmap);
-  return(XGetPixel(pixelimage,0,0));
-}
-
-void CheckJoystickData()
-{
-  int i;
-  int distance = 100;
-
-  for(i=0;i<2;i++)
-  {
-    if (joystick[i].xmiddle <= distance)
-      joystick[i].xmiddle = distance;
-    if (joystick[i].ymiddle <= distance)
-      joystick[i].ymiddle = distance;
-
-    if (joystick[i].xleft >= joystick[i].xmiddle)
-      joystick[i].xleft = joystick[i].xmiddle-distance;
-    if (joystick[i].xright <= joystick[i].xmiddle)
-      joystick[i].xright = joystick[i].xmiddle+distance;
-
-    if (joystick[i].yupper >= joystick[i].ymiddle)
-      joystick[i].yupper = joystick[i].ymiddle-distance;
-    if (joystick[i].ylower <= joystick[i].ymiddle)
-      joystick[i].ylower = joystick[i].ymiddle+distance;
-  }
-}
-
-int JoystickPosition(int middle, int margin, int actual)
-{
-  long range, pos;
-  int percentage;
-
-  if (margin<middle && actual>middle)
-    return(0);
-  if (margin>middle && actual<middle)
-    return(0);
-
-  range = ABS(margin-middle);
-  pos = ABS(actual-middle);
-  percentage = (int)(pos*100/range);
-  if (percentage>100)
-    percentage = 100;
-
-  return(percentage);
-}
-
-int Joystick()
-{
-#ifdef __FreeBSD__
-  struct joystick joy_ctrl;
-#else
-  struct joystick_control
-  {
-    int buttons;
-    int x;
-    int y;
-  } joy_ctrl;
-#endif
-
-  int js_x,js_y, js_b1,js_b2;
-  int left, right, up, down;
-  int result=0;
-
-  if (joystick_status==JOYSTICK_OFF)
-    return(0);
-
-  if (read(joystick_device, &joy_ctrl, sizeof(joy_ctrl)) != sizeof(joy_ctrl))
-  {
-    fprintf(stderr,"%s: cannot read joystick settings - no joystick support\n",
-           progname);
-    joystick_status = JOYSTICK_OFF;
-    return(0);
-  }
-
-  js_x  = joy_ctrl.x;
-  js_y  = joy_ctrl.y;
-#ifdef __FreeBSD__
-  js_b1 = joy_ctrl.b1;
-  js_b2 = joy_ctrl.b2;
-#else
-  js_b1 = joy_ctrl.buttons & 1;
-  js_b2 = joy_ctrl.buttons & 2;
-#endif
-
-  left = JoystickPosition(joystick[joystick_nr].xmiddle,
-                         joystick[joystick_nr].xleft,  js_x);
-  right = JoystickPosition(joystick[joystick_nr].xmiddle,
-                          joystick[joystick_nr].xright, js_x);
-  up =    JoystickPosition(joystick[joystick_nr].ymiddle,
-                          joystick[joystick_nr].yupper, js_y);
-  down =  JoystickPosition(joystick[joystick_nr].ymiddle,
-                          joystick[joystick_nr].ylower, js_y);
-
-  if (left>JOYSTICK_PERCENT)
-    result |= JOY_LEFT;
-  else if (right>JOYSTICK_PERCENT)
-    result |= JOY_RIGHT;
-  if (up>JOYSTICK_PERCENT)
-    result |= JOY_UP;
-  else if (down>JOYSTICK_PERCENT)
-    result |= JOY_DOWN;
-  if (js_b1)
-    result |= JOY_BUTTON_1;
-  if (js_b2)
-    result |= JOY_BUTTON_2;
-
-  return(result);
-}
-
-int JoystickButton()
-{
-  static int last_joy_button = 0;
-  int joy_button = (Joystick() & JOY_BUTTON);
-  int result;
-
-  if (joy_button)
-  {
-    if (last_joy_button)
-      result = JOY_BUTTON_PRESSED;
-    else
-      result = JOY_BUTTON_NEW_PRESSED;
-  }
-  else
-  {
-    if (last_joy_button)
-      result = JOY_BUTTON_NEW_RELEASED;
-    else
-      result = JOY_BUTTON_NOT_PRESSED;
-  }
-
-  last_joy_button = joy_button;
-  return(result);
+  pixelimage = XGetImage(display, d, x, y, 1, 1, AllPlanes, ZPixmap);
+  return(XGetPixel(pixelimage, 0, 0));
 }
 
 int el2gfx(int element)
 {
   switch(element)
   {
-    case EL_LEERRAUM:          return(-1);
-    case EL_ERDREICH:          return(GFX_ERDREICH);
-    case EL_MAUERWERK:         return(GFX_MAUERWERK);
-    case EL_FELSBODEN:         return(GFX_FELSBODEN);
-    case EL_FELSBROCKEN:       return(GFX_FELSBROCKEN);
-    case EL_SCHLUESSEL:                return(GFX_SCHLUESSEL);
-    case EL_EDELSTEIN:         return(GFX_EDELSTEIN);
-    case EL_AUSGANG_ZU:                return(GFX_AUSGANG_ZU);
-    case EL_AUSGANG_ACT:       return(GFX_AUSGANG_ACT);
-    case EL_AUSGANG_AUF:       return(GFX_AUSGANG_AUF);
-    case EL_SPIELFIGUR:                return(GFX_SPIELFIGUR);
-    case EL_SPIELER1:          return(GFX_SPIELER1);
-    case EL_SPIELER2:          return(GFX_SPIELER2);
-    case EL_SPIELER3:          return(GFX_SPIELER3);
-    case EL_SPIELER4:          return(GFX_SPIELER4);
-    case EL_KAEFER:            return(GFX_KAEFER);
-    case EL_KAEFER_R:          return(GFX_KAEFER_R);
-    case EL_KAEFER_O:          return(GFX_KAEFER_O);
-    case EL_KAEFER_L:          return(GFX_KAEFER_L);
-    case EL_KAEFER_U:          return(GFX_KAEFER_U);
-    case EL_FLIEGER:           return(GFX_FLIEGER);
-    case EL_FLIEGER_R:         return(GFX_FLIEGER_R);
-    case EL_FLIEGER_O:         return(GFX_FLIEGER_O);
-    case EL_FLIEGER_L:         return(GFX_FLIEGER_L);
-    case EL_FLIEGER_U:         return(GFX_FLIEGER_U);
-    case EL_MAMPFER:           return(GFX_MAMPFER);
-    case EL_ZOMBIE:            return(GFX_ZOMBIE);
-    case EL_BETON:             return(GFX_BETON);
-    case EL_DIAMANT:           return(GFX_DIAMANT);
-    case EL_MORAST_LEER:       return(GFX_MORAST_LEER);
-    case EL_MORAST_VOLL:       return(GFX_MORAST_VOLL);
-    case EL_TROPFEN:           return(GFX_TROPFEN);
-    case EL_BOMBE:             return(GFX_BOMBE);
-    case EL_SIEB_LEER:         return(GFX_SIEB_LEER);
-    case EL_SIEB_VOLL:         return(GFX_SIEB_VOLL);
-    case EL_SIEB_TOT:          return(GFX_SIEB_TOT);
-    case EL_SALZSAEURE:                return(GFX_SALZSAEURE);
-    case EL_AMOEBE_TOT:                return(GFX_AMOEBE_TOT);
-    case EL_AMOEBE_NASS:       return(GFX_AMOEBE_NASS);
-    case EL_AMOEBE_NORM:       return(GFX_AMOEBE_NORM);
-    case EL_AMOEBE_VOLL:       return(GFX_AMOEBE_VOLL);
-    case EL_AMOEBA2DIAM:       return(GFX_AMOEBA2DIAM);
-    case EL_KOKOSNUSS:         return(GFX_KOKOSNUSS);
-    case EL_LIFE:              return(GFX_LIFE);
-    case EL_LIFE_ASYNC:                return(GFX_LIFE_ASYNC);
-    case EL_DYNAMIT:           return(GFX_DYNAMIT);
-    case EL_BADEWANNE:         return(GFX_BADEWANNE);
-    case EL_BADEWANNE1:                return(GFX_BADEWANNE1);
-    case EL_BADEWANNE2:                return(GFX_BADEWANNE2);
-    case EL_BADEWANNE3:                return(GFX_BADEWANNE3);
-    case EL_BADEWANNE4:                return(GFX_BADEWANNE4);
-    case EL_BADEWANNE5:                return(GFX_BADEWANNE5);
-    case EL_ABLENK_AUS:                return(GFX_ABLENK_AUS);
-    case EL_ABLENK_EIN:                return(GFX_ABLENK_EIN);
-    case EL_SCHLUESSEL1:       return(GFX_SCHLUESSEL1);
-    case EL_SCHLUESSEL2:       return(GFX_SCHLUESSEL2);
-    case EL_SCHLUESSEL3:       return(GFX_SCHLUESSEL3);
-    case EL_SCHLUESSEL4:       return(GFX_SCHLUESSEL4);
-    case EL_PFORTE1:           return(GFX_PFORTE1);
-    case EL_PFORTE2:           return(GFX_PFORTE2);
-    case EL_PFORTE3:           return(GFX_PFORTE3);
-    case EL_PFORTE4:           return(GFX_PFORTE4);
-    case EL_PFORTE1X:          return(GFX_PFORTE1X);
-    case EL_PFORTE2X:          return(GFX_PFORTE2X);
-    case EL_PFORTE3X:          return(GFX_PFORTE3X);
-    case EL_PFORTE4X:          return(GFX_PFORTE4X);
-    case EL_DYNAMIT_AUS:       return(GFX_DYNAMIT_AUS);
-    case EL_PACMAN:            return(GFX_PACMAN);
-    case EL_PACMAN_R:          return(GFX_PACMAN_R);
-    case EL_PACMAN_O:          return(GFX_PACMAN_O);
-    case EL_PACMAN_L:          return(GFX_PACMAN_L);
-    case EL_PACMAN_U:          return(GFX_PACMAN_U);
-    case EL_UNSICHTBAR:                return(GFX_UNSICHTBAR);
-    case EL_ERZ_EDEL:          return(GFX_ERZ_EDEL);
-    case EL_ERZ_DIAM:          return(GFX_ERZ_DIAM);
-    case EL_BIRNE_AUS:         return(GFX_BIRNE_AUS);
-    case EL_BIRNE_EIN:         return(GFX_BIRNE_EIN);
-    case EL_ZEIT_VOLL:         return(GFX_ZEIT_VOLL);
-    case EL_ZEIT_LEER:         return(GFX_ZEIT_LEER);
+    case EL_LEERRAUM:          return -1;
+    case EL_ERDREICH:          return GFX_ERDREICH;
+    case EL_MAUERWERK:         return GFX_MAUERWERK;
+    case EL_FELSBODEN:         return GFX_FELSBODEN;
+    case EL_FELSBROCKEN:       return GFX_FELSBROCKEN;
+    case EL_SCHLUESSEL:                return GFX_SCHLUESSEL;
+    case EL_EDELSTEIN:         return GFX_EDELSTEIN;
+    case EL_AUSGANG_ZU:                return GFX_AUSGANG_ZU;
+    case EL_AUSGANG_ACT:       return GFX_AUSGANG_ACT;
+    case EL_AUSGANG_AUF:       return GFX_AUSGANG_AUF;
+    case EL_SPIELFIGUR:                return GFX_SPIELFIGUR;
+    case EL_SPIELER1:          return GFX_SPIELER1;
+    case EL_SPIELER2:          return GFX_SPIELER2;
+    case EL_SPIELER3:          return GFX_SPIELER3;
+    case EL_SPIELER4:          return GFX_SPIELER4;
+    case EL_KAEFER:            return GFX_KAEFER;
+    case EL_KAEFER_R:          return GFX_KAEFER_R;
+    case EL_KAEFER_O:          return GFX_KAEFER_O;
+    case EL_KAEFER_L:          return GFX_KAEFER_L;
+    case EL_KAEFER_U:          return GFX_KAEFER_U;
+    case EL_FLIEGER:           return GFX_FLIEGER;
+    case EL_FLIEGER_R:         return GFX_FLIEGER_R;
+    case EL_FLIEGER_O:         return GFX_FLIEGER_O;
+    case EL_FLIEGER_L:         return GFX_FLIEGER_L;
+    case EL_FLIEGER_U:         return GFX_FLIEGER_U;
+    case EL_BUTTERFLY:         return GFX_BUTTERFLY;
+    case EL_BUTTERFLY_R:       return GFX_BUTTERFLY_R;
+    case EL_BUTTERFLY_O:       return GFX_BUTTERFLY_O;
+    case EL_BUTTERFLY_L:       return GFX_BUTTERFLY_L;
+    case EL_BUTTERFLY_U:       return GFX_BUTTERFLY_U;
+    case EL_FIREFLY:           return GFX_FIREFLY;
+    case EL_FIREFLY_R:         return GFX_FIREFLY_R;
+    case EL_FIREFLY_O:         return GFX_FIREFLY_O;
+    case EL_FIREFLY_L:         return GFX_FIREFLY_L;
+    case EL_FIREFLY_U:         return GFX_FIREFLY_U;
+    case EL_MAMPFER:           return GFX_MAMPFER;
+    case EL_ROBOT:             return GFX_ROBOT;
+    case EL_BETON:             return GFX_BETON;
+    case EL_DIAMANT:           return GFX_DIAMANT;
+    case EL_MORAST_LEER:       return GFX_MORAST_LEER;
+    case EL_MORAST_VOLL:       return GFX_MORAST_VOLL;
+    case EL_TROPFEN:           return GFX_TROPFEN;
+    case EL_BOMBE:             return GFX_BOMBE;
+    case EL_SIEB_LEER:         return GFX_SIEB_LEER;
+    case EL_SIEB_VOLL:         return GFX_SIEB_VOLL;
+    case EL_SIEB_TOT:          return GFX_SIEB_TOT;
+    case EL_SALZSAEURE:                return GFX_SALZSAEURE;
+    case EL_AMOEBE_TOT:                return GFX_AMOEBE_TOT;
+    case EL_AMOEBE_NASS:       return GFX_AMOEBE_NASS;
+    case EL_AMOEBE_NORM:       return GFX_AMOEBE_NORM;
+    case EL_AMOEBE_VOLL:       return GFX_AMOEBE_VOLL;
+    case EL_AMOEBE_BD:         return GFX_AMOEBE_BD;
+    case EL_AMOEBA2DIAM:       return GFX_AMOEBA2DIAM;
+    case EL_KOKOSNUSS:         return GFX_KOKOSNUSS;
+    case EL_LIFE:              return GFX_LIFE;
+    case EL_LIFE_ASYNC:                return GFX_LIFE_ASYNC;
+    case EL_DYNAMIT:           return GFX_DYNAMIT;
+    case EL_BADEWANNE:         return GFX_BADEWANNE;
+    case EL_BADEWANNE1:                return GFX_BADEWANNE1;
+    case EL_BADEWANNE2:                return GFX_BADEWANNE2;
+    case EL_BADEWANNE3:                return GFX_BADEWANNE3;
+    case EL_BADEWANNE4:                return GFX_BADEWANNE4;
+    case EL_BADEWANNE5:                return GFX_BADEWANNE5;
+    case EL_ABLENK_AUS:                return GFX_ABLENK_AUS;
+    case EL_ABLENK_EIN:                return GFX_ABLENK_EIN;
+    case EL_SCHLUESSEL1:       return GFX_SCHLUESSEL1;
+    case EL_SCHLUESSEL2:       return GFX_SCHLUESSEL2;
+    case EL_SCHLUESSEL3:       return GFX_SCHLUESSEL3;
+    case EL_SCHLUESSEL4:       return GFX_SCHLUESSEL4;
+    case EL_PFORTE1:           return GFX_PFORTE1;
+    case EL_PFORTE2:           return GFX_PFORTE2;
+    case EL_PFORTE3:           return GFX_PFORTE3;
+    case EL_PFORTE4:           return GFX_PFORTE4;
+    case EL_PFORTE1X:          return GFX_PFORTE1X;
+    case EL_PFORTE2X:          return GFX_PFORTE2X;
+    case EL_PFORTE3X:          return GFX_PFORTE3X;
+    case EL_PFORTE4X:          return GFX_PFORTE4X;
+    case EL_DYNAMIT_AUS:       return GFX_DYNAMIT_AUS;
+    case EL_PACMAN:            return GFX_PACMAN;
+    case EL_PACMAN_R:          return GFX_PACMAN_R;
+    case EL_PACMAN_O:          return GFX_PACMAN_O;
+    case EL_PACMAN_L:          return GFX_PACMAN_L;
+    case EL_PACMAN_U:          return GFX_PACMAN_U;
+    case EL_UNSICHTBAR:                return GFX_UNSICHTBAR;
+    case EL_ERZ_EDEL:          return GFX_ERZ_EDEL;
+    case EL_ERZ_DIAM:          return GFX_ERZ_DIAM;
+    case EL_BIRNE_AUS:         return GFX_BIRNE_AUS;
+    case EL_BIRNE_EIN:         return GFX_BIRNE_EIN;
+    case EL_ZEIT_VOLL:         return GFX_ZEIT_VOLL;
+    case EL_ZEIT_LEER:         return GFX_ZEIT_LEER;
+    case EL_MAUER_LEBT:                return GFX_MAUER_LEBT;
+    case EL_MAUER_X:           return GFX_MAUER_X;
+    case EL_MAUER_Y:           return GFX_MAUER_Y;
+    case EL_MAUER_XY:          return GFX_MAUER_XY;
+    case EL_EDELSTEIN_BD:      return GFX_EDELSTEIN_BD;
+    case EL_EDELSTEIN_GELB:    return GFX_EDELSTEIN_GELB;
+    case EL_EDELSTEIN_ROT:     return GFX_EDELSTEIN_ROT;
+    case EL_EDELSTEIN_LILA:    return GFX_EDELSTEIN_LILA;
+    case EL_ERZ_EDEL_BD:       return GFX_ERZ_EDEL_BD;
+    case EL_ERZ_EDEL_GELB:     return GFX_ERZ_EDEL_GELB;
+    case EL_ERZ_EDEL_ROT:      return GFX_ERZ_EDEL_ROT;
+    case EL_ERZ_EDEL_LILA:     return GFX_ERZ_EDEL_LILA;
+    case EL_MAMPFER2:          return GFX_MAMPFER2;
+    case EL_SIEB2_LEER:                return GFX_SIEB2_LEER;
+    case EL_SIEB2_VOLL:                return GFX_SIEB2_VOLL;
+    case EL_SIEB2_TOT:         return GFX_SIEB2_TOT;
+    case EL_DYNABOMB:          return GFX_DYNABOMB;
+    case EL_DYNABOMB_NR:       return GFX_DYNABOMB_NR;
+    case EL_DYNABOMB_SZ:       return GFX_DYNABOMB_SZ;
+    case EL_DYNABOMB_XL:       return GFX_DYNABOMB_XL;
+    case EL_SOKOBAN_OBJEKT:    return GFX_SOKOBAN_OBJEKT;
+    case EL_SOKOBAN_FELD_LEER: return GFX_SOKOBAN_FELD_LEER;
+    case EL_SOKOBAN_FELD_VOLL: return GFX_SOKOBAN_FELD_VOLL;
+    case EL_MAULWURF:          return GFX_MAULWURF;
+    case EL_PINGUIN:           return GFX_PINGUIN;
+    case EL_SCHWEIN:           return GFX_SCHWEIN;
+    case EL_DRACHE:            return GFX_DRACHE;
+    case EL_SONDE:             return GFX_SONDE;
+    case EL_PFEIL_L:           return GFX_PFEIL_L;
+    case EL_PFEIL_R:           return GFX_PFEIL_R;
+    case EL_PFEIL_O:           return GFX_PFEIL_O;
+    case EL_PFEIL_U:           return GFX_PFEIL_U;
     default:
     {
       if (IS_CHAR(element))
-       return(GFX_CHAR_START + (element-EL_CHAR_START));
+       return GFX_CHAR_START + (element - EL_CHAR_START);
       else
-       return(-1);
+       return -1;
     }
   }
 }
index bec220566639f2970186370b6d6f42113c164818..6d70852092b5617f9eb67adb6195019e6eda5978 100644 (file)
@@ -1,13 +1,12 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  tools.h                                                 *
 ***********************************************************/
 #ifndef TOOLS_H
 #define TOOLS_H
 
+#include <sys/time.h>
 #include "main.h"
 
-#include <sys/time.h>
+/* for SetDrawtoField */
+#define DRAW_DIRECT            0
+#define DRAW_BUFFERED          1
+#define DRAW_BACKBUFFER                2
+
+/* for DrawElementShifted */
+#define NO_CUTTING             0
+#define CUT_ABOVE              (1 << 0)
+#define CUT_BELOW              (1 << 1)
+#define CUT_LEFT               (1 << 2)
+#define CUT_RIGHT              (1 << 3)
 
-/* für DrawElementShifted */
-#define CUT_NO_CUTTING 0
-#define CUT_ABOVE      1
-#define CUT_BELOW      2
-#define CUT_LEFT       4
-#define CUT_RIGHT      8
+/* for masking functions */
+#define NO_MASKING             0
+#define USE_MASKING            1
+/* for MoveDoor */
+#define DOOR_OPEN_1            (1 << 0)
+#define DOOR_OPEN_2            (1 << 1)
+#define DOOR_CLOSE_1           (1 << 2)
+#define DOOR_CLOSE_2           (1 << 3)
+#define DOOR_OPEN_BOTH         (DOOR_OPEN_1 | DOOR_OPEN_2)
+#define DOOR_CLOSE_BOTH                (DOOR_CLOSE_1 | DOOR_CLOSE_2)
+#define DOOR_ACTION_1          (DOOR_OPEN_1 | DOOR_CLOSE_1)
+#define DOOR_ACTION_2          (DOOR_OPEN_2 | DOOR_CLOSE_2)
+#define DOOR_ACTION            (DOOR_ACTION_1 | DOOR_ACTION_2)
+#define DOOR_COPY_BACK         (1 << 4)
+#define DOOR_NO_DELAY          (1 << 5)
+#define DOOR_GET_STATE         (1 << 6)
 
-/* für MoveDoor */
-#define DOOR_OPEN_1    1
-#define DOOR_OPEN_2    2
-#define DOOR_CLOSE_1   4
-#define DOOR_CLOSE_2   8
-#define DOOR_OPEN_BOTH (DOOR_OPEN_1 | DOOR_OPEN_2)
-#define DOOR_CLOSE_BOTH        (DOOR_CLOSE_1 | DOOR_CLOSE_2)
-#define DOOR_ACTION_1  (DOOR_OPEN_1 | DOOR_CLOSE_1)
-#define DOOR_ACTION_2  (DOOR_OPEN_2 | DOOR_CLOSE_2)
-#define DOOR_ACTION    (DOOR_ACTION_1 | DOOR_ACTION_2)
-#define DOOR_COPY_BACK 16
-#define DOOR_NO_DELAY  32
+/* for Request */
+#define REQ_ASK                        (1 << 0)
+#define REQ_OPEN               (1 << 1)
+#define REQ_CLOSE              (1 << 2)
+#define REQ_CONFIRM            (1 << 3)
+#define REQ_STAY_CLOSED                (1 << 4)
+#define REQ_STAY_OPEN          (1 << 5)
+#define REQ_PLAYER             (1 << 6)
 
-/* für AreYouSure */
-#define AYS_ASK                1
-#define AYS_OPEN       2
-#define AYS_CLOSE      4
-#define AYS_CONFIRM    8
-#define AYS_STAY_CLOSED        16
-#define AYS_STAY_OPEN  32
+#define REQUEST_WAIT_FOR       (REQ_ASK | REQ_CONFIRM | REQ_PLAYER)
 
+void SetDrawtoField(int);
 void BackToFront();
 void FadeToFront();
 void ClearWindow();
+void DrawTextF(int, int, int, char *, ...);
+void DrawTextFCentered(int, int, char *, ...);
 void DrawText(int, int, char *, int, int);
 void DrawTextExt(Drawable, GC, int, int, char *, int, int);
+void DrawAllPlayers(void);
+void DrawPlayerField(int, int);
+void DrawPlayer(struct PlayerInfo *);
+void DrawGraphicAnimationExt(int, int, int, int, int, int, int);
+void DrawGraphicAnimation(int, int, int, int, int, int);
+void DrawGraphicAnimationThruMask(int, int, int, int, int, int);
 void DrawGraphic(int, int, int);
 void DrawGraphicExt(Drawable, GC, int, int, int);
-void DrawGraphicExtHiRes(Drawable, GC, int, int, int);
 void DrawGraphicThruMask(int, int, int);
-void DrawElementThruMask(int, int, int);
+void DrawGraphicThruMaskExt(Drawable, int, int, int);
 void DrawMiniGraphic(int, int, int);
 void DrawMiniGraphicExt(Drawable, GC, int, int, int);
-void DrawMiniGraphicExtHiRes(Drawable, GC, int, int, int);
-void DrawGraphicShifted(int, int, int, int, int, int);
-void DrawElementShifted(int, int, int, int, int, int);
+void DrawGraphicShifted(int, int, int, int, int, int, int);
+void DrawGraphicShiftedThruMask(int, int, int, int, int, int);
+void DrawScreenElementExt(int, int, int, int, int, int, int);
+void DrawLevelElementExt(int, int, int, int, int, int, int);
+void DrawScreenElementShifted(int, int, int, int, int, int);
+void DrawLevelElementShifted(int, int, int, int, int, int);
+void DrawScreenElementThruMask(int, int, int);
+void DrawLevelElementThruMask(int, int, int);
+void DrawLevelFieldThruMask(int, int);
 void ErdreichAnbroeckeln(int, int);
 void DrawScreenElement(int, int, int);
 void DrawLevelElement(int, int, int);
@@ -73,15 +98,12 @@ void DrawMicroElement(int, int, int);
 void DrawLevel(void);
 void DrawMiniLevel(int, int);
 void DrawMicroLevel(int, int);
-BOOL AreYouSure(char *, unsigned int);
-void OpenDoor(unsigned int);
-void CloseDoor(unsigned int);
-void MoveDoor(unsigned int);
+boolean Request(char *, unsigned int);
+unsigned int OpenDoor(unsigned int);
+unsigned int CloseDoor(unsigned int);
+unsigned int GetDoorState(void);
+unsigned int MoveDoor(unsigned int);
 int ReadPixel(Drawable, int, int);
-void CheckJoystickData(void);
-int JoystickPosition(int, int, int);
-int Joystick(void);
-int JoystickButton(void);
 int el2gfx(int);
 
 #endif