Merge branch 'master' into releases 1.3.0
authorHolger Schemel <info@artsoft.org>
Sat, 30 Aug 2014 08:33:30 +0000 (10:33 +0200)
committerHolger Schemel <info@artsoft.org>
Sat, 30 Aug 2014 08:33:30 +0000 (10:33 +0200)
26 files changed:
README
src/buttons.c
src/buttons.h
src/cartoons.c
src/editor.c
src/editor.h
src/events.c
src/files.c
src/game.c
src/game.h
src/init.c
src/main.c
src/main.h
src/misc.c
src/misc.h
src/msdos.c
src/msdos.h
src/pcx.c
src/screens.c
src/screens.h
src/sound.c
src/sound.h
src/tape.c
src/tape.h
src/tools.c
src/tools.h

diff --git a/README b/README
index a16fd5edc33c3ff16b07d066222cc4a6ca383497..8843b3b4d457c37e7abd4b24e6ba0b3311624e88 100644 (file)
--- a/README
+++ b/README
@@ -1,9 +1,11 @@
+
 Welcome to
 
-   R O C K S   ' N '   D I A M O N D S   ' 9 8
-   -------------------------------------------
+   R O C K S   ' N '   D I A M O N D S   1 . 3 . 0
+   -----------------------------------------------
 
-A game for Unix/X11 by Holger Schemel, (c) 1995-98 by Holger Schemel.
+A game for Unix/X11 by Holger Schemel, (c) 1995-99 by Holger Schemel.
+DOS/Windows port by Guido Schulz.
 
 Introduction
 ============
@@ -12,7 +14,7 @@ Unix system with color X11. You need an 8-Bit color display or better.
 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.
+(Amiga) or "Supaplex" (PC), you know what "ROCKS'N'DIAMONDS" is about.
 
 
 Getting started
@@ -20,7 +22,7 @@ Getting started
 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
+compiled for Linux (i386/glibc) (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.
@@ -28,7 +30,7 @@ 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:
-AMD K6, kernel 2.0.35, libc5, gcc 2.7.2.1, ELF format)
+AMD K6-2, kernel 2.2.1, glibc, gcc 2.7.2.3, ELF format)
 
 
 The Menues
@@ -216,21 +218,58 @@ 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
-to remap it to the new element. Use the arrow widgets to scroll around in
-the level. Use the 'flood fill' field to init exactly ony flood fill
-operation in the level field (you will be prompted). Click on 'control
-window' to switch to the control window.
-
-In the control window you can modify different parameters like the size
-of the level playfield, the name of the level, the scores for different
-elements and something like that. The four 3x3 field on the upper left
-can be edited like the level field and indicate the 'contents' of smashed
-crunchers (just try it out with some crunchers in one of your own levels).
-
-'Undo & Exit' leaves the level editor, throwing away all the changes you
-have done to the level.
-'Save & Exit' leveas the level editor and saves the new level (the old one
-will be deleted).
+to remap it to the new element. Use the scrollbars, arrow buttons or
+cursor keys to scroll around in the level. Use the drawing functions by
+selecting them from the toolbox field or pressing the corresponding key
+(the key shortcuts for each gadget are displayed in the status line at
+the bottom of the window when the mouse pointer is over the gadget).
+
+About the drawing functions: If you know the good old "Deluxe Paint" from
+the Amiga, you will have no problems with the drawing functions -- even
+the key shortcuts are mostly the same. If not, just try them out and you
+will see what happens. ;-)
+
+Some notes: Most drawing functions have different behaviour when different
+mouse buttons are pressed in the drawing area; normally, the left mouse
+button is the 'foreground button' and the middle and right buttons are
+some sort of 'background buttons', often with relation to the chosen
+element mappings on this buttons.
+
+The '?' button brings you to the 'Element settings' page, where you can
+see some information about the element and how many of them are included
+in the current level. The contents of elements with content (some amoeba
+and the Yam Yam (also know as cruncher) can have content) can be edited
+by 'sticking' the page with the corresponding button on their page.
+Yam Yam's can have up to eight content areas.
+
+The 'undo' button gives you the chance to undo up to 10 drawing operations.
+The 'info' button brings you to the 'Level/Editor settings' page.
+The 'save' button asks you to save the current level.
+The 'clear' button clears the drawing area without asking.
+The 'test' button lets you play your new level directly from the editor.
+The 'exit' button asks you to exit the editor (if there are unsaved changes).
+
+In the 'Editor settings' section, you can give your level a new name
+and type in your name which will be displayed as the level's author.
+(On Unix systems, your login name will be taken as default.)
+Then you can modify the level playfield size there, the available time
+(set this to zero to get unlimited time for this level), the score you
+get for each 10 seconds time that are left when reaching the exit, and
+last but not least you can enter the number of emeralds the player has
+to collect before the exit door opens.
+
+Another new button is the 'double speed' button that lets the player move
+twice as fast as normally from start; the effect is the same as collecting
+a speed pill during the game.
+
+In the 'Editor settings' section, you can make some modifications to the
+behaviour of the 'random element placement' button (the dice): Choose if
+the number you can enter means how many percent of the whole level are
+filled with the element chosen as the random element, or if it should mean
+the exact number of elements to be placed in the level field.
+With the 'restrict' button on the left side you can force the random function
+not to overwrite level elements other than the element chosen in the content
+field on the right side.
 
 
 The Tape Recorder
index 62c5a1c1332a2f794f146cba92d05f5def4a5114..7c1e9c9a0ee99bf9a1c84d1ffb1a2fddbdf8dad7 100644 (file)
@@ -11,6 +11,8 @@
 *  buttons.c                                               *
 ***********************************************************/
 
+#include <stdarg.h>
+
 #include "buttons.h"
 #include "tools.h"
 #include "misc.h"
 
 /* some positions in the game control window */
 #define GAME_BUTTON_STOP_XPOS  (GAME_CONTROL_XPOS + 0 * GAME_BUTTON_XSIZE)
+#define GAME_BUTTON_STOP_YPOS  (GAME_CONTROL_YPOS)
 #define GAME_BUTTON_PAUSE_XPOS (GAME_CONTROL_XPOS + 1 * GAME_BUTTON_XSIZE)
+#define GAME_BUTTON_PAUSE_YPOS (GAME_CONTROL_YPOS)
 #define GAME_BUTTON_PLAY_XPOS  (GAME_CONTROL_XPOS + 2 * GAME_BUTTON_XSIZE)
+#define GAME_BUTTON_PLAY_YPOS  (GAME_CONTROL_YPOS)
 #define GAME_BUTTON_ANY_YPOS   (GAME_CONTROL_YPOS)
 
 #define ON_GAME_BUTTON(x,y)    ((x)>=(DX+GAME_CONTROL_XPOS) && \
 /********** drawing buttons and corresponding displays **********/
 /****************************************************************/
 
+void OLD_DrawVideoDisplay(unsigned long state, unsigned long value)
+{
+  int i;
+  int part_label = 0, part_symbol = 1;
+  int xpos = 0, ypos = 1, xsize = 2, ysize = 3;
+  static char *monatsname[12] =
+  {
+    "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
+    "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
+  };
+  static int video_pos[5][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 }}
+  };
+
+  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<10;i++)
+  {
+    if (state & (1<<i))
+    {
+      int pos = i/2, cx, cy = DOOR_GFX_PAGEY2;
+
+      if (i%2)                 /* i ungerade => STATE_ON / PRESS_OFF */
+       cx = DOOR_GFX_PAGEX4;
+      else
+       cx = DOOR_GFX_PAGEX3;   /* i gerade => STATE_OFF / PRESS_ON */
+
+      if (video_pos[pos][part_label][0] && value != VIDEO_DISPLAY_SYMBOL_ONLY)
+       XCopyArea(display,pix[PIX_DOOR],drawto,gc,
+                 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][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;
+    int monat = (value/100) % 100;
+    int jahr = (value/10000);
+
+    DrawText(VX+VIDEO_DATE_XPOS,VY+VIDEO_DATE_YPOS,
+            int2str(tag,2),FS_SMALL,FC_SPECIAL1);
+    DrawText(VX+VIDEO_DATE_XPOS+27,VY+VIDEO_DATE_YPOS,
+            monatsname[monat],FS_SMALL,FC_SPECIAL1);
+    DrawText(VX+VIDEO_DATE_XPOS+64,VY+VIDEO_DATE_YPOS,
+            int2str(jahr,2),FS_SMALL,FC_SPECIAL1);
+  }
+
+  if (state & VIDEO_STATE_TIME_ON)
+  {
+    int min = value / 60;
+    int sec = value % 60;
+
+    DrawText(VX+VIDEO_TIME_XPOS,VY+VIDEO_TIME_YPOS,
+            int2str(min,2),FS_SMALL,FC_SPECIAL1);
+    DrawText(VX+VIDEO_TIME_XPOS+27,VY+VIDEO_TIME_YPOS,
+            int2str(sec,2),FS_SMALL,FC_SPECIAL1);
+  }
+
+  if (state & VIDEO_STATE_DATE)
+    redraw_mask |= REDRAW_VIDEO_1;
+  if ((state & ~VIDEO_STATE_DATE) & VIDEO_STATE)
+    redraw_mask |= REDRAW_VIDEO_2;
+  if (state & VIDEO_PRESS)
+    redraw_mask |= REDRAW_VIDEO_3;
+}
+
 void DrawVideoDisplay(unsigned long state, unsigned long value)
 {
   int i;
@@ -838,7 +983,7 @@ void DrawElemButton(int button_nr, int button_state)
     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 +
+                      DY+ED_BUTTON_ELEM_YPOS+3+shift +
                       (elem_pos / MAX_ELEM_X)*ED_BUTTON_ELEM_YSIZE,
                       graphic);
   }
@@ -1461,3 +1606,1060 @@ int CheckCountButtons(int mx, int my, int button)
   BackToFront();
   return(return_code);
 }
+
+
+/* NEW GADGET STUFF -------------------------------------------------------- */
+
+
+/* values for DrawGadget() */
+#define DG_UNPRESSED           0
+#define DG_PRESSED             1
+#define DG_BUFFERED            0
+#define DG_DIRECT              1
+
+static struct GadgetInfo *gadget_list_first_entry = NULL;
+static struct GadgetInfo *gadget_list_last_entry = NULL;
+static int next_free_gadget_id = 1;
+static boolean gadget_id_wrapped = FALSE;
+
+static struct GadgetInfo *getGadgetInfoFromGadgetID(int id)
+{
+  struct GadgetInfo *gi = gadget_list_first_entry;
+
+  while (gi && gi->id != id)
+    gi = gi->next;
+
+  return gi;
+}
+
+static int getNewGadgetID()
+{
+  int id = next_free_gadget_id++;
+
+  if (next_free_gadget_id <= 0)                /* counter overrun */
+  {
+    gadget_id_wrapped = TRUE;          /* now we must check each ID */
+    next_free_gadget_id = 0;
+  }
+
+  if (gadget_id_wrapped)
+  {
+    next_free_gadget_id++;
+    while (getGadgetInfoFromGadgetID(next_free_gadget_id) != NULL)
+      next_free_gadget_id++;
+  }
+
+  if (next_free_gadget_id <= 0)                /* cannot get new gadget id */
+    Error(ERR_EXIT, "too much gadgets -- this should not happen");
+
+  return id;
+}
+
+static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my)
+{
+  struct GadgetInfo *gi = gadget_list_first_entry;
+
+  while (gi)
+  {
+    if (gi->mapped &&
+       mx >= gi->x && mx < gi->x + gi->width &&
+       my >= gi->y && my < gi->y + gi->height)
+       break;
+
+    gi = gi->next;
+  }
+
+  return gi;
+}
+
+static void default_callback_info(void *ptr)
+{
+  if (game_status == LEVELED)
+    HandleEditorGadgetInfoText(ptr);
+}
+
+static void default_callback_action(void *ptr)
+{
+  return;
+}
+
+static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
+{
+  int state = (pressed ? 1 : 0);
+  struct GadgetDesign *gd = (gi->checked ?
+                            &gi->alt_design[state] :
+                            &gi->design[state]);
+
+  switch (gi->type)
+  {
+    case GD_TYPE_NORMAL_BUTTON:
+    case GD_TYPE_CHECK_BUTTON:
+    case GD_TYPE_RADIO_BUTTON:
+      XCopyArea(display, gd->pixmap, drawto, gc,
+               gd->x, gd->y, gi->width, gi->height, gi->x, gi->y);
+      if (gi->deco.design.pixmap)
+       XCopyArea(display, gi->deco.design.pixmap, drawto, gc,
+                 gi->deco.design.x, gi->deco.design.y,
+                 gi->deco.width, gi->deco.height,
+                 gi->x + gi->deco.x + (pressed ? gi->deco.xshift : 0),
+                 gi->y + gi->deco.y + (pressed ? gi->deco.yshift : 0));
+      break;
+
+    case GD_TYPE_TEXTINPUT_ALPHANUMERIC:
+    case GD_TYPE_TEXTINPUT_NUMERIC:
+      {
+       int i;
+       char cursor_letter;
+       char cursor_string[3];
+       char text[MAX_GADGET_TEXTSIZE + 1];
+       int font_color = FC_YELLOW;
+       int border = gi->design_border;
+       strcpy(text, gi->text.value);
+       strcat(text, " ");
+
+       /* left part of gadget */
+       XCopyArea(display, gd->pixmap, drawto, gc,
+                 gd->x, gd->y, border, gi->height, gi->x, gi->y);
+
+       /* middle part of gadget */
+       for (i=0; i<=gi->text.size; i++)
+         XCopyArea(display, gd->pixmap, drawto, gc,
+                   gd->x + border, gd->y, FONT2_XSIZE, gi->height,
+                   gi->x + border + i * FONT2_XSIZE, gi->y);
+
+       /* right part of gadget */
+       XCopyArea(display, gd->pixmap, drawto, gc,
+                 gd->x + ED_WIN_COUNT_XSIZE - border, gd->y,
+                 border, gi->height, gi->x + gi->width - border, gi->y);
+
+       /* gadget text value */
+       DrawText(gi->x + border, gi->y + border, text, FS_SMALL, font_color);
+
+       cursor_letter = gi->text.value[gi->text.cursor_position];
+       cursor_string[0] = '~';
+       cursor_string[1] = (cursor_letter != '\0' ? cursor_letter : ' ');
+       cursor_string[2] = '\0';
+
+       /* draw cursor, if active */
+       if (pressed)
+         DrawText(gi->x + border + gi->text.cursor_position * FONT2_XSIZE,
+                  gi->y + border, cursor_string, FS_SMALL, font_color);
+      }
+      break;
+
+    case GD_TYPE_SCROLLBAR_VERTICAL:
+      {
+       int i;
+       int xpos = gi->x;
+       int ypos = gi->y + gi->scrollbar.position;
+       int design_full = gi->width;
+       int design_body = design_full - 2 * gi->design_border;
+       int size_full = gi->scrollbar.size;
+       int size_body = size_full - 2 * gi->design_border;
+       int num_steps = size_body / design_body;
+       int step_size_remain = size_body - num_steps * design_body;
+
+       /* clear scrollbar area */
+       XFillRectangle(display, backbuffer, gc,
+                      gi->x, gi->y, gi->width, gi->height);
+
+       /* upper part of gadget */
+       XCopyArea(display, gd->pixmap, drawto, gc,
+                 gd->x, gd->y,
+                 gi->width, gi->design_border,
+                 xpos, ypos);
+
+       /* middle part of gadget */
+       for (i=0; i<num_steps; i++)
+         XCopyArea(display, gd->pixmap, drawto, gc,
+                   gd->x, gd->y + gi->design_border,
+                   gi->width, design_body,
+                   xpos, ypos + gi->design_border + i * design_body);
+
+       /* remaining middle part of gadget */
+       if (step_size_remain > 0)
+         XCopyArea(display, gd->pixmap, drawto, gc,
+                   gd->x,  gd->y + gi->design_border,
+                   gi->width, step_size_remain,
+                   xpos, ypos + gi->design_border + num_steps * design_body);
+
+       /* lower part of gadget */
+       XCopyArea(display, gd->pixmap, drawto, gc,
+                 gd->x, gd->y + design_full - gi->design_border,
+                 gi->width, gi->design_border,
+                 xpos, ypos + size_full - gi->design_border);
+      }
+      break;
+
+    case GD_TYPE_SCROLLBAR_HORIZONTAL:
+      {
+       int i;
+       int xpos = gi->x + gi->scrollbar.position;
+       int ypos = gi->y;
+       int design_full = gi->height;
+       int design_body = design_full - 2 * gi->design_border;
+       int size_full = gi->scrollbar.size;
+       int size_body = size_full - 2 * gi->design_border;
+       int num_steps = size_body / design_body;
+       int step_size_remain = size_body - num_steps * design_body;
+
+       /* clear scrollbar area */
+       XFillRectangle(display, backbuffer, gc,
+                      gi->x, gi->y, gi->width, gi->height);
+
+       /* left part of gadget */
+       XCopyArea(display, gd->pixmap, drawto, gc,
+                 gd->x, gd->y,
+                 gi->design_border, gi->height,
+                 xpos, ypos);
+
+       /* middle part of gadget */
+       for (i=0; i<num_steps; i++)
+         XCopyArea(display, gd->pixmap, drawto, gc,
+                   gd->x + gi->design_border, gd->y,
+                   design_body, gi->height,
+                   xpos + gi->design_border + i * design_body, ypos);
+
+       /* remaining middle part of gadget */
+       if (step_size_remain > 0)
+         XCopyArea(display, gd->pixmap, drawto, gc,
+                   gd->x + gi->design_border, gd->y,
+                   step_size_remain, gi->height,
+                   xpos + gi->design_border + num_steps * design_body, ypos);
+
+       /* right part of gadget */
+       XCopyArea(display, gd->pixmap, drawto, gc,
+                 gd->x + design_full - gi->design_border, gd->y,
+                 gi->design_border, gi->height,
+                 xpos + size_full - gi->design_border, ypos);
+      }
+      break;
+
+    default:
+      return;
+  }
+
+  if (direct)
+    XCopyArea(display, drawto, window, gc,
+             gi->x, gi->y, gi->width, gi->height, gi->x, gi->y);
+  else
+    redraw_mask |= (gi->x < SX + SXSIZE ? REDRAW_FIELD :
+                   gi->y < DY + DYSIZE ? REDRAW_DOOR_1 :
+                   gi->y > VY ? REDRAW_DOOR_2 : REDRAW_DOOR_3);
+}
+
+static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
+{
+  int tag = first_tag;
+
+  while (tag != GDI_END)
+  {
+    switch(tag)
+    {
+      case GDI_CUSTOM_ID:
+       gi->custom_id = va_arg(ap, int);
+       break;
+
+      case GDI_INFO_TEXT:
+       {
+         int max_textsize = MAX_INFO_TEXTSIZE - 1;
+
+         strncpy(gi->info_text, va_arg(ap, char *), max_textsize);
+         gi->info_text[max_textsize] = '\0';
+       }
+       break;
+
+      case GDI_X:
+       gi->x = va_arg(ap, int);
+       break;
+
+      case GDI_Y:
+       gi->y = va_arg(ap, int);
+       break;
+
+      case GDI_WIDTH:
+       gi->width = va_arg(ap, int);
+       break;
+
+      case GDI_HEIGHT:
+       gi->height = va_arg(ap, int);
+       break;
+
+      case GDI_TYPE:
+       gi->type = va_arg(ap, unsigned long);
+       break;
+
+      case GDI_STATE:
+       gi->state = va_arg(ap, unsigned long);
+       break;
+
+      case GDI_CHECKED:
+       gi->checked = va_arg(ap, boolean);
+       break;
+
+      case GDI_RADIO_NR:
+       gi->radio_nr = va_arg(ap, unsigned long);
+       break;
+
+      case GDI_NUMBER_VALUE:
+       gi->text.number_value = va_arg(ap, long);
+       sprintf(gi->text.value, "%d", gi->text.number_value);
+       gi->text.cursor_position = strlen(gi->text.value);
+       break;
+
+      case GDI_NUMBER_MIN:
+       gi->text.number_min = va_arg(ap, long);
+       if (gi->text.number_value < gi->text.number_min)
+       {
+         gi->text.number_value = gi->text.number_min;
+         sprintf(gi->text.value, "%d", gi->text.number_value);
+       }
+       break;
+
+      case GDI_NUMBER_MAX:
+       gi->text.number_max = va_arg(ap, long);
+       if (gi->text.number_value > gi->text.number_max)
+       {
+         gi->text.number_value = gi->text.number_max;
+         sprintf(gi->text.value, "%d", gi->text.number_value);
+       }
+       break;
+
+      case GDI_TEXT_VALUE:
+       {
+         int max_textsize = MAX_GADGET_TEXTSIZE;
+
+         if (gi->text.size)
+           max_textsize = MIN(gi->text.size, MAX_GADGET_TEXTSIZE - 1);
+
+         strncpy(gi->text.value, va_arg(ap, char *), max_textsize);
+         gi->text.value[max_textsize] = '\0';
+         gi->text.cursor_position = strlen(gi->text.value);
+       }
+       break;
+
+      case GDI_TEXT_SIZE:
+       {
+         int tag_value = va_arg(ap, int);
+         int max_textsize = MIN(tag_value, MAX_GADGET_TEXTSIZE - 1);
+
+         gi->text.size = max_textsize;
+         gi->text.value[max_textsize] = '\0';
+
+         if (gi->width == 0 && gi->height == 0)
+         {
+           gi->width = (gi->text.size + 1) * FONT2_XSIZE + 6;
+           gi->height = ED_WIN_COUNT_YSIZE;
+         }
+       }
+       break;
+
+      case GDI_DESIGN_UNPRESSED:
+       gi->design[GD_BUTTON_UNPRESSED].pixmap = va_arg(ap, Pixmap);
+       gi->design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
+       gi->design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
+       break;
+
+      case GDI_DESIGN_PRESSED:
+       gi->design[GD_BUTTON_PRESSED].pixmap = va_arg(ap, Pixmap);
+       gi->design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
+       gi->design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
+       break;
+
+      case GDI_ALT_DESIGN_UNPRESSED:
+       gi->alt_design[GD_BUTTON_UNPRESSED].pixmap= va_arg(ap, Pixmap);
+       gi->alt_design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
+       gi->alt_design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
+       break;
+
+      case GDI_ALT_DESIGN_PRESSED:
+       gi->alt_design[GD_BUTTON_PRESSED].pixmap = va_arg(ap, Pixmap);
+       gi->alt_design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
+       gi->alt_design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
+       break;
+
+      case GDI_DESIGN_BORDER:
+       gi->design_border = va_arg(ap, int);
+       break;
+
+      case GDI_DECORATION_DESIGN:
+       gi->deco.design.pixmap = va_arg(ap, Pixmap);
+       gi->deco.design.x = va_arg(ap, int);
+       gi->deco.design.y = va_arg(ap, int);
+       break;
+
+      case GDI_DECORATION_POSITION:
+       gi->deco.x = va_arg(ap, int);
+       gi->deco.y = va_arg(ap, int);
+       break;
+
+      case GDI_DECORATION_SIZE:
+       gi->deco.width = va_arg(ap, int);
+       gi->deco.height = va_arg(ap, int);
+       break;
+
+      case GDI_DECORATION_SHIFTING:
+       gi->deco.xshift = va_arg(ap, int);
+       gi->deco.yshift = va_arg(ap, int);
+       break;
+
+      case GDI_EVENT_MASK:
+       gi->event_mask = va_arg(ap, unsigned long);
+       break;
+
+      case GDI_AREA_SIZE:
+       gi->drawing.area_xsize = va_arg(ap, int);
+       gi->drawing.area_ysize = va_arg(ap, int);
+
+       /* determine dependent values for drawing area gadget, if needed */
+       if (gi->width == 0 && gi->height == 0 &&
+           gi->drawing.item_xsize !=0 && gi->drawing.item_ysize !=0)
+       {
+         gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
+         gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
+       }
+       else if (gi->drawing.item_xsize == 0 && gi->drawing.item_ysize == 0 &&
+                gi->width != 0 && gi->height != 0)
+       {
+         gi->drawing.item_xsize = gi->width / gi->drawing.area_xsize;
+         gi->drawing.item_ysize = gi->height / gi->drawing.area_ysize;
+       }
+       break;
+
+      case GDI_ITEM_SIZE:
+       gi->drawing.item_xsize = va_arg(ap, int);
+       gi->drawing.item_ysize = va_arg(ap, int);
+
+       /* determine dependent values for drawing area gadget, if needed */
+       if (gi->width == 0 && gi->height == 0 &&
+           gi->drawing.area_xsize !=0 && gi->drawing.area_ysize !=0)
+       {
+         gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
+         gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
+       }
+       else if (gi->drawing.area_xsize == 0 && gi->drawing.area_ysize == 0 &&
+                gi->width != 0 && gi->height != 0)
+       {
+         gi->drawing.area_xsize = gi->width / gi->drawing.item_xsize;
+         gi->drawing.area_ysize = gi->height / gi->drawing.item_ysize;
+       }
+       break;
+
+      case GDI_SCROLLBAR_ITEMS_MAX:
+       gi->scrollbar.items_max = va_arg(ap, int);
+       break;
+
+      case GDI_SCROLLBAR_ITEMS_VISIBLE:
+       gi->scrollbar.items_visible = va_arg(ap, int);
+       break;
+
+      case GDI_SCROLLBAR_ITEM_POSITION:
+       gi->scrollbar.item_position = va_arg(ap, int);
+       break;
+
+      case GDI_CALLBACK_INFO:
+       gi->callback_info = va_arg(ap, gadget_function);
+       break;
+
+      case GDI_CALLBACK_ACTION:
+       gi->callback_action = va_arg(ap, gadget_function);
+       break;
+
+      default:
+       Error(ERR_EXIT, "HandleGadgetTags(): unknown tag %d", tag);
+    }
+
+    tag = va_arg(ap, int);     /* read next tag */
+  }
+
+  /* check if gadget complete */
+  if (gi->type != GD_TYPE_DRAWING_AREA &&
+      (!gi->design[GD_BUTTON_UNPRESSED].pixmap ||
+       !gi->design[GD_BUTTON_PRESSED].pixmap))
+    Error(ERR_EXIT, "gadget incomplete (missing Pixmap)");
+
+  /* adjust gadget values in relation to other gadget values */
+
+  if (gi->type & GD_TYPE_TEXTINPUT_NUMERIC)
+  {
+    struct GadgetTextInput *text = &gi->text;
+    int value = text->number_value;
+
+    text->number_value = (value < text->number_min ? text->number_min :
+                         value > text->number_max ? text->number_max :
+                         value);
+
+    sprintf(text->value, "%d", text->number_value);
+  }
+
+  if (gi->type & GD_TYPE_SCROLLBAR)
+  {
+    struct GadgetScrollbar *gs = &gi->scrollbar;
+
+    if (gi->width == 0 || gi->height == 0 ||
+       gs->items_max == 0 || gs->items_visible == 0)
+      Error(ERR_EXIT, "scrollbar gadget incomplete (missing tags)");
+
+    /* calculate internal scrollbar values */
+    gs->size_max = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
+                   gi->height : gi->width);
+    gs->size = gs->size_max * gs->items_visible / gs->items_max;
+    gs->position = gs->size_max * gs->item_position / gs->items_max;
+    gs->position_max = gs->size_max - gs->size;
+
+    /* finetuning for maximal right/bottom position */
+    if (gs->item_position == gs->items_max - gs->items_visible)
+      gs->position = gs->position_max;
+  }
+}
+
+void ModifyGadget(struct GadgetInfo *gi, int first_tag, ...)
+{
+  va_list ap;
+
+  va_start(ap, first_tag);
+  HandleGadgetTags(gi, first_tag, ap);
+  va_end(ap);
+
+  RedrawGadget(gi);
+}
+
+void RedrawGadget(struct GadgetInfo *gi)
+{
+  if (gi->mapped)
+    DrawGadget(gi, gi->state, DG_DIRECT);
+}
+
+struct GadgetInfo *CreateGadget(int first_tag, ...)
+{
+  struct GadgetInfo *new_gadget = checked_malloc(sizeof(struct GadgetInfo));
+  va_list ap;
+
+  /* always start with reliable default values */
+  memset(new_gadget, 0, sizeof(struct GadgetInfo));    /* zero all fields */
+  new_gadget->id = getNewGadgetID();
+  new_gadget->callback_info = default_callback_info;
+  new_gadget->callback_action = default_callback_action;
+
+  va_start(ap, first_tag);
+  HandleGadgetTags(new_gadget, first_tag, ap);
+  va_end(ap);
+
+  /* insert new gadget into global gadget list */
+  if (gadget_list_last_entry)
+  {
+    gadget_list_last_entry->next = new_gadget;
+    gadget_list_last_entry = gadget_list_last_entry->next;
+  }
+  else
+    gadget_list_first_entry = gadget_list_last_entry = new_gadget;
+
+  return new_gadget;
+}
+
+void FreeGadget(struct GadgetInfo *gi)
+{
+  struct GadgetInfo *gi_previous = gadget_list_first_entry;
+
+  while (gi_previous && gi_previous->next != gi)
+    gi_previous = gi_previous->next;
+
+  if (gi == gadget_list_first_entry)
+    gadget_list_first_entry = gi->next;
+
+  if (gi == gadget_list_last_entry)
+    gadget_list_last_entry = gi_previous;
+
+  gi_previous->next = gi->next;
+  free(gi);
+}
+
+static void CheckRangeOfNumericInputGadget(struct GadgetInfo *gi)
+{
+  if (gi->type != GD_TYPE_TEXTINPUT_NUMERIC)
+    return;
+
+  gi->text.number_value = atoi(gi->text.value);
+
+  if (gi->text.number_value < gi->text.number_min)
+    gi->text.number_value = gi->text.number_min;
+  if (gi->text.number_value > gi->text.number_max)
+    gi->text.number_value = gi->text.number_max;
+
+  sprintf(gi->text.value, "%d", gi->text.number_value);
+
+  if (gi->text.cursor_position < 0)
+    gi->text.cursor_position = 0;
+  else if (gi->text.cursor_position > strlen(gi->text.value))
+    gi->text.cursor_position = strlen(gi->text.value);
+}
+
+/* global pointer to gadget actually in use (when mouse button pressed) */
+static struct GadgetInfo *last_gi = NULL;
+
+static void MapGadgetExt(struct GadgetInfo *gi, boolean redraw)
+{
+  if (gi == NULL || gi->mapped)
+    return;
+
+  gi->mapped = TRUE;
+
+  if (redraw)
+    DrawGadget(gi, DG_UNPRESSED, DG_BUFFERED);
+}
+
+void MapGadget(struct GadgetInfo *gi)
+{
+  MapGadgetExt(gi, TRUE);
+}
+
+void UnmapGadget(struct GadgetInfo *gi)
+{
+  if (gi == NULL || !gi->mapped)
+    return;
+
+  gi->mapped = FALSE;
+
+  if (gi == last_gi)
+    last_gi = NULL;
+}
+
+#define MAX_NUM_GADGETS                1024
+#define MULTIMAP_UNMAP         (1 << 0)
+#define MULTIMAP_REMAP         (1 << 1)
+#define MULTIMAP_REDRAW                (1 << 2)
+#define MULTIMAP_PLAYFIELD     (1 << 3)
+#define MULTIMAP_DOOR_1                (1 << 4)
+#define MULTIMAP_DOOR_2                (1 << 5)
+#define MULTIMAP_ALL           (MULTIMAP_PLAYFIELD | \
+                                MULTIMAP_DOOR_1 | \
+                                MULTIMAP_DOOR_2)
+
+static void MultiMapGadgets(int mode)
+{
+  struct GadgetInfo *gi = gadget_list_first_entry;
+  static boolean map_state[MAX_NUM_GADGETS];
+  int map_count = 0;
+
+  while (gi)
+  {
+    if ((mode & MULTIMAP_PLAYFIELD && gi->x < SX + SXSIZE) ||
+       (mode & MULTIMAP_DOOR_1 && gi->x >= DX && gi->y < DY + DYSIZE) ||
+       (mode & MULTIMAP_DOOR_1 && gi->x >= DX && gi->y > DY + DYSIZE))
+    {
+      if (mode & MULTIMAP_UNMAP)
+      {
+       map_state[map_count++ % MAX_NUM_GADGETS] = gi->mapped;
+       UnmapGadget(gi);
+      }
+      else
+      {
+       if (map_state[map_count++ % MAX_NUM_GADGETS])
+         MapGadgetExt(gi, (mode & MULTIMAP_REDRAW));
+      }
+    }
+
+    gi = gi->next;
+  }
+}
+
+void UnmapAllGadgets()
+{
+  MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_UNMAP);
+}
+
+void RemapAllGadgets()
+{
+  MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP);
+}
+
+boolean anyTextGadgetActive()
+{
+  return (last_gi && last_gi->type & GD_TYPE_TEXTINPUT && last_gi->mapped);
+}
+
+void ClickOnGadget(struct GadgetInfo *gi, int button)
+{
+  /* simulate releasing mouse button over last gadget, if still pressed */
+  if (button_status)
+    HandleGadgets(-1, -1, 0);
+
+  /* simulate pressing mouse button over specified gadget */
+  HandleGadgets(gi->x, gi->y, button);
+
+  /* simulate releasing mouse button over specified gadget */
+  HandleGadgets(gi->x, gi->y, 0);
+}
+
+void HandleGadgets(int mx, int my, int button)
+{
+  static struct GadgetInfo *last_info_gi = NULL;
+  static unsigned long pressed_delay = 0;
+  static int last_button = 0;
+  static int last_mx = 0, last_my = 0;
+  int scrollbar_mouse_pos = 0;
+  struct GadgetInfo *new_gi, *gi;
+  boolean press_event;
+  boolean release_event;
+  boolean mouse_moving;
+  boolean gadget_pressed;
+  boolean gadget_pressed_repeated;
+  boolean gadget_moving;
+  boolean gadget_moving_inside;
+  boolean gadget_moving_off_borders;
+  boolean gadget_released;
+  boolean gadget_released_inside;
+  boolean gadget_released_off_borders;
+  boolean changed_position = FALSE;
+
+  /* check if there are any gadgets defined */
+  if (gadget_list_first_entry == NULL)
+    return;
+
+  /* check which gadget is under the mouse pointer */
+  new_gi = getGadgetInfoFromMousePosition(mx, my);
+
+  /* check if button state has changed since last invocation */
+  press_event = (button != 0 && last_button == 0);
+  release_event = (button == 0 && last_button != 0);
+  last_button = button;
+
+  /* check if mouse has been moved since last invocation */
+  mouse_moving = ((mx != last_mx || my != last_my) && motion_status);
+  last_mx = mx;
+  last_my = my;
+
+  /* special treatment for text and number input gadgets */
+  if (anyTextGadgetActive() && button != 0 && !motion_status)
+  {
+    struct GadgetInfo *gi = last_gi;
+
+    if (new_gi == last_gi)
+    {
+      /* if mouse button pressed inside activated text gadget, set cursor */
+      gi->text.cursor_position = (mx - gi->x) / FONT2_XSIZE;
+
+      if (gi->text.cursor_position < 0)
+       gi->text.cursor_position = 0;
+      else if (gi->text.cursor_position > strlen(gi->text.value))
+       gi->text.cursor_position = strlen(gi->text.value);
+
+      DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+    }
+    else
+    {
+      /* if mouse button pressed outside text input gadget, deactivate it */
+      CheckRangeOfNumericInputGadget(gi);
+      DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
+
+      if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
+       gi->callback_action(gi);
+
+      last_gi = NULL;
+    }
+  }
+
+  gadget_pressed =
+    (button != 0 && last_gi == NULL && new_gi != NULL && press_event);
+  gadget_pressed_repeated =
+    (button != 0 && last_gi != NULL && new_gi == last_gi);
+
+  gadget_released =            (release_event && last_gi != NULL);
+  gadget_released_inside =     (gadget_released && new_gi == last_gi);
+  gadget_released_off_borders =        (gadget_released && new_gi != last_gi);
+
+  gadget_moving =            (button != 0 && last_gi != NULL && mouse_moving);
+  gadget_moving_inside =      (gadget_moving && new_gi == last_gi);
+  gadget_moving_off_borders = (gadget_moving && new_gi != last_gi);
+
+  /* if new gadget pressed, store this gadget  */
+  if (gadget_pressed)
+    last_gi = new_gi;
+
+  /* 'gi' is actually handled gadget */
+  gi = last_gi;
+
+  /* if gadget is scrollbar, choose mouse position value */
+  if (gi && gi->type & GD_TYPE_SCROLLBAR)
+    scrollbar_mouse_pos =
+      (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx - gi->x : my - gi->y);
+
+  /* if mouse button released, no gadget needs to be handled anymore */
+  if (button == 0 && last_gi && !(last_gi->type & GD_TYPE_TEXTINPUT))
+    last_gi = NULL;
+
+  /* modify event position values even if no gadget is pressed */
+  if (button == 0 && !release_event)
+    gi = new_gi;
+
+  if (gi)
+  {
+    int last_x = gi->event.x;
+    int last_y = gi->event.y;
+
+    gi->event.x = mx - gi->x;
+    gi->event.y = my - gi->y;
+
+    if (gi->type == GD_TYPE_DRAWING_AREA)
+    {
+      gi->event.x /= gi->drawing.item_xsize;
+      gi->event.y /= gi->drawing.item_ysize;
+
+      if (last_x != gi->event.x || last_y != gi->event.y)
+       changed_position = TRUE;
+    }
+  }
+
+  /* handle gadget popup info text */
+  if (last_info_gi != new_gi ||
+      (new_gi && new_gi->type == GD_TYPE_DRAWING_AREA && changed_position))
+  {
+    last_info_gi = new_gi;
+
+    if (new_gi != NULL && (button == 0 || new_gi == last_gi))
+    {
+      new_gi->event.type = 0;
+      new_gi->callback_info(new_gi);
+    }
+    else
+      default_callback_info(NULL);
+  }
+
+  if (gadget_pressed)
+  {
+    if (gi->type == GD_TYPE_CHECK_BUTTON)
+    {
+      gi->checked = !gi->checked;
+    }
+    else if (gi->type == GD_TYPE_RADIO_BUTTON)
+    {
+      struct GadgetInfo *rgi = gadget_list_first_entry;
+
+      while (rgi)
+      {
+       if (rgi->mapped &&
+           rgi->type == GD_TYPE_RADIO_BUTTON &&
+           rgi->radio_nr == gi->radio_nr &&
+           rgi != gi)
+       {
+         rgi->checked = FALSE;
+         DrawGadget(rgi, DG_UNPRESSED, DG_DIRECT);
+       }
+
+       rgi = rgi->next;
+      }
+
+      gi->checked = TRUE;
+    }
+    else if (gi->type & GD_TYPE_SCROLLBAR)
+    {
+      int mpos, gpos;
+
+      if (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL)
+      {
+       mpos = mx;
+       gpos = gi->x;
+      }
+      else
+      {
+       mpos = my;
+       gpos = gi->y;
+      }
+
+      if (mpos >= gpos + gi->scrollbar.position &&
+         mpos < gpos + gi->scrollbar.position + gi->scrollbar.size)
+      {
+       /* drag scrollbar */
+       gi->scrollbar.drag_position =
+         scrollbar_mouse_pos - gi->scrollbar.position;
+      }
+      else
+      {
+       /* click scrollbar one scrollbar length up/left or down/right */
+
+       struct GadgetScrollbar *gs = &gi->scrollbar;
+       int old_item_position = gs->item_position;
+
+       changed_position = FALSE;
+
+       gs->item_position +=
+         gs->items_visible * (mpos < gpos + gi->scrollbar.position ? -1 : +1);
+
+       if (gs->item_position < 0)
+         gs->item_position = 0;
+       if (gs->item_position > gs->items_max - gs->items_visible)
+         gs->item_position = gs->items_max - gs->items_visible;
+
+       if (old_item_position != gs->item_position)
+       {
+         gi->event.item_position = gs->item_position;
+         changed_position = TRUE;
+       }
+
+       ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, gs->item_position,
+                    GDI_END);
+
+       gi->state = GD_BUTTON_UNPRESSED;
+       gi->event.type = GD_EVENT_MOVING;
+       gi->event.off_borders = FALSE;
+
+       if (gi->event_mask & GD_EVENT_MOVING && changed_position)
+         gi->callback_action(gi);
+
+       /* don't handle this scrollbar anymore while mouse button pressed */
+       last_gi = NULL;
+
+       return;
+      }
+    }
+
+    DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+
+    gi->state = GD_BUTTON_PRESSED;
+    gi->event.type = GD_EVENT_PRESSED;
+    gi->event.button = button;
+    gi->event.off_borders = FALSE;
+
+    /* initialize delay counter */
+    DelayReached(&pressed_delay, 0);
+
+    if (gi->event_mask & GD_EVENT_PRESSED)
+      gi->callback_action(gi);
+  }
+
+  if (gadget_pressed_repeated)
+  {
+    if (gi->event_mask & GD_EVENT_REPEATED &&
+       DelayReached(&pressed_delay, GADGET_FRAME_DELAY))
+      gi->callback_action(gi);
+  }
+
+  if (gadget_moving)
+  {
+    if (gi->type & GD_TYPE_BUTTON)
+    {
+      if (gadget_moving_inside && gi->state == GD_BUTTON_UNPRESSED)
+       DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+      else if (gadget_moving_off_borders && gi->state == GD_BUTTON_PRESSED)
+       DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
+    }
+
+    if (gi->type & GD_TYPE_SCROLLBAR)
+    {
+      struct GadgetScrollbar *gs = &gi->scrollbar;
+      int old_item_position = gs->item_position;
+
+      gs->position = scrollbar_mouse_pos - gs->drag_position;
+
+      if (gs->position < 0)
+       gs->position = 0;
+      if (gs->position > gs->position_max)
+       gs->position = gs->position_max;
+
+      gs->item_position = gs->items_max * gs->position / gs->size_max;
+
+      if (old_item_position != gs->item_position)
+      {
+       gi->event.item_position = gs->item_position;
+       changed_position = TRUE;
+      }
+
+      DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+    }
+
+    gi->state = (gadget_moving_inside || gi->type & GD_TYPE_SCROLLBAR ?
+                GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
+    gi->event.type = GD_EVENT_MOVING;
+    gi->event.off_borders = gadget_moving_off_borders;
+
+    if (gi->event_mask & GD_EVENT_MOVING && changed_position &&
+       (gadget_moving_inside || gi->event_mask & GD_EVENT_OFF_BORDERS))
+      gi->callback_action(gi);
+  }
+
+  if (gadget_released_inside)
+  {
+    if (!(gi->type & GD_TYPE_TEXTINPUT))
+      DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
+
+    gi->state = GD_BUTTON_UNPRESSED;
+    gi->event.type = GD_EVENT_RELEASED;
+
+    if (gi->event_mask & GD_EVENT_RELEASED)
+      gi->callback_action(gi);
+  }
+
+  if (gadget_released_off_borders)
+  {
+    if (gi->type & GD_TYPE_SCROLLBAR)
+      DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
+
+    gi->event.type = GD_EVENT_RELEASED;
+
+    if (gi->event_mask & GD_EVENT_RELEASED &&
+       gi->event_mask & GD_EVENT_OFF_BORDERS)
+      gi->callback_action(gi);
+  }
+}
+
+void HandleGadgetsKeyInput(KeySym key)
+{
+  struct GadgetInfo *gi = last_gi;
+  char text[MAX_GADGET_TEXTSIZE];
+  int text_length;
+  int cursor_pos;
+  char letter;
+  boolean legal_letter;
+
+  if (gi == NULL || !(gi->type & GD_TYPE_TEXTINPUT) || !gi->mapped)
+    return;
+
+  text_length = strlen(gi->text.value);
+  cursor_pos = gi->text.cursor_position;
+  letter = getCharFromKeySym(key);
+  legal_letter = (gi->type == GD_TYPE_TEXTINPUT_NUMERIC ?
+                 letter >= '0' && letter <= '9' :
+                 letter != 0);
+
+  if (legal_letter && text_length < gi->text.size)
+  {
+    strcpy(text, gi->text.value);
+    strcpy(&gi->text.value[cursor_pos + 1], &text[cursor_pos]);
+    gi->text.value[cursor_pos] = letter;
+    gi->text.cursor_position++;
+    DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+  }
+  else if (key == XK_Left && cursor_pos > 0)
+  {
+    gi->text.cursor_position--;
+    DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+  }
+  else if (key == XK_Right && cursor_pos < text_length)
+  {
+    gi->text.cursor_position++;
+    DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+  }
+  else if (key == XK_BackSpace && cursor_pos > 0)
+  {
+    strcpy(text, gi->text.value);
+    strcpy(&gi->text.value[cursor_pos - 1], &text[cursor_pos]);
+    gi->text.cursor_position--;
+    DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+  }
+  else if (key == XK_Delete && cursor_pos < text_length)
+  {
+    strcpy(text, gi->text.value);
+    strcpy(&gi->text.value[cursor_pos], &text[cursor_pos + 1]);
+    DrawGadget(gi, DG_PRESSED, DG_DIRECT);
+  }
+  else if (key == XK_Return)
+  {
+    CheckRangeOfNumericInputGadget(gi);
+    DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
+
+    if (gi->event_mask & GD_EVENT_TEXT_RETURN)
+      gi->callback_action(gi);
+
+    last_gi = NULL;
+  }
+}
index 8111aaebcb543040e24b1ace624abf134b094667..fcd23247594809e78f286a43a1f93032c48b4e69 100644 (file)
 #define ED_BUTTON_EDOWN_Y2POS  165
 #define ED_BUTTON_ELEM_Y2POS   190
 
+#define ED_CURSORBUTTON_XSIZE  30
+#define ED_CURSORBUTTON_YSIZE  20
+
 #define ED_BUTTON_CTRL_XPOS    5
 #define ED_BUTTON_CTRL_YPOS    5
 #define ED_BUTTON_CTRL_XSIZE   90
 #define ED_BUTTON_FILL_YSIZE   20
 #define ED_BUTTON_LEFT_XPOS    5
 #define ED_BUTTON_LEFT_YPOS    65
-#define ED_BUTTON_LEFT_XSIZE   30
-#define ED_BUTTON_LEFT_YSIZE   20
+#define ED_BUTTON_LEFT_XSIZE   ED_CURSORBUTTON_XSIZE
+#define ED_BUTTON_LEFT_YSIZE   ED_CURSORBUTTON_YSIZE
 #define ED_BUTTON_UP_XPOS      35
 #define ED_BUTTON_UP_YPOS      55
-#define ED_BUTTON_UP_XSIZE     30
-#define ED_BUTTON_UP_YSIZE     20
+#define ED_BUTTON_UP_XSIZE     ED_CURSORBUTTON_XSIZE
+#define ED_BUTTON_UP_YSIZE     ED_CURSORBUTTON_YSIZE
 #define ED_BUTTON_DOWN_XPOS    35
 #define ED_BUTTON_DOWN_YPOS    75
-#define ED_BUTTON_DOWN_XSIZE   30
-#define ED_BUTTON_DOWN_YSIZE   20
+#define ED_BUTTON_DOWN_XSIZE   ED_CURSORBUTTON_XSIZE
+#define ED_BUTTON_DOWN_YSIZE   ED_CURSORBUTTON_YSIZE
 #define ED_BUTTON_RIGHT_XPOS   65
 #define ED_BUTTON_RIGHT_YPOS   65
-#define ED_BUTTON_RIGHT_XSIZE  30
-#define ED_BUTTON_RIGHT_YSIZE  20
+#define ED_BUTTON_RIGHT_XSIZE  ED_CURSORBUTTON_XSIZE
+#define ED_BUTTON_RIGHT_YSIZE  ED_CURSORBUTTON_YSIZE
 
 #define ED_BUTTON_EDIT_XPOS    5
 #define ED_BUTTON_EDIT_YPOS    5
 #define ED_BUTTON_EXIT_XSIZE   90
 #define ED_BUTTON_EXIT_YSIZE   20
 
+#define ED_BUTTON_COUNT_YPOS   60
+#define ED_BUTTON_COUNT_XSIZE  20
+#define ED_BUTTON_COUNT_YSIZE  20
 #define ED_BUTTON_MINUS_XPOS   2
-#define ED_BUTTON_MINUS_YPOS   60
-#define ED_BUTTON_MINUS_XSIZE  20
-#define ED_BUTTON_MINUS_YSIZE  20
+#define ED_BUTTON_MINUS_YPOS   ED_BUTTON_COUNT_YPOS
+#define ED_BUTTON_MINUS_XSIZE  ED_BUTTON_COUNT_XSIZE
+#define ED_BUTTON_MINUS_YSIZE  ED_BUTTON_COUNT_YSIZE
 #define ED_WIN_COUNT_XPOS      (ED_BUTTON_MINUS_XPOS+ED_BUTTON_MINUS_XSIZE+2)
-#define ED_WIN_COUNT_YPOS      ED_BUTTON_MINUS_YPOS
+#define ED_WIN_COUNT_YPOS      ED_BUTTON_COUNT_YPOS
 #define ED_WIN_COUNT_XSIZE     52
-#define ED_WIN_COUNT_YSIZE     ED_BUTTON_MINUS_YSIZE
+#define ED_WIN_COUNT_YSIZE     ED_BUTTON_COUNT_YSIZE
 #define ED_BUTTON_PLUS_XPOS    (ED_WIN_COUNT_XPOS+ED_WIN_COUNT_XSIZE+2)
-#define ED_BUTTON_PLUS_YPOS    ED_BUTTON_MINUS_YPOS
-#define ED_BUTTON_PLUS_XSIZE   ED_BUTTON_MINUS_XSIZE
-#define ED_BUTTON_PLUS_YSIZE   ED_BUTTON_MINUS_YSIZE
+#define ED_BUTTON_PLUS_YPOS    ED_BUTTON_COUNT_YPOS
+#define ED_BUTTON_PLUS_XSIZE   ED_BUTTON_COUNT_XSIZE
+#define ED_BUTTON_PLUS_YSIZE   ED_BUTTON_COUNT_YSIZE
 
 #define ED_COUNT_GADGET_XPOS   16
 #define ED_COUNT_GADGET_YPOS   (16+3*MINI_TILEY+64)
@@ -264,4 +270,175 @@ int CheckCtrlButtons(int, int, int);
 int CheckElemButtons(int, int, int);
 int CheckCountButtons(int, int, int);
 
+
+/* NEW GADGET STUFF -------------------------------------------------------- */
+
+
+/* gadget types */
+#define GD_TYPE_NORMAL_BUTTON          (1 << 0)
+#define GD_TYPE_CHECK_BUTTON           (1 << 1)
+#define GD_TYPE_RADIO_BUTTON           (1 << 2)
+#define GD_TYPE_DRAWING_AREA           (1 << 3)
+#define GD_TYPE_TEXTINPUT_ALPHANUMERIC (1 << 4)
+#define GD_TYPE_TEXTINPUT_NUMERIC      (1 << 5)
+#define GD_TYPE_SCROLLBAR_VERTICAL     (1 << 6)
+#define GD_TYPE_SCROLLBAR_HORIZONTAL   (1 << 7)
+
+#define GD_TYPE_BUTTON                 (GD_TYPE_NORMAL_BUTTON | \
+                                        GD_TYPE_CHECK_BUTTON | \
+                                        GD_TYPE_RADIO_BUTTON)
+#define GD_TYPE_SCROLLBAR              (GD_TYPE_SCROLLBAR_VERTICAL | \
+                                        GD_TYPE_SCROLLBAR_HORIZONTAL)
+#define GD_TYPE_TEXTINPUT              (GD_TYPE_TEXTINPUT_ALPHANUMERIC | \
+                                        GD_TYPE_TEXTINPUT_NUMERIC)
+
+/* gadget events */
+#define GD_EVENT_PRESSED               (1 << 0)
+#define GD_EVENT_RELEASED              (1 << 1)
+#define GD_EVENT_MOVING                        (1 << 2)
+#define GD_EVENT_REPEATED              (1 << 3)
+#define GD_EVENT_OFF_BORDERS           (1 << 4)
+#define GD_EVENT_TEXT_RETURN           (1 << 5)
+#define GD_EVENT_TEXT_LEAVING          (1 << 6)
+
+/* gadget button states */
+#define GD_BUTTON_UNPRESSED            0
+#define GD_BUTTON_PRESSED              1
+
+/* gadget structure constants */
+#define MAX_GADGET_TEXTSIZE            1024
+#define MAX_INFO_TEXTSIZE              1024
+
+/* gadget creation tags */
+#define GDI_END                                0
+#define GDI_CUSTOM_ID                  1
+#define GDI_X                          2
+#define GDI_Y                          3
+#define GDI_WIDTH                      4
+#define GDI_HEIGHT                     5
+#define GDI_TYPE                       6
+#define GDI_STATE                      7
+#define GDI_CHECKED                    8
+#define GDI_RADIO_NR                   9
+#define GDI_NUMBER_VALUE               10
+#define GDI_NUMBER_MIN                 11
+#define GDI_NUMBER_MAX                 12
+#define GDI_TEXT_VALUE                 13
+#define GDI_TEXT_SIZE                  14
+#define GDI_DESIGN_UNPRESSED           15
+#define GDI_DESIGN_PRESSED             16
+#define GDI_ALT_DESIGN_UNPRESSED       17
+#define GDI_ALT_DESIGN_PRESSED         18
+#define GDI_DESIGN_BORDER              19
+#define GDI_DECORATION_DESIGN          20
+#define GDI_DECORATION_POSITION                22
+#define GDI_DECORATION_SIZE            21
+#define GDI_DECORATION_SHIFTING                23
+#define GDI_EVENT_MASK                 24
+#define GDI_EVENT                      25
+#define GDI_CALLBACK_INFO              26
+#define GDI_CALLBACK_ACTION            27
+#define GDI_AREA_SIZE                  28
+#define GDI_ITEM_SIZE                  29
+#define GDI_SCROLLBAR_ITEMS_MAX                30
+#define GDI_SCROLLBAR_ITEMS_VISIBLE    31
+#define GDI_SCROLLBAR_ITEM_POSITION    32
+#define GDI_INFO_TEXT                  33
+
+typedef void (*gadget_function)(void *);
+
+struct GadgetDesign
+{
+  Pixmap pixmap;                       /* Pixmap with gadget surface */
+  int x, y;                            /* position of rectangle in Pixmap */
+};
+
+struct GadgetDecoration
+{
+  struct GadgetDesign design;          /* decoration design structure */
+  int x, y;                            /* position of deco on the gadget */
+  int width, height;                   /* width and height of decoration */
+  int xshift, yshift;                  /* deco shifting when gadget pressed */
+};
+
+struct GadgetEvent
+{
+  unsigned long type;                  /* event type */
+  int button;                          /* button number for button events */
+  int x, y;                            /* gadget position at event time */
+  boolean off_borders;                 /* mouse pointer outside gadget? */
+  int item_x, item_y, item_position;   /* new item position */
+};
+
+struct GadgetDrawingArea
+{
+  int area_xsize, area_ysize;          /* size of drawing area (in items) */
+  int item_xsize, item_ysize;          /* size of each item in drawing area */
+};
+
+struct GadgetTextInput
+{
+  char value[MAX_GADGET_TEXTSIZE];     /* text string in input field */
+  int number_value;                    /* integer value, if numeric */
+  int number_min;                      /* minimal allowed numeric value */
+  int number_max;                      /* maximal allowed numeric value */
+  int size;                            /* maximal size of input text */
+  int cursor_position;                 /* actual cursor position */
+};
+
+struct GadgetScrollbar
+{
+  int items_max;                       /* number of items to access */
+  int items_visible;                   /* number of visible items */
+  int item_position;                   /* actual item position */
+  int size_max;                                /* this is either width or height */
+  int size;                            /* scrollbar size on screen */
+  int position;                                /* scrollbar position on screen */
+  int position_max;                    /* bottom/right scrollbar position */
+  int drag_position;                   /* drag position on scrollbar */
+};
+
+struct GadgetInfo
+{
+  int id;                              /* internal gadget identifier */
+  int custom_id;                       /* custom gadget identifier */
+  char info_text[MAX_INFO_TEXTSIZE];   /* short popup info text */
+  int x, y;                            /* gadget position */
+  int width, height;                   /* gadget size */
+  unsigned long type;                  /* type (button, text input, ...) */
+  unsigned long state;                 /* state (pressed, released, ...) */
+  boolean checked;                     /* check/radio button state */
+  int radio_nr;                                /* number of radio button series */
+  boolean mapped;                      /* gadget is active */
+  struct GadgetDesign design[2];       /* 0: normal; 1: pressed */
+  struct GadgetDesign alt_design[2];   /* alternative design */
+  struct GadgetDecoration deco;                /* decoration on top of gadget */
+  int design_border;                   /* border size of gadget decoration */
+  unsigned long event_mask;            /* possible events for this gadget */
+  struct GadgetEvent event;            /* actual gadget event */
+  gadget_function callback_info;       /* function for pop-up info text */
+  gadget_function callback_action;     /* function for gadget action */
+  struct GadgetDrawingArea drawing;    /* fields for drawing area gadget */
+  struct GadgetTextInput text;         /* fields for text input gadget */
+  struct GadgetScrollbar scrollbar;    /* fields for scrollbar gadget */
+  struct GadgetInfo *next;             /* next list entry */
+};
+
+struct GadgetInfo *CreateGadget(int, ...);
+void FreeGadget(struct GadgetInfo *);
+
+void ModifyGadget(struct GadgetInfo *, int, ...);
+void RedrawGadget(struct GadgetInfo *);
+
+void MapGadget(struct GadgetInfo *);
+void UnmapGadget(struct GadgetInfo *);
+void UnmapAllGadgets();
+void RemapAllGadgets();
+
+boolean anyTextGadgetActive();
+void ClickOnGadget(struct GadgetInfo *, int);
+
+void HandleGadgets(int, int, int);
+void HandleGadgetsKeyInput(KeySym);
+
 #endif
index 834996b73c53ff4a49d594226fc40ee6de9f5dcb..bd00d1af01a6a04bc2dc9906a8dca6465a25f724 100644 (file)
@@ -462,10 +462,12 @@ boolean AnimateToon(int toon_nr, boolean restart)
 
   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);
+    if ((game_status == HELPSCREEN ||
+        (game_status == MAINMENU && redraw_mask & REDRAW_MICROLEVEL))
+       && !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);
   }
index 2955faecfba6787249536d83bcab1ccae933c9d3..ab28fe0cf4d002a8d4760c7243992b925f634650 100644 (file)
 *  editor.c                                                *
 ***********************************************************/
 
+#include <math.h>
+
 #include "editor.h"
 #include "screens.h"
 #include "tools.h"
 #include "misc.h"
 #include "buttons.h"
 #include "files.h"
+#include "game.h"
+#include "tape.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
+#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 boolean edit_mode;
-static boolean name_typing;
+#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
+
+/* screens in the level editor */
+#define ED_MODE_DRAWING                        0
+#define ED_MODE_INFO                   1
+#define ED_MODE_PROPERTIES             2
+
+/* how many steps can be cancelled */
+#define NUM_UNDO_STEPS                 (10 + 1)
+
+/* values for elements with score */
+#define MIN_SCORE                      0
+#define MAX_SCORE                      255
+
+/* values for elements with content */
+#define MIN_ELEM_CONTENT               1
+#define MAX_ELEM_CONTENT               8
+
+/* values for the control window */
+#define ED_CTRL_BUTTONS_GFX_YPOS       236
+#define ED_CTRL_BUTTONS_ALT_GFX_YPOS   142
+
+#define ED_CTRL1_BUTTONS_HORIZ         4
+#define ED_CTRL1_BUTTONS_VERT          4
+#define ED_CTRL1_BUTTON_XSIZE          22
+#define ED_CTRL1_BUTTON_YSIZE          22
+#define ED_CTRL1_BUTTONS_XPOS          6
+#define ED_CTRL1_BUTTONS_YPOS          6
+#define ED_CTRL2_BUTTONS_HORIZ         3
+#define ED_CTRL2_BUTTONS_VERT          2
+#define ED_CTRL2_BUTTON_XSIZE          30
+#define ED_CTRL2_BUTTON_YSIZE          20
+#define ED_CTRL2_BUTTONS_XPOS          5
+#define ED_CTRL2_BUTTONS_YPOS          99
+#define ED_NUM_CTRL1_BUTTONS   (ED_CTRL1_BUTTONS_HORIZ * ED_CTRL1_BUTTONS_VERT)
+#define ED_NUM_CTRL2_BUTTONS   (ED_CTRL2_BUTTONS_HORIZ * ED_CTRL2_BUTTONS_VERT)
+#define ED_NUM_CTRL_BUTTONS    (ED_NUM_CTRL1_BUTTONS + ED_NUM_CTRL2_BUTTONS)
+
+/* values for the element list */
+#define ED_ELEMENTLIST_UP_XPOS         35
+#define ED_ELEMENTLIST_UP_YPOS         5
+#define ED_ELEMENTLIST_UP_ALT_YPOS     140
+#define ED_ELEMENTLIST_DOWN_XPOS       35
+#define ED_ELEMENTLIST_DOWN_YPOS       250
+#define ED_ELEMENTLIST_DOWN_ALT_YPOS   165
+#define ED_ELEMENTLIST_UPDOWN_XSIZE    30
+#define ED_ELEMENTLIST_UPDOWN_YSIZE    25
+#define ED_ELEMENTLIST_XPOS            6
+#define ED_ELEMENTLIST_YPOS            30
+#define ED_ELEMENTLIST_ALT_YPOS                190
+#define ED_ELEMENTLIST_XSIZE           22
+#define ED_ELEMENTLIST_YSIZE           22
+#define ED_ELEMENTLIST_BUTTONS_HORIZ   4
+#define ED_ELEMENTLIST_BUTTONS_VERT    10
+#define ED_NUM_ELEMENTLIST_BUTTONS     (ED_ELEMENTLIST_BUTTONS_HORIZ * \
+                                        ED_ELEMENTLIST_BUTTONS_VERT)
+
+/* values for the setting windows */
+#define ED_SETTINGS_XPOS               (MINI_TILEX + 8)
+#define ED_SETTINGS2_XPOS              MINI_TILEX
+#define ED_SETTINGS_YPOS               MINI_TILEY
+#define ED_SETTINGS2_YPOS              (ED_SETTINGS_YPOS + 12 * TILEY - 2)
+
+/* values for counter gadgets */
+#define ED_COUNT_ELEM_SCORE_XPOS       ED_SETTINGS_XPOS
+#define ED_COUNT_ELEM_SCORE_YPOS       (14 * MINI_TILEY)
+#define ED_COUNT_ELEM_CONTENT_XPOS     ED_SETTINGS_XPOS
+#define ED_COUNT_ELEM_CONTENT_YPOS     (19 * MINI_TILEY)
+
+#define ED_COUNTER_YSTART              (ED_SETTINGS_YPOS + 2 * TILEY)
+#define ED_COUNTER_YDISTANCE           (3 * MINI_TILEY)
+#define ED_COUNTER_YPOS(n)             (ED_COUNTER_YSTART + \
+                                        n * ED_COUNTER_YDISTANCE)
+#define ED_COUNTER2_YPOS(n)            (ED_COUNTER_YSTART + \
+                                        n * ED_COUNTER_YDISTANCE - 2)
+/* standard distances */
+#define ED_BORDER_SIZE                 3
+#define ED_GADGET_DISTANCE             2
+
+/* values for element content drawing areas */
+#define ED_AREA_ELEM_CONTENT_XPOS      ( 2 * MINI_TILEX)
+#define ED_AREA_ELEM_CONTENT_YPOS      (22 * MINI_TILEY)
+
+/* values for random placement background drawing area */
+#define ED_AREA_RANDOM_BACKGROUND_XPOS (29 * MINI_TILEX)
+#define ED_AREA_RANDOM_BACKGROUND_YPOS (31 * MINI_TILEY)
+
+/* values for scrolling gadgets */
+#define ED_SCROLLBUTTON_XPOS           24
+#define ED_SCROLLBUTTON_YPOS           0
+#define ED_SCROLLBAR_XPOS              24
+#define ED_SCROLLBAR_YPOS              64
+
+#define ED_SCROLLBUTTON_XSIZE          16
+#define ED_SCROLLBUTTON_YSIZE          16
+
+#define ED_SCROLL_UP_XPOS              (SXSIZE - ED_SCROLLBUTTON_XSIZE)
+#define ED_SCROLL_UP_YPOS              (0)
+#define ED_SCROLL_DOWN_XPOS            ED_SCROLL_UP_XPOS
+#define ED_SCROLL_DOWN_YPOS            (SYSIZE - 3 * ED_SCROLLBUTTON_YSIZE)
+#define ED_SCROLL_LEFT_XPOS            (0)
+#define ED_SCROLL_LEFT_YPOS            (SYSIZE - 2 * ED_SCROLLBUTTON_YSIZE)
+#define ED_SCROLL_RIGHT_XPOS           (SXSIZE - 2 * ED_SCROLLBUTTON_XSIZE)
+#define ED_SCROLL_RIGHT_YPOS           ED_SCROLL_LEFT_YPOS
+#define ED_SCROLL_HORIZONTAL_XPOS (ED_SCROLL_LEFT_XPOS + ED_SCROLLBUTTON_XSIZE)
+#define ED_SCROLL_HORIZONTAL_YPOS      ED_SCROLL_LEFT_YPOS
+#define ED_SCROLL_HORIZONTAL_XSIZE     (SXSIZE - 3 * ED_SCROLLBUTTON_XSIZE)
+#define ED_SCROLL_HORIZONTAL_YSIZE     ED_SCROLLBUTTON_YSIZE
+#define ED_SCROLL_VERTICAL_XPOS                ED_SCROLL_UP_XPOS
+#define ED_SCROLL_VERTICAL_YPOS          (ED_SCROLL_UP_YPOS + ED_SCROLLBUTTON_YSIZE)
+#define ED_SCROLL_VERTICAL_XSIZE       ED_SCROLLBUTTON_XSIZE
+#define ED_SCROLL_VERTICAL_YSIZE       (SYSIZE - 4 * ED_SCROLLBUTTON_YSIZE)
+
+/* values for checkbutton gadgets */
+#define ED_CHECKBUTTON_XSIZE           ED_BUTTON_COUNT_XSIZE
+#define ED_CHECKBUTTON_YSIZE           ED_BUTTON_COUNT_YSIZE
+#define ED_CHECKBUTTON_UNCHECKED_XPOS  ED_BUTTON_MINUS_XPOS
+#define ED_CHECKBUTTON_CHECKED_XPOS    ED_BUTTON_PLUS_XPOS
+#define ED_CHECKBUTTON_YPOS            (ED_BUTTON_MINUS_YPOS + 22)
+#define ED_STICKYBUTTON_YPOS           (ED_BUTTON_MINUS_YPOS + 88)
+
+#define GADGET_ID_NONE                 -1
+
+/* drawing toolbox buttons */
+#define GADGET_ID_SINGLE_ITEMS         0
+#define GADGET_ID_CONNECTED_ITEMS      1
+#define GADGET_ID_LINE                 2
+#define GADGET_ID_ARC                  3
+#define GADGET_ID_RECTANGLE            4
+#define GADGET_ID_FILLED_BOX           5
+#define GADGET_ID_WRAP_UP              6
+#define GADGET_ID_TEXT                 7
+#define GADGET_ID_FLOOD_FILL           8
+#define GADGET_ID_WRAP_LEFT            9
+#define GADGET_ID_PROPERTIES           10
+#define GADGET_ID_WRAP_RIGHT           11
+#define GADGET_ID_RANDOM_PLACEMENT     12
+#define GADGET_ID_GRAB_BRUSH           13
+#define GADGET_ID_WRAP_DOWN            14
+#define GADGET_ID_PICK_ELEMENT         15
+#define GADGET_ID_UNDO                 16
+#define GADGET_ID_INFO                 17
+#define GADGET_ID_SAVE                 18
+#define GADGET_ID_CLEAR                        19
+#define GADGET_ID_TEST                 20
+#define GADGET_ID_EXIT                 21
+
+/* counter button identifiers */
+#define GADGET_ID_ELEM_SCORE_DOWN      22
+#define GADGET_ID_ELEM_SCORE_TEXT      23
+#define GADGET_ID_ELEM_SCORE_UP                24
+#define GADGET_ID_ELEM_CONTENT_DOWN    25
+#define GADGET_ID_ELEM_CONTENT_TEXT    26
+#define GADGET_ID_ELEM_CONTENT_UP      27
+#define GADGET_ID_LEVEL_XSIZE_DOWN     28
+#define GADGET_ID_LEVEL_XSIZE_TEXT     29
+#define GADGET_ID_LEVEL_XSIZE_UP       30
+#define GADGET_ID_LEVEL_YSIZE_DOWN     31
+#define GADGET_ID_LEVEL_YSIZE_TEXT     32
+#define GADGET_ID_LEVEL_YSIZE_UP       33
+#define GADGET_ID_LEVEL_RANDOM_DOWN    34
+#define GADGET_ID_LEVEL_RANDOM_TEXT    35
+#define GADGET_ID_LEVEL_RANDOM_UP      36
+#define GADGET_ID_LEVEL_COLLECT_DOWN   37
+#define GADGET_ID_LEVEL_COLLECT_TEXT   38
+#define GADGET_ID_LEVEL_COLLECT_UP     39
+#define GADGET_ID_LEVEL_TIMELIMIT_DOWN 40
+#define GADGET_ID_LEVEL_TIMELIMIT_TEXT 41
+#define GADGET_ID_LEVEL_TIMELIMIT_UP   42
+#define GADGET_ID_LEVEL_TIMESCORE_DOWN 43
+#define GADGET_ID_LEVEL_TIMESCORE_TEXT 44
+#define GADGET_ID_LEVEL_TIMESCORE_UP   45
+
+/* drawing area identifiers */
+#define GADGET_ID_DRAWING_LEVEL        46
+#define GADGET_ID_ELEM_CONTENT_0       47
+#define GADGET_ID_ELEM_CONTENT_1       48
+#define GADGET_ID_ELEM_CONTENT_2       49
+#define GADGET_ID_ELEM_CONTENT_3       50
+#define GADGET_ID_ELEM_CONTENT_4       51
+#define GADGET_ID_ELEM_CONTENT_5       52
+#define GADGET_ID_ELEM_CONTENT_6       53
+#define GADGET_ID_ELEM_CONTENT_7       54
+#define GADGET_ID_AMOEBA_CONTENT       55
+
+/* text input identifiers */
+#define GADGET_ID_LEVEL_NAME           56
+#define GADGET_ID_LEVEL_AUTHOR         57
+
+/* gadgets for scrolling of drawing area */
+#define GADGET_ID_SCROLL_UP            58
+#define GADGET_ID_SCROLL_DOWN          59
+#define GADGET_ID_SCROLL_LEFT          60
+#define GADGET_ID_SCROLL_RIGHT         61
+#define GADGET_ID_SCROLL_HORIZONTAL    62
+#define GADGET_ID_SCROLL_VERTICAL      63
+
+/* gadgets for scrolling element list */
+#define GADGET_ID_ELEMENTLIST_UP       64
+#define GADGET_ID_ELEMENTLIST_DOWN     65
+
+/* gadgets for buttons in element list */
+#define GADGET_ID_ELEMENTLIST_FIRST    66
+#define GADGET_ID_ELEMENTLIST_LAST     105
+
+/* buttons for level settings */
+#define GADGET_ID_RANDOM_PERCENTAGE    106
+#define GADGET_ID_RANDOM_QUANTITY      107
+#define GADGET_ID_RANDOM_RESTRICTED    108
+#define GADGET_ID_DOUBLE_SPEED         109
+#define GADGET_ID_STICK_ELEMENT                110
+
+/* another drawing area for random placement */
+#define GADGET_ID_RANDOM_BACKGROUND    111
+
+#define NUM_EDITOR_GADGETS             112
+
+/* radio button numbers */
+#define RADIO_NR_NONE                  0
+#define RADIO_NR_DRAWING_TOOLBOX       1
+#define RADIO_NR_RANDOM_ELEMENTS       2
+
+/* values for counter gadgets */
+#define ED_COUNTER_ID_ELEM_SCORE       0
+#define ED_COUNTER_ID_ELEM_CONTENT     1
+#define ED_COUNTER_ID_LEVEL_XSIZE      2
+#define ED_COUNTER_ID_LEVEL_YSIZE      3
+#define ED_COUNTER_ID_LEVEL_COLLECT    4
+#define ED_COUNTER_ID_LEVEL_TIMELIMIT  5
+#define ED_COUNTER_ID_LEVEL_TIMESCORE  6
+#define ED_COUNTER_ID_LEVEL_RANDOM     7
+
+#define ED_NUM_COUNTERBUTTONS          8
+
+#define ED_COUNTER_ID_LEVEL_FIRST      ED_COUNTER_ID_LEVEL_XSIZE
+#define ED_COUNTER_ID_LEVEL_LAST       ED_COUNTER_ID_LEVEL_RANDOM
+
+/* values for scrollbutton gadgets */
+#define ED_SCROLLBUTTON_ID_AREA_UP     0
+#define ED_SCROLLBUTTON_ID_AREA_DOWN   1
+#define ED_SCROLLBUTTON_ID_AREA_LEFT   2
+#define ED_SCROLLBUTTON_ID_AREA_RIGHT  3
+#define ED_SCROLLBUTTON_ID_LIST_UP     4
+#define ED_SCROLLBUTTON_ID_LIST_DOWN   5
+
+#define ED_NUM_SCROLLBUTTONS           6
+
+/* values for scrollbar gadgets */
+#define ED_SCROLLBAR_ID_HORIZONTAL     0
+#define ED_SCROLLBAR_ID_VERTICAL       1
+
+#define ED_NUM_SCROLLBARS              2
+
+/* values for text input gadgets */
+#define ED_TEXTINPUT_ID_LEVEL_NAME     0
+#define ED_TEXTINPUT_ID_LEVEL_AUTHOR   1
+
+#define ED_NUM_TEXTINPUT               2
+
+#define ED_TEXTINPUT_ID_LEVEL_FIRST    ED_TEXTINPUT_ID_LEVEL_NAME
+#define ED_TEXTINPUT_ID_LEVEL_LAST     ED_TEXTINPUT_ID_LEVEL_AUTHOR
+
+/* values for checkbutton gadgets */
+#define ED_CHECKBUTTON_ID_DOUBLE_SPEED         0
+#define ED_CHECKBUTTON_ID_RANDOM_RESTRICTED    1
+#define ED_CHECKBUTTON_ID_STICK_ELEMENT                2
+
+#define ED_NUM_CHECKBUTTONS                    3
+
+#define ED_CHECKBUTTON_ID_LEVEL_FIRST  ED_CHECKBUTTON_ID_DOUBLE_SPEED
+#define ED_CHECKBUTTON_ID_LEVEL_LAST   ED_CHECKBUTTON_ID_RANDOM_RESTRICTED
+
+/* values for radiobutton gadgets */
+#define ED_RADIOBUTTON_ID_PERCENTAGE   0
+#define ED_RADIOBUTTON_ID_QUANTITY             1
+
+#define ED_NUM_RADIOBUTTONS            2
+
+#define ED_RADIOBUTTON_ID_LEVEL_FIRST  ED_RADIOBUTTON_ID_PERCENTAGE
+#define ED_RADIOBUTTON_ID_LEVEL_LAST   ED_RADIOBUTTON_ID_QUANTITY
+
+/* values for CopyLevelToUndoBuffer() */
+#define UNDO_IMMEDIATE                 0
+#define UNDO_ACCUMULATE                        1
+
+/* values for ClearEditorGadgetInfoText() and HandleGadgetInfoText() */
+#define INFOTEXT_XPOS                  SX
+#define INFOTEXT_YPOS                  (SY + SYSIZE - MINI_TILEX + 2)
+#define INFOTEXT_XSIZE                 SXSIZE
+#define INFOTEXT_YSIZE                 MINI_TILEX
+#define MAX_INFOTEXT_LEN               (SXSIZE / FONT2_XSIZE)
+
+static struct
+{
+  char shortcut;
+  char *text;
+} control_info[ED_NUM_CTRL_BUTTONS] =
+{
+  { 's', "draw single items" },
+  { 'd', "draw connected items" },
+  { 'l', "draw lines" },
+  { 'a', "draw arcs" },
+  { 'r', "draw outline rectangles" },
+  { 'R', "draw filled rectangles" },
+  { '\0', "wrap (rotate) level up" },
+  { 't', "enter text elements" },
+  { 'f', "flood fill" },
+  { '\0', "wrap (rotate) level left" },
+  { '?', "properties of drawing element" },
+  { '\0', "wrap (rotate) level right" },
+  { '\0', "random element placement" },
+  { 'b', "grab brush" },
+  { '\0', "wrap (rotate) level down" },
+  { ',', "pick drawing element" },
+  { 'U', "undo last operation" },
+  { 'I', "level properties" },
+  { 'S', "save level" },
+  { 'C', "clear level" },
+  { 'T', "test level" },
+  { 'E', "exit level editor" }
+};
+
+/* pointers to counter values */
+static int *gadget_elem_score_value = NULL;
+static int *gadget_elem_content_value = NULL;
+static int *gadget_level_xsize_value = NULL;
+static int *gadget_level_ysize_value = NULL;
+static int *gadget_level_random_value = NULL;
+static int *gadget_level_collect_value = NULL;
+static int *gadget_level_timelimit_value = NULL;
+static int *gadget_level_timescore_value = NULL;
+
+static struct
+{
+  int x, y;
+  int min_value, max_value;
+  int gadget_id_down, gadget_id_up;
+  int gadget_id_text;
+  int **counter_value;
+  char *infotext_above, *infotext_right;
+} counterbutton_info[ED_NUM_COUNTERBUTTONS] =
+{
+  {
+    ED_COUNT_ELEM_SCORE_XPOS,          ED_COUNT_ELEM_SCORE_YPOS,
+    MIN_SCORE,                         MAX_SCORE,
+    GADGET_ID_ELEM_SCORE_DOWN,         GADGET_ID_ELEM_SCORE_UP,
+    GADGET_ID_ELEM_SCORE_TEXT,
+    &gadget_elem_score_value,
+    "element score",                   NULL
+  },
+  {
+    ED_COUNT_ELEM_CONTENT_XPOS,                ED_COUNT_ELEM_CONTENT_YPOS,
+    MIN_ELEM_CONTENT,                  MAX_ELEM_CONTENT,
+    GADGET_ID_ELEM_CONTENT_DOWN,       GADGET_ID_ELEM_CONTENT_UP,
+    GADGET_ID_ELEM_CONTENT_TEXT,
+    &gadget_elem_content_value,
+    "element content",                 NULL
+  },
+  {
+    ED_SETTINGS_XPOS,                  ED_COUNTER_YPOS(2),
+    MIN_LEV_FIELDX,                    MAX_LEV_FIELDX,
+    GADGET_ID_LEVEL_XSIZE_DOWN,                GADGET_ID_LEVEL_XSIZE_UP,
+    GADGET_ID_LEVEL_XSIZE_TEXT,
+    &gadget_level_xsize_value,
+    "playfield size",                  "width",
+  },
+  {
+    ED_SETTINGS_XPOS + 2 * DXSIZE,     ED_COUNTER_YPOS(2),
+    MIN_LEV_FIELDY,                    MAX_LEV_FIELDY,
+    GADGET_ID_LEVEL_YSIZE_DOWN,                GADGET_ID_LEVEL_YSIZE_UP,
+    GADGET_ID_LEVEL_YSIZE_TEXT,
+    &gadget_level_ysize_value,
+    NULL,                              "height",
+  },
+  {
+    ED_SETTINGS_XPOS,                  ED_COUNTER_YPOS(3),
+    0,                                 999,
+    GADGET_ID_LEVEL_COLLECT_DOWN,      GADGET_ID_LEVEL_COLLECT_UP,
+    GADGET_ID_LEVEL_COLLECT_TEXT,
+    &gadget_level_collect_value,
+    "number of emeralds to collect",   NULL
+  },
+  {
+    ED_SETTINGS_XPOS,                  ED_COUNTER_YPOS(4),
+    0,                                 999,
+    GADGET_ID_LEVEL_TIMELIMIT_DOWN,    GADGET_ID_LEVEL_TIMELIMIT_UP,
+    GADGET_ID_LEVEL_TIMELIMIT_TEXT,
+    &gadget_level_timelimit_value,
+    "time available to solve level",   "(0 => no time limit)"
+  },
+  {
+    ED_SETTINGS_XPOS,                  ED_COUNTER_YPOS(5),
+    0,                                 255,
+    GADGET_ID_LEVEL_TIMESCORE_DOWN,    GADGET_ID_LEVEL_TIMESCORE_UP,
+    GADGET_ID_LEVEL_TIMESCORE_TEXT,
+    &gadget_level_timescore_value,
+    "score for each 10 seconds left",  NULL
+  },
+  {
+    ED_SETTINGS_XPOS,                  ED_COUNTER2_YPOS(8),
+    1,                                 100,
+    GADGET_ID_LEVEL_RANDOM_DOWN,       GADGET_ID_LEVEL_RANDOM_UP,
+    GADGET_ID_LEVEL_RANDOM_TEXT,
+    &gadget_level_random_value,
+    "random element placement",                "in"
+  }
+};
+
+static struct
+{
+  int x, y;
+  int gadget_id;
+  int size;
+  char *value;
+  char *infotext;
+} textinput_info[ED_NUM_TEXTINPUT] =
+{
+  {
+    ED_SETTINGS_XPOS,                  ED_COUNTER_YPOS(0),
+    GADGET_ID_LEVEL_NAME,
+    MAX_LEVEL_NAME_LEN,
+    level.name,
+    "Title"
+  },
+  {
+    ED_SETTINGS_XPOS,                  ED_COUNTER_YPOS(1),
+    GADGET_ID_LEVEL_AUTHOR,
+    MAX_LEVEL_AUTHOR_LEN,
+    level.author,
+    "Author"
+  }
+};
+
+static struct
+{
+  int xpos, ypos;
+  int x, y;
+  int gadget_id;
+  char *infotext;
+} scrollbutton_info[ED_NUM_SCROLLBUTTONS] =
+{
+  {
+    ED_SCROLLBUTTON_XPOS,   ED_SCROLLBUTTON_YPOS + 0 * ED_SCROLLBUTTON_YSIZE,
+    ED_SCROLL_UP_XPOS,      ED_SCROLL_UP_YPOS,
+    GADGET_ID_SCROLL_UP,
+    "scroll level editing area up"
+  },
+  {
+    ED_SCROLLBUTTON_XPOS,   ED_SCROLLBUTTON_YPOS + 1 * ED_SCROLLBUTTON_YSIZE,
+    ED_SCROLL_DOWN_XPOS,    ED_SCROLL_DOWN_YPOS,
+    GADGET_ID_SCROLL_DOWN,
+    "scroll level editing area down"
+  },
+  {
+    ED_SCROLLBUTTON_XPOS,   ED_SCROLLBUTTON_YPOS + 2 * ED_SCROLLBUTTON_YSIZE,
+    ED_SCROLL_LEFT_XPOS,    ED_SCROLL_LEFT_YPOS,
+    GADGET_ID_SCROLL_LEFT,
+    "scroll level editing area left"
+  },
+  {
+    ED_SCROLLBUTTON_XPOS,   ED_SCROLLBUTTON_YPOS + 3 * ED_SCROLLBUTTON_YSIZE,
+    ED_SCROLL_RIGHT_XPOS,   ED_SCROLL_RIGHT_YPOS,
+    GADGET_ID_SCROLL_RIGHT,
+    "scroll level editing area right"
+  },
+  {
+    ED_ELEMENTLIST_UP_XPOS, ED_ELEMENTLIST_UP_ALT_YPOS,
+    ED_ELEMENTLIST_UP_XPOS, ED_ELEMENTLIST_UP_YPOS,
+    GADGET_ID_ELEMENTLIST_UP,
+    "scroll element list up ('Page Up')"
+  },
+  {
+    ED_ELEMENTLIST_DOWN_XPOS, ED_ELEMENTLIST_DOWN_ALT_YPOS,
+    ED_ELEMENTLIST_DOWN_XPOS, ED_ELEMENTLIST_DOWN_YPOS,
+    GADGET_ID_ELEMENTLIST_DOWN,
+    "scroll element list down ('Page Down')"
+  }
+};
+
+static struct
+{
+  int xpos, ypos;
+  int x, y;
+  int width, height;
+  int type;
+  int gadget_id;
+  char *infotext;
+} scrollbar_info[ED_NUM_SCROLLBARS] =
+{
+  {
+    ED_SCROLLBAR_XPOS,         ED_SCROLLBAR_YPOS,
+    ED_SCROLL_HORIZONTAL_XPOS, ED_SCROLL_HORIZONTAL_YPOS,
+    ED_SCROLL_HORIZONTAL_XSIZE,        ED_SCROLL_HORIZONTAL_YSIZE,
+    GD_TYPE_SCROLLBAR_HORIZONTAL,
+    GADGET_ID_SCROLL_HORIZONTAL,
+    "scroll level editing area horizontally"
+  },
+  {
+    ED_SCROLLBAR_XPOS,         ED_SCROLLBAR_YPOS,
+    ED_SCROLL_VERTICAL_XPOS,   ED_SCROLL_VERTICAL_YPOS,
+    ED_SCROLL_VERTICAL_XSIZE,  ED_SCROLL_VERTICAL_YSIZE,
+    GD_TYPE_SCROLLBAR_VERTICAL,
+    GADGET_ID_SCROLL_VERTICAL,
+    "scroll level editing area vertically"
+  }
+};
+
+/* values for random placement */
+#define RANDOM_USE_PERCENTAGE          0
+#define RANDOM_USE_QUANTITY            1
+
+static int random_placement_value = 10;
+static int random_placement_method = RANDOM_USE_QUANTITY;
+static int random_placement_background_element = EL_ERDREICH;
+static boolean random_placement_background_restricted = FALSE;
+static boolean stick_element_properties_window = FALSE;
+
+static struct
+{
+  int x, y;
+  int gadget_id;
+  int radio_button_nr;
+  int *value;
+  int checked_value;
+  char *text, *infotext;
+} radiobutton_info[ED_NUM_RADIOBUTTONS] =
+{
+  {
+    ED_SETTINGS_XPOS + 160,            ED_COUNTER2_YPOS(8),
+    GADGET_ID_RANDOM_PERCENTAGE,
+    RADIO_NR_RANDOM_ELEMENTS,
+    &random_placement_method,          RANDOM_USE_PERCENTAGE,
+    "percentage",                      "use percentage for random elements"
+  },
+  {
+    ED_SETTINGS_XPOS + 340,            ED_COUNTER2_YPOS(8),
+    GADGET_ID_RANDOM_QUANTITY,
+    RADIO_NR_RANDOM_ELEMENTS,
+    &random_placement_method,          RANDOM_USE_QUANTITY,
+    "quantity",                                "use quantity for random elements"
+  }
+};
+
+static struct
+{
+  int x, y;
+  int gadget_id;
+  boolean *value;
+  char *text, *infotext;
+} checkbutton_info[ED_NUM_CHECKBUTTONS] =
+{
+  {
+    ED_SETTINGS_XPOS,                  ED_COUNTER_YPOS(6) - MINI_TILEY,
+    GADGET_ID_DOUBLE_SPEED,
+    &level.double_speed,
+    "double speed movement",           "set movement speed of player"
+  },
+  {
+    ED_SETTINGS_XPOS,                  ED_COUNTER2_YPOS(9) - MINI_TILEY,
+    GADGET_ID_RANDOM_RESTRICTED,
+    &random_placement_background_restricted,
+    "restrict random placement to",    "set random placement restriction"
+  },
+  {
+    ED_SETTINGS_XPOS,                  ED_COUNTER_YPOS(4),
+    GADGET_ID_STICK_ELEMENT,
+    &stick_element_properties_window,
+    "stick window to edit content",    "stick window to edit content"
+  }
+};
+
+/* maximal size of level editor drawing area */
+#define MAX_ED_FIELDX          (2 * SCR_FIELDX)
+#define MAX_ED_FIELDY          (2 * SCR_FIELDY - 1)
+
+/* actual size of level editor drawing area */
+static int ed_fieldx = MAX_ED_FIELDX - 1, ed_fieldy = MAX_ED_FIELDY - 1;
+
+/* actual position of level editor drawing area in level playfield */
+static int level_xpos = -1, level_ypos = -1;
+
+#define IN_ED_FIELD(x,y)  ((x)>=0 && (x)<ed_fieldx && (y)>=0 &&(y)<ed_fieldx)
+
+/* drawing elements on the three mouse buttons */
 static int new_element1 = EL_MAUERWERK;
 static int new_element2 = EL_LEERRAUM;
 static int new_element3 = EL_ERDREICH;
 
-int element_shift;
+#define BUTTON_ELEMENT(button) (button == 1 ? new_element1 : \
+                               button == 2 ? new_element2 : \
+                               button == 3 ? new_element3 : EL_LEERRAUM)
+
+/* forward declaration for internal use */
+static void DrawDrawingWindow();
+static void DrawLevelInfoWindow();
+static void DrawPropertiesWindow();
+static void CopyLevelToUndoBuffer(int);
+static void HandleControlButtons(struct GadgetInfo *);
+static void HandleCounterButtons(struct GadgetInfo *);
+static void HandleDrawingAreas(struct GadgetInfo *);
+static void HandleDrawingAreaInfo(struct GadgetInfo *);
+static void HandleTextInputGadgets(struct GadgetInfo *);
+
+static struct GadgetInfo *level_editor_gadget[NUM_EDITOR_GADGETS];
+
+static int drawing_function = GADGET_ID_SINGLE_ITEMS;
+static int last_drawing_function = GADGET_ID_SINGLE_ITEMS;
+static boolean draw_with_brush = FALSE;
+static int properties_element = 0;
+
+static short ElementContent[MAX_ELEM_CONTENT][3][3];
+static short FieldBackup[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+static short UndoBuffer[NUM_UNDO_STEPS][MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+static int undo_buffer_position = 0;
+static int undo_buffer_steps = 0;
+
+static int edit_mode;
+
+static int counter_xsize = DXSIZE + FONT2_XSIZE - 2 * ED_GADGET_DISTANCE;
+
+int element_shift = 0;
+
 int editor_element[] =
 {
   EL_CHAR_A + ('B' - 'A'),
@@ -69,7 +682,7 @@ int editor_element[] =
   EL_BETON,
 
   EL_FELSBODEN,
-  EL_SIEB2_LEER,
+  EL_SIEB2_INAKTIV,
   EL_AUSGANG_ZU,
   EL_AUSGANG_AUF,
 
@@ -116,7 +729,7 @@ int editor_element[] =
   EL_BETON,
   EL_MAUERWERK,
   EL_FELSBODEN,
-  EL_SIEB_LEER,
+  EL_SIEB_INAKTIV,
 
   EL_EDELSTEIN,
   EL_DIAMANT,
@@ -228,6 +841,11 @@ int editor_element[] =
   EL_MAUER_Y,
   EL_MAUER_XY,
 
+  EL_INVISIBLE_STEEL,
+  EL_UNSICHTBAR,
+  EL_SPEED_PILL,
+  EL_BLACK_ORB,
+
   EL_CHAR_A + ('S' - 'A'),
   EL_CHAR_A + ('O' - 'A'),
   EL_CHAR_A + ('K' - 'A'),
@@ -243,7 +861,72 @@ int editor_element[] =
   EL_SOKOBAN_FELD_VOLL,
   EL_BETON,
 
-/*
+  EL_LEERRAUM,
+  EL_LEERRAUM,
+  EL_LEERRAUM,
+  EL_LEERRAUM,
+
+  EL_CHAR('S'),
+  EL_CHAR('U'),
+  EL_CHAR('P'),
+  EL_CHAR('A'),
+
+  EL_CHAR('P'),
+  EL_CHAR('L'),
+  EL_CHAR('E'),
+  EL_CHAR('X'),
+
+  EL_SP_EMPTY,
+  EL_SP_ZONK,
+  EL_SP_BASE,
+  EL_SP_MURPHY,
+
+  EL_SP_INFOTRON,
+  EL_SP_CHIP_SINGLE,
+  EL_SP_HARD_GRAY,
+  EL_SP_EXIT,
+
+  EL_SP_DISK_ORANGE,
+  EL_SP_PORT1_RIGHT,
+  EL_SP_PORT1_DOWN,
+  EL_SP_PORT1_LEFT,
+
+  EL_SP_PORT1_UP,
+  EL_SP_PORT2_RIGHT,
+  EL_SP_PORT2_DOWN,
+  EL_SP_PORT2_LEFT,
+
+  EL_SP_PORT2_UP,
+  EL_SP_SNIKSNAK,
+  EL_SP_DISK_YELLOW,
+  EL_SP_TERMINAL,
+
+  EL_SP_DISK_RED,
+  EL_SP_PORT_Y,
+  EL_SP_PORT_X,
+  EL_SP_PORT_XY,
+
+  EL_SP_ELECTRON,
+  EL_SP_BUG,
+  EL_SP_CHIP_LEFT,
+  EL_SP_CHIP_RIGHT,
+
+  EL_SP_HARD_BASE1,
+  EL_SP_HARD_GREEN,
+  EL_SP_HARD_BLUE,
+  EL_SP_HARD_RED,
+
+  EL_SP_HARD_YELLOW,
+  EL_SP_HARD_BASE2,
+  EL_SP_HARD_BASE3,
+  EL_SP_HARD_BASE4,
+
+  EL_SP_HARD_BASE5,
+  EL_SP_HARD_BASE6,
+  EL_SP_CHIP_UPPER,
+  EL_SP_CHIP_LOWER,
+
+  /*
   EL_CHAR_A + ('D' - 'A'),
   EL_CHAR_A + ('Y' - 'A'),
   EL_CHAR_A + ('N' - 'A'),
@@ -258,103 +941,824 @@ int editor_element[] =
   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,
-  EL_CHAR_DOLLAR,
-  EL_CHAR_PROZ,
-
-  EL_CHAR_APOSTR,
-  EL_CHAR_KLAMM1,
-  EL_CHAR_KLAMM2,
-  EL_CHAR_PLUS,
-
-  EL_CHAR_KOMMA,
-  EL_CHAR_MINUS,
-  EL_CHAR_PUNKT,
-  EL_CHAR_SLASH,
-
-  EL_CHAR_0 + 0,
-  EL_CHAR_0 + 1,
-  EL_CHAR_0 + 2,
-  EL_CHAR_0 + 3,
-
-  EL_CHAR_0 + 4,
-  EL_CHAR_0 + 5,
-  EL_CHAR_0 + 6,
-  EL_CHAR_0 + 7,
-
-  EL_CHAR_0 + 8,
-  EL_CHAR_0 + 9,
-  EL_CHAR_DOPPEL,
-  EL_CHAR_SEMIKL,
-
-  EL_CHAR_LT,
-  EL_CHAR_GLEICH,
-  EL_CHAR_GT,
-  EL_CHAR_FRAGE,
-
-  EL_CHAR_AT,
-  EL_CHAR_A + 0,
-  EL_CHAR_A + 1,
-  EL_CHAR_A + 2,
-
-  EL_CHAR_A + 3,
-  EL_CHAR_A + 4,
-  EL_CHAR_A + 5,
-  EL_CHAR_A + 6,
-
-  EL_CHAR_A + 7,
-  EL_CHAR_A + 8,
-  EL_CHAR_A + 9,
-  EL_CHAR_A + 10,
-
-  EL_CHAR_A + 11,
-  EL_CHAR_A + 12,
-  EL_CHAR_A + 13,
-  EL_CHAR_A + 14,
-
-  EL_CHAR_A + 15,
-  EL_CHAR_A + 16,
-  EL_CHAR_A + 17,
-  EL_CHAR_A + 18,
-
-  EL_CHAR_A + 19,
-  EL_CHAR_A + 20,
-  EL_CHAR_A + 21,
-  EL_CHAR_A + 22,
-
-  EL_CHAR_A + 23,
-  EL_CHAR_A + 24,
-  EL_CHAR_A + 25,
-  EL_CHAR_AE,
-
-  EL_CHAR_OE,
-  EL_CHAR_UE,
-  EL_CHAR_COPY
+  EL_CHAR(' '),
+  EL_CHAR('!'),
+  EL_CHAR('"'),
+  EL_CHAR('#'),
+
+  EL_CHAR('$'),
+  EL_CHAR('%'),
+  EL_CHAR('&'),
+  EL_CHAR('\''),
+
+  EL_CHAR('('),
+  EL_CHAR(')'),
+  EL_CHAR('*'),
+  EL_CHAR('+'),
+
+  EL_CHAR(','),
+  EL_CHAR('-'),
+  EL_CHAR('.'),
+  EL_CHAR('/'),
+
+  EL_CHAR('0'),
+  EL_CHAR('1'),
+  EL_CHAR('2'),
+  EL_CHAR('3'),
+
+  EL_CHAR('4'),
+  EL_CHAR('5'),
+  EL_CHAR('6'),
+  EL_CHAR('7'),
+
+  EL_CHAR('8'),
+  EL_CHAR('9'),
+  EL_CHAR(':'),
+  EL_CHAR(';'),
+
+  EL_CHAR('<'),
+  EL_CHAR('='),
+  EL_CHAR('>'),
+  EL_CHAR('?'),
+
+  EL_CHAR('@'),
+  EL_CHAR('A'),
+  EL_CHAR('B'),
+  EL_CHAR('C'),
+
+  EL_CHAR('D'),
+  EL_CHAR('E'),
+  EL_CHAR('F'),
+  EL_CHAR('G'),
+
+  EL_CHAR('H'),
+  EL_CHAR('I'),
+  EL_CHAR('J'),
+  EL_CHAR('K'),
+
+  EL_CHAR('L'),
+  EL_CHAR('M'),
+  EL_CHAR('N'),
+  EL_CHAR('O'),
+
+  EL_CHAR('P'),
+  EL_CHAR('Q'),
+  EL_CHAR('R'),
+  EL_CHAR('S'),
+
+  EL_CHAR('T'),
+  EL_CHAR('U'),
+  EL_CHAR('V'),
+  EL_CHAR('W'),
+
+  EL_CHAR('X'),
+  EL_CHAR('Y'),
+  EL_CHAR('Z'),
+  EL_CHAR('Ä'),
+
+  EL_CHAR('Ö'),
+  EL_CHAR('Ãœ'),
+  EL_CHAR('^'),
+  EL_CHAR(' ')
 };
 int elements_in_list = sizeof(editor_element)/sizeof(int);
 
+static void ScrollMiniLevel(int from_x, int from_y, int scroll)
+{
+  int x,y;
+  int dx = (scroll == ED_SCROLL_LEFT ? -1 : scroll == ED_SCROLL_RIGHT ? 1 : 0);
+  int dy = (scroll == ED_SCROLL_UP   ? -1 : scroll == ED_SCROLL_DOWN  ? 1 : 0);
+
+  XCopyArea(display, drawto, drawto, gc,
+           SX + (dx == -1 ? MINI_TILEX : 0),
+           SY + (dy == -1 ? MINI_TILEY : 0),
+           (ed_fieldx * MINI_TILEX) - (dx != 0 ? MINI_TILEX : 0),
+           (ed_fieldy * MINI_TILEY) - (dy != 0 ? MINI_TILEY : 0),
+           SX + (dx == +1 ? MINI_TILEX : 0),
+           SY + (dy == +1 ? MINI_TILEY : 0));
+  if (dx)
+  {
+    x = (dx == 1 ? 0 : ed_fieldx - 1);
+    for(y=0; y<ed_fieldy; y++)
+      DrawMiniElementOrWall(x, y, from_x, from_y);
+  }
+  else if (dy)
+  {
+    y = (dy == 1 ? 0 : ed_fieldy - 1);
+    for(x=0; x<ed_fieldx; x++)
+      DrawMiniElementOrWall(x, y, from_x, from_y);
+  }
+
+  redraw_mask |= REDRAW_FIELD;
+  BackToFront();
+}
+
+static void CreateControlButtons()
+{
+  Pixmap gd_pixmap = pix[PIX_DOOR];
+  struct GadgetInfo *gi;
+  unsigned long event_mask;
+  int i;
+
+  /* create toolbox buttons */
+  for (i=0; i<ED_NUM_CTRL_BUTTONS; i++)
+  {
+    int id = i;
+    int width, height;
+    int gd_xoffset, gd_yoffset;
+    int gd_x1, gd_x2, gd_y1, gd_y2;
+    int button_type;
+    int radio_button_nr;
+    boolean checked;
+
+    if (id == GADGET_ID_SINGLE_ITEMS ||
+       id == GADGET_ID_CONNECTED_ITEMS ||
+       id == GADGET_ID_LINE ||
+       id == GADGET_ID_ARC ||
+       id == GADGET_ID_TEXT ||
+       id == GADGET_ID_RECTANGLE ||
+       id == GADGET_ID_FILLED_BOX ||
+       id == GADGET_ID_FLOOD_FILL ||
+       id == GADGET_ID_GRAB_BRUSH ||
+       id == GADGET_ID_PICK_ELEMENT)
+    {
+      button_type = GD_TYPE_RADIO_BUTTON;
+      radio_button_nr = RADIO_NR_DRAWING_TOOLBOX;
+      checked = (id == drawing_function ? TRUE : FALSE);
+      event_mask = GD_EVENT_PRESSED;
+    }
+    else
+    {
+      button_type = GD_TYPE_NORMAL_BUTTON;
+      radio_button_nr = RADIO_NR_NONE;
+      checked = FALSE;
+
+      if (id == GADGET_ID_WRAP_LEFT ||
+         id == GADGET_ID_WRAP_RIGHT ||
+         id == GADGET_ID_WRAP_UP ||
+         id == GADGET_ID_WRAP_DOWN)
+       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
+      else
+       event_mask = GD_EVENT_RELEASED;
+    }
+
+    if (id < ED_NUM_CTRL1_BUTTONS)
+    {
+      int x = i % ED_CTRL1_BUTTONS_HORIZ;
+      int y = i / ED_CTRL1_BUTTONS_HORIZ;
+
+      gd_xoffset = ED_CTRL1_BUTTONS_XPOS + x * ED_CTRL1_BUTTON_XSIZE;
+      gd_yoffset = ED_CTRL1_BUTTONS_YPOS + y * ED_CTRL1_BUTTON_YSIZE;
+      width = ED_CTRL1_BUTTON_XSIZE;
+      height = ED_CTRL1_BUTTON_YSIZE;
+    }
+    else
+    {
+      int x = (i - ED_NUM_CTRL1_BUTTONS) % ED_CTRL2_BUTTONS_HORIZ;
+      int y = (i - ED_NUM_CTRL1_BUTTONS) / ED_CTRL2_BUTTONS_HORIZ;
+
+      gd_xoffset = ED_CTRL2_BUTTONS_XPOS + x * ED_CTRL2_BUTTON_XSIZE;
+      gd_yoffset = ED_CTRL2_BUTTONS_YPOS + y * ED_CTRL2_BUTTON_YSIZE;
+      width = ED_CTRL2_BUTTON_XSIZE;
+      height = ED_CTRL2_BUTTON_YSIZE;
+    }
+
+    gd_x1 = DOOR_GFX_PAGEX8 + gd_xoffset;
+    gd_x2 = DOOR_GFX_PAGEX7 + gd_xoffset;
+    gd_y1  = DOOR_GFX_PAGEY1 + ED_CTRL_BUTTONS_GFX_YPOS + gd_yoffset;
+    gd_y2  = DOOR_GFX_PAGEY1 + ED_CTRL_BUTTONS_ALT_GFX_YPOS + gd_yoffset;
+
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_INFO_TEXT, control_info[i].text,
+                     GDI_X, EX + gd_xoffset,
+                     GDI_Y, EY + gd_yoffset,
+                     GDI_WIDTH, width,
+                     GDI_HEIGHT, height,
+                     GDI_TYPE, button_type,
+                     GDI_STATE, GD_BUTTON_UNPRESSED,
+                     GDI_RADIO_NR, radio_button_nr,
+                     GDI_CHECKED, checked,
+                     GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y1,
+                     GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y1,
+                     GDI_ALT_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y2,
+                     GDI_ALT_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y2,
+                     GDI_EVENT_MASK, event_mask,
+                     GDI_CALLBACK_ACTION, HandleControlButtons,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    level_editor_gadget[id] = gi;
+  }
+
+  /* create buttons for scrolling of drawing area and element list */
+  for (i=0; i<ED_NUM_SCROLLBUTTONS; i++)
+  {
+    int id = scrollbutton_info[i].gadget_id;
+    int x, y, width, height;
+    int gd_x1, gd_x2, gd_y1, gd_y2;
+
+    x = scrollbutton_info[i].x;
+    y = scrollbutton_info[i].y;
+
+    event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
+
+    if (id == GADGET_ID_ELEMENTLIST_UP ||
+       id == GADGET_ID_ELEMENTLIST_DOWN)
+    {
+      x += DX;
+      y += DY;
+      width = ED_ELEMENTLIST_UPDOWN_XSIZE;
+      height = ED_ELEMENTLIST_UPDOWN_YSIZE;
+      gd_x1 = DOOR_GFX_PAGEX6 + scrollbutton_info[i].xpos;
+      gd_y1 = DOOR_GFX_PAGEY1 + scrollbutton_info[i].y;
+      gd_x2 = gd_x1;
+      gd_y2 = DOOR_GFX_PAGEY1 + scrollbutton_info[i].ypos;
+    }
+    else
+    {
+      x += SX;
+      y += SY;
+      width = ED_SCROLLBUTTON_XSIZE;
+      height = ED_SCROLLBUTTON_YSIZE;
+      gd_x1 = DOOR_GFX_PAGEX8 + scrollbutton_info[i].xpos;
+      gd_y1 = DOOR_GFX_PAGEY1 + scrollbutton_info[i].ypos;
+      gd_x2 = gd_x1 - ED_SCROLLBUTTON_XSIZE;
+      gd_y2 = gd_y1;
+   }
+
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_INFO_TEXT, scrollbutton_info[i].infotext,
+                     GDI_X, x,
+                     GDI_Y, y,
+                     GDI_WIDTH, width,
+                     GDI_HEIGHT, height,
+                     GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
+                     GDI_STATE, GD_BUTTON_UNPRESSED,
+                     GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y1,
+                     GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y2,
+                     GDI_EVENT_MASK, event_mask,
+                     GDI_CALLBACK_ACTION, HandleControlButtons,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    level_editor_gadget[id] = gi;
+  }
+
+  /* create buttons for element list */
+  for (i=0; i<ED_NUM_ELEMENTLIST_BUTTONS; i++)
+  {
+    Pixmap deco_pixmap;
+    int deco_x, deco_y, deco_xpos, deco_ypos;
+    int gd_xoffset, gd_yoffset;
+    int gd_x, gd_y1, gd_y2;
+    int x = i % ED_ELEMENTLIST_BUTTONS_HORIZ;
+    int y = i / ED_ELEMENTLIST_BUTTONS_HORIZ;
+    int id = GADGET_ID_ELEMENTLIST_FIRST + i;
+
+    event_mask = GD_EVENT_RELEASED;
+
+    gd_xoffset = ED_ELEMENTLIST_XPOS + x * ED_ELEMENTLIST_XSIZE;
+    gd_yoffset = ED_ELEMENTLIST_YPOS + y * ED_ELEMENTLIST_YSIZE;
+
+    gd_x = DOOR_GFX_PAGEX6 + ED_ELEMENTLIST_XPOS;
+    gd_y1 = DOOR_GFX_PAGEY1 + ED_ELEMENTLIST_YPOS;
+    gd_y2 = DOOR_GFX_PAGEY1 + ED_ELEMENTLIST_ALT_YPOS;
+
+    getMiniGraphicSource(el2gfx(editor_element[i]),
+                        &deco_pixmap, &deco_x, &deco_y);
+    deco_xpos = (ED_ELEMENTLIST_XSIZE - MINI_TILEX) / 2;
+    deco_ypos = (ED_ELEMENTLIST_YSIZE - MINI_TILEY) / 2;
+
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_INFO_TEXT, element_info[editor_element[i]],
+                     GDI_X, DX + gd_xoffset,
+                     GDI_Y, DY + gd_yoffset,
+                     GDI_WIDTH, ED_ELEMENTLIST_XSIZE,
+                     GDI_HEIGHT, ED_ELEMENTLIST_YSIZE,
+                     GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
+                     GDI_STATE, GD_BUTTON_UNPRESSED,
+                     GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x, gd_y1,
+                     GDI_DESIGN_PRESSED, gd_pixmap, gd_x, gd_y2,
+                     GDI_DECORATION_DESIGN, deco_pixmap, deco_x, deco_y,
+                     GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
+                     GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
+                     GDI_DECORATION_SHIFTING, 1, 1,
+                     GDI_EVENT_MASK, event_mask,
+                     GDI_CALLBACK_ACTION, HandleControlButtons,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    level_editor_gadget[id] = gi;
+  }
+}
+
+static void CreateCounterButtons()
+{
+  int i;
+
+  for (i=0; i<ED_NUM_COUNTERBUTTONS; i++)
+  {
+    int j;
+    int xpos = SX + counterbutton_info[i].x;   /* xpos of down count button */
+    int ypos = SY + counterbutton_info[i].y;
+
+    for (j=0; j<2; j++)
+    {
+      Pixmap gd_pixmap = pix[PIX_DOOR];
+      struct GadgetInfo *gi;
+      int id = (j == 0 ?
+               counterbutton_info[i].gadget_id_down :
+               counterbutton_info[i].gadget_id_up);
+      int gd_xoffset;
+      int gd_x, gd_x1, gd_x2, gd_y;
+      unsigned long event_mask;
+      char infotext[MAX_INFOTEXT_LEN + 1];
+
+      event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
+
+      gd_xoffset = (j == 0 ? ED_BUTTON_MINUS_XPOS : ED_BUTTON_PLUS_XPOS);
+      gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
+      gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
+      gd_y  = DOOR_GFX_PAGEY1 + ED_BUTTON_COUNT_YPOS;
+
+      sprintf(infotext, "%s counter value by 1, 5 or 10",
+             (j == 0 ? "decrease" : "increase"));
+
+      gi = CreateGadget(GDI_CUSTOM_ID, id,
+                       GDI_INFO_TEXT, infotext,
+                       GDI_X, xpos,
+                       GDI_Y, ypos,
+                       GDI_WIDTH, ED_BUTTON_COUNT_XSIZE,
+                       GDI_HEIGHT, ED_BUTTON_COUNT_YSIZE,
+                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
+                       GDI_STATE, GD_BUTTON_UNPRESSED,
+                       GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y,
+                       GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y,
+                       GDI_EVENT_MASK, event_mask,
+                       GDI_CALLBACK_ACTION, HandleCounterButtons,
+                       GDI_END);
+
+      if (gi == NULL)
+       Error(ERR_EXIT, "cannot create gadget");
+
+      level_editor_gadget[id] = gi;
+      xpos += gi->width + ED_GADGET_DISTANCE;  /* xpos of text count button */
+
+      if (j == 0)
+      {
+       id = counterbutton_info[i].gadget_id_text;
+       event_mask = GD_EVENT_TEXT_RETURN | GD_EVENT_TEXT_LEAVING;
+
+       gd_x = DOOR_GFX_PAGEX4 + ED_WIN_COUNT_XPOS;
+       gd_y = DOOR_GFX_PAGEY1 + ED_WIN_COUNT_YPOS;
+
+       gi = CreateGadget(GDI_CUSTOM_ID, id,
+                         GDI_INFO_TEXT, "enter counter value",
+                         GDI_X, xpos,
+                         GDI_Y, ypos,
+                         GDI_TYPE, GD_TYPE_TEXTINPUT_NUMERIC,
+                         GDI_NUMBER_VALUE, 0,
+                         GDI_NUMBER_MIN, counterbutton_info[i].min_value,
+                         GDI_NUMBER_MAX, counterbutton_info[i].max_value,
+                         GDI_TEXT_SIZE, 3,
+                         GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x, gd_y,
+                         GDI_DESIGN_PRESSED, gd_pixmap, gd_x, gd_y,
+                         GDI_DESIGN_BORDER, ED_BORDER_SIZE,
+                         GDI_EVENT_MASK, event_mask,
+                         GDI_CALLBACK_ACTION, HandleCounterButtons,
+                         GDI_END);
+
+       if (gi == NULL)
+         Error(ERR_EXIT, "cannot create gadget");
+
+       level_editor_gadget[id] = gi;
+       xpos += gi->width + ED_GADGET_DISTANCE; /* xpos of up count button */
+      }
+    }
+  }
+}
+
+static void CreateDrawingAreas()
+{
+  struct GadgetInfo *gi;
+  unsigned long event_mask;
+  int id;
+  int i;
+
+  event_mask =
+    GD_EVENT_PRESSED | GD_EVENT_RELEASED | GD_EVENT_MOVING |
+    GD_EVENT_OFF_BORDERS;
+
+  /* one for the level drawing area ... */
+  id = GADGET_ID_DRAWING_LEVEL;
+  gi = CreateGadget(GDI_CUSTOM_ID, id,
+                   GDI_X, SX,
+                   GDI_Y, SY,
+                   GDI_TYPE, GD_TYPE_DRAWING_AREA,
+                   GDI_AREA_SIZE, ed_fieldx, ed_fieldy,
+                   GDI_ITEM_SIZE, MINI_TILEX, MINI_TILEY,
+                   GDI_EVENT_MASK, event_mask,
+                   GDI_CALLBACK_INFO, HandleDrawingAreaInfo,
+                   GDI_CALLBACK_ACTION, HandleDrawingAreas,
+                   GDI_END);
+
+  if (gi == NULL)
+    Error(ERR_EXIT, "cannot create gadget");
+
+  level_editor_gadget[id] = gi;
+
+  /* ... up to eight areas for element content ... */
+  for (i=0; i<MAX_ELEM_CONTENT; i++)
+  {
+    int gx = SX + ED_AREA_ELEM_CONTENT_XPOS + 5 * (i % 4) * MINI_TILEX;
+    int gy = SX + ED_AREA_ELEM_CONTENT_YPOS + 6 * (i / 4) * MINI_TILEY;
+
+    id = GADGET_ID_ELEM_CONTENT_0 + i;
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_X, gx,
+                     GDI_Y, gy,
+                     GDI_WIDTH, 3 * MINI_TILEX,
+                     GDI_HEIGHT, 3 * MINI_TILEY,
+                     GDI_TYPE, GD_TYPE_DRAWING_AREA,
+                     GDI_ITEM_SIZE, MINI_TILEX, MINI_TILEY,
+                     GDI_EVENT_MASK, event_mask,
+                     GDI_CALLBACK_INFO, HandleDrawingAreaInfo,
+                     GDI_CALLBACK_ACTION, HandleDrawingAreas,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    level_editor_gadget[id] = gi;
+  }
+
+  /* ... one for the amoeba content */
+  id = GADGET_ID_AMOEBA_CONTENT;
+  gi = CreateGadget(GDI_CUSTOM_ID, id,
+                   GDI_X, SX + ED_AREA_ELEM_CONTENT_XPOS,
+                   GDI_Y, SY + ED_AREA_ELEM_CONTENT_YPOS,
+                   GDI_WIDTH, MINI_TILEX,
+                   GDI_HEIGHT, MINI_TILEY,
+                   GDI_TYPE, GD_TYPE_DRAWING_AREA,
+                   GDI_ITEM_SIZE, MINI_TILEX, MINI_TILEY,
+                   GDI_EVENT_MASK, event_mask,
+                   GDI_CALLBACK_INFO, HandleDrawingAreaInfo,
+                   GDI_CALLBACK_ACTION, HandleDrawingAreas,
+                   GDI_END);
+
+  if (gi == NULL)
+    Error(ERR_EXIT, "cannot create gadget");
+
+  level_editor_gadget[id] = gi;
+
+  /* ... and one for random placement background restrictions */
+
+  id = GADGET_ID_RANDOM_BACKGROUND;
+  gi = CreateGadget(GDI_CUSTOM_ID, id,
+                   GDI_X, SX + ED_AREA_RANDOM_BACKGROUND_XPOS,
+                   GDI_Y, SY + ED_AREA_RANDOM_BACKGROUND_YPOS,
+                   GDI_WIDTH, MINI_TILEX,
+                   GDI_HEIGHT, MINI_TILEY,
+                   GDI_TYPE, GD_TYPE_DRAWING_AREA,
+                   GDI_ITEM_SIZE, MINI_TILEX, MINI_TILEY,
+                   GDI_EVENT_MASK, event_mask,
+                   GDI_CALLBACK_INFO, HandleDrawingAreaInfo,
+                   GDI_CALLBACK_ACTION, HandleDrawingAreas,
+                   GDI_END);
+
+  if (gi == NULL)
+    Error(ERR_EXIT, "cannot create gadget");
+
+  level_editor_gadget[id] = gi;
+}
+
+static void CreateTextInputGadgets()
+{
+  int i;
+
+  for (i=0; i<ED_NUM_TEXTINPUT; i++)
+  {
+    Pixmap gd_pixmap = pix[PIX_DOOR];
+    int gd_x, gd_y;
+    struct GadgetInfo *gi;
+    unsigned long event_mask;
+    char infotext[1024];
+    int id = textinput_info[i].gadget_id;
+
+    event_mask = GD_EVENT_TEXT_RETURN | GD_EVENT_TEXT_LEAVING;
+
+    gd_x = DOOR_GFX_PAGEX4 + ED_WIN_COUNT_XPOS;
+    gd_y = DOOR_GFX_PAGEY1 + ED_WIN_COUNT_YPOS;
+
+    sprintf(infotext, "Enter %s", textinput_info[i].infotext);
+    infotext[MAX_INFOTEXT_LEN] = '\0';
+
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_INFO_TEXT, infotext,
+                     GDI_X, SX + textinput_info[i].x,
+                     GDI_Y, SY + textinput_info[i].y,
+                     GDI_TYPE, GD_TYPE_TEXTINPUT_ALPHANUMERIC,
+                     GDI_TEXT_VALUE, textinput_info[i].value,
+                     GDI_TEXT_SIZE, textinput_info[i].size,
+                     GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x, gd_y,
+                     GDI_DESIGN_PRESSED, gd_pixmap, gd_x, gd_y,
+                     GDI_DESIGN_BORDER, ED_BORDER_SIZE,
+                     GDI_EVENT_MASK, event_mask,
+                     GDI_CALLBACK_ACTION, HandleTextInputGadgets,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    level_editor_gadget[id] = gi;
+  }
+}
+
+static void CreateScrollbarGadgets()
+{
+  int i;
+
+  for (i=0; i<ED_NUM_SCROLLBARS; i++)
+  {
+    int id = scrollbar_info[i].gadget_id;
+    Pixmap gd_pixmap = pix[PIX_DOOR];
+    int gd_x1, gd_x2, gd_y1, gd_y2;
+    struct GadgetInfo *gi;
+    int items_max, items_visible, item_position;
+    unsigned long event_mask;
+
+    if (scrollbar_info[i].type == GD_TYPE_SCROLLBAR_HORIZONTAL)
+    {
+      items_max = MAX(lev_fieldx + 2, ed_fieldx);
+      items_visible = ed_fieldx;
+      item_position = 0;
+    }
+    else
+    {
+      items_max = MAX(lev_fieldy + 2, ed_fieldy);
+      items_visible = ed_fieldy;
+      item_position = 0;
+    }
+
+    event_mask = GD_EVENT_MOVING | GD_EVENT_OFF_BORDERS;
+
+    gd_x1 = DOOR_GFX_PAGEX8 + scrollbar_info[i].xpos;
+    gd_x2 = gd_x1 - ED_SCROLLBUTTON_XSIZE;
+    gd_y1 = DOOR_GFX_PAGEY1 + scrollbar_info[i].ypos;
+    gd_y2 = DOOR_GFX_PAGEY1 + scrollbar_info[i].ypos;
+
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_INFO_TEXT, scrollbar_info[i].infotext,
+                     GDI_X, SX + scrollbar_info[i].x,
+                     GDI_Y, SY + scrollbar_info[i].y,
+                     GDI_WIDTH, scrollbar_info[i].width,
+                     GDI_HEIGHT, scrollbar_info[i].height,
+                     GDI_TYPE, scrollbar_info[i].type,
+                     GDI_SCROLLBAR_ITEMS_MAX, items_max,
+                     GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
+                     GDI_SCROLLBAR_ITEM_POSITION, item_position,
+                     GDI_STATE, GD_BUTTON_UNPRESSED,
+                     GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y1,
+                     GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y2,
+                     GDI_DESIGN_BORDER, ED_BORDER_SIZE,
+                     GDI_EVENT_MASK, event_mask,
+                     GDI_CALLBACK_ACTION, HandleControlButtons,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    level_editor_gadget[id] = gi;
+  }
+}
+
+static void CreateCheckbuttonGadgets()
+{
+  Pixmap gd_pixmap = pix[PIX_DOOR];
+  struct GadgetInfo *gi;
+  unsigned long event_mask;
+  int gd_x1, gd_x2, gd_x3, gd_x4, gd_y;
+  boolean checked;
+  int i;
+
+  event_mask = GD_EVENT_PRESSED;
+
+  gd_x1 = DOOR_GFX_PAGEX4 + ED_CHECKBUTTON_UNCHECKED_XPOS;
+  gd_x2 = DOOR_GFX_PAGEX3 + ED_CHECKBUTTON_UNCHECKED_XPOS;
+  gd_x3 = DOOR_GFX_PAGEX4 + ED_CHECKBUTTON_CHECKED_XPOS;
+  gd_x4 = DOOR_GFX_PAGEX3 + ED_CHECKBUTTON_CHECKED_XPOS;
+  gd_y  = DOOR_GFX_PAGEY1 + ED_CHECKBUTTON_YPOS;
+
+  for (i=0; i<ED_NUM_RADIOBUTTONS; i++)
+  {
+    int id = radiobutton_info[i].gadget_id;
+
+    checked =
+      (*radiobutton_info[i].value == radiobutton_info[i].checked_value);
+
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_INFO_TEXT, radiobutton_info[i].infotext,
+                     GDI_X, SX + radiobutton_info[i].x,
+                     GDI_Y, SY + radiobutton_info[i].y,
+                     GDI_WIDTH, ED_CHECKBUTTON_XSIZE,
+                     GDI_HEIGHT, ED_CHECKBUTTON_YSIZE,
+                     GDI_TYPE, GD_TYPE_RADIO_BUTTON,
+                     GDI_RADIO_NR, radiobutton_info[i].radio_button_nr,
+                     GDI_CHECKED, checked,
+                     GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y,
+                     GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y,
+                     GDI_ALT_DESIGN_UNPRESSED, gd_pixmap, gd_x3, gd_y,
+                     GDI_ALT_DESIGN_PRESSED, gd_pixmap, gd_x4, gd_y,
+                     GDI_EVENT_MASK, event_mask,
+                     GDI_CALLBACK_ACTION, HandleControlButtons,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    level_editor_gadget[id] = gi;
+  }
+
+  for (i=0; i<ED_NUM_CHECKBUTTONS; i++)
+  {
+    int id = checkbutton_info[i].gadget_id;
+
+    if (id == GADGET_ID_STICK_ELEMENT)
+      gd_y  = DOOR_GFX_PAGEY1 + ED_STICKYBUTTON_YPOS;
+    else
+      gd_y  = DOOR_GFX_PAGEY1 + ED_CHECKBUTTON_YPOS;
+
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_INFO_TEXT, checkbutton_info[i].infotext,
+                     GDI_X, SX + checkbutton_info[i].x,
+                     GDI_Y, SY + checkbutton_info[i].y,
+                     GDI_WIDTH, ED_CHECKBUTTON_XSIZE,
+                     GDI_HEIGHT, ED_CHECKBUTTON_YSIZE,
+                     GDI_TYPE, GD_TYPE_CHECK_BUTTON,
+                     GDI_CHECKED, *checkbutton_info[i].value,
+                     GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y,
+                     GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y,
+                     GDI_ALT_DESIGN_UNPRESSED, gd_pixmap, gd_x3, gd_y,
+                     GDI_ALT_DESIGN_PRESSED, gd_pixmap, gd_x4, gd_y,
+                     GDI_EVENT_MASK, event_mask,
+                     GDI_CALLBACK_ACTION, HandleControlButtons,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    level_editor_gadget[id] = gi;
+  }
+}
+
+void CreateLevelEditorGadgets()
+{
+  CreateControlButtons();
+  CreateCounterButtons();
+  CreateDrawingAreas();
+  CreateTextInputGadgets();
+  CreateScrollbarGadgets();
+  CreateCheckbuttonGadgets();
+}
+
+static void MapControlButtons()
+{
+  int i;
+
+  for (i=0; i<ED_NUM_CTRL_BUTTONS; i++)
+    MapGadget(level_editor_gadget[i]);
+  for (i=0; i<ED_NUM_ELEMENTLIST_BUTTONS; i++)
+    MapGadget(level_editor_gadget[GADGET_ID_ELEMENTLIST_FIRST + i]);
+}
+
+static void MapCounterButtons(int id)
+{
+  MapGadget(level_editor_gadget[counterbutton_info[id].gadget_id_down]);
+  MapGadget(level_editor_gadget[counterbutton_info[id].gadget_id_text]);
+  MapGadget(level_editor_gadget[counterbutton_info[id].gadget_id_up]);
+}
+
+static void MapDrawingArea(int id)
+{
+  MapGadget(level_editor_gadget[id]);
+}
+
+static void MapTextInputGadget(int id)
+{
+  MapGadget(level_editor_gadget[textinput_info[id].gadget_id]);
+}
+
+static void MapRadiobuttonGadget(int id)
+{
+  MapGadget(level_editor_gadget[radiobutton_info[id].gadget_id]);
+}
+
+static void MapCheckbuttonGadget(int id)
+{
+  MapGadget(level_editor_gadget[checkbutton_info[id].gadget_id]);
+}
+
+static void MapMainDrawingArea()
+{
+  boolean no_horizontal_scrollbar = (lev_fieldx + 2 <= ed_fieldx);
+  boolean no_vertical_scrollbar = (lev_fieldy + 2 <= ed_fieldy);
+  int i;
+
+  for (i=0; i<ED_NUM_SCROLLBUTTONS; i++)
+  {
+    if (((i == ED_SCROLLBUTTON_ID_AREA_LEFT ||
+         i == ED_SCROLLBUTTON_ID_AREA_RIGHT) &&
+        no_horizontal_scrollbar) ||
+       ((i == ED_SCROLLBUTTON_ID_AREA_UP ||
+         i == ED_SCROLLBUTTON_ID_AREA_DOWN) &&
+        no_vertical_scrollbar))
+      continue;
+
+    MapGadget(level_editor_gadget[scrollbutton_info[i].gadget_id]);
+  }
+
+  for (i=0; i<ED_NUM_SCROLLBARS; i++)
+  {
+    if ((i == ED_SCROLLBAR_ID_HORIZONTAL && no_horizontal_scrollbar) ||
+       (i == ED_SCROLLBAR_ID_VERTICAL && no_vertical_scrollbar))
+      continue;
+
+    MapGadget(level_editor_gadget[scrollbar_info[i].gadget_id]);
+  }
+
+  MapDrawingArea(GADGET_ID_DRAWING_LEVEL);
+}
+
+static void UnmapDrawingArea(int id)
+{
+  UnmapGadget(level_editor_gadget[id]);
+}
+
+void UnmapLevelEditorWindowGadgets()
+{
+  int i;
+
+  for (i=0; i<NUM_EDITOR_GADGETS; i++)
+    if (level_editor_gadget[i]->x < DX)
+      UnmapGadget(level_editor_gadget[i]);
+}
+
+void UnmapLevelEditorGadgets()
+{
+  int i;
+
+  for (i=0; i<NUM_EDITOR_GADGETS; i++)
+    UnmapGadget(level_editor_gadget[i]);
+}
+
 void DrawLevelEd()
 {
-  int i, graphic;
+  int i, x, y, graphic;
+
+  edit_mode = ED_MODE_DRAWING;
+
+  CloseDoor(DOOR_CLOSE_ALL);
+
+  OpenDoor(DOOR_OPEN_2 | DOOR_NO_DELAY);
+
+  if (level_editor_test_game)
+  {
+    for(x=0; x<lev_fieldx; x++)
+      for(y=0; y<lev_fieldy; y++)
+       Feld[x][y] = Ur[x][y];
 
-  level_xpos=-1;
-  level_ypos=-1;
-  edit_mode = TRUE;
-  name_typing = FALSE;
-  element_shift = 0;
+    for(x=0; x<lev_fieldx; x++)
+      for(y=0; y<lev_fieldy; y++)
+       Ur[x][y] = FieldBackup[x][y];
 
-  CloseDoor(DOOR_CLOSE_2);
+    level_editor_test_game = FALSE;
+  }
+  else
+  {
+    level_xpos = -1;
+    level_ypos = -1;
+    undo_buffer_position = -1;
+    undo_buffer_steps = -1;
+    CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
+  }
 
-  DrawMiniLevel(level_xpos,level_ypos);
+  /*
+  DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
   FadeToFront();
+  */
 
   XCopyArea(display,pix[PIX_DOOR],pix[PIX_DB_DOOR],gc,
            DOOR_GFX_PAGEX6,DOOR_GFX_PAGEY1,
@@ -370,7 +1774,7 @@ void DrawLevelEd()
   for(i=0;i<MAX_ELEM_X*MAX_ELEM_Y;i++)
   {
     if (i < elements_in_list)
-      graphic = el2gfx(editor_element[i]);
+      graphic = el2gfx(editor_element[i + element_shift]);
     else
       graphic = GFX_LEERRAUM;
 
@@ -416,212 +1820,969 @@ void DrawLevelEd()
            VXSIZE,VYSIZE,
            DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY2);
 
+  /* draw bigger door */
+  XCopyArea(display, pix[PIX_DOOR], drawto, gc,
+           DOOR_GFX_PAGEX7, 0,
+           108, 64,
+           EX - 4, EY - 12);
+
+  /* draw new control window */
+  XCopyArea(display, pix[PIX_DOOR], drawto, gc,
+           DOOR_GFX_PAGEX8, 236,
+           EXSIZE, EYSIZE,
+           EX, EY);
+
+  redraw_mask |= REDRAW_ALL;
+
+  OpenDoor(DOOR_OPEN_1);
+
+  strcpy(level_editor_gadget[GADGET_ID_LEVEL_NAME]->text.value, level.name);
+
+  MapControlButtons();
+
+  /*
+  MapMainDrawingArea();
+  */
+
+  DrawDrawingWindow();
+  FadeToFront();
+
+  /*
   OpenDoor(DOOR_OPEN_1 | DOOR_OPEN_2);
+  */
 }
 
-void DrawControlWindow()
+static void AdjustDrawingAreaGadgets()
 {
-  int i,x,y;
+  int ed_xsize = lev_fieldx + 2;
+  int ed_ysize = lev_fieldy + 2;
+  int max_ed_fieldx = MAX_ED_FIELDX;
+  int max_ed_fieldy = MAX_ED_FIELDY;
+  boolean horizontal_scrollbar_needed;
+  boolean vertical_scrollbar_needed;
+  int x, y, width, height;
+  int xoffset, yoffset;
+
+  /* check if we need any scrollbars */
+  horizontal_scrollbar_needed = (ed_xsize > max_ed_fieldx);
+  vertical_scrollbar_needed = (ed_ysize > max_ed_fieldy);
+
+  /* check if we have a smaller editor field because of scrollbars */
+  if (horizontal_scrollbar_needed)
+    max_ed_fieldy = MAX_ED_FIELDY - 1;
+  if (vertical_scrollbar_needed)
+    max_ed_fieldx = MAX_ED_FIELDX - 1;
+
+  /* check again if we now need more scrollbars because of less space */
+  horizontal_scrollbar_needed = (ed_xsize > max_ed_fieldx);
+  vertical_scrollbar_needed = (ed_ysize > max_ed_fieldy);
+
+  /* check if editor field gets even smaller after adding new scrollbars */
+  if (horizontal_scrollbar_needed)
+    max_ed_fieldy = MAX_ED_FIELDY - 1;
+  if (vertical_scrollbar_needed)
+    max_ed_fieldx = MAX_ED_FIELDX - 1;
+
+  ed_fieldx = (ed_xsize < MAX_ED_FIELDX ? ed_xsize : max_ed_fieldx);
+  ed_fieldy = (ed_ysize < MAX_ED_FIELDY ? ed_ysize : max_ed_fieldy);
+
+  ModifyGadget(level_editor_gadget[GADGET_ID_DRAWING_LEVEL],
+              GDI_WIDTH, ed_fieldx * MINI_TILEX,
+              GDI_HEIGHT, ed_fieldy * MINI_TILEY,
+              GDI_AREA_SIZE, ed_fieldx, ed_fieldy,
+              GDI_END);
+
+  xoffset = (ed_fieldx == MAX_ED_FIELDX ? ED_SCROLLBUTTON_XSIZE : 0);
+  yoffset = (ed_fieldy == MAX_ED_FIELDY ? ED_SCROLLBUTTON_YSIZE : 0);
+
+  x = SX + scrollbutton_info[ED_SCROLLBUTTON_ID_AREA_RIGHT].x + xoffset;
+  y = SX + scrollbutton_info[ED_SCROLLBUTTON_ID_AREA_DOWN].y + yoffset;
+
+  ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_RIGHT], GDI_X, x, GDI_END);
+  ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_DOWN], GDI_Y, y, GDI_END);
+
+  width = scrollbar_info[ED_SCROLLBAR_ID_HORIZONTAL].width + xoffset;
+  height = scrollbar_info[ED_SCROLLBAR_ID_VERTICAL].height + yoffset;
+
+  ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_HORIZONTAL],
+              GDI_WIDTH, width,
+              GDI_SCROLLBAR_ITEMS_VISIBLE, ed_fieldx,
+              GDI_END);
+  ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_VERTICAL],
+              GDI_HEIGHT, height,
+              GDI_SCROLLBAR_ITEMS_VISIBLE, ed_fieldy,
+              GDI_END);
+}
 
-  ClearWindow();
+static void AdjustLevelScrollPosition()
+{
+  if (level_xpos < -1)
+    level_xpos = -1;
+  if (level_xpos > lev_fieldx - ed_fieldx + 1)
+    level_xpos = lev_fieldx - ed_fieldx + 1;
+  if (lev_fieldx < ed_fieldx - 2)
+    level_xpos = -1;
 
-  /* Inhalt der Mampfer */
-  DrawText(ED_COUNT_GADGET_XPOS+1,SY+6,
-          "Contents of a smashed cruncher:",FS_SMALL,FC_YELLOW);
-  for(i=0;i<4;i++) for(y=0;y<4;y++) for(x=0;x<4;x++)
-  {
-    DrawMiniElement(1+5*i+x,2+y,EL_ERDREICH);
-    XFillRectangle(display,drawto,gc,
-                  SX+(1+5*i)*MINI_TILEX+MINI_TILEX/2-1,
-                  SY+(2)*MINI_TILEY+MINI_TILEY/2-1,
-                  3*MINI_TILEX+2,3*MINI_TILEY+2);
-  }
-  XCopyArea(display,drawto,drawto,gc,
-           SX+1*MINI_TILEX,SY+2*MINI_TILEY,
-           4*5*MINI_TILEX,5*MINI_TILEY,
-           SX+1*MINI_TILEX-MINI_TILEX/2,SY+2*MINI_TILEY-MINI_TILEY/2);
-  for(i=0;i<4;i++)
-  {
-    for(y=0;y<3;y++) for(x=0;x<3;x++)
-      DrawMiniElement(1+5*i+x,2+y,level.mampfer_inhalt[i][x][y]);
+  if (level_ypos < -1)
+    level_ypos = -1;
+  if (level_ypos > lev_fieldy - ed_fieldy + 1)
+    level_ypos = lev_fieldy - ed_fieldy + 1;
+  if (lev_fieldy < ed_fieldy - 2)
+    level_ypos = -1;
+}
 
-    DrawText(SX+MINI_TILEX+(5*i+1)*MINI_TILEX+1,
-            SY+2*MINI_TILEY+(4)*MINI_TILEY-4,
-            int2str(i+1,1),FS_SMALL,FC_YELLOW);
-  }
+static void AdjustEditorScrollbar(int id)
+{
+  struct GadgetInfo *gi = level_editor_gadget[id];
+  int items_max, items_visible, item_position;
 
-  /* Inhalt der Amöbe */
-  for(y=0;y<2;y++) for(x=0;x<2;x++)
+  if (id == GADGET_ID_SCROLL_HORIZONTAL)
   {
-    DrawMiniElement(29+x,26+y,EL_ERDREICH);
-    XFillRectangle(display,drawto,gc,
-                  SX+29*MINI_TILEX+MINI_TILEX/2-1,
-                  SY+26*MINI_TILEY+MINI_TILEY/2-1,
-                  MINI_TILEX+2,MINI_TILEY+2);
+    items_max = MAX(lev_fieldx + 2, ed_fieldx);
+    items_visible = ed_fieldx;
+    item_position = level_xpos + 1;
   }
-  XCopyArea(display,drawto,drawto,gc,
-           SX+29*MINI_TILEX,SY+26*MINI_TILEY,
-           3*MINI_TILEX,3*MINI_TILEY,
-           SX+29*MINI_TILEX-MINI_TILEX/2,SY+26*MINI_TILEY-MINI_TILEY/2);
-  DrawMiniElement(29,26,level.amoebe_inhalt);
-
-  for(i=0;i<11+3+2;i++)
-  {
-    XCopyArea(display,pix[PIX_DOOR],drawto,gc,
-             DOOR_GFX_PAGEX4+ED_BUTTON_MINUS_XPOS,
-             DOOR_GFX_PAGEY1+ED_BUTTON_MINUS_YPOS,
-             DXSIZE-4,ED_BUTTON_MINUS_YSIZE,
-             ED_COUNT_GADGET_XPOS,
-             ED_COUNT_GADGET_YPOS+i*ED_COUNT_GADGET_YSIZE);
-
-    if (i<11)
-      DrawText(ED_COUNT_VALUE_XPOS,
-              ED_COUNT_VALUE_YPOS+i*ED_COUNT_GADGET_YSIZE,
-              int2str(level.score[i],3),FS_SMALL,FC_YELLOW);
-    else if (i==11)
-      DrawText(ED_COUNT_VALUE_XPOS,
-              ED_COUNT_VALUE_YPOS+11*ED_COUNT_GADGET_YSIZE,
-              int2str(level.tempo_amoebe,3),FS_SMALL,FC_YELLOW);
-    else if (i==12)
-      DrawText(ED_COUNT_VALUE_XPOS,
-              ED_COUNT_VALUE_YPOS+12*ED_COUNT_GADGET_YSIZE,
-              int2str(level.dauer_sieb,3),FS_SMALL,FC_YELLOW);
-    else if (i==13)
-      DrawText(ED_COUNT_VALUE_XPOS,
-              ED_COUNT_VALUE_YPOS+13*ED_COUNT_GADGET_YSIZE,
-              int2str(level.dauer_ablenk,3),FS_SMALL,FC_YELLOW);
-    else if (i==14)
-      DrawText(ED_COUNT_VALUE_XPOS,
-              ED_COUNT_VALUE_YPOS+14*ED_COUNT_GADGET_YSIZE,
-              int2str(level.edelsteine,3),FS_SMALL,FC_YELLOW);
-    else if (i==15)
-      DrawText(ED_COUNT_VALUE_XPOS,
-              ED_COUNT_VALUE_YPOS+15*ED_COUNT_GADGET_YSIZE,
-              int2str(level.time,3),FS_SMALL,FC_YELLOW);
+  else
+  {
+    items_max = MAX(lev_fieldy + 2, ed_fieldy);
+    items_visible = ed_fieldy;
+    item_position = level_ypos + 1;
   }
 
-  DrawText(ED_COUNT_TEXT_XPOS,ED_COUNT_TEXT_YPOS+0*ED_COUNT_TEXT_YSIZE,
-          "Score for Emerald",FS_SMALL,FC_YELLOW);
-  DrawText(ED_COUNT_TEXT_XPOS,ED_COUNT_TEXT_YPOS+1*ED_COUNT_TEXT_YSIZE,
-          "Score for Diamond",FS_SMALL,FC_YELLOW);
-  DrawText(ED_COUNT_TEXT_XPOS,ED_COUNT_TEXT_YPOS+2*ED_COUNT_TEXT_YSIZE,
-          "Score for smashing a Bug",FS_SMALL,FC_YELLOW);
-  DrawText(ED_COUNT_TEXT_XPOS,ED_COUNT_TEXT_YPOS+3*ED_COUNT_TEXT_YSIZE,
-          "Score for smashing a Spaceship",FS_SMALL,FC_YELLOW);
-  DrawText(ED_COUNT_TEXT_XPOS,ED_COUNT_TEXT_YPOS+4*ED_COUNT_TEXT_YSIZE,
-          "Score for smashing a Cruncher",FS_SMALL,FC_YELLOW);
-  DrawText(ED_COUNT_TEXT_XPOS,ED_COUNT_TEXT_YPOS+5*ED_COUNT_TEXT_YSIZE,
-          "Score for smashing an Alien",FS_SMALL,FC_YELLOW);
-  DrawText(ED_COUNT_TEXT_XPOS,ED_COUNT_TEXT_YPOS+6*ED_COUNT_TEXT_YSIZE,
-          "Score for smashing a Pacman",FS_SMALL,FC_YELLOW);
-  DrawText(ED_COUNT_TEXT_XPOS,ED_COUNT_TEXT_YPOS+7*ED_COUNT_TEXT_YSIZE,
-          "Score for cracking a nut",FS_SMALL,FC_YELLOW);
-  DrawText(ED_COUNT_TEXT_XPOS,ED_COUNT_TEXT_YPOS+8*ED_COUNT_TEXT_YSIZE,
-          "Score for dynamite",FS_SMALL,FC_YELLOW);
-  DrawText(ED_COUNT_TEXT_XPOS,ED_COUNT_TEXT_YPOS+9*ED_COUNT_TEXT_YSIZE,
-          "Score for key",FS_SMALL,FC_YELLOW);
-  DrawText(ED_COUNT_TEXT_XPOS,ED_COUNT_TEXT_YPOS+10*ED_COUNT_TEXT_YSIZE,
-          "Score for each 10 seconds left",FS_SMALL,FC_YELLOW);
-  DrawText(ED_COUNT_TEXT_XPOS,ED_COUNT_TEXT_YPOS+11*ED_COUNT_TEXT_YSIZE,
-          "Speed of the amoeba / Content",FS_SMALL,FC_YELLOW);
-  DrawText(ED_COUNT_TEXT_XPOS,ED_COUNT_TEXT_YPOS+12*ED_COUNT_TEXT_YSIZE,
-          "Time for magic wall",FS_SMALL,FC_YELLOW);
-  DrawText(ED_COUNT_TEXT_XPOS,ED_COUNT_TEXT_YPOS+13*ED_COUNT_TEXT_YSIZE,
-          "Time for wheel",FS_SMALL,FC_YELLOW);
-  DrawText(ED_COUNT_TEXT_XPOS,ED_COUNT_TEXT_YPOS+14*ED_COUNT_TEXT_YSIZE,
-          "Emeralds needed in this level",FS_SMALL,FC_YELLOW);
-  DrawText(ED_COUNT_TEXT_XPOS,ED_COUNT_TEXT_YPOS+15*ED_COUNT_TEXT_YSIZE,
-          "Time available for this level",FS_SMALL,FC_YELLOW);
-
-  XCopyArea(display,pix[PIX_DOOR],drawto,gc,
-           DOOR_GFX_PAGEX4+ED_WIN_COUNT_XPOS,
-           DOOR_GFX_PAGEY1+ED_WIN_COUNT_YPOS,
-           ED_WIN_COUNT_XSIZE,ED_WIN_COUNT_YSIZE,
-           ED_COUNT_GADGET_XPOS,
-           ED_COUNT_GADGET_YPOS+16*ED_COUNT_GADGET_YSIZE);
-  for(i=1;i<31;i++)
-    XCopyArea(display,pix[PIX_DOOR],drawto,gc,
-             DOOR_GFX_PAGEX4+ED_WIN_COUNT_XPOS+3+2*FONT2_XSIZE,
-             DOOR_GFX_PAGEY1+ED_WIN_COUNT_YPOS,
-             ED_WIN_COUNT_XSIZE-3-2*FONT2_XSIZE,ED_WIN_COUNT_YSIZE,
-             ED_COUNT_GADGET_XPOS+3+i*FONT2_XSIZE,
-             ED_COUNT_GADGET_YPOS+16*ED_COUNT_GADGET_YSIZE);
-  DrawText(ED_COUNT_GADGET_XPOS+5,
-          ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
-          level.name,FS_SMALL,FC_YELLOW);
-  DrawText(ED_COUNT_GADGET_XPOS+(30+3)*FONT2_XSIZE-5,
-          ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
-          "Title",FS_SMALL,FC_YELLOW);
-
-  DrawText(ED_SIZE_GADGET_XPOS,ED_SIZE_GADGET_YPOS-18,
-          "Playfield size:",FS_SMALL,FC_YELLOW);
-  XCopyArea(display,pix[PIX_DOOR],drawto,gc,
-           DOOR_GFX_PAGEX4+ED_BUTTON_MINUS_XPOS,
-           DOOR_GFX_PAGEY1+ED_BUTTON_MINUS_YPOS,
-           DXSIZE-4,ED_BUTTON_MINUS_YSIZE,
-           ED_SIZE_GADGET_XPOS,
-           ED_SIZE_GADGET_YPOS+0*ED_COUNT_GADGET_YSIZE);
-  XCopyArea(display,pix[PIX_DOOR],drawto,gc,
-           DOOR_GFX_PAGEX4+ED_BUTTON_MINUS_XPOS,
-           DOOR_GFX_PAGEY1+ED_BUTTON_MINUS_YPOS,
-           DXSIZE-4,ED_BUTTON_MINUS_YSIZE,
-           ED_SIZE_GADGET_XPOS,
-           ED_SIZE_GADGET_YPOS+1*ED_COUNT_GADGET_YSIZE);
-  DrawText(ED_SIZE_TEXT_XPOS,ED_SIZE_TEXT_YPOS+0*ED_SIZE_TEXT_YSIZE,
-          "Width",FS_SMALL,FC_YELLOW);
-  DrawText(ED_SIZE_TEXT_XPOS,ED_SIZE_TEXT_YPOS+1*ED_SIZE_TEXT_YSIZE,
-          "Height",FS_SMALL,FC_YELLOW);
-  DrawText(ED_SIZE_VALUE_XPOS,ED_SIZE_VALUE_YPOS+0*ED_SIZE_GADGET_YSIZE,
-          int2str(level.fieldx,3),FS_SMALL,FC_YELLOW);
-  DrawText(ED_SIZE_VALUE_XPOS,ED_SIZE_VALUE_YPOS+1*ED_SIZE_GADGET_YSIZE,
-          int2str(level.fieldy,3),FS_SMALL,FC_YELLOW);
+  if (item_position > items_max - items_visible)
+    item_position = items_max - items_visible;
+
+  ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max,
+              GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
 }
 
-void ScrollMiniLevel(int from_x, int from_y, int scroll)
+static void ModifyEditorTextInput(int textinput_id, char *new_text)
 {
-  int x,y;
-  int dx = (scroll==ED_SCROLL_LEFT ? -1 : scroll==ED_SCROLL_RIGHT ? 1 : 0);
-  int dy = (scroll==ED_SCROLL_UP   ? -1 : scroll==ED_SCROLL_DOWN  ? 1 : 0);
+  int gadget_id = textinput_info[textinput_id].gadget_id;
+  struct GadgetInfo *gi = level_editor_gadget[gadget_id];
 
-  XCopyArea(display,drawto,drawto,gc,
-           SX+MINI_TILEX*(dx==-1),SY+MINI_TILEY*(dy==-1),
-           SXSIZE-MINI_TILEX*ABS(dx),SYSIZE-MINI_TILEY*ABS(dy),
-           SX+MINI_TILEX*(dx==+1),SY+MINI_TILEY*(dy==+1));
-  if (dx)
+  ModifyGadget(gi, GDI_TEXT_VALUE, new_text, GDI_END);
+}
+
+static void ModifyEditorCounter(int counter_id, int new_value)
+{
+  int *counter_value = *counterbutton_info[counter_id].counter_value;
+  int gadget_id = counterbutton_info[counter_id].gadget_id_text;
+  struct GadgetInfo *gi = level_editor_gadget[gadget_id];
+
+  ModifyGadget(gi, GDI_NUMBER_VALUE, new_value, GDI_END);
+
+  if (counter_value != NULL)
+    *counter_value = gi->text.number_value;
+}
+
+static void PickDrawingElement(int button, int element)
+{
+  if (button < 1 || button > 3)
+    return;
+
+  if (button == 1)
   {
-    x = (dx==1 ? 0 : 2*SCR_FIELDX-1);
-    for(y=0;y<2*SCR_FIELDY;y++)
-      DrawMiniElementOrWall(x,y,from_x,from_y);
+    new_element1 = element;
+    DrawMiniGraphicExt(drawto, gc,
+                      DX + ED_WIN_MB_LEFT_XPOS,
+                      DY + ED_WIN_MB_LEFT_YPOS,
+                      el2gfx(new_element1));
   }
-  else if (dy)
+  else if (button == 2)
+  {
+    new_element2 = element;
+    DrawMiniGraphicExt(drawto, gc,
+                      DX + ED_WIN_MB_MIDDLE_XPOS,
+                      DY + ED_WIN_MB_MIDDLE_YPOS,
+                      el2gfx(new_element2));
+  }
+  else
   {
-    y = (dy==1 ? 0 : 2*SCR_FIELDY-1);
-    for(x=0;x<2*SCR_FIELDX;x++)
-      DrawMiniElementOrWall(x,y,from_x,from_y);
+    new_element3 = element;
+    DrawMiniGraphicExt(drawto, gc,
+                      DX + ED_WIN_MB_RIGHT_XPOS,
+                      DY + ED_WIN_MB_RIGHT_YPOS,
+                      el2gfx(new_element3));
   }
 
-  redraw_mask |= REDRAW_FIELD;
-  BackToFront();
+  redraw_mask |= REDRAW_DOOR_1;
 }
 
-void AdjustLevelScrollPosition()
+static void DrawDrawingWindow()
 {
-  if (level_xpos<-1)
-    level_xpos = -1;
-  if (level_xpos>lev_fieldx-2*SCR_FIELDX+1)
-    level_xpos = lev_fieldx-2*SCR_FIELDX+1;
-  if (lev_fieldx<2*SCR_FIELDX-2)
-    level_xpos = -1;
+  ClearWindow();
+  UnmapLevelEditorWindowGadgets();
+  AdjustDrawingAreaGadgets();
+  AdjustLevelScrollPosition();
+  AdjustEditorScrollbar(GADGET_ID_SCROLL_HORIZONTAL);
+  AdjustEditorScrollbar(GADGET_ID_SCROLL_VERTICAL);
+  DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
+  MapMainDrawingArea();
+}
 
-  if (level_ypos<-1)
-    level_ypos = -1;
-  if (level_ypos>lev_fieldy-2*SCR_FIELDY+1)
-    level_ypos = lev_fieldy-2*SCR_FIELDY+1;
-  if (lev_fieldy<2*SCR_FIELDY-2)
-    level_ypos = -1;
+static void DrawRandomPlacementBackgroundArea()
+{
+  int area_x = ED_AREA_RANDOM_BACKGROUND_XPOS / MINI_TILEX;
+  int area_y = ED_AREA_RANDOM_BACKGROUND_YPOS / MINI_TILEY;
+  int area_sx = SX + ED_AREA_RANDOM_BACKGROUND_XPOS;
+  int area_sy = SY + ED_AREA_RANDOM_BACKGROUND_YPOS;
+  int x, y;
+
+  ElementContent[0][0][0] = random_placement_background_element;
+
+  /* draw decorative border for the object */
+  for (y=0; y<2; y++)
+    for (x=0; x<2; x++)
+      DrawMiniElement(area_x + x, area_y + y, EL_ERDREICH);
+
+  XFillRectangle(display, drawto, gc,
+                area_sx + MINI_TILEX/2 - 1, area_sy + MINI_TILEY/2 - 1,
+                MINI_TILEX + 2, MINI_TILEY + 2);
+
+  /* copy border to the right location */
+  XCopyArea(display, drawto, drawto, gc,
+           area_sx, area_sy, 3 * MINI_TILEX, 3 * MINI_TILEY,
+           area_sx - MINI_TILEX/2, area_sy - MINI_TILEY/2);
+
+  DrawMiniElement(area_x, area_y, ElementContent[0][0][0]);
+
+  MapDrawingArea(GADGET_ID_RANDOM_BACKGROUND);
+}
+
+static void DrawLevelInfoWindow()
+{
+  char infotext[1024];
+  int xoffset_above = 0;
+  int yoffset_above = -(MINI_TILEX + ED_GADGET_DISTANCE);
+  int xoffset_right = counter_xsize;
+  int yoffset_right = ED_BORDER_SIZE;
+  int xoffset_right2 = ED_CHECKBUTTON_XSIZE + 2 * ED_GADGET_DISTANCE;
+  int yoffset_right2 = ED_BORDER_SIZE;
+  int font_color = FC_GREEN;
+  int i, x, y;
+
+  ClearWindow();
+  UnmapLevelEditorWindowGadgets();
+
+  DrawText(SX + ED_SETTINGS2_XPOS, SY + ED_SETTINGS_YPOS,
+          "Level Settings", FS_BIG, FC_YELLOW);
+  DrawText(SX + ED_SETTINGS2_XPOS, SY + ED_SETTINGS2_YPOS,
+          "Editor Settings", FS_BIG, FC_YELLOW);
+
+  gadget_level_xsize_value = &lev_fieldx;
+  gadget_level_ysize_value = &lev_fieldy;
+  gadget_level_random_value = &random_placement_value;
+  gadget_level_collect_value = &level.edelsteine;
+  gadget_level_timelimit_value = &level.time;
+  gadget_level_timescore_value = &level.score[10];
+
+  /* draw counter gadgets */
+  for (i=ED_COUNTER_ID_LEVEL_FIRST; i<=ED_COUNTER_ID_LEVEL_LAST; i++)
+  {
+    if (counterbutton_info[i].infotext_above)
+    {
+      x = counterbutton_info[i].x + xoffset_above;
+      y = counterbutton_info[i].y + yoffset_above;
+
+      sprintf(infotext, "%s:", counterbutton_info[i].infotext_above);
+      infotext[MAX_INFOTEXT_LEN] = '\0';
+      DrawTextF(x, y, font_color, infotext);
+    }
+
+    if (counterbutton_info[i].infotext_right)
+    {
+      x = counterbutton_info[i].x + xoffset_right;
+      y = counterbutton_info[i].y + yoffset_right;
+
+      sprintf(infotext, "%s", counterbutton_info[i].infotext_right);
+      infotext[MAX_INFOTEXT_LEN] = '\0';
+      DrawTextF(x, y, font_color, infotext);
+    }
+
+    ModifyEditorCounter(i, **counterbutton_info[i].counter_value);
+    MapCounterButtons(i);
+  }
+
+  /* draw text input gadgets */
+  for (i=ED_TEXTINPUT_ID_LEVEL_FIRST; i<=ED_TEXTINPUT_ID_LEVEL_LAST; i++)
+  {
+    x = textinput_info[i].x + xoffset_above;
+    y = textinput_info[i].y + yoffset_above;
+
+    sprintf(infotext, "%s:", textinput_info[i].infotext);
+    infotext[MAX_INFOTEXT_LEN] = '\0';
+
+    DrawTextF(x, y, font_color, infotext);
+    ModifyEditorTextInput(i, textinput_info[i].value);
+    MapTextInputGadget(i);
+  }
+
+  /* draw radiobutton gadgets */
+  for (i=ED_RADIOBUTTON_ID_LEVEL_FIRST; i<=ED_RADIOBUTTON_ID_LEVEL_LAST; i++)
+  {
+    boolean checked =
+      (*radiobutton_info[i].value == radiobutton_info[i].checked_value);
+
+    x = radiobutton_info[i].x + xoffset_right2;
+    y = radiobutton_info[i].y + yoffset_right2;
+
+    DrawTextF(x, y, font_color, radiobutton_info[i].text);
+    ModifyGadget(level_editor_gadget[radiobutton_info[i].gadget_id],
+                GDI_CHECKED, checked, GDI_END);
+    MapRadiobuttonGadget(i);
+  }
+
+  /* draw checkbutton gadgets */
+  for (i=ED_CHECKBUTTON_ID_LEVEL_FIRST; i<=ED_CHECKBUTTON_ID_LEVEL_LAST; i++)
+  {
+    x = checkbutton_info[i].x + xoffset_right2;
+    y = checkbutton_info[i].y + yoffset_right2;
+
+    DrawTextF(x, y, font_color, checkbutton_info[i].text);
+    ModifyGadget(level_editor_gadget[checkbutton_info[i].gadget_id],
+                GDI_CHECKED, *checkbutton_info[i].value, GDI_END);
+    MapCheckbuttonGadget(i);
+  }
+
+  /* draw drawing area */
+  DrawRandomPlacementBackgroundArea();
+}
+
+static void DrawAmoebaContentArea()
+{
+  int area_x = ED_AREA_ELEM_CONTENT_XPOS / MINI_TILEX;
+  int area_y = ED_AREA_ELEM_CONTENT_YPOS / MINI_TILEY;
+  int area_sx = SX + ED_AREA_ELEM_CONTENT_XPOS;
+  int area_sy = SY + ED_AREA_ELEM_CONTENT_YPOS;
+  int font_color = FC_GREEN;
+  int x, y;
+
+  ElementContent[0][0][0] = level.amoebe_inhalt;
+
+  /* draw decorative border for the object */
+  for (y=0; y<2; y++)
+    for (x=0; x<2; x++)
+      DrawMiniElement(area_x + x, area_y + y, EL_ERDREICH);
+
+  XFillRectangle(display, drawto, gc,
+                area_sx + MINI_TILEX/2 - 1, area_sy + MINI_TILEY/2 - 1,
+                MINI_TILEX + 2, MINI_TILEY + 2);
+
+  /* copy border to the right location */
+  XCopyArea(display, drawto, drawto, gc,
+           area_sx, area_sy, 3 * MINI_TILEX, 3 * MINI_TILEY,
+           area_sx - MINI_TILEX/2, area_sy - MINI_TILEY/2);
+
+  DrawText(area_sx + TILEX, area_sy + 1, "Content of amoeba",
+          FS_SMALL, font_color);
+
+  DrawMiniElement(area_x, area_y, ElementContent[0][0][0]);
+
+  MapDrawingArea(GADGET_ID_AMOEBA_CONTENT);
+}
+
+static void DrawElementContentAreas()
+{
+  int *num_areas = &MampferMax;
+  int area_x = ED_AREA_ELEM_CONTENT_XPOS / MINI_TILEX;
+  int area_y = ED_AREA_ELEM_CONTENT_YPOS / MINI_TILEY;
+  int area_sx = SX + ED_AREA_ELEM_CONTENT_XPOS;
+  int area_sy = SY + ED_AREA_ELEM_CONTENT_YPOS;
+  int xoffset_right = counter_xsize;
+  int yoffset_right = ED_BORDER_SIZE;
+  int font_color = FC_GREEN;
+  int i, x, y;
+
+  for (i=0; i<MAX_ELEM_CONTENT; i++)
+    for (y=0; y<3; y++)
+      for (x=0; x<3; x++)
+       ElementContent[i][x][y] = level.mampfer_inhalt[i][x][y];
+
+  for (i=0; i<MAX_ELEM_CONTENT; i++)
+    UnmapDrawingArea(GADGET_ID_ELEM_CONTENT_0 + i);
+
+  /* display counter to choose number of element content areas */
+  gadget_elem_content_value = num_areas;
+
+  x = counterbutton_info[ED_COUNTER_ID_ELEM_CONTENT].x + xoffset_right;
+  y = counterbutton_info[ED_COUNTER_ID_ELEM_CONTENT].y + yoffset_right;
+  DrawTextF(x, y, font_color, "number of content areas");
+  ModifyEditorCounter(ED_COUNTER_ID_ELEM_CONTENT, *gadget_elem_content_value);
+  MapCounterButtons(ED_COUNTER_ID_ELEM_CONTENT);
+
+  /* delete content areas in case of reducing number of them */
+  XFillRectangle(display, backbuffer, gc,
+                SX, area_sy - MINI_TILEX,
+                SXSIZE, 12 * MINI_TILEY);
+
+  /* draw some decorative border for the objects */
+  for (i=0; i<*num_areas; i++)
+  {
+    for (y=0; y<4; y++)
+      for (x=0; x<4; x++)
+       DrawMiniElement(area_x + 5 * (i % 4) + x, area_y + 6 * (i / 4) + y,
+                       EL_ERDREICH);
+
+    XFillRectangle(display, drawto, gc,
+                  area_sx + 5 * (i % 4) * MINI_TILEX + MINI_TILEX/2 - 1,
+                  area_sy + 6 * (i / 4) * MINI_TILEY + MINI_TILEY/2 - 1,
+                  3 * MINI_TILEX + 2, 3 * MINI_TILEY + 2);
+  }
+
+  /* copy border to the right location */
+  XCopyArea(display, drawto, drawto, gc,
+           area_sx, area_sy, (5 * 4 + 1) * MINI_TILEX, 11 * MINI_TILEY,
+           area_sx - MINI_TILEX/2, area_sy - MINI_TILEY/2);
+
+  DrawText(area_sx + (5 * 4 - 1) * MINI_TILEX, area_sy + 0 * MINI_TILEY + 1,
+          "Content", FS_SMALL, font_color);
+  DrawText(area_sx + (5 * 4 - 1) * MINI_TILEX, area_sy + 1 * MINI_TILEY + 1,
+          "when", FS_SMALL, font_color);
+  DrawText(area_sx + (5 * 4 - 1) * MINI_TILEX, area_sy + 2 * MINI_TILEY + 1,
+          "smashed", FS_SMALL, font_color);
+
+  for (i=0; i<*num_areas; i++)
+  {
+    for (y=0; y<3; y++)
+      for (x=0; x<3; x++)
+       DrawMiniElement(area_x + 5 * (i % 4) + x, area_y + 6 * (i / 4) + y,
+                       ElementContent[i][x][y]);
+
+    DrawTextF(area_sx - SX + 5 * (i % 4) * MINI_TILEX + MINI_TILEX + 1,
+             area_sy - SY + 6 * (i / 4) * MINI_TILEY + 4 * MINI_TILEY - 4,
+             font_color, "%d", i + 1);
+  }
+
+  for (i=0; i<*num_areas; i++)
+    MapDrawingArea(GADGET_ID_ELEM_CONTENT_0 + i);
+}
+
+#define TEXT_COLLECTING                "Score for collecting"
+#define TEXT_SMASHING          "Score for smashing"
+#define TEXT_CRACKING          "Score for cracking"
+#define TEXT_SPEED             "Speed of amoeba growth"
+#define TEXT_DURATION          "Duration when activated"
+
+static void DrawPropertiesWindow()
+{
+  int num_elements_in_level;
+  float percentage;
+  int xoffset_right = counter_xsize;
+  int yoffset_right = ED_BORDER_SIZE;
+  int xoffset_right2 = ED_CHECKBUTTON_XSIZE + 2 * ED_GADGET_DISTANCE;
+  int yoffset_right2 = ED_BORDER_SIZE;
+  int xstart = 2;
+  int ystart = 4;
+  int font_color = FC_GREEN;
+  int i, x, y;
+  static struct
+  {
+    int element;
+    int *counter_value;
+    char *text;
+  } elements_with_counter[] =
+  {
+    { EL_EDELSTEIN,    &level.score[0],        TEXT_COLLECTING },
+    { EL_EDELSTEIN_BD, &level.score[0],        TEXT_COLLECTING },
+    { EL_EDELSTEIN_GELB,&level.score[0],       TEXT_COLLECTING },
+    { EL_EDELSTEIN_ROT,        &level.score[0],        TEXT_COLLECTING },
+    { EL_EDELSTEIN_LILA,&level.score[0],       TEXT_COLLECTING },
+    { EL_DIAMANT,      &level.score[1],        TEXT_COLLECTING },
+    { EL_KAEFER_R,     &level.score[2],        TEXT_SMASHING },
+    { EL_KAEFER_O,     &level.score[2],        TEXT_SMASHING },
+    { EL_KAEFER_L,     &level.score[2],        TEXT_SMASHING },
+    { EL_KAEFER_U,     &level.score[2],        TEXT_SMASHING },
+    { EL_BUTTERFLY_R,  &level.score[2],        TEXT_SMASHING },
+    { EL_BUTTERFLY_O,  &level.score[2],        TEXT_SMASHING },
+    { EL_BUTTERFLY_L,  &level.score[2],        TEXT_SMASHING },
+    { EL_BUTTERFLY_U,  &level.score[2],        TEXT_SMASHING },
+    { EL_FLIEGER_R,    &level.score[3],        TEXT_SMASHING },
+    { EL_FLIEGER_O,    &level.score[3],        TEXT_SMASHING },
+    { EL_FLIEGER_L,    &level.score[3],        TEXT_SMASHING },
+    { EL_FLIEGER_U,    &level.score[3],        TEXT_SMASHING },
+    { EL_FIREFLY_R,    &level.score[3],        TEXT_SMASHING },
+    { EL_FIREFLY_O,    &level.score[3],        TEXT_SMASHING },
+    { EL_FIREFLY_L,    &level.score[3],        TEXT_SMASHING },
+    { EL_FIREFLY_U,    &level.score[3],        TEXT_SMASHING },
+    { EL_MAMPFER,      &level.score[4],        TEXT_SMASHING },
+    { EL_MAMPFER2,     &level.score[4],        TEXT_SMASHING },
+    { EL_ROBOT,                &level.score[5],        TEXT_SMASHING },
+    { EL_PACMAN_R,     &level.score[6],        TEXT_SMASHING },
+    { EL_PACMAN_O,     &level.score[6],        TEXT_SMASHING },
+    { EL_PACMAN_L,     &level.score[6],        TEXT_SMASHING },
+    { EL_PACMAN_U,     &level.score[6],        TEXT_SMASHING },
+    { EL_KOKOSNUSS,    &level.score[7],        TEXT_CRACKING },
+    { EL_DYNAMIT_AUS,  &level.score[8],        TEXT_COLLECTING },
+    { EL_SCHLUESSEL1,  &level.score[9],        TEXT_COLLECTING },
+    { EL_SCHLUESSEL2,  &level.score[9],        TEXT_COLLECTING },
+    { EL_SCHLUESSEL3,  &level.score[9],        TEXT_COLLECTING },
+    { EL_SCHLUESSEL4,  &level.score[9],        TEXT_COLLECTING },
+    { EL_AMOEBE_NASS,  &level.tempo_amoebe,    TEXT_SPEED },
+    { EL_AMOEBE_NORM,  &level.tempo_amoebe,    TEXT_SPEED },
+    { EL_AMOEBE_VOLL,  &level.tempo_amoebe,    TEXT_SPEED },
+    { EL_AMOEBE_BD,    &level.tempo_amoebe,    TEXT_SPEED },
+    { EL_SIEB_INAKTIV, &level.dauer_sieb,      TEXT_DURATION },
+    { EL_ABLENK_AUS,   &level.dauer_ablenk,    TEXT_DURATION },
+    { -1, NULL, NULL }
+  };
+
+  ClearWindow();
+  UnmapLevelEditorWindowGadgets();
+
+  DrawText(SX + ED_SETTINGS2_XPOS, SY + ED_SETTINGS_YPOS,
+          "Element Settings", FS_BIG, FC_YELLOW);
+
+  /* draw some decorative border for the object */
+  for (y=0; y<3; y++)
+    for (x=0; x<3; x++)
+      DrawMiniElement(xstart + x , ystart + y, EL_ERDREICH);
+
+  XFillRectangle(display, drawto, gc,
+                SX + xstart * MINI_TILEX + MINI_TILEX/2 - 1,
+                SY + ystart * MINI_TILEY + MINI_TILEY/2 - 1,
+                TILEX + 2, TILEY + 2);
+
+  /* copy border to the right location */
+  XCopyArea(display, drawto, drawto, gc,
+           SX + xstart * MINI_TILEX,
+           SY + ystart * MINI_TILEY,
+           2 * TILEX, 2 * TILEY,
+           SX + xstart * MINI_TILEX - MINI_TILEX/2,
+           SY + ystart * MINI_TILEY - MINI_TILEY/2);
+
+  DrawGraphic(xstart/2, ystart/2, el2gfx(properties_element));
+
+  /* copy the whole stuff to the definitive location */
+  XCopyArea(display, drawto, drawto, gc,
+           SX + xstart * MINI_TILEX - MINI_TILEX/2,
+           SY + ystart * MINI_TILEY - MINI_TILEY,
+           2 * TILEX, 2 * TILEY,
+           SX + xstart * MINI_TILEX - MINI_TILEX/2,
+           SY + ystart * MINI_TILEY - MINI_TILEY/2);
+
+  DrawTextF((xstart + 3) * MINI_TILEX, (ystart + 1) * MINI_TILEY,
+           font_color, element_info[properties_element]);
+
+  num_elements_in_level = 0;
+  for (y=0; y<lev_fieldy; y++) 
+    for (x=0; x<lev_fieldx; x++)
+      if (Feld[x][y] == properties_element)
+       num_elements_in_level++;
+  percentage = num_elements_in_level * 100.0 / (lev_fieldx * lev_fieldy);
+
+  DrawTextF(ED_SETTINGS_XPOS, 5 * TILEY, font_color, "In this level:");
+  DrawTextF(ED_SETTINGS_XPOS + 15 * FONT2_XSIZE, 5 * TILEY, FC_YELLOW,
+           "%d (%.2f%%)", num_elements_in_level, percentage);
+
+  /* check if there are elements where a score can be chosen for */
+  for (i=0; elements_with_counter[i].element != -1; i++)
+  {
+    if (elements_with_counter[i].element == properties_element)
+    {
+      int x = counterbutton_info[ED_COUNTER_ID_ELEM_SCORE].x + xoffset_right;
+      int y = counterbutton_info[ED_COUNTER_ID_ELEM_SCORE].y + yoffset_right;
+
+      gadget_elem_score_value = elements_with_counter[i].counter_value;
+
+      DrawTextF(x, y, font_color, elements_with_counter[i].text);
+      ModifyEditorCounter(ED_COUNTER_ID_ELEM_SCORE, *gadget_elem_score_value);
+      MapCounterButtons(ED_COUNTER_ID_ELEM_SCORE);
+      break;
+    }
+  }
+
+  if (HAS_CONTENT(properties_element))
+  {
+
+#if 1
+    /* draw stickybutton gadget */
+    i = ED_CHECKBUTTON_ID_STICK_ELEMENT;
+    x = checkbutton_info[i].x + xoffset_right2;
+    y = checkbutton_info[i].y + yoffset_right2;
+
+    DrawTextF(x, y, font_color, checkbutton_info[i].text);
+    ModifyGadget(level_editor_gadget[checkbutton_info[i].gadget_id],
+                GDI_CHECKED, *checkbutton_info[i].value, GDI_END);
+    MapCheckbuttonGadget(i);
+#endif
+
+
+    if (IS_AMOEBOID(properties_element))
+      DrawAmoebaContentArea();
+    else
+      DrawElementContentAreas();
+  }
+}
+
+static void swap_numbers(int *i1, int *i2)
+{
+  int help = *i1;
+
+  *i1 = *i2;
+  *i2 = help;
+}
+
+static void swap_number_pairs(int *x1, int *y1, int *x2, int *y2)
+{
+  int help_x = *x1;
+  int help_y = *y1;
+
+  *x1 = *x2;
+  *x2 = help_x;
+
+  *y1 = *y2;
+  *y2 = help_y;
+}
+
+static void DrawLineElement(int sx, int sy, int element, boolean change_level)
+{
+  int lx = sx + level_xpos;
+  int ly = sy + level_ypos;
+
+  DrawMiniElement(sx, sy, (element < 0 ? Feld[lx][ly] : element));
+
+  if (change_level)
+    Feld[lx][ly] = element;
+}
+
+static void DrawLine(int from_x, int from_y, int to_x, int to_y,
+                    int element, boolean change_level)
+{
+  if (from_y == to_y)                  /* horizontal line */
+  {
+    int x;
+    int y = from_y;
+
+    if (from_x > to_x)
+      swap_numbers(&from_x, &to_x);
+
+    for (x=from_x; x<=to_x; x++)
+      DrawLineElement(x, y, element, change_level);
+  }
+  else if (from_x == to_x)             /* vertical line */
+  {
+    int x = from_x;
+    int y;
+
+    if (from_y > to_y)
+      swap_numbers(&from_y, &to_y);
+
+    for (y=from_y; y<=to_y; y++)
+      DrawLineElement(x, y, element, change_level);
+  }
+  else                                 /* diagonal line */
+  {
+    int len_x = ABS(to_x - from_x);
+    int len_y = ABS(to_y - from_y);
+    int x, y;
+
+    if (len_y < len_x)                 /* a < 1 */
+    {
+      float a = (float)len_y / (float)len_x;
+
+      if (from_x > to_x)
+       swap_number_pairs(&from_x, &from_y, &to_x, &to_y);
+
+      for (x=0; x<=len_x; x++)
+      {
+       y = (int)(a * x + 0.5) * (to_y < from_y ? -1 : +1);
+       DrawLineElement(from_x + x, from_y + y, element, change_level);
+      }
+    }
+    else                               /* a >= 1 */
+    {
+      float a = (float)len_x / (float)len_y;
+
+      if (from_y > to_y)
+       swap_number_pairs(&from_x, &from_y, &to_x, &to_y);
+
+      for (y=0; y<=len_y; y++)
+      {
+       x = (int)(a * y + 0.5) * (to_x < from_x ? -1 : +1);
+       DrawLineElement(from_x + x, from_y + y, element, change_level);
+      }
+    }
+  }
+}
+
+static void DrawRectangle(int from_x, int from_y, int to_x, int to_y,
+                         int element, boolean change_level)
+{
+  DrawLine(from_x, from_y, from_x, to_y, element, change_level);
+  DrawLine(from_x, to_y, to_x, to_y, element, change_level);
+  DrawLine(to_x, to_y, to_x, from_y, element, change_level);
+  DrawLine(to_x, from_y, from_x, from_y, element, change_level);
+}
+
+static void DrawFilledBox(int from_x, int from_y, int to_x, int to_y,
+                         int element, boolean change_level)
+{
+  int y;
+
+  if (from_y > to_y)
+    swap_number_pairs(&from_x, &from_y, &to_x, &to_y);
+
+  for (y=from_y; y<=to_y; y++)
+    DrawLine(from_x, y, to_x, y, element, change_level);
 }
 
-void FloodFill(int from_x, int from_y, int fill_element)
+static void DrawArcExt(int from_x, int from_y, int to_x2, int to_y2,
+                      int element, boolean change_level)
+{
+  int to_x = to_x2 - (to_x2 > from_x ? +1 : -1);
+  int to_y = to_y2 - (to_y2 > from_y ? +1 : -1);
+  int len_x = ABS(to_x - from_x);
+  int len_y = ABS(to_y - from_y);
+  int radius, x, y;
+
+  radius = (int)(sqrt((float)(len_x * len_x + len_y * len_y)) + 0.5);
+
+  /* not optimal (some points get drawn twice) but simple,
+     and fast enough for the few points we are drawing */
+
+  for (x=0; x<=radius; x++)
+  {
+    int sx, sy, lx, ly;
+
+    y = (int)(sqrt((float)(radius * radius - x * x)) + 0.5);
+
+    sx = from_x + x * (from_x < to_x2 ? +1 : -1);
+    sy = from_y + y * (from_y < to_y2 ? +1 : -1);
+    lx = sx + level_xpos;
+    ly = sy + level_ypos;
+
+    if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
+      DrawLineElement(sx, sy, element, change_level);
+  }
+
+  for (y=0; y<=radius; y++)
+  {
+    int sx, sy, lx, ly;
+
+    x = (int)(sqrt((float)(radius * radius - y * y)) + 0.5);
+
+    sx = from_x + x * (from_x < to_x2 ? +1 : -1);
+    sy = from_y + y * (from_y < to_y2 ? +1 : -1);
+    lx = sx + level_xpos;
+    ly = sy + level_ypos;
+
+    if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
+      DrawLineElement(sx, sy, element, change_level);
+  }
+}
+
+static void DrawArc(int from_x, int from_y, int to_x, int to_y,
+                   int element, boolean change_level)
+{
+  int to_x2 = to_x + (to_x < from_x ? -1 : +1);
+  int to_y2 = to_y + (to_y > from_y ? +1 : -1);
+
+  DrawArcExt(from_x, from_y, to_x2, to_y2, element, change_level);
+}
+
+#define DRAW_CIRCLES_BUTTON_AVAILABLE  0
+#if DRAW_CIRCLES_BUTTON_AVAILABLE
+static void DrawCircle(int from_x, int from_y, int to_x, int to_y,
+                      int element, boolean change_level)
+{
+  int to_x2 = to_x + (to_x < from_x ? -1 : +1);
+  int to_y2 = to_y + (to_y > from_y ? +1 : -1);
+  int mirror_to_x2 = from_x - (to_x2 - from_x);
+  int mirror_to_y2 = from_y - (to_y2 - from_y);
+
+  DrawArcExt(from_x, from_y, to_x2, to_y2, element, change_level);
+  DrawArcExt(from_x, from_y, mirror_to_x2, to_y2, element, change_level);
+  DrawArcExt(from_x, from_y, to_x2, mirror_to_y2, element, change_level);
+  DrawArcExt(from_x, from_y, mirror_to_x2, mirror_to_y2, element,change_level);
+}
+#endif
+
+static void DrawAreaBorder(int from_x, int from_y, int to_x, int to_y)
+{
+  int from_sx, from_sy;
+  int to_sx, to_sy;
+
+  if (from_x > to_x)
+    swap_numbers(&from_x, &to_x);
+
+  if (from_y > to_y)
+    swap_numbers(&from_y, &to_y);
+
+  from_sx = SX + from_x * MINI_TILEX;
+  from_sy = SY + from_y * MINI_TILEY;
+  to_sx = SX + to_x * MINI_TILEX + MINI_TILEX - 1;
+  to_sy = SY + to_y * MINI_TILEY + MINI_TILEY - 1;
+
+  XSetForeground(display, gc, WhitePixel(display, screen));
+
+  XDrawLine(display, drawto, gc, from_sx, from_sy, to_sx, from_sy);
+  XDrawLine(display, drawto, gc, to_sx, from_sy, to_sx, to_sy);
+  XDrawLine(display, drawto, gc, to_sx, to_sy, from_sx, to_sy);
+  XDrawLine(display, drawto, gc, from_sx, to_sy, from_sx, from_sy);
+
+  XSetForeground(display, gc, BlackPixel(display, screen));
+
+  if (from_x == to_x && from_y == to_y)
+    MarkTileDirty(from_x/2, from_y/2);
+  else
+    redraw_mask |= REDRAW_FIELD;
+}
+
+static void SelectArea(int from_x, int from_y, int to_x, int to_y,
+                      int element, boolean change_level)
+{
+  if (element == -1 || change_level)
+    DrawRectangle(from_x, from_y, to_x, to_y, -1, FALSE);
+  else
+    DrawAreaBorder(from_x, from_y, to_x, to_y);
+}
+
+/* values for CopyBrushExt() */
+#define CB_AREA_TO_BRUSH       0
+#define CB_BRUSH_TO_CURSOR     1
+#define CB_BRUSH_TO_LEVEL      2
+#define CB_DELETE_OLD_CURSOR   3
+
+static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y,
+                        int button, int mode)
+{
+  static short brush_buffer[MAX_ED_FIELDX][MAX_ED_FIELDY];
+  static int brush_width, brush_height;
+  static int last_cursor_x = -1, last_cursor_y = -1;
+  static boolean delete_old_brush;
+  int new_element;
+  int x, y;
+
+  if (mode == CB_DELETE_OLD_CURSOR && !delete_old_brush)
+    return;
+
+  new_element = (button == 1 ? new_element1 :
+                button == 2 ? new_element2 :
+                button == 3 ? new_element3 : 0);
+
+  if (mode == CB_AREA_TO_BRUSH)
+  {
+    int from_lx, from_ly;
+
+    if (from_x > to_x)
+      swap_numbers(&from_x, &to_x);
+
+    if (from_y > to_y)
+      swap_numbers(&from_y, &to_y);
+
+    brush_width = to_x - from_x + 1;
+    brush_height = to_y - from_y + 1;
+
+    from_lx = from_x + level_xpos;
+    from_ly = from_y + level_ypos;
+
+    for (y=0; y<brush_height; y++)
+    {
+      for (x=0; x<brush_width; x++)
+      {
+       brush_buffer[x][y] = Feld[from_lx + x][from_ly + y];
+
+       if (button != 1)
+         DrawLineElement(from_x + x, from_y + y, new_element, TRUE);
+      }
+    }
+
+    if (button != 1)
+      CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
+
+    delete_old_brush = FALSE;
+  }
+  else if (mode == CB_BRUSH_TO_CURSOR || mode == CB_DELETE_OLD_CURSOR ||
+          mode == CB_BRUSH_TO_LEVEL)
+  {
+    int cursor_x = (mode == CB_DELETE_OLD_CURSOR ? last_cursor_x : from_x);
+    int cursor_y = (mode == CB_DELETE_OLD_CURSOR ? last_cursor_y : from_y);
+    int cursor_from_x = cursor_x - brush_width / 2;
+    int cursor_from_y = cursor_y - brush_height / 2;
+    int border_from_x = cursor_x, border_from_y = cursor_y;
+    int border_to_x = cursor_x, border_to_y = cursor_y;
+
+    if (mode != CB_DELETE_OLD_CURSOR && delete_old_brush)
+      CopyBrushExt(0, 0, 0, 0, 0, CB_DELETE_OLD_CURSOR);
+
+    if (!IN_ED_FIELD(cursor_x, cursor_y) ||
+       !IN_LEV_FIELD(cursor_x + level_xpos, cursor_y + level_ypos))
+    {
+      delete_old_brush = FALSE;
+      return;
+    }
+
+    for (y=0; y<brush_height; y++)
+    {
+      for (x=0; x<brush_width; x++)
+      {
+       int sx = cursor_from_x + x;
+       int sy = cursor_from_y + y;
+       int lx = sx + level_xpos;
+       int ly = sy + level_ypos;
+       boolean change_level = (mode == CB_BRUSH_TO_LEVEL);
+       int element = (mode == CB_DELETE_OLD_CURSOR ? -1 :
+                      mode == CB_BRUSH_TO_CURSOR || button == 1 ?
+                      brush_buffer[x][y] : new_element);
+
+       if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
+       {
+         if (sx < border_from_x)
+           border_from_x = sx;
+         else if (sx > border_to_x)
+           border_to_x = sx;
+         if (sy < border_from_y)
+           border_from_y = sy;
+         else if (sy > border_to_y)
+           border_to_y = sy;
+
+         DrawLineElement(sx, sy, element, change_level);
+       }
+      }
+    }
+
+    /*
+    printf("%d, %d - %d, %d in level and screen\n",
+          border_from_x, border_from_y, border_to_x, border_to_y);
+    */
+
+    if (mode != CB_DELETE_OLD_CURSOR)
+      DrawAreaBorder(border_from_x, border_from_y, border_to_x, border_to_y);
+
+    /*
+    if (mode == CB_BRUSH_TO_LEVEL)
+      CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
+    */
+
+    last_cursor_x = cursor_x;
+    last_cursor_y = cursor_y;
+    delete_old_brush = TRUE;
+  }
+}
+
+static void CopyAreaToBrush(int from_x, int from_y, int to_x, int to_y,
+                           int button)
+{
+  CopyBrushExt(from_x, from_y, to_x, to_y, button, CB_AREA_TO_BRUSH);
+}
+
+static void CopyBrushToLevel(int x, int y, int button)
+{
+  CopyBrushExt(x, y, 0, 0, button, CB_BRUSH_TO_LEVEL);
+}
+
+static void CopyBrushToCursor(int x, int y)
+{
+  CopyBrushExt(x, y, 0, 0, 0, CB_BRUSH_TO_CURSOR);
+}
+
+static void DeleteBrushFromCursor()
+{
+  CopyBrushExt(0, 0, 0, 0, 0, CB_DELETE_OLD_CURSOR);
+}
+
+static void FloodFill(int from_x, int from_y, int fill_element)
 {
   int i,x,y;
   int old_element;
@@ -649,624 +2810,1252 @@ void FloodFill(int from_x, int from_y, int fill_element)
       FloodFill(x, y, fill_element);
   }
 
-  safety--;
+  safety--;
+}
+
+/* values for DrawLevelText() modes */
+#define TEXT_INIT              0
+#define TEXT_SETCURSOR         1
+#define TEXT_WRITECHAR         2
+#define TEXT_BACKSPACE         3
+#define TEXT_NEWLINE           4
+#define TEXT_END               5
+#define TEXT_QUERY_TYPING      6
+
+static int DrawLevelText(int sx, int sy, char letter, int mode)
+{
+  static short delete_buffer[MAX_LEV_FIELDX];
+  static int start_sx, start_sy;
+  static int last_sx, last_sy;
+  static boolean typing = FALSE;
+  int letter_element = EL_CHAR_ASCII0 + letter;
+  int lx = 0, ly = 0;
+
+  /* map lower case letters to upper case and convert special characters */
+  if (letter >= 'a' && letter <= 'z')
+    letter_element = EL_CHAR_ASCII0 + letter + (int)('A' - 'a');
+  else if (letter == 'ä' || letter == 'Ä')
+    letter_element = EL_CHAR_AE;
+  else if (letter == 'ö' || letter == 'Ö')
+    letter_element = EL_CHAR_OE;
+  else if (letter == 'ü' || letter == 'Ãœ')
+    letter_element = EL_CHAR_UE;
+  else if (letter == '^')
+    letter_element = EL_CHAR_COPY;
+  else
+    letter_element = EL_CHAR_ASCII0 + letter;
+
+  if (mode != TEXT_INIT)
+  {
+    if (!typing)
+      return FALSE;
+
+    if (mode != TEXT_SETCURSOR)
+    {
+      sx = last_sx;
+      sy = last_sy;
+    }
+
+    lx = last_sx + level_xpos;
+    ly = last_sy + level_ypos;
+  }
+
+  switch (mode)
+  {
+    case TEXT_INIT:
+      if (typing)
+       DrawLevelText(0, 0, 0, TEXT_END);
+
+      typing = TRUE;
+      start_sx = last_sx = sx;
+      start_sy = last_sy = sy;
+      DrawLevelText(sx, sy, 0, TEXT_SETCURSOR);
+      break;
+
+    case TEXT_SETCURSOR:
+      DrawMiniElement(last_sx, last_sy, Feld[lx][ly]);
+      DrawAreaBorder(sx, sy, sx, sy);
+      last_sx = sx;
+      last_sy = sy;
+      break;
+
+    case TEXT_WRITECHAR:
+      if (letter_element >= EL_CHAR_START && letter_element <= EL_CHAR_END)
+      {
+       delete_buffer[sx - start_sx] = Feld[lx][ly];
+       Feld[lx][ly] = letter_element;
+
+       if (sx + 1 < ed_fieldx && lx + 1 < lev_fieldx)
+         DrawLevelText(sx + 1, sy, 0, TEXT_SETCURSOR);
+       else if (sy + 1 < ed_fieldy && ly + 1 < lev_fieldy)
+         DrawLevelText(start_sx, sy + 1, 0, TEXT_SETCURSOR);
+       else
+         DrawLevelText(0, 0, 0, TEXT_END);
+      }
+      break;
+
+    case TEXT_BACKSPACE:
+      if (sx > start_sx)
+      {
+       Feld[lx - 1][ly] = delete_buffer[sx - start_sx - 1];
+       DrawMiniElement(sx - 1, sy, Feld[lx - 1][ly]);
+       DrawLevelText(sx - 1, sy, 0, TEXT_SETCURSOR);
+      }
+      break;
+
+    case TEXT_NEWLINE:
+      if (sy + 1 < ed_fieldy - 1 && ly + 1 < lev_fieldy - 1)
+       DrawLevelText(start_sx, sy + 1, 0, TEXT_SETCURSOR);
+      else
+       DrawLevelText(0, 0, 0, TEXT_END);
+      break;
+
+    case TEXT_END:
+      CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
+      DrawMiniElement(sx, sy, Feld[lx][ly]);
+      typing = FALSE;
+      break;
+
+    case TEXT_QUERY_TYPING:
+      break;
+
+    default:
+      break;
+  }
+
+  return typing;
+}
+
+static void SetTextCursor(int unused_sx, int unused_sy, int sx, int sy,
+                         int element, boolean change_level)
+{
+  int lx = sx + level_xpos;
+  int ly = sy + level_ypos;
+
+  if (element == -1)
+    DrawMiniElement(sx, sy, Feld[lx][ly]);
+  else
+    DrawAreaBorder(sx, sy, sx, sy);
+}
+
+static void CopyLevelToUndoBuffer(int mode)
+{
+  static boolean accumulated_undo = FALSE;
+  boolean new_undo_buffer_position = TRUE;
+  int last_border_element;
+  int x, y;
+
+  switch (mode)
+  {
+    case UNDO_IMMEDIATE:
+      accumulated_undo = FALSE;
+      break;
+
+    case UNDO_ACCUMULATE:
+      if (accumulated_undo)
+       new_undo_buffer_position = FALSE;
+      accumulated_undo = TRUE;
+      break;
+
+    default:
+      break;
+  }
+
+  if (new_undo_buffer_position)
+  {
+    /* new position in undo buffer ring */
+    undo_buffer_position = (undo_buffer_position + 1) % NUM_UNDO_STEPS;
+
+    if (undo_buffer_steps < NUM_UNDO_STEPS - 1)
+      undo_buffer_steps++;
+  }
+
+  for(x=0; x<lev_fieldx; x++)
+    for(y=0; y<lev_fieldy; y++)
+      UndoBuffer[undo_buffer_position][x][y] = Feld[x][y];
+
+  /* check if change of border style was forced by drawing operation */
+  last_border_element = BorderElement;
+  SetBorderElement();
+  if (BorderElement != last_border_element)
+    DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
+
+#if 0
+#ifdef DEBUG
+  printf("level saved to undo buffer\n");
+#endif
+#endif
+}
+
+static void RandomPlacement(int new_element)
+{
+  static boolean free_position[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+  int num_free_positions;
+  int num_percentage;
+  int num_elements;
+  int x, y;
+
+  /* determine number of free positions for the new elements */
+  /* (maybe this statement should be formatted a bit more readable...) */
+  num_free_positions = 0;
+  for (x=0; x<lev_fieldx; x++)
+    for (y=0; y<lev_fieldy; y++)
+      if ((free_position[x][y] =
+          ((random_placement_background_restricted &&
+            Feld[x][y] == random_placement_background_element) ||
+           (!random_placement_background_restricted &&
+            Feld[x][y] != new_element))) == TRUE)
+       num_free_positions++;
+
+  /* determine number of new elements to place there */
+  num_percentage = num_free_positions * random_placement_value / 100;
+  num_elements = (random_placement_method == RANDOM_USE_PERCENTAGE ?
+                 num_percentage : random_placement_value);
+
+  /* if not more free positions than elements to place, fill whole level */
+  if (num_elements >= num_free_positions)
+  {
+    for (x=0; x<lev_fieldx; x++)
+      for (y=0; y<lev_fieldy; y++)
+       Feld[x][y] = new_element;
+
+    DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
+    CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
+    return;
+  }
+
+  while (num_elements > 0)
+  {
+    x = RND(lev_fieldx);
+    y = RND(lev_fieldy);
+
+    /* don't place element at the same position twice */
+    if (free_position[x][y])
+    {
+      free_position[x][y] = FALSE;
+      Feld[x][y] = new_element;
+      num_elements--;
+    }
+  }
+
+  DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
+  CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
+}
+
+void WrapLevel(int dx, int dy)
+{
+  int wrap_dx = lev_fieldx - dx;
+  int wrap_dy = lev_fieldy - dy;
+  int x, y;
+
+  for(x=0; x<lev_fieldx; x++)
+    for(y=0; y<lev_fieldy; y++)
+      FieldBackup[x][y] = Feld[x][y];
+
+  for(x=0; x<lev_fieldx; x++)
+    for(y=0; y<lev_fieldy; y++)
+      Feld[x][y] =
+       FieldBackup[(x + wrap_dx) % lev_fieldx][(y + wrap_dy) % lev_fieldy];
+
+  DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
+  CopyLevelToUndoBuffer(UNDO_ACCUMULATE);
 }
 
-void LevelEd(int mx, int my, int button)
+static void HandleDrawingAreas(struct GadgetInfo *gi)
 {
-  static int last_button = 0;
-  static int in_field_pressed = FALSE;
-  static boolean use_floodfill = FALSE;
-  int x = (mx-SX)/MINI_TILEX; 
-  int y = (my-SY)/MINI_TILEY; 
+  static boolean started_inside_drawing_area = FALSE;
+  int id = gi->custom_id;
+  boolean button_press_event;
+  boolean button_release_event;
+  boolean inside_drawing_area = !gi->event.off_borders;
+  boolean draw_level = (id == GADGET_ID_DRAWING_LEVEL);
+  int actual_drawing_function;
+  int button = gi->event.button;
+  int new_element = BUTTON_ELEMENT(button);
+  int sx = gi->event.x, sy = gi->event.y;
+  int min_sx = 0, min_sy = 0;
+  int max_sx = gi->drawing.area_xsize - 1, max_sy = gi->drawing.area_ysize - 1;
+  int lx = 0, ly = 0;
+  int min_lx = 0, min_ly = 0;
+  int max_lx = lev_fieldx - 1, max_ly = lev_fieldy - 1;
+  int x, y;
+
+  /* handle info callback for each invocation of action callback */
+  gi->callback_info(gi);
+
+  /*
+  if (edit_mode != ED_MODE_DRAWING)
+    return;
+  */
+
+  button_press_event = (gi->event.type == GD_EVENT_PRESSED);
+  button_release_event = (gi->event.type == GD_EVENT_RELEASED);
+
+  /* make sure to stay inside drawing area boundaries */
+  sx = (sx < min_sx ? min_sx : sx > max_sx ? max_sx : sx);
+  sy = (sy < min_sy ? min_sy : sy > max_sy ? max_sy : sy);
 
-  if (use_floodfill)           /********** FLOOD FILL **********/
+  if (draw_level)
   {
-    if (button)
-    {
-      if (mx>=SX && mx<SX+SXSIZE && my>=SY && my<SY+SYSIZE)
-      {
-       int from_x, from_y;
-       int fill_element;
-
-       if (x>lev_fieldx || y>lev_fieldy ||
-           (x==0 && level_xpos<0) ||
-           (x==2*SCR_FIELDX-1 && level_xpos>lev_fieldx-2*SCR_FIELDX) ||
-           (y==0 && level_ypos<0) ||
-           (y==2*SCR_FIELDY-1 && level_ypos>lev_fieldy-2*SCR_FIELDY))
-         return;
-
-       from_x = x+level_xpos;
-       from_y = y+level_ypos;
-       fill_element = (button==1 ? new_element1 :
-                       button==2 ? new_element2 :
-                       button==3 ? new_element3 : 0);
-
-       FloodFill(from_x,from_y,fill_element);
-       DrawMiniLevel(level_xpos,level_ypos);
-      }
+    /* get positions inside level field */
+    lx = sx + level_xpos;
+    ly = sy + level_ypos;
 
-      use_floodfill = FALSE;
-      CloseDoor(DOOR_CLOSE_1);
-      OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
-    }
-    return;
+    if (!IN_LEV_FIELD(lx, ly))
+      inside_drawing_area = FALSE;
+
+    /* make sure to stay inside level field boundaries */
+    lx = (lx < min_lx ? min_lx : lx > max_lx ? max_lx : lx);
+    ly = (ly < min_ly ? min_ly : ly > max_ly ? max_ly : ly);
+
+    /* correct drawing area positions accordingly */
+    sx = lx - level_xpos;
+    sy = ly - level_ypos;
   }
-  else                         /********** EDIT/CTRL-FENSTER **********/
-  {
-    static unsigned long choice_delay = 0;
-    int choice = CheckElemButtons(mx,my,button);
-    int elem_pos = choice-ED_BUTTON_ELEM;
 
-    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))
-    {
-      int i, step;
+  if (button_press_event)
+    started_inside_drawing_area = inside_drawing_area;
 
-      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);
+  if (!started_inside_drawing_area)
+    return;
 
-      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 (!button && !button_release_event)
+    return;
 
-      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))
-      {
-       case ED_BUTTON_CTRL:
-         CloseDoor(DOOR_CLOSE_2);
-         DrawControlWindow();
-         XCopyArea(display,pix[PIX_DOOR],pix[PIX_DB_DOOR],gc,
-                   DOOR_GFX_PAGEX4,DOOR_GFX_PAGEY1+80,
-                   VXSIZE,VYSIZE,
-                   DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY2);
-         OpenDoor(DOOR_OPEN_2);
-         edit_mode = FALSE;
-         break;
-       case ED_BUTTON_FILL:
-         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;
-
-           level_xpos -= (button==1 ? 1 : button==2 ? 5 : lev_fieldx);
-           if (level_xpos<-1)
-             level_xpos = -1;
-           if (button==1)
-             ScrollMiniLevel(level_xpos,level_ypos,ED_SCROLL_RIGHT);
-           else
-             DrawMiniLevel(level_xpos,level_ypos);
-         }
-         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;
-
-           level_xpos += (button==1 ? 1 : button==2 ? 5 : lev_fieldx);
-           if (level_xpos>lev_fieldx-2*SCR_FIELDX+1)
-             level_xpos = lev_fieldx-2*SCR_FIELDX+1;
-           if (button==1)
-             ScrollMiniLevel(level_xpos,level_ypos,ED_SCROLL_LEFT);
-           else
-             DrawMiniLevel(level_xpos,level_ypos);
-         }
-         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;
-
-           level_ypos -= (button==1 ? 1 : button==2 ? 5 : lev_fieldy);
-           if (level_ypos<-1)
-             level_ypos = -1;
-           if (button==1)
-             ScrollMiniLevel(level_xpos,level_ypos,ED_SCROLL_DOWN);
-           else
-             DrawMiniLevel(level_xpos,level_ypos);
-         }
-         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;
-
-           level_ypos += (button==1 ? 1 : button==2 ? 5 : lev_fieldy);
-           if (level_ypos>lev_fieldy-2*SCR_FIELDY+1)
-             level_ypos = lev_fieldy-2*SCR_FIELDY+1;
-           if (button==1)
-             ScrollMiniLevel(level_xpos,level_ypos,ED_SCROLL_UP);
-           else
-             DrawMiniLevel(level_xpos,level_ypos);
-         }
-         break;
-       default:
-         break;
-      }
 
-      if (mx>=SX && mx<SX+SXSIZE && my>=SY && my<SY+SYSIZE)
-      {
-       int new_element;
+#if 0
+  if (button_release_event)
+    button = 0;
+#endif
 
-       if (button && !motion_status)
-         in_field_pressed = TRUE;
+#if 0
+  if (!draw_level && drawing_function != GADGET_ID_SINGLE_ITEMS)
+    return;
+#endif
+
+  /* automatically switch to 'single item' drawing mode, if needed */
+  actual_drawing_function =
+    (draw_level ? drawing_function : GADGET_ID_SINGLE_ITEMS);
+
+  switch (actual_drawing_function)
+  {
+    case GADGET_ID_SINGLE_ITEMS:
+      if (draw_level)
+      {
+       if (button_release_event)
+       {
+         CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
 
-       if (!button || !in_field_pressed || button<1 || button>3 ||
-           (y==0 && level_ypos<0) ||
-           (y==2*SCR_FIELDY-1 && level_ypos>lev_fieldy-2*SCR_FIELDY) ||
-           (x==0 && level_xpos<0) ||
-           (x==2*SCR_FIELDX-1 && level_xpos>lev_fieldx-2*SCR_FIELDX) ||
-           x>lev_fieldx || y>lev_fieldy)
-         return;
+         if (edit_mode == ED_MODE_DRAWING && draw_with_brush &&
+             !inside_drawing_area)
+           DeleteBrushFromCursor();
+       }
 
-       new_element = (button==1 ? new_element1 :
-                      button==2 ? new_element2 :
-                      button==3 ? new_element3 : 0);
+       if (!button)
+         break;
 
-       if (new_element != Feld[x+level_xpos][y+level_ypos])
+       if (draw_with_brush)
+       {
+         if (!button_release_event)
+           CopyBrushToLevel(sx, sy, button);
+       }
+       else if (new_element != Feld[lx][ly])
        {
-         if (new_element==EL_SPIELFIGUR) /* Jeder nur EINE Figur bitte... */
+         if (new_element == EL_SPIELFIGUR)
          {
-           int x,y;
-
-           for(x=0;x<lev_fieldx;x++) for(y=0;y<lev_fieldy;y++)
+           /* remove player at old position */
+           for(y=0; y<lev_fieldy; y++)
            {
-             if (Feld[x][y]==EL_SPIELFIGUR || Feld[x][y]==EL_SPIELER1)
+             for(x=0; x<lev_fieldx; x++)
              {
-               Feld[x][y] = EL_LEERRAUM;
-               if (x-level_xpos>=0 && x-level_xpos<2*SCR_FIELDX &&
-                   y-level_ypos>=0 && y-level_ypos<2*SCR_FIELDY)
-                 DrawMiniElement(x-level_xpos,y-level_ypos,EL_LEERRAUM);
+               if (Feld[x][y] == EL_SPIELFIGUR || Feld[x][y] == EL_SPIELER1)
+               {
+                 Feld[x][y] = EL_LEERRAUM;
+                 if (x - level_xpos >= 0 && x - level_xpos < ed_fieldx &&
+                     y - level_ypos >= 0 && y - level_ypos < ed_fieldy)
+                   DrawMiniElement(x - level_xpos, y - level_ypos,
+                                   EL_LEERRAUM);
+               }
              }
            }
          }
 
-         Feld[x+level_xpos][y+level_ypos] = new_element;
-         DrawMiniElement(x,y,new_element);
+         Feld[lx][ly] = new_element;
+         DrawMiniElement(sx, sy, new_element);
        }
       }
-      else if (!motion_status) /* Mauszeiger nicht im Level-Feld */
-       in_field_pressed = FALSE;
-    }
-    else                       /********** KONTROLL-FENSTER **********/
-    {
-      int choice = CheckCountButtons(mx,my,button);
-      int step = (button==1 ? 1 : button==2 ? 5 : button==3 ? 10 : 0);
+      else
+      {
+       DrawMiniGraphicExt(drawto, gc,
+                          gi->x + sx * MINI_TILEX,
+                          gi->y + sy * MINI_TILEY,
+                          el2gfx(new_element));
+       DrawMiniGraphicExt(window, gc,
+                          gi->x + sx * MINI_TILEX,
+                          gi->y + sy * MINI_TILEY,
+                          el2gfx(new_element));
+
+       if (id == GADGET_ID_AMOEBA_CONTENT)
+         level.amoebe_inhalt = new_element;
+       else if (id == GADGET_ID_RANDOM_BACKGROUND)
+         random_placement_background_element = new_element;
+       else if (id >= GADGET_ID_ELEM_CONTENT_0 &&
+                id <= GADGET_ID_ELEM_CONTENT_7)
+         level.mampfer_inhalt[id - GADGET_ID_ELEM_CONTENT_0][sx][sy] =
+           new_element;
+      }
+      break;
 
-      if (choice >= 0 && choice < 36 &&
-         DelayReached(&choice_delay, CHOICE_DELAY_VALUE))
+    case GADGET_ID_CONNECTED_ITEMS:
       {
-       if (!(choice % 2))
-         step = -step;
+       static int last_sx = -1;
+       static int last_sy = -1;
 
-       choice /= 2;
+       if (button_release_event)
+         CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
 
-       if (choice<11)
-       {
-         level.score[choice] += step;
-         if (level.score[choice]<0)
-           level.score[choice] = 0;
-         else if (level.score[choice]>255)
-           level.score[choice] = 255;
-       }
-       else if (choice==11)
-       {
-         level.tempo_amoebe += step;
-         if (level.tempo_amoebe<0)
-           level.tempo_amoebe = 0;
-         else if (level.tempo_amoebe>255)
-           level.tempo_amoebe = 255;
-       }
-       else if (choice==12)
-       {
-         level.dauer_sieb += step;
-         if (level.dauer_sieb<0)
-           level.dauer_sieb = 0;
-         else if (level.dauer_sieb>255)
-           level.dauer_sieb = 255;
-       }
-       else if (choice==13)
+       if (button)
        {
-         level.dauer_ablenk += step;
-         if (level.dauer_ablenk<0)
-           level.dauer_ablenk = 0;
-         else if (level.dauer_ablenk>255)
-           level.dauer_ablenk = 255;
-       }
-       else if (choice==14)
-       {
-         level.edelsteine += step;
-         if (level.edelsteine<0)
-           level.edelsteine = 0;
-         else if (level.edelsteine>999)
-           level.edelsteine = 999;
+         if (!button_press_event)
+           DrawLine(last_sx, last_sy, sx, sy, new_element, TRUE);
+
+         last_sx = sx;
+         last_sy = sy;
        }
-       else if (choice==15)
+      }
+      break;
+
+    case GADGET_ID_LINE:
+    case GADGET_ID_ARC:
+    case GADGET_ID_RECTANGLE:
+    case GADGET_ID_FILLED_BOX:
+    case GADGET_ID_GRAB_BRUSH:
+    case GADGET_ID_TEXT:
+      {
+       static int last_sx = -1;
+       static int last_sy = -1;
+       static int start_sx = -1;
+       static int start_sy = -1;
+       void (*draw_func)(int, int, int, int, int, boolean);
+
+       if (drawing_function == GADGET_ID_LINE)
+         draw_func = DrawLine;
+       else if (drawing_function == GADGET_ID_ARC)
+         draw_func = DrawArc;
+       else if (drawing_function == GADGET_ID_RECTANGLE)
+         draw_func = DrawRectangle;
+       else if (drawing_function == GADGET_ID_FILLED_BOX)
+         draw_func = DrawFilledBox;
+       else if (drawing_function == GADGET_ID_GRAB_BRUSH)
+         draw_func = SelectArea;
+       else /* (drawing_function == GADGET_ID_TEXT) */
+         draw_func = SetTextCursor;
+
+       if (button_press_event)
        {
-         level.time += step;
-         if (level.time<0)
-           level.time = 0;
-         else if (level.time>999)
-           level.time = 999;
+         draw_func(sx, sy, sx, sy, new_element, FALSE);
+         start_sx = last_sx = sx;
+         start_sy = last_sy = sy;
+
+         if (drawing_function == GADGET_ID_TEXT)
+           DrawLevelText(0, 0, 0, TEXT_END);
        }
-       else if (choice==16)
+       else if (button_release_event)
        {
-         lev_fieldx += step;
-         if (lev_fieldx<MIN_LEV_FIELDX)
-           lev_fieldx = MIN_LEV_FIELDX;
-         else if (lev_fieldx>MAX_LEV_FIELDX)
-           lev_fieldx = MAX_LEV_FIELDX;
-         level.fieldx = lev_fieldx;
+         draw_func(start_sx, start_sy, sx, sy, new_element, TRUE);
+         if (drawing_function == GADGET_ID_GRAB_BRUSH)
+         {
+           CopyAreaToBrush(start_sx, start_sy, sx, sy, button);
+           CopyBrushToCursor(sx, sy);
+           ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS],MB_LEFT);
+           draw_with_brush = TRUE;
+         }
+         else if (drawing_function == GADGET_ID_TEXT)
+           DrawLevelText(sx, sy, 0, TEXT_INIT);
+         else
+           CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
        }
-       else if (choice==17)
+       else if (last_sx != sx || last_sy != sy)
        {
-         lev_fieldy += step;
-         if (lev_fieldy<MIN_LEV_FIELDY)
-           lev_fieldy = MIN_LEV_FIELDY;
-         else if (lev_fieldy>MAX_LEV_FIELDY)
-           lev_fieldy = MAX_LEV_FIELDY;
-         level.fieldy = lev_fieldy;
+         draw_func(start_sx, start_sy, last_sx, last_sy, -1, FALSE);
+         draw_func(start_sx, start_sy, sx, sy, new_element, FALSE);
+         last_sx = sx;
+         last_sy = sy;
        }
+      }
+      break;
 
-       if (choice<11)
-         DrawText(ED_COUNT_VALUE_XPOS,
-                  ED_COUNT_VALUE_YPOS+choice*ED_COUNT_GADGET_YSIZE,
-                  int2str(level.score[choice],3),FS_SMALL,FC_YELLOW);
-       else if (choice==11)
-         DrawText(ED_COUNT_VALUE_XPOS,
-                  ED_COUNT_VALUE_YPOS+11*ED_COUNT_GADGET_YSIZE,
-                  int2str(level.tempo_amoebe,3),FS_SMALL,FC_YELLOW);
-       else if (choice==12)
-         DrawText(ED_COUNT_VALUE_XPOS,
-                  ED_COUNT_VALUE_YPOS+12*ED_COUNT_GADGET_YSIZE,
-                  int2str(level.dauer_sieb,3),FS_SMALL,FC_YELLOW);
-       else if (choice==13)
-         DrawText(ED_COUNT_VALUE_XPOS,
-                  ED_COUNT_VALUE_YPOS+13*ED_COUNT_GADGET_YSIZE,
-                  int2str(level.dauer_ablenk,3),FS_SMALL,FC_YELLOW);
-       else if (choice==14)
-         DrawText(ED_COUNT_VALUE_XPOS,
-                  ED_COUNT_VALUE_YPOS+14*ED_COUNT_GADGET_YSIZE,
-                  int2str(level.edelsteine,3),FS_SMALL,FC_YELLOW);
-       else if (choice==15)
-         DrawText(ED_COUNT_VALUE_XPOS,
-                  ED_COUNT_VALUE_YPOS+15*ED_COUNT_GADGET_YSIZE,
-                  int2str(level.time,3),FS_SMALL,FC_YELLOW);
-       else if (choice==16)
-         DrawText(ED_SIZE_VALUE_XPOS,
-                  ED_SIZE_VALUE_YPOS+0*ED_SIZE_GADGET_YSIZE,
-                  int2str(level.fieldx,3),FS_SMALL,FC_YELLOW);
-       else if (choice==17)
-         DrawText(ED_SIZE_VALUE_XPOS,
-                  ED_SIZE_VALUE_YPOS+1*ED_SIZE_GADGET_YSIZE,
-                  int2str(level.fieldy,3),FS_SMALL,FC_YELLOW);
-
-       redraw_mask &= ~REDRAW_FIELD;
-       if (choice<16)
-         XCopyArea(display,drawto,window,gc,
-                   ED_COUNT_VALUE_XPOS,
-                   ED_COUNT_VALUE_YPOS+choice*ED_COUNT_GADGET_YSIZE,
-                   3*FONT2_XSIZE,FONT2_YSIZE,
-                   ED_COUNT_VALUE_XPOS,
-                   ED_COUNT_VALUE_YPOS+choice*ED_COUNT_GADGET_YSIZE);
-       else
-         XCopyArea(display,drawto,window,gc,
-                   ED_SIZE_VALUE_XPOS,
-                   ED_SIZE_VALUE_YPOS+(choice-16)*ED_SIZE_GADGET_YSIZE,
-                   3*FONT2_XSIZE,FONT2_YSIZE,
-                   ED_SIZE_VALUE_XPOS,
-                   ED_SIZE_VALUE_YPOS+(choice-16)*ED_SIZE_GADGET_YSIZE);
-       XFlush(display);
+    case GADGET_ID_FLOOD_FILL:
+      if (button_press_event && Feld[lx][ly] != new_element)
+      {
+       FloodFill(lx, ly, new_element);
+       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
+       CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
       }
+      break;
+
+    case GADGET_ID_PICK_ELEMENT:
+
+      /*
+      if (button_press_event)
+       PickDrawingElement(button, Feld[lx][ly]);
+      */
+
+      if (button_release_event)
+       ClickOnGadget(level_editor_gadget[last_drawing_function], MB_LEFT);
+      else
+       PickDrawingElement(button, Feld[lx][ly]);
+
+      break;
+
+    default:
+      break;
+  }
+}
+
+static void HandleCounterButtons(struct GadgetInfo *gi)
+{
+  int id = gi->custom_id;
+  int button = gi->event.button;
+  int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
+
+  switch (id)
+  {
+    case GADGET_ID_ELEM_SCORE_DOWN:
+    case GADGET_ID_ELEM_SCORE_UP:
+      step *= (id == GADGET_ID_ELEM_SCORE_DOWN ? -1 : 1);
+      ModifyEditorCounter(ED_COUNTER_ID_ELEM_SCORE,
+                         *gadget_elem_score_value + step);
+      break;
+    case GADGET_ID_ELEM_SCORE_TEXT:
+      *gadget_elem_score_value = gi->text.number_value;
+      break;
+
+    case GADGET_ID_ELEM_CONTENT_DOWN:
+    case GADGET_ID_ELEM_CONTENT_UP:
+      step *= (id == GADGET_ID_ELEM_CONTENT_DOWN ? -1 : 1);
+      ModifyEditorCounter(ED_COUNTER_ID_ELEM_CONTENT,
+                         *gadget_elem_content_value + step);
+      DrawElementContentAreas();
+      break;
+    case GADGET_ID_ELEM_CONTENT_TEXT:
+      *gadget_elem_content_value = gi->text.number_value;
+      DrawElementContentAreas();
+      break;
+
+    case GADGET_ID_LEVEL_XSIZE_DOWN:
+    case GADGET_ID_LEVEL_XSIZE_UP:
+      step *= (id == GADGET_ID_LEVEL_XSIZE_DOWN ? -1 : 1);
+      ModifyEditorCounter(ED_COUNTER_ID_LEVEL_XSIZE,
+                         *gadget_level_xsize_value + step);
+      level.fieldx = lev_fieldx;
+      break;
+    case GADGET_ID_LEVEL_XSIZE_TEXT:
+      *gadget_level_xsize_value = gi->text.number_value;
+      level.fieldx = lev_fieldx;
+      break;
+
+    case GADGET_ID_LEVEL_YSIZE_DOWN:
+    case GADGET_ID_LEVEL_YSIZE_UP:
+      step *= (id == GADGET_ID_LEVEL_YSIZE_DOWN ? -1 : 1);
+      ModifyEditorCounter(ED_COUNTER_ID_LEVEL_YSIZE,
+                         *gadget_level_ysize_value + step);
+      level.fieldy = lev_fieldy;
+      break;
+    case GADGET_ID_LEVEL_YSIZE_TEXT:
+      *gadget_level_ysize_value = gi->text.number_value;
+      level.fieldy = lev_fieldy;
+      break;
+
+    case GADGET_ID_LEVEL_RANDOM_DOWN:
+    case GADGET_ID_LEVEL_RANDOM_UP:
+      step *= (id == GADGET_ID_LEVEL_RANDOM_DOWN ? -1 : 1);
+      ModifyEditorCounter(ED_COUNTER_ID_LEVEL_RANDOM,
+                         *gadget_level_random_value + step);
+      break;
+    case GADGET_ID_LEVEL_RANDOM_TEXT:
+      *gadget_level_random_value = gi->text.number_value;
+      break;
+
+    case GADGET_ID_LEVEL_COLLECT_DOWN:
+    case GADGET_ID_LEVEL_COLLECT_UP:
+      step *= (id == GADGET_ID_LEVEL_COLLECT_DOWN ? -1 : 1);
+      ModifyEditorCounter(ED_COUNTER_ID_LEVEL_COLLECT,
+                         *gadget_level_collect_value + step);
+      break;
+    case GADGET_ID_LEVEL_COLLECT_TEXT:
+      *gadget_level_collect_value = gi->text.number_value;
+      break;
+
+    case GADGET_ID_LEVEL_TIMELIMIT_DOWN:
+    case GADGET_ID_LEVEL_TIMELIMIT_UP:
+      step *= (id == GADGET_ID_LEVEL_TIMELIMIT_DOWN ? -1 : 1);
+      ModifyEditorCounter(ED_COUNTER_ID_LEVEL_TIMELIMIT,
+                         *gadget_level_timelimit_value + step);
+      break;
+    case GADGET_ID_LEVEL_TIMELIMIT_TEXT:
+      *gadget_level_timelimit_value = gi->text.number_value;
+      break;
+
+    case GADGET_ID_LEVEL_TIMESCORE_DOWN:
+    case GADGET_ID_LEVEL_TIMESCORE_UP:
+      step *= (id == GADGET_ID_LEVEL_TIMESCORE_DOWN ? -1 : 1);
+      ModifyEditorCounter(ED_COUNTER_ID_LEVEL_TIMESCORE,
+                         *gadget_level_timescore_value + step);
+      break;
+    case GADGET_ID_LEVEL_TIMESCORE_TEXT:
+      *gadget_level_timescore_value = gi->text.number_value;
+      break;
+
+    default:
+      break;
+  }
+}
+
+static void HandleTextInputGadgets(struct GadgetInfo *gi)
+{
+  int id = gi->custom_id;
+
+  switch (id)
+  {
+    case GADGET_ID_LEVEL_NAME:
+      strcpy(level.name, gi->text.value);
+      break;
+
+    case GADGET_ID_LEVEL_AUTHOR:
+      strcpy(level.author, gi->text.value);
+      break;
+
+    default:
+      break;
+  }
+}
+
+static void HandleControlButtons(struct GadgetInfo *gi)
+{
+  int id = gi->custom_id;
+  int button = gi->event.button;
+  int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
+  int new_element = BUTTON_ELEMENT(button);
+  int player_present = FALSE;
+  int level_changed = FALSE;
+  int i, x, y;
+
+  if (edit_mode == ED_MODE_DRAWING && drawing_function == GADGET_ID_TEXT)
+    DrawLevelText(0, 0, 0, TEXT_END);
+
+  if (id < ED_NUM_CTRL1_BUTTONS && id != GADGET_ID_PROPERTIES &&
+      edit_mode != ED_MODE_DRAWING)
+  {
+    DrawDrawingWindow();
+    edit_mode = ED_MODE_DRAWING;
+  }
 
-      switch(CheckCtrlButtons(mx,my,button))
+  switch (id)
+  {
+    case GADGET_ID_SCROLL_LEFT:
+      if (level_xpos >= 0)
       {
-       case ED_BUTTON_EDIT:
-         CloseDoor(DOOR_CLOSE_2);
-         AdjustLevelScrollPosition();
-         DrawMiniLevel(level_xpos,level_ypos);
-         XCopyArea(display,pix[PIX_DOOR],pix[PIX_DB_DOOR],gc,
-                   DOOR_GFX_PAGEX6,DOOR_GFX_PAGEY2,
-                   VXSIZE,VYSIZE,
-                   DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY2);
-         OpenDoor(DOOR_OPEN_2);
-         edit_mode = TRUE;
-         break;
-       case ED_BUTTON_CLEAR:
-         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++) 
-               Feld[x][y] = EL_ERDREICH;
-           DrawMiniLevel(level_xpos,level_ypos);
-         }
+       int gadget_id = GADGET_ID_SCROLL_HORIZONTAL;
+       struct GadgetInfo *gi = level_editor_gadget[gadget_id];
+
+       if (lev_fieldx < ed_fieldx - 2)
          break;
-       case ED_BUTTON_UNDO:
-         if (leveldir[leveldir_nr].readonly ||
-             Request("Exit without saving ?",REQ_ASK | REQ_STAY_OPEN))
-         {
-           CloseDoor(DOOR_CLOSE_BOTH);
-           game_status=MAINMENU;
-           DrawMainMenu();
-         }
-         else
-         {
-           CloseDoor(DOOR_CLOSE_1);
-           OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
-         }
+
+       level_xpos -= step;
+       if (level_xpos < -1)
+         level_xpos = -1;
+       if (button == 1)
+         ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_RIGHT);
+       else
+         DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
+
+       ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, level_xpos + 1, GDI_END);
+      }
+      break;
+
+    case GADGET_ID_SCROLL_RIGHT:
+      if (level_xpos <= lev_fieldx - ed_fieldx)
+      {
+       int gadget_id = GADGET_ID_SCROLL_HORIZONTAL;
+       struct GadgetInfo *gi = level_editor_gadget[gadget_id];
+
+       if (lev_fieldx < ed_fieldx - 2)
          break;
-       case ED_BUTTON_EXIT:
-         {
-           int figur_vorhanden = FALSE;
 
-           if (leveldir[leveldir_nr].readonly)
-           {
-             Request("This level is read only !",REQ_CONFIRM);
-             break;
-           }
+       level_xpos += step;
+       if (level_xpos > lev_fieldx - ed_fieldx + 1)
+         level_xpos = lev_fieldx - ed_fieldx + 1;
+       if (button == 1)
+         ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_LEFT);
+       else
+         DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
 
-           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;
+       ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, level_xpos + 1, GDI_END);
+      }
+      break;
 
-           if (!figur_vorhanden)
-             Request("No Level without Gregor Mc Duffin please !",
-                        REQ_CONFIRM);
-           else
-           {
-             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++) 
-                   Ur[x][y]=Feld[x][y];
-               SaveLevel(level_nr);
-             }
-             CloseDoor(DOOR_CLOSE_BOTH);
-             game_status=MAINMENU;
-             DrawMainMenu();
-           }
-         }
+    case GADGET_ID_SCROLL_UP:
+      if (level_ypos >= 0)
+      {
+       int gadget_id = GADGET_ID_SCROLL_VERTICAL;
+       struct GadgetInfo *gi = level_editor_gadget[gadget_id];
+
+       if (lev_fieldy < ed_fieldy - 2)
          break;
-       default:
+
+       level_ypos -= step;
+       if (level_ypos < -1)
+         level_ypos = -1;
+       if (button == 1)
+         ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_DOWN);
+       else
+         DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
+
+       ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, level_ypos + 1, GDI_END);
+      }
+      break;
+
+    case GADGET_ID_SCROLL_DOWN:
+      if (level_ypos <= lev_fieldy - ed_fieldy)
+      {
+       int gadget_id = GADGET_ID_SCROLL_VERTICAL;
+       struct GadgetInfo *gi = level_editor_gadget[gadget_id];
+
+       if (lev_fieldy < ed_fieldy - 2)
          break;
+
+       level_ypos += step;
+       if (level_ypos > lev_fieldy - ed_fieldy + 1)
+         level_ypos = lev_fieldy - ed_fieldy + 1;
+       if (button == 1)
+         ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_UP);
+       else
+         DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
+
+       ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, level_ypos + 1, GDI_END);
       }
+      break;
+
+    case GADGET_ID_SCROLL_HORIZONTAL:
+      level_xpos = gi->event.item_position - 1;
+      DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
+      break;
+
+    case GADGET_ID_SCROLL_VERTICAL:
+      level_ypos = gi->event.item_position - 1;
+      DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
+      break;
+
+    case GADGET_ID_ELEMENTLIST_UP:
+    case GADGET_ID_ELEMENTLIST_DOWN:
+      step *= (id == GADGET_ID_ELEMENTLIST_UP ? -1 : +1);
+      element_shift += step * ED_ELEMENTLIST_BUTTONS_HORIZ;
 
-      if (mx>=ED_COUNT_GADGET_XPOS &&
-         mx<ED_COUNT_GADGET_XPOS+31*FONT2_XSIZE+10 &&
-         my>=ED_COUNT_GADGET_YPOS+16*ED_COUNT_GADGET_YSIZE &&
-         my<ED_COUNT_GADGET_YPOS+16*ED_COUNT_GADGET_YSIZE+ED_WIN_COUNT_YSIZE)
+      if (element_shift < 0)
+       element_shift = 0;
+      if (element_shift > elements_in_list - ED_NUM_ELEMENTLIST_BUTTONS)
+       element_shift = elements_in_list - ED_NUM_ELEMENTLIST_BUTTONS;
+
+      for (i=0; i<ED_NUM_ELEMENTLIST_BUTTONS; i++)
       {
-       if (!name_typing)
-       {
-         name_typing = TRUE;
-         DrawText(ED_COUNT_GADGET_XPOS+5,
-                  ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
-                  level.name,FS_SMALL,FC_GREEN);
-         DrawText(ED_COUNT_GADGET_XPOS+5+strlen(level.name)*FONT2_XSIZE,
-                  ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
-                  "<",FS_SMALL,FC_RED);
-       }
+       int gadget_id = GADGET_ID_ELEMENTLIST_FIRST + i;
+       struct GadgetInfo *gi = level_editor_gadget[gadget_id];
+       struct GadgetDesign *gd = &gi->deco.design;
+       int element = editor_element[element_shift + i];
+
+       UnmapGadget(gi);
+       getMiniGraphicSource(el2gfx(element), &gd->pixmap, &gd->x, &gd->y);
+       ModifyGadget(gi, GDI_INFO_TEXT, element_info[element], GDI_END);
+       MapGadget(gi);
+      }
+      break;
+
+    case GADGET_ID_WRAP_LEFT:
+      WrapLevel(-step, 0);
+      break;
+
+    case GADGET_ID_WRAP_RIGHT:
+      WrapLevel(step, 0);
+      break;
+
+    case GADGET_ID_WRAP_UP:
+      WrapLevel(0, -step);
+      break;
+
+    case GADGET_ID_WRAP_DOWN:
+      WrapLevel(0, step);
+      break;
+
+    case GADGET_ID_SINGLE_ITEMS:
+    case GADGET_ID_CONNECTED_ITEMS:
+    case GADGET_ID_LINE:
+    case GADGET_ID_ARC:
+    case GADGET_ID_TEXT:
+    case GADGET_ID_RECTANGLE:
+    case GADGET_ID_FILLED_BOX:
+    case GADGET_ID_FLOOD_FILL:
+    case GADGET_ID_GRAB_BRUSH:
+    case GADGET_ID_PICK_ELEMENT:
+      last_drawing_function = drawing_function;
+      drawing_function = id;
+      draw_with_brush = FALSE;
+      break;
+
+    case GADGET_ID_RANDOM_PLACEMENT:
+      RandomPlacement(new_element);
+      break;
+
+    case GADGET_ID_PROPERTIES:
+      if (edit_mode != ED_MODE_PROPERTIES)
+      {
+       properties_element = new_element;
+       DrawPropertiesWindow();
+       edit_mode = ED_MODE_PROPERTIES;
       }
       else
       {
-       if (name_typing)
-       {
-         name_typing = FALSE;
-         DrawText(ED_COUNT_GADGET_XPOS+5,
-                  ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
-                  level.name,FS_SMALL,FC_YELLOW);
-         DrawText(ED_COUNT_GADGET_XPOS+5+strlen(level.name)*FONT2_XSIZE,
-                  ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
-                  " ",FS_SMALL,FC_RED);
-       }
+       DrawDrawingWindow();
+       edit_mode = ED_MODE_DRAWING;
+      }
+      break;
+
+    case GADGET_ID_UNDO:
+      if (undo_buffer_steps == 0)
+      {
+       Request("Undo buffer empty !", REQ_CONFIRM);
+       break;
+      }
+
+      undo_buffer_position =
+       (undo_buffer_position - 1 + NUM_UNDO_STEPS) % NUM_UNDO_STEPS;
+      undo_buffer_steps--;
+
+      for(x=0; x<lev_fieldx; x++)
+       for(y=0; y<lev_fieldy; y++)
+         Feld[x][y] = UndoBuffer[undo_buffer_position][x][y];
+      DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos,level_ypos);
+      break;
+
+    case GADGET_ID_INFO:
+      if (edit_mode != ED_MODE_INFO)
+      {
+       DrawLevelInfoWindow();
+       edit_mode = ED_MODE_INFO;
       }
+      else
+      {
+       DrawDrawingWindow();
+       edit_mode = ED_MODE_DRAWING;
+      }
+      break;
+
+    case GADGET_ID_CLEAR:
+      for(x=0; x<MAX_LEV_FIELDX; x++) 
+       for(y=0; y<MAX_LEV_FIELDY; y++) 
+         Feld[x][y] = (button == 1 ? EL_LEERRAUM : new_element);
+      CopyLevelToUndoBuffer(GADGET_ID_CLEAR);
 
-      if (mx>=SX+29*MINI_TILEX && mx<SX+30*MINI_TILEX &&
-         my>=SY+26*MINI_TILEY && my<SY+27*MINI_TILEY)
+      DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
+      break;
+
+    case GADGET_ID_SAVE:
+      if (leveldir[leveldir_nr].readonly)
       {
-       int new_element;
+       Request("This level is read only !", REQ_CONFIRM);
+       break;
+      }
 
-       if (!button || button<1 || button>3)
-         return;
+      for(y=0; y<lev_fieldy; y++) 
+       for(x=0; x<lev_fieldx; x++)
+         if (Feld[x][y] != Ur[x][y])
+           level_changed = TRUE;
+
+      if (0 && !level_changed)
+      {
+       Request("Level has not changed !", REQ_CONFIRM);
+       break;
+      }
 
-       new_element = (button==1 ? new_element1 :
-                      button==2 ? new_element2 :
-                      button==3 ? new_element3 : 0);
+      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 ||
+             Feld[x][y] == EL_SP_MURPHY) 
+           player_present = TRUE;
 
-       if (new_element != level.amoebe_inhalt)
+      if (!player_present)
+       Request("No Level without Gregor Mc Duffin please !", REQ_CONFIRM);
+      else
+      {
+       if (Request("Save this level and kill the old ?", REQ_ASK))
        {
-         level.amoebe_inhalt = new_element;
-         DrawMiniElement(29,26,new_element);
+         for(x=0; x<lev_fieldx; x++)
+           for(y=0; y<lev_fieldy; y++) 
+             Ur[x][y] = Feld[x][y];
+         SaveLevel(level_nr);
        }
       }
+      break;
+
+    case GADGET_ID_TEST:
+      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 ||
+             Feld[x][y] == EL_SP_MURPHY) 
+           player_present = TRUE;
+
+      if (!player_present)
+       Request("No Level without Gregor Mc Duffin please !", REQ_CONFIRM);
+      else
+      {
+       for(x=0; x<lev_fieldx; x++)
+         for(y=0; y<lev_fieldy; y++)
+           FieldBackup[x][y] = Ur[x][y];
+
+       for(x=0; x<lev_fieldx; x++)
+         for(y=0; y<lev_fieldy; y++)
+           Ur[x][y] = Feld[x][y];
+
+       UnmapLevelEditorGadgets();
+
+       /* draw smaller door */
+       XCopyArea(display, pix[PIX_DOOR], drawto, gc,
+                 DOOR_GFX_PAGEX7, 64,
+                 108, 64,
+                 EX - 4, EY - 12);
+       redraw_mask |= REDRAW_ALL;
+
+       CloseDoor(DOOR_CLOSE_ALL);
+
+       DrawCompleteVideoDisplay();
+
+       if (setup.autorecord)
+         TapeStartRecording();
+
+       level_editor_test_game = TRUE;
+       game_status = PLAYING;
+
+       InitGame();
+      }
+      break;
+
+    case GADGET_ID_EXIT:
+      for(y=0; y<lev_fieldy; y++) 
+       for(x=0; x<lev_fieldx; x++)
+         if (Feld[x][y] != Ur[x][y])
+           level_changed = TRUE;
 
-      if (mx>=SX+1*MINI_TILEX && mx<SX+(1+4*5)*MINI_TILEX &&
-         my>=SY+2*MINI_TILEY && my<SY+(2+3)*MINI_TILEY)
+      if (!level_changed ||
+         Request("Level has changed! Exit without saving ?",
+                 REQ_ASK | REQ_STAY_OPEN))
       {
-       int x = (mx-SX-1*MINI_TILEX)/MINI_TILEX;
-       int y = (my-SY-2*MINI_TILEY)/MINI_TILEY;
-       int i = x/5;
-       int new_element;
+       CloseDoor(DOOR_CLOSE_1);
 
-       x = x-i*5;
-       if (i>=0 && i<43 && x>=0 && x<3 && y>=0 && y<3)
-       {
-         if (button && !motion_status)
-           in_field_pressed = TRUE;
+       /*
+       CloseDoor(DOOR_CLOSE_ALL);
+       */
+
+       /* draw smaller door */
+       XCopyArea(display, pix[PIX_DOOR], drawto, gc,
+                 DOOR_GFX_PAGEX7, 64,
+                 108, 64,
+                 EX - 4, EY - 12);
+       redraw_mask |= REDRAW_ALL;
 
-         if (!button || !in_field_pressed || button<1 || button>3)
-           return;
+       game_status = MAINMENU;
+       DrawMainMenu();
+      }
+      else
+      {
+       CloseDoor(DOOR_CLOSE_1);
+       XCopyArea(display, pix[PIX_DB_DOOR], pix[PIX_DB_DOOR], gc,
+                 DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
+                 DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+       OpenDoor(DOOR_OPEN_1);
+      }
+      break;
 
-         new_element = (button==1 ? new_element1 :
-                        button==2 ? new_element2 :
-                        button==3 ? new_element3 : 0);
+    case GADGET_ID_RANDOM_PERCENTAGE:
+      *radiobutton_info[ED_RADIOBUTTON_ID_PERCENTAGE].value =
+       radiobutton_info[ED_RADIOBUTTON_ID_PERCENTAGE].checked_value;
+      break;
 
-         if (new_element != level.mampfer_inhalt[i][x][y])
-         {
-           level.mampfer_inhalt[i][x][y] = new_element;
-           DrawMiniElement(1+5*i+x,2+y,new_element);
-         }
+    case GADGET_ID_RANDOM_QUANTITY:
+      *radiobutton_info[ED_RADIOBUTTON_ID_QUANTITY].value =
+       radiobutton_info[ED_RADIOBUTTON_ID_QUANTITY].checked_value;
+      break;
+
+    case GADGET_ID_RANDOM_RESTRICTED:
+      *checkbutton_info[ED_CHECKBUTTON_ID_RANDOM_RESTRICTED].value ^= TRUE;
+      break;
+
+    case GADGET_ID_DOUBLE_SPEED:
+      *checkbutton_info[ED_CHECKBUTTON_ID_DOUBLE_SPEED].value ^= TRUE;
+      break;
+
+    case GADGET_ID_STICK_ELEMENT:
+      *checkbutton_info[ED_CHECKBUTTON_ID_STICK_ELEMENT].value ^= TRUE;
+      break;
+
+    default:
+      if (id >= GADGET_ID_ELEMENTLIST_FIRST &&
+         id <= GADGET_ID_ELEMENTLIST_LAST)
+      {
+       int element_position = id - GADGET_ID_ELEMENTLIST_FIRST;
+       int new_element = editor_element[element_position + element_shift];
+
+       PickDrawingElement(button, new_element);
+
+       if (!HAS_CONTENT(properties_element) ||
+           !stick_element_properties_window)
+       {
+         properties_element = new_element;
+         if (edit_mode == ED_MODE_PROPERTIES)
+           DrawPropertiesWindow();
        }
-       else if (!motion_status)/* Mauszeiger nicht im Cruncher-Feld */
-         in_field_pressed = FALSE;
+
+       if (drawing_function == GADGET_ID_PICK_ELEMENT)
+         ClickOnGadget(level_editor_gadget[last_drawing_function], MB_LEFT);
       }
-      else if (!motion_status) /* Mauszeiger nicht im Cruncher-Feld */
-       in_field_pressed = FALSE;
-    }
+#ifdef DEBUG
+      else if (gi->event.type == GD_EVENT_PRESSED)
+       printf("default: HandleControlButtons: GD_EVENT_PRESSED(%d)\n", id);
+      else if (gi->event.type == GD_EVENT_RELEASED)
+       printf("default: HandleControlButtons: GD_EVENT_RELEASED(%d)\n", id);
+      else if (gi->event.type == GD_EVENT_MOVING)
+       printf("default: HandleControlButtons: GD_EVENT_MOVING(%d)\n", id);
+      else
+       printf("default: HandleControlButtons: ? (id == %d)\n", id);
+#endif
+      break;
   }
+}
 
-  last_button = button;
+void HandleLevelEditorKeyInput(KeySym key)
+{
+  char letter = getCharFromKeySym(key);
+  int button = MB_LEFT;
 
-  BackToFront();
+  if (drawing_function == GADGET_ID_TEXT &&
+      DrawLevelText(0, 0, 0, TEXT_QUERY_TYPING) == TRUE)
+  {
+    if (letter)
+      DrawLevelText(0, 0, letter, TEXT_WRITECHAR);
+    else if (key == XK_Delete || key == XK_BackSpace)
+      DrawLevelText(0, 0, 0, TEXT_BACKSPACE);
+    else if (key == XK_Return)
+      DrawLevelText(0, 0, 0, TEXT_NEWLINE);
+  }
+  else if (button_status == MB_RELEASED)
+  {
+    int i, id;
+
+    switch (key)
+    {
+      case XK_Left:
+       id = GADGET_ID_SCROLL_LEFT;
+       break;
+      case XK_Right:
+       id = GADGET_ID_SCROLL_RIGHT;
+       break;
+      case XK_Up:
+       id = GADGET_ID_SCROLL_UP;
+       break;
+      case XK_Down:
+       id = GADGET_ID_SCROLL_DOWN;
+       break;
+      case XK_Page_Up:
+       id = GADGET_ID_ELEMENTLIST_UP;
+       button = 3;
+       break;
+      case XK_Page_Down:
+       id = GADGET_ID_ELEMENTLIST_DOWN;
+       button = 3;
+       break;
+
+      default:
+       id = GADGET_ID_NONE;
+       break;
+    }
+
+    if (id != GADGET_ID_NONE)
+      ClickOnGadget(level_editor_gadget[id], button);
+    else if (letter == '.')
+      ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS], button);
+    else
+      for (i=0; i<ED_NUM_CTRL_BUTTONS; i++)
+       if (letter && letter == control_info[i].shortcut)
+         if (!anyTextGadgetActive())
+           ClickOnGadget(level_editor_gadget[i], button);
+  }
 }
 
-void LevelNameTyping(KeySym key)
+void ClearEditorGadgetInfoText()
 {
-  unsigned char ascii = 0;
-  int len = strlen(level.name);
+  XFillRectangle(display, drawto, gc,
+                INFOTEXT_XPOS, INFOTEXT_YPOS, INFOTEXT_XSIZE, INFOTEXT_YSIZE);
+  redraw_mask |= REDRAW_FIELD;
+}
+
+void HandleEditorGadgetInfoText(void *ptr)
+{
+  struct GadgetInfo *gi = (struct GadgetInfo *)ptr;
+  char infotext[MAX_INFOTEXT_LEN + 1];
+  char shortcut[20];
 
-  if (!name_typing)
+  ClearEditorGadgetInfoText();
+
+  /* misuse this function to delete brush cursor, if needed */
+  if (edit_mode == ED_MODE_DRAWING && draw_with_brush)
+    DeleteBrushFromCursor();
+
+  if (gi == NULL || gi->info_text == NULL)
     return;
 
-  if (key>=XK_A && key<=XK_Z)
-    ascii = 'A'+(char)(key-XK_A);
-  else if (key>=XK_a && key<=XK_z)
-    ascii = 'a'+(char)(key-XK_a);
-  else if (key>=XK_0 && key<=XK_9)
-    ascii = '0'+(char)(key-XK_0);
-#ifdef XK_LATIN1
-  else if (key>=XK_space && key<=XK_at)
-    ascii = ' '+(char)(key-XK_space);
-  else if (key==XK_Adiaeresis)
-    ascii = 'Ä';
-  else if (key==XK_Odiaeresis)
-    ascii = 'Ö';
-  else if (key==XK_Udiaeresis)
-    ascii = 'Ãœ';
-  else if (key==XK_adiaeresis)
-    ascii = 'ä';
-  else if (key==XK_odiaeresis)
-    ascii = 'ö';
-  else if (key==XK_udiaeresis)
-    ascii = 'ü';
-  else if (key==XK_underscore)
-    ascii = '_';
-#endif
+  strncpy(infotext, gi->info_text, MAX_INFOTEXT_LEN);
+  infotext[MAX_INFOTEXT_LEN] = '\0';
 
-  if (ascii && len<MAX_LEVNAMLEN-2)
-  {
-    level.name[len] = ascii;
-    level.name[len+1] = 0;
-    len++;
-
-    DrawTextExt(drawto,gc,
-               ED_COUNT_GADGET_XPOS+5,
-               ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
-               level.name,FS_SMALL,FC_GREEN);
-    DrawTextExt(window,gc,
-               ED_COUNT_GADGET_XPOS+5,
-               ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
-               level.name,FS_SMALL,FC_GREEN);
-    DrawTextExt(drawto,gc,
-               ED_COUNT_GADGET_XPOS+5+len*FONT2_XSIZE,
-               ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
-               "<",FS_SMALL,FC_RED);
-    DrawTextExt(window,gc,
-               ED_COUNT_GADGET_XPOS+5+len*FONT2_XSIZE,
-               ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
-               "<",FS_SMALL,FC_RED);
-  }
-  else if ((key==XK_Delete || key==XK_BackSpace) && len>0)
-  {
-    level.name[len-1] = 0;
-    len--;
-
-    DrawTextExt(drawto,gc,
-               ED_COUNT_GADGET_XPOS+5+len*FONT2_XSIZE,
-               ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
-               "< ",FS_SMALL,FC_GREEN);
-    DrawTextExt(window,gc,
-               ED_COUNT_GADGET_XPOS+5+len*FONT2_XSIZE,
-               ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
-               "< ",FS_SMALL,FC_GREEN);
+  if (gi->custom_id < ED_NUM_CTRL_BUTTONS)
+  {
+    int key = control_info[gi->custom_id].shortcut;
+
+    if (key)
+    {
+      sprintf(shortcut, " ('%s%c')",
+             (key >= 'A' && key <= 'Z' ? "Shift-" :
+              gi->custom_id == GADGET_ID_SINGLE_ITEMS ? ".' or '" : ""),
+             key);
+
+      if (strlen(infotext) + strlen(shortcut) <= MAX_INFOTEXT_LEN)
+       strcat(infotext, shortcut);
+    }
   }
-  else if (key==XK_Return)
-  {
-    DrawTextExt(drawto,gc,
-               ED_COUNT_GADGET_XPOS+5,
-               ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
-               level.name,FS_SMALL,FC_YELLOW);
-    DrawTextExt(window,gc,
-               ED_COUNT_GADGET_XPOS+5,
-               ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
-               level.name,FS_SMALL,FC_YELLOW);
-    DrawTextExt(drawto,gc,
-               ED_COUNT_GADGET_XPOS+5+len*FONT2_XSIZE,
-               ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
-               " ",FS_SMALL,FC_YELLOW);
-    DrawTextExt(window,gc,
-               ED_COUNT_GADGET_XPOS+5+len*FONT2_XSIZE,
-               ED_COUNT_TEXT_YPOS+16*ED_COUNT_TEXT_YSIZE,
-               " ",FS_SMALL,FC_YELLOW);
-
-    name_typing = FALSE;
+
+  DrawText(INFOTEXT_XPOS, INFOTEXT_YPOS, infotext, FS_SMALL, FC_YELLOW);
+}
+
+static void HandleDrawingAreaInfo(struct GadgetInfo *gi)
+{
+  static int start_lx, start_ly;
+  char *infotext;
+  int id = gi->custom_id;
+  int sx = gi->event.x;
+  int sy = gi->event.y;
+  int lx = sx + level_xpos;
+  int ly = sy + level_ypos;
+  int min_sx = 0, min_sy = 0;
+  int max_sx = gi->drawing.area_xsize - 1;
+  int max_sy = gi->drawing.area_ysize - 1;
+
+  ClearEditorGadgetInfoText();
+
+  /* make sure to stay inside drawing area boundaries */
+  sx = (sx < min_sx ? min_sx : sx > max_sx ? max_sx : sx);
+  sy = (sy < min_sy ? min_sy : sy > max_sy ? max_sy : sy);
+
+  if (id == GADGET_ID_DRAWING_LEVEL)
+  {
+    if (button_status)
+    {
+      int min_lx = 0, min_ly = 0;
+      int max_lx = lev_fieldx - 1, max_ly = lev_fieldy - 1;
+
+      /* get positions inside level field */
+      lx = sx + level_xpos;
+      ly = sy + level_ypos;
+
+      /* make sure to stay inside level field boundaries */
+      lx = (lx < min_lx ? min_lx : lx > max_lx ? max_lx : lx);
+      ly = (ly < min_ly ? min_ly : ly > max_ly ? max_ly : ly);
+
+      /* correct drawing area positions accordingly */
+      sx = lx - level_xpos;
+      sy = ly - level_ypos;
+    }
+
+    if (IN_ED_FIELD(sx,sy) && IN_LEV_FIELD(lx, ly))
+    {
+      if (button_status)       /* if (gi->state == GD_BUTTON_PRESSED) */
+      {
+       if (gi->event.type == GD_EVENT_PRESSED)
+       {
+         start_lx = lx;
+         start_ly = ly;
+       }
+
+       switch (drawing_function)
+       {
+         case GADGET_ID_SINGLE_ITEMS:
+           infotext = "Drawing single items";
+           break;
+         case GADGET_ID_CONNECTED_ITEMS:
+           infotext = "Drawing connected items";
+           break;
+         case GADGET_ID_LINE:
+           infotext = "Drawing line";
+           break;
+         case GADGET_ID_ARC:
+           infotext = "Drawing arc";
+           break;
+         case GADGET_ID_TEXT:
+           infotext = "Setting text cursor";
+           break;
+         case GADGET_ID_RECTANGLE:
+           infotext = "Drawing rectangle";
+           break;
+         case GADGET_ID_FILLED_BOX:
+           infotext = "Drawing filled box";
+           break;
+         case GADGET_ID_FLOOD_FILL:
+           infotext = "Flood fill";
+           break;
+         case GADGET_ID_GRAB_BRUSH:
+           infotext = "Grabbing brush";
+           break;
+         case GADGET_ID_PICK_ELEMENT:
+           infotext = "Picking element";
+           break;
+
+         default:
+           infotext = "Drawing position";
+           break;
+       }
+
+       if (drawing_function == GADGET_ID_PICK_ELEMENT)
+         DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FC_YELLOW,
+                   "%s: %d, %d", infotext, lx, ly);
+       else
+         DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FC_YELLOW,
+                   "%s: %d, %d", infotext,
+                   ABS(lx - start_lx) + 1, ABS(ly - start_ly) + 1);
+      }
+      else if (drawing_function == GADGET_ID_PICK_ELEMENT)
+       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FC_YELLOW,
+                 "%s", element_info[Feld[lx][ly]]);
+      else
+       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FC_YELLOW,
+                 "Level position: %d, %d", lx, ly);
+    }
+
+    /* misuse this function to draw brush cursor, if needed */
+    if (edit_mode == ED_MODE_DRAWING && draw_with_brush && !button_status)
+    {
+      if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
+       CopyBrushToCursor(sx, sy);
+      else
+       DeleteBrushFromCursor();
+    }
   }
+  else if (id == GADGET_ID_AMOEBA_CONTENT)
+    DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FC_YELLOW,
+             "Amoeba content");
+  else if (id == GADGET_ID_RANDOM_BACKGROUND)
+    DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FC_YELLOW,
+             "Random placement background");
+  else
+    DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FC_YELLOW,
+             "Content area %d position: %d, %d",
+             id - GADGET_ID_ELEM_CONTENT_0 + 1, sx, sy);
 }
index dae87c12e6d4a72a1f115a6acabc9e993142c1fe..c77fef7ecbb917c68e0badc8b48f9cc5f2d32fcf 100644 (file)
@@ -24,8 +24,10 @@ extern int element_shift;
 extern int editor_element[];
 extern int elements_in_list;
 
+void CreateLevelEditorGadgets();
+void UnmapLevelEditorGadgets();
 void DrawLevelEd(void);
-void LevelEd(int, int, int);
-void LevelNameTyping(KeySym);
+void HandleLevelEditorKeyInput(KeySym);
+void HandleEditorGadgetInfoText(void *ptr);
 
 #endif
index 2d8eca3e861657b6442169036ac88d5395f08934..a336f013d3a5ae35140ad8095734aa216e643a58 100644 (file)
@@ -20,6 +20,7 @@
 #include "misc.h"
 #include "tape.h"
 #include "joystick.h"
+#include "buttons.h"
 #include "network.h"
 
 /* values for key_status */
@@ -70,6 +71,9 @@ void EventLoop(void)
       Delay(10);
     }
 
+    /* refresh window contents from drawing buffer, if needed */
+    BackToFront();
+
     if (game_status == EXITGAME)
       return;
   }
@@ -165,7 +169,7 @@ void SleepWhileUnmapped()
     }
   }
 
-  if (game_status==PLAYING)
+  if (game_status == PLAYING)
     XAutoRepeatOff(display);
 }
 
@@ -212,7 +216,7 @@ void HandleButtonEvent(XButtonEvent *event)
 {
   motion_status = FALSE;
 
-  if (event->type==ButtonPress)
+  if (event->type == ButtonPress)
     button_status = event->button;
   else
     button_status = MB_RELEASED;
@@ -222,16 +226,44 @@ void HandleButtonEvent(XButtonEvent *event)
 
 void HandleMotionEvent(XMotionEvent *event)
 {
+  Window root, child;
+  int root_x, root_y;
+  int win_x, win_y;
+  unsigned int mask;
+
+  if (!XQueryPointer(display, window, &root, &child, &root_x, &root_y,
+                    &win_x, &win_y, &mask))
+    return;
+
+  if (!button_status && game_status != LEVELED)
+    return;
+
   motion_status = TRUE;
 
-  HandleButton(event->x, event->y, button_status);
+  HandleButton(win_x, win_y, button_status);
 }
 
 void HandleKeyEvent(XKeyEvent *event)
 {
   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);
+  KeySym key;
+
+  if (game_status == PLAYING)
+  {
+    /* use '0' instead of 'event->state' to get the key without modifiers */
+    key = XLookupKeysym(event, 0);
+  }
+  else
+  {
+    /* get the key with all modifiers */
+    char buffer[10];
+    int buffer_size = 10;
+    XComposeStatus compose;
+    int char_count;
+
+    char_count = XLookupString(event, buffer, buffer_size, &key, &compose);
+    buffer[char_count] = '\0';
+  }
 
   HandleKey(key, key_status);
 }
@@ -242,13 +274,36 @@ void HandleFocusEvent(XFocusChangeEvent *event)
 
   if (event->type == FocusOut)
   {
+    int i;
+
     XAutoRepeatOn(display);
     old_joystick_status = joystick_status;
     joystick_status = JOYSTICK_OFF;
+
+    /* simulate key release events for still pressed keys */
     key_joystick_mapping = 0;
+    for (i=0; i<MAX_PLAYERS; i++)
+      stored_player[i].action = 0;
   }
   else if (event->type == FocusIn)
   {
+    /* When there are two Rocks'n'Diamonds windows which overlap and
+       the player moves the pointer from one game window to the other,
+       a 'FocusOut' event is generated for the window the pointer is
+       leaving and a 'FocusIn' event is generated for the window the
+       pointer is entering. In some cases, it can happen that the
+       'FocusIn' event is handled by the one game process before the
+       'FocusOut' event by the other game process. In this case the
+       X11 environment would end up with activated keyboard auto repeat,
+       because unfortunately this is a global setting and not (which
+       would be far better) set for each X11 window individually.
+       The effect would be keyboard auto repeat while playing the game
+       (game_status == PLAYING), which is not desired.
+       To avoid this special case, we just wait 1/50 second before
+       processing the 'FocusIn' event.
+    */
+
+    Delay(20);
     if (game_status == PLAYING)
       XAutoRepeatOff(display);
     if (old_joystick_status != -1)
@@ -269,21 +324,26 @@ void HandleButton(int mx, int my, int button)
 {
   static int old_mx = 0, old_my = 0;
 
-  if (mx<0 || my<0)
+  if (button < 0)
   {
     mx = old_mx;
     my = old_my;
+    button = -button;
   }
   else
   {
     old_mx = mx;
     old_my = my;
 
+    /*
     HandleVideoButtons(mx,my, button);
     HandleSoundButtons(mx,my, button);
     HandleGameButtons(mx,my, button);
+    */
   }
 
+  HandleGadgets(mx, my, button);
+
   switch(game_status)
   {
     case MAINMENU:
@@ -303,7 +363,6 @@ void HandleButton(int mx, int my, int button)
       break;
 
     case LEVELED:
-      LevelEd(mx,my, button);
       break;
 
     case HELPSCREEN:
@@ -330,7 +389,12 @@ void HandleButton(int mx, int my, int button)
          int x = LEVELX(sx);
          int y = LEVELY(sy);
 
-         printf("INFO: Feld[%d][%d] == %d\n", x,y, Feld[x][y]);
+         printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
+
+         if (!IN_LEV_FIELD(x, y))
+           break;
+
+         printf("      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]);
@@ -427,7 +491,17 @@ void HandleKey(KeySym key, int key_status)
   /* 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);
+    if (game_status == LEVELED)
+    {
+      /* draw smaller door */
+      XCopyArea(display, pix[PIX_DOOR], drawto, gc,
+               DOOR_GFX_PAGEX7, 64,
+               108, 64,
+               EX - 4, EY - 12);
+      redraw_mask |= REDRAW_ALL;
+    }
+
+    CloseDoor(DOOR_CLOSE_1 | DOOR_OPEN_2 | DOOR_NO_DELAY);
     game_status = MAINMENU;
     DrawMainMenu();
     return;
@@ -444,6 +518,8 @@ void HandleKey(KeySym key, int key_status)
 
 
 
+  HandleGadgetsKeyInput(key);
+
   switch(game_status)
   {
     case TYPENAME:
@@ -491,7 +567,7 @@ void HandleKey(KeySym key, int key_status)
       break;
 
     case LEVELED:
-      LevelNameTyping(key);
+      HandleLevelEditorKeyInput(key);
       break;
 
     case PLAYING:
@@ -534,6 +610,22 @@ void HandleKey(KeySym key, int key_status)
          break;
 #endif
 
+#if 1
+       case XK_m:
+         if (MoveSpeed == 8)
+         {
+           MoveSpeed = 4;
+           ScrollStepSize = TILEX/4;
+         }
+         else
+         {
+           MoveSpeed = 8;
+           ScrollStepSize = TILEX/8;
+         }
+         printf("MoveSpeed == %d\n", MoveSpeed);
+         break;
+#endif
+
        case XK_f:
          ScrollStepSize = TILEX/8;
          printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
@@ -598,7 +690,7 @@ void HandleNoXEvent()
 {
   if (button_status && game_status != PLAYING)
   {
-    HandleButton(-1,-1, button_status);
+    HandleButton(0, 0, -button_status);
     return;
   }
 
@@ -664,7 +756,8 @@ void HandleJoystick()
     {
       static unsigned long joystickmove_delay = 0;
 
-      if (joystick && !button && !DelayReached(&joystickmove_delay, 150))
+      if (joystick && !button &&
+         !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
        newbutton = dx = dy = 0;
 
       if (game_status==MAINMENU)
index 21309b825bf505ea3fc4ba98271d55874a3f0fb0..71d01e945fd9f35c3d38ee7736d2e38e2f1d44a0 100644 (file)
@@ -26,7 +26,7 @@
 #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 LEVEL_HEADER_UNUSED    17      /* 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 TAPE_PERMS             LEVEL_PERMS
 #define SETUP_PERMS            LEVEL_PERMS
 
+/* sort priorities of level series (also used as level series classes) */
+#define LEVELCLASS_TUTORIAL_START      10
+#define LEVELCLASS_TUTORIAL_END                99
+#define LEVELCLASS_CLASSICS_START      100
+#define LEVELCLASS_CLASSICS_END                199
+#define LEVELCLASS_CONTRIBUTION_START  200
+#define LEVELCLASS_CONTRIBUTION_END    299
+#define LEVELCLASS_USER_START          300
+#define LEVELCLASS_USER_END            399
+
+#define LEVELCLASS_TUTORIAL            LEVELCLASS_TUTORIAL_START
+#define LEVELCLASS_CLASSICS            LEVELCLASS_CLASSICS_START
+#define LEVELCLASS_CONTRIBUTION                LEVELCLASS_CONTRIBUTION_START
+#define LEVELCLASS_USER                        LEVELCLASS_USER_START
+#define LEVELCLASS_UNDEFINED           999
+
+#define IS_LEVELCLASS_TUTORIAL(n) \
+       (leveldir[n].sort_priority >= LEVELCLASS_TUTORIAL_START && \
+        leveldir[n].sort_priority <= LEVELCLASS_TUTORIAL_END)
+#define IS_LEVELCLASS_CLASSICS(n) \
+       (leveldir[n].sort_priority >= LEVELCLASS_CLASSICS_START && \
+        leveldir[n].sort_priority <= LEVELCLASS_CLASSICS_END)
+#define IS_LEVELCLASS_CONTRIBUTION(n) \
+       (leveldir[n].sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
+        leveldir[n].sort_priority <= LEVELCLASS_CONTRIBUTION_END)
+#define IS_LEVELCLASS_USER(n) \
+       (leveldir[n].sort_priority >= LEVELCLASS_USER_START && \
+        leveldir[n].sort_priority <= LEVELCLASS_USER_END)
+
+#define LEVELCLASS(n)  (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
+                        IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
+                        IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
+                        IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
+                        LEVELCLASS_UNDEFINED)
+
+#define LEVELCOLOR(n)  (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
+                        IS_LEVELCLASS_CLASSICS(n) ? FC_YELLOW : \
+                        IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
+                        IS_LEVELCLASS_USER(n) ? FC_RED : FC_BLUE)
+
 static void SaveUserLevelInfo();               /* for 'InitUserLevelDir()' */
 static char *getSetupLine(char *, int);                /* for 'SaveUserLevelInfo()' */
 
@@ -235,6 +275,27 @@ static void InitUserLevelDirectory(char *level_subdir)
   }
 }
 
+static void getFileChunk(FILE *file, char *chunk_buffer, int *chunk_length)
+{
+  fgets(chunk_buffer, CHUNK_ID_LEN + 1, file);
+
+  *chunk_length =
+    (fgetc(file) << 24) |
+    (fgetc(file) << 16) |
+    (fgetc(file) <<  8)  |
+    (fgetc(file) <<  0);
+}
+
+static void putFileChunk(FILE *file, char *chunk_name, int chunk_length)
+{
+  fputs(chunk_name, 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);
+}
+
 static void setLevelInfoToDefaults()
 {
   int i, x, y;
@@ -242,8 +303,8 @@ static void setLevelInfoToDefaults()
   lev_fieldx = level.fieldx = STD_LEV_FIELDX;
   lev_fieldy = level.fieldy = STD_LEV_FIELDY;
 
-  for(x=0; x<MAX_LEV_FIELDX; x++) 
-    for(y=0; y<MAX_LEV_FIELDY; y++) 
+  for(x=0; x<MAX_LEV_FIELDX; x++)
+    for(y=0; y<MAX_LEV_FIELDY; y++)
       Feld[x][y] = Ur[x][y] = EL_ERDREICH;
 
   level.time = 100;
@@ -252,13 +313,21 @@ static void setLevelInfoToDefaults()
   level.dauer_sieb = 10;
   level.dauer_ablenk = 10;
   level.amoebe_inhalt = EL_DIAMANT;
+  level.double_speed = FALSE;
+
+  for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
+    level.name[i] = '\0';
+  for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
+    level.author[i] = '\0';
 
-  strcpy(level.name, "Nameless Level");
+  strcpy(level.name, NAMELESS_LEVEL_NAME);
+  strcpy(level.author, ANONYMOUS_NAME);
 
   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
     level.score[i] = 10;
 
-  for(i=0; i<4; i++)
+  MampferMax = 4;
+  for(i=0; i<8; i++)
     for(x=0; x<3; x++)
       for(y=0; y<3; y++)
        level.mampfer_inhalt[i][x][y] = EL_FELSBROCKEN;
@@ -266,6 +335,38 @@ static void setLevelInfoToDefaults()
   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;
+
+  BorderElement = EL_BETON;
+
+  /* try to determine better author name than 'anonymous' */
+  if (strcmp(leveldir[leveldir_nr].author, ANONYMOUS_NAME) != 0)
+  {
+    strncpy(level.author, leveldir[leveldir_nr].author, MAX_LEVEL_AUTHOR_LEN);
+    level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
+  }
+  else
+  {
+    switch (LEVELCLASS(leveldir_nr))
+    {
+      case LEVELCLASS_TUTORIAL:
+       strcpy(level.author, PROGRAM_AUTHOR_STRING);
+       break;
+
+      case LEVELCLASS_CONTRIBUTION:
+       strncpy(level.author, leveldir[leveldir_nr].name,MAX_LEVEL_AUTHOR_LEN);
+       level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
+       break;
+
+      case LEVELCLASS_USER:
+       strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
+       level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
+       break;
+
+      default:
+       /* keep default value */
+       break;
+    }
+  }
 }
 
 void LoadLevel(int level_nr)
@@ -304,10 +405,7 @@ void LoadLevel(int level_nr)
   /* read chunk "HEAD" */
   if (file_version >= FILE_VERSION_1_2)
   {
-    /* 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);
+    getFileChunk(file, chunk, &chunk_length);
     if (strcmp(chunk, "HEAD") || chunk_length != LEVEL_HEADER_SIZE)
     {
       Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
@@ -322,22 +420,33 @@ void LoadLevel(int level_nr)
   level.time           = (fgetc(file)<<8) | fgetc(file);
   level.edelsteine     = (fgetc(file)<<8) | fgetc(file);
 
-  for(i=0; i<MAX_LEVNAMLEN; i++)
+  for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
     level.name[i]      = fgetc(file);
-  level.name[MAX_LEVNAMLEN - 1] = 0;
+  level.name[MAX_LEVEL_NAME_LEN] = 0;
 
   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
     level.score[i]     = fgetc(file);
 
-  for(i=0; i<4; i++)
+  MampferMax = 4;
+  for(i=0; i<8; i++)
+  {
     for(y=0; y<3; y++)
+    {
       for(x=0; x<3; x++)
-       level.mampfer_inhalt[i][x][y] = fgetc(file);
+      {
+       if (i < 4)
+         level.mampfer_inhalt[i][x][y] = fgetc(file);
+       else
+         level.mampfer_inhalt[i][x][y] = EL_LEERRAUM;
+      }
+    }
+  }
 
   level.tempo_amoebe   = fgetc(file);
   level.dauer_sieb     = fgetc(file);
   level.dauer_ablenk   = fgetc(file);
-  level.amoebe_inhalt = fgetc(file);
+  level.amoebe_inhalt  = fgetc(file);
+  level.double_speed   = (fgetc(file) == 1 ? TRUE : FALSE);
 
   for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* skip unused header bytes */
     fgetc(file);
@@ -345,10 +454,35 @@ void LoadLevel(int level_nr)
   /* read chunk "BODY" */
   if (file_version >= FILE_VERSION_1_2)
   {
+    getFileChunk(file, chunk, &chunk_length);
+
+    /* look for optional author chunk */
+    if (strcmp(chunk, "AUTH") == 0 && chunk_length == MAX_LEVEL_AUTHOR_LEN)
+    {
+      for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
+       level.author[i] = fgetc(file);
+      level.author[MAX_LEVEL_NAME_LEN] = 0;
+
+      getFileChunk(file, chunk, &chunk_length);
+    }
+
+    /* look for optional content chunk */
+    if (strcmp(chunk, "CONT") == 0 && chunk_length == 4 + 8 * 3 * 3)
+    {
+      fgetc(file);
+      MampferMax = fgetc(file);
+      fgetc(file);
+      fgetc(file);
+
+      for(i=0; i<8; i++)
+       for(y=0; y<3; y++)
+         for(x=0; x<3; x++)
+           level.mampfer_inhalt[i][x][y] = fgetc(file);
+
+      getFileChunk(file, chunk, &chunk_length);
+    }
+
     /* 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)
     {
       Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
@@ -357,14 +491,26 @@ void LoadLevel(int level_nr)
     }
   }
 
-  for(y=0; y<lev_fieldy; y++) 
-    for(x=0; x<lev_fieldx; x++) 
+  /* clear all other level fields (needed if resized in level editor later) */
+  for(x=0; x<MAX_LEV_FIELDX; x++)
+    for(y=0; y<MAX_LEV_FIELDY; y++)
+      Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
+
+  /* now read in the valid level fields from level file */
+  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;
+  /* player was faster than monsters in pre-1.0 levels */
+  if (file_version == FILE_VERSION_1_0 &&
+      IS_LEVELCLASS_CONTRIBUTION(leveldir_nr))
+  {
+    Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
+    Error(ERR_WARN, "using high speed movement for player");
+    level.double_speed = TRUE;
+  }
 }
 
 void SaveLevel(int level_nr)
@@ -372,7 +518,6 @@ void SaveLevel(int level_nr)
   int i, x, y;
   char *filename = getLevelFilename(level_nr);
   FILE *file;
-  int chunk_length;
 
   if (!(file = fopen(filename, "w")))
   {
@@ -383,14 +528,7 @@ void SaveLevel(int level_nr)
   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);
+  putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE);
 
   fputc(level.fieldx, file);
   fputc(level.fieldy, file);
@@ -399,7 +537,7 @@ void SaveLevel(int level_nr)
   fputc(level.edelsteine / 256, file);
   fputc(level.edelsteine % 256, file);
 
-  for(i=0; i<MAX_LEVNAMLEN; i++)
+  for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
     fputc(level.name[i], file);
   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
     fputc(level.score[i], file);
@@ -411,17 +549,29 @@ void SaveLevel(int level_nr)
   fputc(level.dauer_sieb, file);
   fputc(level.dauer_ablenk, file);
   fputc(level.amoebe_inhalt, file);
+  fputc((level.double_speed ? 1 : 0), 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;
+  putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
 
-  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(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
+    fputc(level.author[i], file);
+
+  putFileChunk(file, "CONT", 4 + 8 * 3 * 3);
+
+  fputc(EL_MAMPFER, file);
+  fputc(MampferMax, file);
+  fputc(0, file);
+  fputc(0, file);
+
+  for(i=0; i<8; i++)
+    for(y=0; y<3; y++)
+      for(x=0; x<3; x++)
+       fputc(level.mampfer_inhalt[i][x][y], file);
+
+  putFileChunk(file, "BODY", lev_fieldx * lev_fieldy);
 
   for(y=0; y<lev_fieldy; y++) 
     for(x=0; x<lev_fieldx; x++) 
@@ -812,9 +962,11 @@ void SaveScore(int level_nr)
 
 /* 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 LEVELINFO_TOKEN_AUTHOR         30
+#define LEVELINFO_TOKEN_LEVELS         31
+#define LEVELINFO_TOKEN_FIRST_LEVEL    32
+#define LEVELINFO_TOKEN_SORT_PRIORITY  33
+#define LEVELINFO_TOKEN_READONLY       34
 
 #define FIRST_GLOBAL_SETUP_TOKEN       SETUP_TOKEN_PLAYER_NAME
 #define LAST_GLOBAL_SETUP_TOKEN                SETUP_TOKEN_TEAM_MODE
@@ -876,7 +1028,9 @@ static struct
 
   /* level directory info */
   { TYPE_STRING,  &ldi.name,           "name"                          },
+  { TYPE_STRING,  &ldi.author,         "author"                        },
   { TYPE_INTEGER, &ldi.levels,         "levels"                        },
+  { TYPE_INTEGER, &ldi.first_level,    "first_level"                   },
   { TYPE_INTEGER, &ldi.sort_priority,  "sort_priority"                 },
   { TYPE_BOOLEAN, &ldi.readonly,       "readonly"                      }
 };
@@ -1049,7 +1203,7 @@ static struct SetupFileList *loadSetupFileList(char *filename)
     /* cut trailing comment or whitespace from input line */
     for (line_ptr = line; *line_ptr; line_ptr++)
     {
-      if (*line_ptr == '#' || *line_ptr == '\n')
+      if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
       {
        *line_ptr = '\0';
        break;
@@ -1138,9 +1292,11 @@ static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
 
 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
 {
-  ldi->name = getStringCopy("non-existing");
+  ldi->name = getStringCopy(ANONYMOUS_NAME);
+  ldi->author = getStringCopy(ANONYMOUS_NAME);
   ldi->levels = 0;
-  ldi->sort_priority = 999;    /* default: least priority */
+  ldi->first_level = 0;
+  ldi->sort_priority = LEVELCLASS_UNDEFINED;   /* default: least priority */
   ldi->readonly = TRUE;
 }
 
@@ -1269,7 +1425,7 @@ int getLastPlayedLevelOfLevelSeries(char *level_series_name)
 {
   char *token_value;
   int level_series_nr = getLevelSeriesNrFromLevelSeriesName(level_series_name);
-  int last_level_nr = 0;
+  int last_level_nr = leveldir[level_series_nr].first_level;
 
   if (!level_series_name)
     return 0;
@@ -1278,14 +1434,12 @@ int getLastPlayedLevelOfLevelSeries(char *level_series_name)
 
   if (token_value)
   {
-    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;
+    if (last_level_nr < leveldir[level_series_nr].first_level)
+      last_level_nr = leveldir[level_series_nr].first_level;
+    if (last_level_nr > leveldir[level_series_nr].last_level)
+      last_level_nr = leveldir[level_series_nr].last_level;
   }
 
   return last_level_nr;
@@ -1362,8 +1516,12 @@ static int LoadLevelInfoFromLevelDir(char *level_directory, int start_entry)
       leveldir[current_entry] = ldi;
 
       leveldir[current_entry].filename = getStringCopy(dir_entry->d_name);
+      leveldir[current_entry].last_level =
+       leveldir[current_entry].first_level +
+       leveldir[current_entry].levels - 1;
       leveldir[current_entry].user_defined =
        (level_directory == options.level_directory ? FALSE : TRUE);
+      leveldir[current_entry].color = LEVELCOLOR(current_entry);
 
       freeSetupFileList(setup_file_list);
       current_entry++;
@@ -1423,9 +1581,14 @@ static void SaveUserLevelInfo()
     return;
   }
 
+  /* always start with reliable default values */
+  setLevelDirInfoToDefaults(&ldi);
+
   ldi.name = getLoginName();
+  ldi.author = getRealName();
   ldi.levels = 100;
-  ldi.sort_priority = 300;
+  ldi.first_level = 1;
+  ldi.sort_priority = LEVELCLASS_USER_START;
   ldi.readonly = FALSE;
 
   fprintf(file, "%s\n\n",
index 26cc84cc2a8a61d0e95a9cd347f74368a549ebff..fada15db70f1733e76a78b67ac8cfdfea807e05f 100644 (file)
 #define SC_SCHLUESSEL          9
 #define SC_ZEITBONUS           10
 
-/* values for game_emulation */
-#define EMU_NONE               0
-#define EMU_BOULDERDASH                1
-#define EMU_SOKOBAN            2
+/* game button identifiers */
+#define GAME_CTRL_ID_STOP              0
+#define GAME_CTRL_ID_PAUSE             1
+#define GAME_CTRL_ID_PLAY              2
+#define SOUND_CTRL_ID_MUSIC            3
+#define SOUND_CTRL_ID_LOOPS            4
+#define SOUND_CTRL_ID_SIMPLE           5
+
+#define NUM_GAME_BUTTONS               6
+
+/* forward declaration for internal use */
+static void MapGameButtons();
+static void HandleGameButtons(struct GadgetInfo *);
+
+static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
+
+
+
+#ifdef DEBUG
+#if 0
+static unsigned int getStateCheckSum(int counter)
+{
+  int x, y;
+  unsigned int mult = 1;
+  unsigned int checksum = 0;
+  /*
+  static short lastFeld[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+  */
+  static boolean first_game = TRUE;
+
+  for (y=0; y<lev_fieldy; y++) for(x=0; x<lev_fieldx; x++)
+  {
+    /*
+    if (counter == 3)
+    {
+      if (first_game)
+       lastFeld[x][y] = Feld[x][y];
+      else if (lastFeld[x][y] != Feld[x][y])
+       printf("DIFF: [%d][%d]: lastFeld == %d != %d == Feld\n",
+              x, y, lastFeld[x][y], Feld[x][y]);
+    }
+    */
+
+    checksum += mult++ * Ur[x][y];
+    checksum += mult++ * Feld[x][y];
+
+    /*
+    checksum += mult++ * MovPos[x][y];
+    checksum += mult++ * MovDir[x][y];
+    checksum += mult++ * MovDelay[x][y];
+    checksum += mult++ * Store[x][y];
+    checksum += mult++ * Store2[x][y];
+    checksum += mult++ * StorePlayer[x][y];
+    checksum += mult++ * Frame[x][y];
+    checksum += mult++ * AmoebaNr[x][y];
+    checksum += mult++ * JustHit[x][y];
+    checksum += mult++ * Stop[x][y];
+    */
+  }
+
+  if (counter == 3 && first_game)
+    first_game = FALSE;
+
+  return checksum;
+}
+#endif
+#endif
+
+
 
-/* to control special behaviour of certain game elements */
-int game_emulation = EMU_NONE;
 
 void GetPlayerConfig()
 {
@@ -115,11 +178,151 @@ void GetPlayerConfig()
   InitJoysticks();
 }
 
+static void InitField(int x, int y, boolean init_game)
+{
+  switch (Feld[x][y])
+  {
+    case EL_SPIELFIGUR:
+    case EL_SP_MURPHY:
+      if (init_game)
+       Feld[x][y] = EL_SPIELER1;
+      /* no break! */
+    case EL_SPIELER1:
+    case EL_SPIELER2:
+    case EL_SPIELER3:
+    case EL_SPIELER4:
+      if (init_game)
+      {
+       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)
+       Feld[x][y] = EL_BADEWANNE1;
+      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)
+       Feld[x][y] = EL_BADEWANNE3;
+      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)
+       Feld[x][y] = EL_BADEWANNE5;
+      break;
+
+    case EL_KAEFER_R:
+    case EL_KAEFER_O:
+    case EL_KAEFER_L:
+    case EL_KAEFER_U:
+    case EL_KAEFER:
+    case EL_FLIEGER_R:
+    case EL_FLIEGER_O:
+    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_MAMPFER2:
+    case EL_ROBOT:
+    case EL_PACMAN:
+    case EL_SP_SNIKSNAK:
+    case EL_SP_ELECTRON:
+      InitMovDir(x, y);
+      break;
+
+    case EL_AMOEBE_VOLL:
+    case EL_AMOEBE_BD:
+      InitAmoebaNr(x, y);
+      break;
+
+    case EL_TROPFEN:
+      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;
+
+    case EL_SP_EMPTY:
+      Feld[x][y] = EL_LEERRAUM;
+      break;
+
+    default:
+      break;
+  }
+}
+
 void InitGame()
 {
   int i, j, x, y;
   boolean emulate_bd = TRUE;   /* unless non-BOULDERDASH elements found */
   boolean emulate_sb = TRUE;   /* unless non-SOKOBAN     elements found */
+  boolean emulate_sp = TRUE;   /* unless non-SUPAPLEX    elements found */
 
   /* don't play tapes over network */
   network_playing = (options.network && !tape.playing);
@@ -195,13 +398,19 @@ void InitGame()
   MampferNr = 0;
   FrameCounter = 0;
   TimeFrames = 0;
+  TimePlayed = 0;
   TimeLeft = level.time;
 
   ScreenMovDir = MV_NO_MOVING;
   ScreenMovPos = 0;
   ScreenGfxPos = 0;
 
-  AllPlayersGone = SiebAktiv = FALSE;
+  MoveSpeed = (level.double_speed ? 4 : 8);
+  ScrollStepSize = TILEX / MoveSpeed;
+
+  AllPlayersGone = FALSE;
+  SiebAktiv = FALSE;
+  SiebCount = 0;
 
   for (i=0; i<MAX_NUM_AMOEBA; i++)
     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
@@ -220,129 +429,18 @@ void InitGame()
     }
   }
 
-  for(y=0; y<lev_fieldy; y++) for(x=0; x<lev_fieldx; x++)
+  for(y=0; y<lev_fieldy; y++)
   {
-    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])
+    for(x=0; x<lev_fieldx; x++)
     {
-      case EL_SPIELFIGUR:
-       Feld[x][y] = EL_SPIELER1;
-       /* no break! */
-      case EL_SPIELER1:
-      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)
-         Feld[x][y] = EL_BADEWANNE1;
-       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)
-         Feld[x][y] = EL_BADEWANNE3;
-       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)
-         Feld[x][y] = EL_BADEWANNE5;
-       break;
-      case EL_KAEFER_R:
-      case EL_KAEFER_O:
-      case EL_KAEFER_L:
-      case EL_KAEFER_U:
-      case EL_KAEFER:
-      case EL_FLIEGER_R:
-      case EL_FLIEGER_O:
-      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_MAMPFER2:
-      case EL_ROBOT:
-      case EL_PACMAN:
-       InitMovDir(x, y);
-       break;
-      case EL_AMOEBE_VOLL:
-      case EL_AMOEBE_BD:
-       InitAmoebaNr(x, y);
-       break;
-      case EL_TROPFEN:
-       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;
+      if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
+       emulate_bd = FALSE;
+      if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
+       emulate_sb = FALSE;
+      if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
+       emulate_sp = FALSE;
+
+      InitField(x, y, TRUE);
     }
   }
 
@@ -441,17 +539,43 @@ void InitGame()
   }
 
   game_emulation = (emulate_bd ? EMU_BOULDERDASH :
-                   emulate_sb ? EMU_SOKOBAN : EMU_NONE);
+                   emulate_sb ? EMU_SOKOBAN :
+                   emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
+
+  /* determine border element for this level */
+  SetBorderElement();
+
+  if (BorderElement == EL_LEERRAUM)
+  {
+    SBX_Left = 0;
+    SBX_Right = lev_fieldx - SCR_FIELDX;
+    SBY_Upper = 0;
+    SBY_Lower = lev_fieldy - SCR_FIELDY;
+  }
+  else
+  {
+    SBX_Left = -1;
+    SBX_Right = lev_fieldx - SCR_FIELDX + 1;
+    SBY_Upper = -1;
+    SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
+  }
+
+  if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
+    SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
 
-  scroll_x = scroll_y = -1;
-  if (local_player->jx >= MIDPOSX-1)
-    scroll_x = (local_player->jx <= lev_fieldx-MIDPOSX ?
+  if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
+    SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
+
+  scroll_x = SBX_Left;
+  scroll_y = SBY_Upper;
+  if (local_player->jx >= SBX_Left + MIDPOSX)
+    scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
                local_player->jx - MIDPOSX :
-               lev_fieldx - SCR_FIELDX + 1);
-  if (local_player->jy >= MIDPOSY-1)
-    scroll_y = (local_player->jy <= lev_fieldy-MIDPOSY ?
+               SBX_Right);
+  if (local_player->jy >= SBY_Upper + MIDPOSY)
+    scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
                local_player->jy - MIDPOSY :
-               lev_fieldy - SCR_FIELDY + 1);
+               SBY_Lower);
 
   CloseDoor(DOOR_CLOSE_1);
 
@@ -478,19 +602,33 @@ void InitGame()
              DOOR_GFX_PAGEX1 + XX_TIME, DOOR_GFX_PAGEY1 + YY_TIME,
              int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
 
+
+
+#if 0
   DrawGameButton(BUTTON_GAME_STOP);
   DrawGameButton(BUTTON_GAME_PAUSE);
   DrawGameButton(BUTTON_GAME_PLAY);
   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));
+#else
+  UnmapGameButtons();
+  game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
+  game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
+  game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
+  MapGameButtons();
+  MapTapeButtons();
+#endif
+
   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);
+
+
+  OpenDoor(DOOR_OPEN_ALL);
 
   if (setup.sound_music)
     PlaySoundLoop(background_loop[level_nr % num_bg_loops]);
@@ -530,6 +668,7 @@ void InitMovDir(int x, int y)
       Feld[x][y] = EL_KAEFER;
       MovDir[x][y] = direction[0][element - EL_KAEFER_R];
       break;
+
     case EL_FLIEGER_R:
     case EL_FLIEGER_O:
     case EL_FLIEGER_L:
@@ -537,6 +676,7 @@ void InitMovDir(int x, int y)
       Feld[x][y] = EL_FLIEGER;
       MovDir[x][y] = direction[0][element - EL_FLIEGER_R];
       break;
+
     case EL_BUTTERFLY_R:
     case EL_BUTTERFLY_O:
     case EL_BUTTERFLY_L:
@@ -544,6 +684,7 @@ void InitMovDir(int x, int y)
       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:
@@ -551,6 +692,7 @@ void InitMovDir(int x, int y)
       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:
@@ -558,6 +700,15 @@ void InitMovDir(int x, int y)
       Feld[x][y] = EL_PACMAN;
       MovDir[x][y] = direction[0][element - EL_PACMAN_R];
       break;
+
+    case EL_SP_SNIKSNAK:
+      MovDir[x][y] = MV_UP;
+      break;
+
+    case EL_SP_ELECTRON:
+      MovDir[x][y] = MV_LEFT;
+      break;
+
     default:
       MovDir[x][y] = 1 << RND(4);
       if (element != EL_KAEFER &&
@@ -578,7 +729,8 @@ void InitMovDir(int x, int y)
            MovDir[x][y] = direction[0][i];
            break;
          }
-         else if (element == EL_FLIEGER || element == EL_FIREFLY)
+         else if (element == EL_FLIEGER || element == EL_FIREFLY ||
+                  element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
          {
            MovDir[x][y] = direction[1][i];
            break;
@@ -630,7 +782,7 @@ void GameWon()
     {
       if (!setup.sound_loops)
        PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
-      if (TimeLeft && !(TimeLeft % 10))
+      if (TimeLeft > 0 && !(TimeLeft % 10))
        RaiseScore(level.score[SC_ZEITBONUS]);
       if (TimeLeft > 100 && !(TimeLeft % 10))
        TimeLeft -= 10;
@@ -644,6 +796,29 @@ void GameWon()
     if (setup.sound_loops)
       StopSound(SND_SIRR);
   }
+  else if (level.time == 0)            /* level without time limit */
+  {
+    if (setup.sound_loops)
+      PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
+
+    while(TimePlayed < 999)
+    {
+      if (!setup.sound_loops)
+       PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
+      if (TimePlayed < 999 && !(TimePlayed % 10))
+       RaiseScore(level.score[SC_ZEITBONUS]);
+      if (TimePlayed < 900 && !(TimePlayed % 10))
+       TimePlayed += 10;
+      else
+       TimePlayed++;
+      DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
+      BackToFront();
+      Delay(10);
+    }
+
+    if (setup.sound_loops)
+      StopSound(SND_SIRR);
+  }
 
   FadeSounds();
 
@@ -680,7 +855,7 @@ void GameWon()
   BackToFront();
 }
 
-boolean NewHiScore()
+int NewHiScore()
 {
   int k, l;
   int position = -1;
@@ -864,7 +1039,9 @@ void DrawDynamite(int x, int y)
       phase = 7 - phase;
   }
 
-  if (Store[x][y])
+  if (game_emulation == EMU_SUPAPLEX)
+    DrawGraphic(sx, sy, GFX_SP_DISK_RED);
+  else if (Store[x][y])
     DrawGraphicThruMask(sx, sy, graphic + phase);
   else
     DrawGraphic(sx, sy, graphic + phase);
@@ -899,6 +1076,7 @@ void Explode(int ex, int ey, int phase, int mode)
   int num_phase = 9, delay = 2;
   int last_phase = num_phase * delay;
   int half_phase = (num_phase / 2) * delay;
+  int first_phase_after_start = EX_PHASE_START + 1;
 
   if (phase == EX_PHASE_START)         /* initialize 'Store[][]' field */
   {
@@ -920,7 +1098,7 @@ void Explode(int ex, int ey, int phase, int mode)
        RemoveMovingField(x, y);
       }
 
-      if (!IN_LEV_FIELD(x, y) || IS_MASSIV(element) || element == EL_BURNING)
+      if (!IN_LEV_FIELD(x, y) || IS_MASSIVE(element) || element == EL_BURNING)
        continue;
 
       if ((mode!=EX_NORMAL || center_element == EL_AMOEBA2DIAM) &&
@@ -948,6 +1126,9 @@ void Explode(int ex, int ey, int phase, int mode)
            Store[x][y] = EL_EDELSTEIN_GELB;
            break;
        }
+
+       if (game_emulation == EMU_SUPAPLEX)
+         Store[x][y] = EL_LEERRAUM;
       }
       else if (center_element == EL_MAULWURF)
        Store[x][y] = EL_EDELSTEIN_ROT;
@@ -957,6 +1138,8 @@ void Explode(int ex, int ey, int phase, int mode)
        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_SP_ELECTRON)
+       Store[x][y] = EL_SP_INFOTRON;
       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)
@@ -997,7 +1180,7 @@ void Explode(int ex, int ey, int phase, int mode)
     }
 
     if (center_element == EL_MAMPFER)
-      MampferNr = (MampferNr+1) % 4;
+      MampferNr = (MampferNr + 1) % MampferMax;
 
     return;
   }
@@ -1008,9 +1191,20 @@ void Explode(int ex, int ey, int phase, int mode)
   x = ex;
   y = ey;
 
-  Frame[x][y] = (phase<last_phase ? phase+1 : 0);
+  Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
 
-  if (phase == half_phase)
+  if (phase == first_phase_after_start)
+  {
+    int element = Store2[x][y];
+
+    if (element == EL_BLACK_ORB)
+    {
+      Feld[x][y] = Store2[x][y];
+      Store2[x][y] = 0;
+      Bang(x, y);
+    }
+  }
+  else if (phase == half_phase)
   {
     int element = Store2[x][y];
 
@@ -1033,16 +1227,24 @@ void Explode(int ex, int ey, int phase, int mode)
     element = Feld[x][y] = Store[x][y];
     Store[x][y] = Store2[x][y] = 0;
     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
+    InitField(x, y, FALSE);
     if (CAN_MOVE(element) || COULD_MOVE(element))
       InitMovDir(x, y);
     DrawLevelField(x, y);
   }
-  else if (!(phase%delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+  else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
   {
+    int graphic = GFX_EXPLOSION;
+
+    if (game_emulation == EMU_SUPAPLEX)
+      graphic = (Store[x][y] == EL_SP_INFOTRON ?
+                GFX_SP_EXPLODE_INFOTRON :
+                GFX_SP_EXPLODE_EMPTY);
+
     if (phase == delay)
       ErdreichAnbroeckeln(SCREENX(x), SCREENY(y));
 
-    DrawGraphic(SCREENX(x), SCREENY(y), GFX_EXPLOSION+(phase/delay-1));
+    DrawGraphic(SCREENX(x), SCREENY(y), graphic + (phase / delay - 1));
   }
 }
 
@@ -1070,7 +1272,7 @@ void DynaExplode(int ex, int ey)
       int y = ey+j*xy[i%4][1];
       int element;
 
-      if (!IN_LEV_FIELD(x, y) || IS_MASSIV(Feld[x][y]))
+      if (!IN_LEV_FIELD(x, y) || IS_MASSIVE(Feld[x][y]))
        break;
 
       element = Feld[x][y];
@@ -1091,7 +1293,10 @@ void Bang(int x, int y)
 {
   int element = Feld[x][y];
 
-  PlaySoundLevel(x, y, SND_ROAAAR);
+  if (game_emulation == EMU_SUPAPLEX)
+    PlaySoundLevel(x, y, SND_SP_BOOOM);
+  else
+    PlaySoundLevel(x, y, SND_ROAAAR);
 
   if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
     element = EL_LEERRAUM;
@@ -1194,7 +1399,8 @@ void Impact(int x, int y)
     return;
   }
 
-  if (element == EL_BOMBE && (lastline || object_hit)) /* element is bomb */
+  if ((element == EL_BOMBE || element == EL_SP_DISK_ORANGE) &&
+      (lastline || object_hit))        /* element is bomb */
   {
     Bang(x, y);
     return;
@@ -1217,8 +1423,22 @@ void Impact(int x, int y)
   if (!lastline && object_hit)         /* check which object was hit */
   {
     if (CAN_CHANGE(element) && 
-       (smashed == EL_SIEB_LEER || smashed == EL_SIEB2_LEER) && !SiebAktiv)
-      SiebAktiv = level.dauer_sieb * FRAMES_PER_SECOND;
+       (smashed == EL_SIEB_INAKTIV || smashed == EL_SIEB2_INAKTIV))
+    {
+      int x, y;
+      int activated_magic_wall =
+       (smashed == EL_SIEB_INAKTIV ? EL_SIEB_LEER : EL_SIEB2_LEER);
+
+      /* activate magic wall / mill */
+
+      for (y=0; y<lev_fieldy; y++)
+       for (x=0; x<lev_fieldx; x++)
+         if (Feld[x][y] == smashed)
+           Feld[x][y] = activated_magic_wall;
+
+      SiebCount = level.dauer_sieb * FRAMES_PER_SECOND;
+      SiebAktiv = TRUE;
+    }
 
     if (IS_PLAYER(x, y+1))
     {
@@ -1238,10 +1458,11 @@ void Impact(int x, int y)
        return;
       }
     }
-    else if (element == EL_FELSBROCKEN)
+    else if (element == EL_FELSBROCKEN || element == EL_SP_ZONK)
     {
-      if (IS_ENEMY(smashed) || smashed == EL_BOMBE || smashed == EL_SONDE ||
-         smashed == EL_SCHWEIN || smashed == EL_DRACHE)
+      if (IS_ENEMY(smashed) ||
+         smashed == EL_BOMBE || smashed == EL_SP_DISK_ORANGE ||
+         smashed == EL_SONDE || smashed == EL_SCHWEIN || smashed == EL_DRACHE)
       {
        Bang(x, y+1);
        return;
@@ -1291,6 +1512,7 @@ void Impact(int x, int y)
       case EL_EDELSTEIN_ROT:
       case EL_EDELSTEIN_LILA:
       case EL_DIAMANT:
+      case EL_SP_INFOTRON:
         sound = SND_PLING;
        break;
       case EL_KOKOSNUSS:
@@ -1299,6 +1521,9 @@ void Impact(int x, int y)
       case EL_FELSBROCKEN:
        sound = SND_KLOPF;
        break;
+      case EL_SP_ZONK:
+       sound = SND_SP_ZONKDOWN;
+       break;
       case EL_SCHLUESSEL:
       case EL_SCHLUESSEL1:
       case EL_SCHLUESSEL2:
@@ -1380,7 +1605,8 @@ void TurnRound(int x, int y)
     else if (element == EL_BUTTERFLY)  /* && MovDir[x][y] == left_dir) */
       MovDelay[x][y] = 1;
   }
-  else if (element == EL_FLIEGER || element == EL_FIREFLY)
+  else if (element == EL_FLIEGER || element == EL_FIREFLY ||
+          element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
   {
     TestIfBadThingHitsOtherBadThing(x, y);
 
@@ -1391,7 +1617,9 @@ void TurnRound(int x, int y)
             !IS_FREE_OR_PLAYER(move_x, move_y))
       MovDir[x][y] = right_dir;
 
-    if (element == EL_FLIEGER && MovDir[x][y] != old_move_dir)
+    if ((element == EL_FLIEGER ||
+        element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
+       && MovDir[x][y] != old_move_dir)
       MovDelay[x][y] = 9;
     else if (element == EL_FIREFLY)    /* && MovDir[x][y] == right_dir) */
       MovDelay[x][y] = 1;
@@ -1811,7 +2039,7 @@ void StartMoving(int x, int y)
        Store2[x][y] = 0;
       }
     }
-    else if (SiebAktiv && CAN_CHANGE(element) &&
+    else if (CAN_CHANGE(element) &&
             (Feld[x][y+1] == EL_SIEB_LEER || Feld[x][y+1] == EL_SIEB2_LEER))
     {
       InitMovingField(x, y, MV_DOWN);
@@ -1869,7 +2097,9 @@ void StartMoving(int x, int y)
       if (element!=EL_MAMPFER && element!=EL_MAMPFER2 && element!=EL_PACMAN)
       {
        TurnRound(x, y);
-       if (MovDelay[x][y] && (element == EL_KAEFER || element == EL_FLIEGER))
+       if (MovDelay[x][y] && (element == EL_KAEFER || element == EL_FLIEGER ||
+                              element == EL_SP_SNIKSNAK ||
+                              element == EL_SP_ELECTRON))
          DrawLevelField(x, y);
       }
     }
@@ -1878,8 +2108,8 @@ void StartMoving(int x, int y)
     {
       MovDelay[x][y]--;
 
-      if (element == EL_ROBOT || element == EL_MAMPFER ||
-         element == EL_MAMPFER2)
+      if (element == EL_ROBOT ||
+         element == EL_MAMPFER || element == EL_MAMPFER2)
       {
        int phase = MovDelay[x][y] % 8;
 
@@ -1893,6 +2123,8 @@ void StartMoving(int x, int y)
            && MovDelay[x][y]%4 == 3)
          PlaySoundLevel(x, y, SND_NJAM);
       }
+      else if (element == EL_SP_ELECTRON)
+       DrawGraphicAnimation(x, y, GFX2_SP_ELECTRON, 8, 2, ANIM_NORMAL);
       else if (element == EL_DRACHE)
       {
        int i;
@@ -2114,12 +2346,15 @@ void StartMoving(int x, int y)
 
       TurnRound(x, y);
 
-      if (element == EL_KAEFER || element == EL_FLIEGER)
+      if (element == EL_KAEFER || element == EL_FLIEGER ||
+         element == EL_SP_SNIKSNAK)
        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);
+      else if (element == EL_SP_ELECTRON)
+       DrawGraphicAnimation(x, y, GFX2_SP_ELECTRON, 8, 2, ANIM_NORMAL);
 
       return;
     }
@@ -2144,7 +2379,7 @@ void ContinueMoving(int x, int y)
   int newx = x + dx, newy = y + dy;
   int step = (horiz_move ? dx : dy) * TILEX/8;
 
-  if (CAN_FALL(element) && horiz_move)
+  if (CAN_FALL(element) && horiz_move && !IS_SP_ELEMENT(element))
     step*=2;
   else if (element == EL_TROPFEN)
     step/=2;
@@ -2245,13 +2480,13 @@ int AmoebeNachbarNr(int ax, int ay)
 
   for (i=0; i<4; i++)
   {
-    int x = ax+xy[i%4][0];
-    int y = ay+xy[i%4][1];
+    int x = ax + xy[i][0];
+    int y = ay + xy[i][1];
 
     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];
   }
 
@@ -2270,13 +2505,13 @@ void AmoebenVereinigen(int ax, int ay)
     { 0, +1 }
   };
 
-  if (!new_group_nr)
+  if (new_group_nr == 0)
     return;
 
   for (i=0; i<4; i++)
   {
-    x = ax+xy[i%4][0];
-    y = ay+xy[i%4][1];
+    x = ax + xy[i][0];
+    y = ay + xy[i][1];
 
     if (!IN_LEV_FIELD(x, y))
       continue;
@@ -2288,14 +2523,22 @@ void AmoebenVereinigen(int ax, int ay)
     {
       int old_group_nr = AmoebaNr[x][y];
 
+      if (old_group_nr == 0)
+       return;
+
       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)
-         AmoebaNr[xx][yy] = new_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;
+       }
+      }
     }
   }
 }
@@ -2303,33 +2546,47 @@ void AmoebenVereinigen(int ax, int ay)
 void AmoebeUmwandeln(int ax, int ay)
 {
   int i, x, y;
-  int group_nr = AmoebaNr[ax][ay];
-  static int xy[4][2] =
-  {
-    { 0, -1 },
-    { -1, 0 },
-    { +1, 0 },
-    { 0, +1 }
-  };
 
   if (Feld[ax][ay] == EL_AMOEBE_TOT)
   {
-    for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+    int group_nr = AmoebaNr[ax][ay];
+
+#ifdef DEBUG
+    if (group_nr == 0)
     {
-      if (Feld[x][y] == EL_AMOEBE_TOT && AmoebaNr[x][y] == group_nr)
+      printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
+      printf("AmoebeUmwandeln(): This should never happen!\n");
+      return;
+    }
+#endif
+
+    for (y=0; y<lev_fieldy; y++)
+    {
+      for (x=0; x<lev_fieldx; x++)
       {
-       AmoebaNr[x][y] = 0;
-       Feld[x][y] = EL_AMOEBA2DIAM;
+       if (Feld[x][y] == EL_AMOEBE_TOT && AmoebaNr[x][y] == group_nr)
+       {
+         AmoebaNr[x][y] = 0;
+         Feld[x][y] = EL_AMOEBA2DIAM;
+       }
       }
     }
     Bang(ax, ay);
   }
   else
   {
+    static int xy[4][2] =
+    {
+      { 0, -1 },
+      { -1, 0 },
+      { +1, 0 },
+      { 0, +1 }
+    };
+
     for (i=0; i<4; i++)
     {
-      x = ax+xy[i%4][0];
-      y = ay+xy[i%4][1];
+      x = ax + xy[i][0];
+      y = ay + xy[i][1];
 
       if (!IN_LEV_FIELD(x, y))
        continue;
@@ -2340,23 +2597,36 @@ void AmoebeUmwandeln(int ax, int ay)
   }
 }
 
-void AmoebeUmwandeln2(int ax, int ay, int new_element)
+void AmoebeUmwandelnBD(int ax, int ay, int new_element)
 {
   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++)
+#ifdef DEBUG
+  if (group_nr == 0)
   {
-    if (AmoebaNr[x][y] == group_nr &&
-       (Feld[x][y] == EL_AMOEBE_TOT ||
-        Feld[x][y] == EL_AMOEBE_BD ||
-        Feld[x][y] == EL_AMOEBING))
+    printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
+    printf("AmoebeUmwandelnBD(): This should never happen!\n");
+    return;
+  }
+#endif
+
+  for (y=0; y<lev_fieldy; y++)
+  {
+    for (x=0; x<lev_fieldx; x++)
     {
-      AmoebaNr[x][y] = 0;
-      Feld[x][y] = new_element;
-      DrawLevelField(x, y);
-      done = TRUE;
+      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;
+       InitField(x, y, FALSE);
+       DrawLevelField(x, y);
+       done = TRUE;
+      }
     }
   }
 
@@ -2385,7 +2655,7 @@ void AmoebeWaechst(int x, int y)
   {
     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);
+      DrawGraphic(SCREENX(x), SCREENY(y), GFX_AMOEBING + 3 - MovDelay[x][y]/2);
 
     if (!MovDelay[x][y])
     {
@@ -2417,7 +2687,7 @@ void AmoebeAbleger(int ax, int ay)
   }
 
   if (!MovDelay[ax][ay])       /* start making new amoeba field */
-    MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25/(1+level.tempo_amoebe));
+    MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.tempo_amoebe));
 
   if (MovDelay[ax][ay])                /* wait some time before making new amoeba */
   {
@@ -2429,8 +2699,8 @@ void AmoebeAbleger(int ax, int ay)
   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];
+    int x = ax + xy[start][0];
+    int y = ay + xy[start][1];
 
     if (!IN_LEV_FIELD(x, y))
       return;
@@ -2445,16 +2715,16 @@ void AmoebeAbleger(int ax, int ay)
     if (newax == ax && neway == ay)
       return;
   }
-  else                         /* normal or "filled" amoeba */
+  else                         /* normal or "filled" (BD style) amoeba */
   {
     int start = RND(4);
     boolean waiting_for_player = FALSE;
 
     for (i=0; i<4; i++)
     {
-      int j = (start+i)%4;
-      int x = ax+xy[j][0];
-      int y = ay+xy[j][1];
+      int j = (start + i) % 4;
+      int x = ax + xy[j][0];
+      int y = ay + xy[j][1];
 
       if (!IN_LEV_FIELD(x, y))
        continue;
@@ -2470,43 +2740,56 @@ void AmoebeAbleger(int ax, int ay)
        waiting_for_player = TRUE;
     }
 
-    if (newax == ax && neway == ay)
+    if (newax == ax && neway == ay)            /* amoeba cannot grow */
     {
-      if (i == 4 && !waiting_for_player)
+      if (i == 4 && (!waiting_for_player || game_emulation == EMU_BOULDERDASH))
       {
        Feld[ax][ay] = EL_AMOEBE_TOT;
        DrawLevelField(ax, ay);
        AmoebaCnt[AmoebaNr[ax][ay]]--;
 
-       if (AmoebaCnt[AmoebaNr[ax][ay]]<=0)     /* amoeba is completely dead */
+       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);
+           AmoebeUmwandelnBD(ax, ay, level.amoebe_inhalt);
        }
       }
       return;
     }
     else if (element == EL_AMOEBE_VOLL || element == EL_AMOEBE_BD)
     {
+      /* amoeba gets larger by growing in some direction */
+
       int new_group_nr = AmoebaNr[ax][ay];
 
+#ifdef DEBUG
+  if (new_group_nr == 0)
+  {
+    printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
+    printf("AmoebeAbleger(): This should never happen!\n");
+    return;
+  }
+#endif
+
       AmoebaNr[newax][neway] = new_group_nr;
       AmoebaCnt[new_group_nr]++;
       AmoebaCnt2[new_group_nr]++;
+
+      /* if amoeba touches other amoeba(s) after growing, unify them */
       AmoebenVereinigen(newax, neway);
 
-      if (AmoebaCnt2[new_group_nr] >= 200 && element == EL_AMOEBE_BD)
+      if (element == EL_AMOEBE_BD && AmoebaCnt2[new_group_nr] >= 200)
       {
-       AmoebeUmwandeln2(newax, neway, EL_FELSBROCKEN);
+       AmoebeUmwandelnBD(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;
@@ -2673,10 +2956,9 @@ void NussKnacken(int x, int y)
 
 void SiebAktivieren(int x, int y, int typ)
 {
-  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);
+  int graphic = (typ == 1 ? GFX_SIEB_VOLL : GFX_SIEB2_VOLL) + 3;
+
+  DrawGraphicAnimation(x, y, graphic, 4, 4, ANIM_REVERSE);
 }
 
 void AusgangstuerPruefen(int x, int y)
@@ -2957,6 +3239,68 @@ void CheckForDragon(int x, int y)
   }
 }
 
+static void CheckBuggyBase(int x, int y)
+{
+  int element = Feld[x][y];
+
+  if (element == EL_SP_BUG)
+  {
+    if (!MovDelay[x][y])       /* start activating buggy base */
+      MovDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
+
+    if (MovDelay[x][y])                /* wait some time before activating base */
+    {
+      MovDelay[x][y]--;
+      if (MovDelay[x][y] < 5 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+       DrawGraphic(SCREENX(x), SCREENY(y), GFX_SP_BUG_WARNING);
+      if (MovDelay[x][y])
+       return;
+
+      Feld[x][y] = EL_SP_BUG_ACTIVE;
+    }
+  }
+  else if (element == EL_SP_BUG_ACTIVE)
+  {
+    if (!MovDelay[x][y])       /* start activating buggy base */
+      MovDelay[x][y] = 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND);
+
+    if (MovDelay[x][y])                /* wait some time before activating base */
+    {
+      MovDelay[x][y]--;
+      if (MovDelay[x][y])
+      {
+       int i;
+       static int xy[4][2] =
+       {
+         { 0, -1 },
+         { -1, 0 },
+         { +1, 0 },
+         { 0, +1 }
+       };
+
+       if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+         DrawGraphic(SCREENX(x),SCREENY(y), GFX_SP_BUG_ACTIVE + SimpleRND(4));
+
+       for (i=0; i<4; i++)
+       {
+         int xx = x + xy[i][0], yy = y + xy[i][1];
+
+         if (IS_PLAYER(xx, yy))
+         {
+           PlaySoundLevel(x, y, SND_SP_BUG);
+           break;
+         }
+       }
+
+       return;
+      }
+
+      Feld[x][y] = EL_SP_BUG;
+      DrawLevelField(x, y);
+    }
+  }
+}
+
 static void PlayerActions(struct PlayerInfo *player, byte player_action)
 {
   static byte stored_player_action[MAX_PLAYERS];
@@ -3200,9 +3544,9 @@ void GameActions()
     extern unsigned int last_RND();
 
     printf("DEBUG: %03d last RND was %d \t [state checksum is %d]\n",
-          level.time - TimeLeft,
+          TimePlayed,
           last_RND(),
-          getStateCheckSum(level.time - TimeLeft));
+          getStateCheckSum(TimePlayed));
   }
   */
 #endif
@@ -3295,6 +3639,12 @@ void GameActions()
       MauerAbleger(x, y);
     else if (element == EL_BURNING)
       CheckForDragon(x, y);
+    else if (element == EL_SP_BUG || element == EL_SP_BUG_ACTIVE)
+      CheckBuggyBase(x, y);
+    else if (element == EL_SP_TERMINAL)
+      DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL, 7, 12, ANIM_NORMAL);
+    else if (element == EL_SP_TERMINAL_ACTIVE)
+      DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL_ACTIVE, 7, 4, ANIM_NORMAL);
 
     if (SiebAktiv)
     {
@@ -3325,44 +3675,57 @@ void GameActions()
 
   if (SiebAktiv)
   {
-    if (!(SiebAktiv%4))
+    if (!(SiebCount % 4))
       PlaySoundLevel(sieb_x, sieb_y, SND_MIEP);
-    SiebAktiv--;
-    if (!SiebAktiv)
+
+    if (SiebCount > 0)
     {
-      for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+      SiebCount--;
+      if (!SiebCount)
       {
-       element = Feld[x][y];
-       if (element == EL_SIEB_LEER || element == EL_SIEB_VOLL)
+       for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
        {
-         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);
+         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);
+         }
        }
+
+       SiebAktiv = FALSE;
       }
     }
   }
 
-  if (TimeLeft>0 && TimeFrames>=(1000/GameFrameDelay) && !tape.pausing)
+  if (TimeFrames >= (1000 / GameFrameDelay) && !tape.pausing)
   {
     TimeFrames = 0;
-    TimeLeft--;
+    TimePlayed++;
 
     if (tape.recording || tape.playing)
-      DrawVideoDisplay(VIDEO_STATE_TIME_ON, level.time-TimeLeft);
+      DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
 
-    if (TimeLeft<=10)
-      PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
+    if (TimeLeft > 0)
+    {
+      TimeLeft--;
 
-    DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
+      if (TimeLeft <= 10)
+       PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
 
-    if (!TimeLeft)
-      for (i=0; i<MAX_PLAYERS; i++)
-       KillHero(&stored_player[i]);
+      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]);
+    }
+    else if (level.time == 0)          /* level without time limit */
+      DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
   }
 
   DrawAllPlayers();
@@ -3489,7 +3852,7 @@ boolean MoveFigureOneStep(struct PlayerInfo *player,
   jy = player->jy = new_jy;
   StorePlayer[jx][jy] = player->element_nr;
 
-  player->MovPos = (dx > 0 || dy > 0 ? -1 : 1) * 7*TILEX/8;
+  player->MovPos = (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / MoveSpeed);
 
   ScrollFigure(player, SCROLL_INIT);
 
@@ -3513,10 +3876,15 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
     /* should only happen if pre-1.2 tape recordings are played */
     /* this is only for backward compatibility */
 
+    int old_move_speed = MoveSpeed;
+
 #if DEBUG
     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES.\n");
 #endif
 
+    /* scroll remaining steps with finest movement resolution */
+    MoveSpeed = 8;
+
     while (player->MovPos)
     {
       ScrollFigure(player, SCROLL_GO_ON);
@@ -3525,6 +3893,8 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
       DrawAllPlayers();
       BackToFront();
     }
+
+    MoveSpeed = old_move_speed;
   }
 
   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
@@ -3559,13 +3929,13 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
     {
       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))
+       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);
+       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
+         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
 
        /* don't scroll more than one field at a time */
        scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
@@ -3577,13 +3947,13 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
       }
       else                     /* player has moved vertically */
       {
-       if ((player->MovDir == MV_UP && scroll_y > jy-MIDPOSY+offset) ||
-           (player->MovDir == MV_DOWN && scroll_y < jy-MIDPOSY-offset))
+       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);
+       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
+         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
 
        /* don't scroll more than one field at a time */
        scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
@@ -3659,7 +4029,7 @@ void ScrollFigure(struct PlayerInfo *player, int mode)
   else if (!FrameReached(&player->actual_frame_counter, 1))
     return;
 
-  player->MovPos += (player->MovPos > 0 ? -1 : 1) * TILEX/8;
+  player->MovPos += (player->MovPos > 0 ? -1 : 1) * TILEX / MoveSpeed;
   player->GfxPos = ScrollStepSize * (player->MovPos / ScrollStepSize);
 
   if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
@@ -3699,7 +4069,7 @@ void ScrollScreen(struct PlayerInfo *player, int mode)
 
   if (ScreenMovPos)
   {
-    ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * TILEX/8;
+    ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * TILEX / MoveSpeed;
     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
     redraw_mask |= REDRAW_FIELD;
   }
@@ -3936,10 +4306,18 @@ int DigField(struct PlayerInfo *player,
   switch(element)
   {
     case EL_LEERRAUM:
+      PlaySoundLevel(x, y, SND_EMPTY);
       break;
 
     case EL_ERDREICH:
       Feld[x][y] = EL_LEERRAUM;
+      PlaySoundLevel(x, y, SND_SCHLURF);
+      break;
+
+    case EL_SP_BASE:
+    case EL_SP_BUG:
+      Feld[x][y] = EL_LEERRAUM;
+      PlaySoundLevel(x, y, SND_SP_BASE);
       break;
 
     case EL_EDELSTEIN:
@@ -3948,6 +4326,7 @@ int DigField(struct PlayerInfo *player,
     case EL_EDELSTEIN_ROT:
     case EL_EDELSTEIN_LILA:
     case EL_DIAMANT:
+    case EL_SP_INFOTRON:
       RemoveField(x, y);
       local_player->gems_still_needed -= (element == EL_DIAMANT ? 3 : 1);
       if (local_player->gems_still_needed < 0)
@@ -3956,17 +4335,31 @@ int DigField(struct PlayerInfo *player,
       DrawText(DX_EMERALDS, DY_EMERALDS,
               int2str(local_player->gems_still_needed, 3),
               FS_SMALL, FC_YELLOW);
+      if (element == EL_SP_INFOTRON)
+       PlaySoundLevel(x, y, SND_SP_INFOTRON);
+      else
+       PlaySoundLevel(x, y, SND_PONG);
+      break;
+
+    case EL_SPEED_PILL:
+      RemoveField(x, y);
+      MoveSpeed = 4;
+      ScrollStepSize = TILEX/4;
       PlaySoundLevel(x, y, SND_PONG);
       break;
 
     case EL_DYNAMIT_AUS:
+    case EL_SP_DISK_RED:
       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);
+      if (element == EL_SP_DISK_RED)
+       PlaySoundLevel(x, y, SND_SP_INFOTRON);
+      else
+       PlaySoundLevel(x, y, SND_PONG);
       break;
 
     case EL_DYNABOMB_NR:
@@ -4019,10 +4412,39 @@ int DigField(struct PlayerInfo *player,
       return MF_ACTION;
       break;
 
+    case EL_SP_TERMINAL:
+      {
+       int xx, yy;
+
+       for (yy=0; yy<lev_fieldy; yy++)
+       {
+         for (xx=0; xx<lev_fieldx; xx++)
+         {
+           if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
+             Bang(xx, yy);
+           else if (Feld[xx][yy] == EL_SP_TERMINAL)
+             Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
+         }
+       }
+
+       return MF_ACTION;
+      }
+      break;
+
+    case EL_SP_EXIT:
+      if (local_player->gems_still_needed > 0)
+       return MF_NO_ACTION;
+
+      player->LevelSolved = player->GameOver = TRUE;
+      PlaySoundStereo(SND_SP_EXIT, PSND_MAX_RIGHT);
+      break;
+
     case EL_FELSBROCKEN:
     case EL_BOMBE:
     case EL_KOKOSNUSS:
     case EL_ZEIT_LEER:
+    case EL_SP_ZONK:
+    case EL_SP_DISK_ORANGE:
       if (dy || mode == DF_SNAP)
        return MF_NO_ACTION;
 
@@ -4053,6 +4475,8 @@ int DigField(struct PlayerInfo *player,
        PlaySoundLevel(x+dx, y+dy, SND_PUSCH);
       else if (element == EL_KOKOSNUSS)
        PlaySoundLevel(x+dx, y+dy, SND_KNURK);
+      else if (IS_SP_ELEMENT(element))
+       PlaySoundLevel(x+dx, y+dy, SND_SP_ZONKPUSH);
       else
        PlaySoundLevel(x+dx, y+dy, SND_KLOPF);
       break;
@@ -4073,6 +4497,42 @@ int DigField(struct PlayerInfo *player,
        return MF_NO_ACTION;
       break;
 
+    case EL_SP_PORT1_LEFT:
+    case EL_SP_PORT2_LEFT:
+    case EL_SP_PORT1_RIGHT:
+    case EL_SP_PORT2_RIGHT:
+    case EL_SP_PORT1_UP:
+    case EL_SP_PORT2_UP:
+    case EL_SP_PORT1_DOWN:
+    case EL_SP_PORT2_DOWN:
+    case EL_SP_PORT_X:
+    case EL_SP_PORT_Y:
+    case EL_SP_PORT_XY:
+      if ((dx == -1 &&
+          element != EL_SP_PORT1_LEFT &&
+          element != EL_SP_PORT2_LEFT &&
+          element != EL_SP_PORT_X &&
+          element != EL_SP_PORT_XY) ||
+         (dx == +1 &&
+          element != EL_SP_PORT1_RIGHT &&
+          element != EL_SP_PORT2_RIGHT &&
+          element != EL_SP_PORT_X &&
+          element != EL_SP_PORT_XY) ||
+         (dy == -1 &&
+          element != EL_SP_PORT1_UP &&
+          element != EL_SP_PORT2_UP &&
+          element != EL_SP_PORT_Y &&
+          element != EL_SP_PORT_XY) ||
+         (dy == +1 &&
+          element != EL_SP_PORT1_DOWN &&
+          element != EL_SP_PORT2_DOWN &&
+          element != EL_SP_PORT_Y &&
+          element != EL_SP_PORT_XY) ||
+         !IN_LEV_FIELD(x + dx, y + dy) ||
+         !IS_FREE(x + dx, y + dy))
+       return MF_NO_ACTION;
+      break;
+
     case EL_AUSGANG_ZU:
     case EL_AUSGANG_ACT:
       /* door is not (yet) open */
@@ -4118,6 +4578,7 @@ int DigField(struct PlayerInfo *player,
     case EL_SOKOBAN_FELD_VOLL:
     case EL_SOKOBAN_OBJEKT:
     case EL_SONDE:
+    case EL_SP_DISK_YELLOW:
       if (mode == DF_SNAP)
        return MF_NO_ACTION;
 
@@ -4196,7 +4657,6 @@ int DigField(struct PlayerInfo *player,
 
     default:
       return MF_NO_ACTION;
-      break;
   }
 
   player->push_delay = 0;
@@ -4265,7 +4725,12 @@ boolean PlaceBomb(struct PlayerInfo *player)
     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);
+    {
+      if (game_emulation == EMU_SUPAPLEX)
+       DrawGraphic(SCREENX(jx), SCREENY(jy), GFX_SP_DISK_RED);
+      else
+       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), GFX_DYNAMIT);
+    }
   }
   else
   {
@@ -4368,3 +4833,237 @@ void RaiseScoreElement(int element)
       break;
   }
 }
+
+/* ---------- new game button stuff ---------------------------------------- */
+
+/* graphic position values for game buttons */
+#define GAME_BUTTON_XSIZE      30
+#define GAME_BUTTON_YSIZE      30
+#define GAME_BUTTON_XPOS       5
+#define GAME_BUTTON_YPOS       215
+#define SOUND_BUTTON_XPOS      5
+#define SOUND_BUTTON_YPOS      (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
+
+#define GAME_BUTTON_STOP_XPOS  (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
+#define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
+#define GAME_BUTTON_PLAY_XPOS  (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
+#define SOUND_BUTTON_MUSIC_XPOS        (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
+#define SOUND_BUTTON_LOOPS_XPOS        (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
+#define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
+
+static struct
+{
+  int x, y;
+  int gadget_id;
+  char *infotext;
+} gamebutton_info[NUM_GAME_BUTTONS] =
+{
+  {
+    GAME_BUTTON_STOP_XPOS,     GAME_BUTTON_YPOS,
+    GAME_CTRL_ID_STOP,
+    "stop game"
+  },
+  {
+    GAME_BUTTON_PAUSE_XPOS,    GAME_BUTTON_YPOS,
+    GAME_CTRL_ID_PAUSE,
+    "pause game"
+  },
+  {
+    GAME_BUTTON_PLAY_XPOS,     GAME_BUTTON_YPOS,
+    GAME_CTRL_ID_PLAY,
+    "play game"
+  },
+  {
+    SOUND_BUTTON_MUSIC_XPOS,   SOUND_BUTTON_YPOS,
+    SOUND_CTRL_ID_MUSIC,
+    "background music on/off"
+  },
+  {
+    SOUND_BUTTON_LOOPS_XPOS,   SOUND_BUTTON_YPOS,
+    SOUND_CTRL_ID_LOOPS,
+    "sound loops on/off"
+  },
+  {
+    SOUND_BUTTON_SIMPLE_XPOS,  SOUND_BUTTON_YPOS,
+    SOUND_CTRL_ID_SIMPLE,
+    "normal sounds on/off"
+  }
+};
+
+void CreateGameButtons()
+{
+  int i;
+
+  for (i=0; i<NUM_GAME_BUTTONS; i++)
+  {
+    Pixmap gd_pixmap = pix[PIX_DOOR];
+    struct GadgetInfo *gi;
+    int button_type;
+    boolean checked;
+    unsigned long event_mask;
+    int gd_xoffset, gd_yoffset;
+    int gd_x1, gd_x2, gd_y1, gd_y2;
+    int id = i;
+
+    gd_xoffset = gamebutton_info[i].x;
+    gd_yoffset = gamebutton_info[i].y;
+    gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
+    gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
+
+    if (id == GAME_CTRL_ID_STOP ||
+       id == GAME_CTRL_ID_PAUSE ||
+       id == GAME_CTRL_ID_PLAY)
+    {
+      button_type = GD_TYPE_NORMAL_BUTTON;
+      checked = FALSE;
+      event_mask = GD_EVENT_RELEASED;
+      gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
+      gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
+    }
+    else
+    {
+      button_type = GD_TYPE_CHECK_BUTTON;
+      checked =
+       ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
+        (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
+        (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
+      event_mask = GD_EVENT_PRESSED;
+      gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
+      gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
+    }
+
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_INFO_TEXT, gamebutton_info[i].infotext,
+                     GDI_X, DX + gd_xoffset,
+                     GDI_Y, DY + gd_yoffset,
+                     GDI_WIDTH, GAME_BUTTON_XSIZE,
+                     GDI_HEIGHT, GAME_BUTTON_YSIZE,
+                     GDI_TYPE, button_type,
+                     GDI_STATE, GD_BUTTON_UNPRESSED,
+                     GDI_CHECKED, checked,
+                     GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y1,
+                     GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y1,
+                     GDI_ALT_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y2,
+                     GDI_ALT_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y2,
+                     GDI_EVENT_MASK, event_mask,
+                     GDI_CALLBACK_ACTION, HandleGameButtons,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    game_gadget[id] = gi;
+  }
+}
+
+static void MapGameButtons()
+{
+  int i;
+
+  for (i=0; i<NUM_GAME_BUTTONS; i++)
+    MapGadget(game_gadget[i]);
+}
+
+void UnmapGameButtons()
+{
+  int i;
+
+  for (i=0; i<NUM_GAME_BUTTONS; i++)
+    UnmapGadget(game_gadget[i]);
+}
+
+static void HandleGameButtons(struct GadgetInfo *gi)
+{
+  int id = gi->custom_id;
+
+  if (game_status != PLAYING)
+    return;
+
+  switch (id)
+  {
+    case GAME_CTRL_ID_STOP:
+      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 GAME_CTRL_ID_PAUSE:
+      if (options.network)
+      {
+#ifndef MSDOS
+       if (tape.pausing)
+         SendToServer_ContinuePlaying();
+       else
+         SendToServer_PausePlaying();
+#endif
+      }
+      else
+       TapeTogglePause();
+      break;
+
+    case GAME_CTRL_ID_PLAY:
+      if (tape.pausing)
+      {
+#ifndef MSDOS
+       if (options.network)
+         SendToServer_ContinuePlaying();
+       else
+#endif
+       {
+         tape.pausing = FALSE;
+         DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
+       }
+      }
+      break;
+
+    case SOUND_CTRL_ID_MUSIC:
+      if (setup.sound_music)
+      { 
+       setup.sound_music = FALSE;
+       FadeSound(background_loop[level_nr % num_bg_loops]);
+      }
+      else if (sound_loops_allowed)
+      { 
+       setup.sound = setup.sound_music = TRUE;
+       PlaySoundLoop(background_loop[level_nr % num_bg_loops]);
+      }
+      break;
+
+    case SOUND_CTRL_ID_LOOPS:
+      if (setup.sound_loops)
+       setup.sound_loops = FALSE;
+      else if (sound_loops_allowed)
+       setup.sound = setup.sound_loops = TRUE;
+      break;
+
+    case SOUND_CTRL_ID_SIMPLE:
+      if (setup.sound_simple)
+       setup.sound_simple = FALSE;
+      else if (sound_status==SOUND_AVAILABLE)
+       setup.sound = setup.sound_simple = TRUE;
+      break;
+
+    default:
+      break;
+  }
+}
index 4834b79646ef8b84fdc1effea4d8b88f38e133f0..8e33f098209ecf14cde3865631260073dd779173 100644 (file)
@@ -21,7 +21,7 @@ void InitGame(void);
 void InitMovDir(int, int);
 void InitAmoebaNr(int, int);
 void GameWon(void);
-boolean NewHiScore(void);
+int NewHiScore(void);
 void InitMovingField(int, int, int);
 void Moving2Blocked(int, int, int *, int *);
 void Blocked2Moving(int, int, int *, int *);
@@ -39,7 +39,7 @@ void StartMoving(int, int);
 void ContinueMoving(int, int);
 int AmoebeNachbarNr(int, int);
 void AmoebeUmwandeln(int, int);
-void AmoebeUmwandeln2(int, int, int);
+void AmoebeUmwandelnBD(int, int, int);
 void AmoebeWaechst(int, int);
 void AmoebeAbleger(int, int);
 void Life(int, int);
@@ -78,4 +78,7 @@ void PlaySoundLevel(int, int, int);
 void RaiseScore(int);
 void RaiseScoreElement(int);
 
+void CreateGameButtons();
+void UnmapGameButtons();
+
 #endif
index bc72b99a2787ca8f660bfafabbb38ca8baf9b712..7c62ad953e0e56ee95b5c0b3ac99751220b38208 100644 (file)
@@ -17,6 +17,9 @@
 #include "misc.h"
 #include "sound.h"
 #include "screens.h"
+#include "editor.h"
+#include "game.h"
+#include "tape.h"
 #include "tools.h"
 #include "files.h"
 #include "joystick.h"
@@ -47,6 +50,7 @@ static void InitSoundServer(void);
 static void InitWindow(int, char **);
 static void InitGfx(void);
 static void LoadGfx(int, struct PictureFileInfo *);
+static void InitGadgets(void);
 static void InitElementProperties(void);
 
 void OpenAll(int argc, char *argv[])
@@ -78,6 +82,7 @@ void OpenAll(int argc, char *argv[])
 
   InitGfx();
   InitElementProperties();
+  InitGadgets();
 
   DrawMainMenu();
 
@@ -346,8 +351,8 @@ void InitWindow(int argc, char *argv[])
   XSizeHints size_hints;
   XWMHints wm_hints;
   XClassHint class_hints;
-  char *window_name = WINDOWTITLE_STRING;
-  char *icon_name = WINDOWTITLE_STRING;
+  char *window_name = WINDOW_TITLE_STRING;
+  char *icon_name = WINDOW_TITLE_STRING;
   long window_event_mask;
   Atom proto_atom = None, delete_atom = None;
 #endif
@@ -434,9 +439,11 @@ void InitWindow(int argc, char *argv[])
   XFree(iconName.value);
 
   /* Select event types wanted */
-  window_event_mask = ExposureMask | StructureNotifyMask | FocusChangeMask |
-                      ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
-                      KeyPressMask | KeyReleaseMask;
+  window_event_mask =
+    ExposureMask | StructureNotifyMask | FocusChangeMask |
+    ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
+    PointerMotionHintMask | KeyPressMask | KeyReleaseMask;
+
   XSelectInput(display, window, window_event_mask);
 #endif
 
@@ -473,6 +480,7 @@ void InitGfx()
     { "Door",  TRUE },
     { "Heroes",        TRUE },
     { "Toons", TRUE },
+    { "More",  TRUE },
     { "Font",  FALSE },
     { "Font2", FALSE }
   }; 
@@ -483,11 +491,27 @@ void InitGfx()
     { "RocksDoor",     TRUE },
     { "RocksHeroes",   TRUE },
     { "RocksToons",    TRUE },
+    { "RocksMore",     TRUE },
     { "RocksFont",     FALSE },
     { "RocksFont2",    FALSE }
   }; 
 #endif
 
+#ifdef DEBUG
+#if 0
+  static struct PictureFileInfo test_pic1 =
+  {
+    "RocksFont2",
+    FALSE
+  };
+  static struct PictureFileInfo test_pic2 =
+  {
+    "mouse",
+    FALSE
+  };
+#endif
+#endif
+
   static struct
   {
     int start;
@@ -519,6 +543,15 @@ void InitGfx()
     { GFX_SPIELER4_RIGHT, 4 },
     { GFX_SPIELER4_PUSH_LEFT, 4 },
     { GFX_SPIELER4_PUSH_RIGHT, 4 },
+    { GFX_SP_MURPHY, 1 },
+    { GFX_MURPHY_GO_LEFT, 3 },
+    { GFX_MURPHY_GO_RIGHT, 3 },
+    { GFX_MURPHY_SNAP_UP, 1 },
+    { GFX_MURPHY_SNAP_DOWN, 1 },
+    { GFX_MURPHY_SNAP_RIGHT, 1 },
+    { GFX_MURPHY_SNAP_LEFT, 1 },
+    { GFX_MURPHY_PUSH_RIGHT, 1 },
+    { GFX_MURPHY_PUSH_LEFT, 1 },
     { GFX_GEBLUBBER, 4 },
     { GFX_DYNAMIT, 7 },
     { GFX_DYNABOMB, 4 },
@@ -533,8 +566,21 @@ void InitGfx()
   debug_print_timestamp(0, NULL);      /* initialize timestamp function */
 #endif
 
+#ifdef DEBUG
+#if 0
+  printf("Test: Loading RocksFont2.pcx ...\n");
+  LoadGfx(PIX_SMALLFONT,&test_pic1);
+  printf("Test: Done.\n");
+  printf("Test: Loading mouse.pcx ...\n");
+  LoadGfx(PIX_SMALLFONT,&test_pic2);
+  printf("Test: Done.\n");
+#endif
+#endif
+
+
+
   LoadGfx(PIX_SMALLFONT,&pic[PIX_SMALLFONT]);
-  DrawInitText(WINDOWTITLE_STRING,20,FC_YELLOW);
+  DrawInitText(WINDOW_TITLE_STRING,20,FC_YELLOW);
   DrawInitText(COPYRIGHT_STRING,50,FC_RED);
 #ifdef MSDOS
   DrawInitText("MSDOS version done by Guido Schulz",210,FC_BLUE);
@@ -584,6 +630,7 @@ void InitGfx()
       int src_x, src_y;
       Pixmap src_pixmap;
 
+#if 0
       if (graphic >= GFX_START_ROCKSSCREEN &&
          graphic <= GFX_END_ROCKSSCREEN)
       {
@@ -611,6 +658,14 @@ void InitGfx()
       }
       else
        break;
+#else
+
+      int pixmap_nr;
+
+      getGraphicSource(graphic, &pixmap_nr, &src_x, &src_y);
+      src_pixmap = clipmask[pixmap_nr];
+
+#endif
 
       tile_clipmask[tile] = XCreatePixmap(display, window, TILEX,TILEY, 1);
 
@@ -785,6 +840,14 @@ void LoadGfx(int pos, struct PictureFileInfo *pic)
   }
 }
 
+void InitGadgets()
+{
+  CreateLevelEditorGadgets();
+  CreateGameButtons();
+  CreateTapeButtons();
+  CreateToolButtons();
+}
+
 void InitElementProperties()
 {
   int i,j;
@@ -849,11 +912,13 @@ void InitElementProperties()
     EL_AMOEBE_BD,
     EL_MORAST_VOLL,
     EL_MORAST_LEER,
-    EL_SIEB_VOLL,
+    EL_SIEB_INAKTIV,
     EL_SIEB_LEER,
+    EL_SIEB_VOLL,
     EL_SIEB_TOT,
-    EL_SIEB2_VOLL,
+    EL_SIEB2_INAKTIV,
     EL_SIEB2_LEER,
+    EL_SIEB2_VOLL,
     EL_SIEB2_TOT,
     EL_LIFE,
     EL_LIFE_ASYNC,
@@ -861,11 +926,30 @@ void InitElementProperties()
     EL_BADEWANNE2,
     EL_BADEWANNE3,
     EL_BADEWANNE4,
-    EL_BADEWANNE5
+    EL_BADEWANNE5,
+    EL_SP_CHIP_SINGLE,
+    EL_SP_CHIP_LEFT,
+    EL_SP_CHIP_RIGHT,
+    EL_SP_CHIP_UPPER,
+    EL_SP_CHIP_LOWER,
+    EL_SP_HARD_GRAY,
+    EL_SP_HARD_GREEN,
+    EL_SP_HARD_BLUE,
+    EL_SP_HARD_RED,
+    EL_SP_HARD_YELLOW,
+    EL_SP_HARD_BASE1,
+    EL_SP_HARD_BASE2,
+    EL_SP_HARD_BASE3,
+    EL_SP_HARD_BASE4,
+    EL_SP_HARD_BASE5,
+    EL_SP_HARD_BASE6,
+    EL_SP_TERMINAL,
+    EL_SP_EXIT,
+    EL_INVISIBLE_STEEL
   };
   static int ep_solid_num = sizeof(ep_solid)/sizeof(int);
 
-  static int ep_massiv[] =
+  static int ep_massive[] =
   {
     EL_BETON,
     EL_SALZSAEURE,
@@ -881,9 +965,21 @@ void InitElementProperties()
     EL_PFORTE1X,
     EL_PFORTE2X,
     EL_PFORTE3X,
-    EL_PFORTE4X
+    EL_PFORTE4X,
+    EL_SP_HARD_GRAY,
+    EL_SP_HARD_GREEN,
+    EL_SP_HARD_BLUE,
+    EL_SP_HARD_RED,
+    EL_SP_HARD_YELLOW,
+    EL_SP_HARD_BASE1,
+    EL_SP_HARD_BASE2,
+    EL_SP_HARD_BASE3,
+    EL_SP_HARD_BASE4,
+    EL_SP_HARD_BASE5,
+    EL_SP_HARD_BASE6,
+    EL_INVISIBLE_STEEL
   };
-  static int ep_massiv_num = sizeof(ep_massiv)/sizeof(int);
+  static int ep_massive_num = sizeof(ep_massive)/sizeof(int);
 
   static int ep_slippery[] =
   {
@@ -905,7 +1001,15 @@ void InitElementProperties()
     EL_BIRNE_AUS,
     EL_BADEWANNE1,
     EL_BADEWANNE2,
-    EL_SONDE
+    EL_SONDE,
+    EL_SP_ZONK,
+    EL_SP_INFOTRON,
+    EL_SP_CHIP_SINGLE,
+    EL_SP_CHIP_LEFT,
+    EL_SP_CHIP_RIGHT,
+    EL_SP_CHIP_UPPER,
+    EL_SP_CHIP_LOWER,
+    EL_SPEED_PILL
   };
   static int ep_slippery_num = sizeof(ep_slippery)/sizeof(int);
 
@@ -918,7 +1022,9 @@ void InitElementProperties()
     EL_MAMPFER,
     EL_MAMPFER2,
     EL_ROBOT,
-    EL_PACMAN
+    EL_PACMAN,
+    EL_SP_SNIKSNAK,
+    EL_SP_ELECTRON
   };
   static int ep_enemy_num = sizeof(ep_enemy)/sizeof(int);
 
@@ -942,7 +1048,26 @@ void InitElementProperties()
     EL_MAUER_X,
     EL_MAUER_Y,
     EL_MAUER_XY,
-    EL_MAUERND
+    EL_MAUERND,
+    EL_SP_CHIP_SINGLE,
+    EL_SP_CHIP_LEFT,
+    EL_SP_CHIP_RIGHT,
+    EL_SP_CHIP_UPPER,
+    EL_SP_CHIP_LOWER,
+    EL_SP_HARD_GRAY,
+    EL_SP_HARD_GREEN,
+    EL_SP_HARD_BLUE,
+    EL_SP_HARD_RED,
+    EL_SP_HARD_YELLOW,
+    EL_SP_HARD_BASE1,
+    EL_SP_HARD_BASE2,
+    EL_SP_HARD_BASE3,
+    EL_SP_HARD_BASE4,
+    EL_SP_HARD_BASE5,
+    EL_SP_HARD_BASE6,
+    EL_SP_TERMINAL,
+    EL_SP_EXIT,
+    EL_INVISIBLE_STEEL
   };
   static int ep_mauer_num = sizeof(ep_mauer)/sizeof(int);
 
@@ -962,7 +1087,10 @@ void InitElementProperties()
     EL_SIEB_VOLL,
     EL_SIEB2_VOLL,
     EL_ZEIT_VOLL,
-    EL_ZEIT_LEER
+    EL_ZEIT_LEER,
+    EL_SP_ZONK,
+    EL_SP_INFOTRON,
+    EL_SP_DISK_ORANGE
   };
   static int ep_can_fall_num = sizeof(ep_can_fall)/sizeof(int);
 
@@ -983,7 +1111,10 @@ void InitElementProperties()
     EL_KOKOSNUSS,
     EL_TROPFEN,
     EL_ZEIT_VOLL,
-    EL_ZEIT_LEER
+    EL_ZEIT_LEER,
+    EL_SP_ZONK,
+    EL_SP_INFOTRON,
+    EL_SP_DISK_ORANGE
   };
   static int ep_can_smash_num = sizeof(ep_can_smash)/sizeof(int);
 
@@ -1013,7 +1144,9 @@ void InitElementProperties()
     EL_PINGUIN,
     EL_SCHWEIN,
     EL_DRACHE,
-    EL_SONDE
+    EL_SONDE,
+    EL_SP_SNIKSNAK,
+    EL_SP_ELECTRON
   };
   static int ep_can_move_num = sizeof(ep_can_move)/sizeof(int);
 
@@ -1062,7 +1195,10 @@ void InitElementProperties()
     EL_ROBOT,
     EL_PACMAN,
     EL_TROPFEN,
-    EL_SALZSAEURE
+    EL_SALZSAEURE,
+    EL_SP_SNIKSNAK,
+    EL_SP_ELECTRON,
+    EL_SP_BUG_ACTIVE
   };
   static int ep_dont_go_to_num = sizeof(ep_dont_go_to)/sizeof(int);
 
@@ -1098,7 +1234,7 @@ void InitElementProperties()
     EL_FELSBODEN,
     EL_FELSBROCKEN,
     EL_EDELSTEIN_BD,
-    EL_SIEB2_LEER,
+    EL_SIEB2_INAKTIV,
     EL_AUSGANG_ZU,
     EL_AUSGANG_AUF,
     EL_BETON,
@@ -1125,7 +1261,8 @@ void InitElementProperties()
     EL_SOKOBAN_OBJEKT,
     EL_SOKOBAN_FELD_LEER,
     EL_SOKOBAN_FELD_VOLL,
-    EL_SPIELFIGUR
+    EL_SPIELFIGUR,
+    EL_INVISIBLE_STEEL
   };
   static int ep_sb_element_num = sizeof(ep_sb_element)/sizeof(int);
 
@@ -1136,7 +1273,8 @@ void InitElementProperties()
     EL_EDELSTEIN_GELB,
     EL_EDELSTEIN_ROT,
     EL_EDELSTEIN_LILA,
-    EL_DIAMANT
+    EL_DIAMANT,
+    EL_SP_INFOTRON
   };
   static int ep_gem_num = sizeof(ep_gem)/sizeof(int);
 
@@ -1185,10 +1323,45 @@ void InitElementProperties()
     EL_BADEWANNE3,
     EL_BADEWANNE4,
     EL_BADEWANNE5,
+    EL_SIEB_INAKTIV,
     EL_SIEB_TOT,
+    EL_SIEB2_INAKTIV,
     EL_SIEB2_TOT,
     EL_AMOEBA2DIAM,
-    EL_BLOCKED
+    EL_BLOCKED,
+    EL_SP_EMPTY,
+    EL_SP_BASE,
+    EL_SP_PORT1_RIGHT,
+    EL_SP_PORT1_DOWN,
+    EL_SP_PORT1_LEFT,
+    EL_SP_PORT1_UP,
+    EL_SP_PORT2_RIGHT,
+    EL_SP_PORT2_DOWN,
+    EL_SP_PORT2_LEFT,
+    EL_SP_PORT2_UP,
+    EL_SP_PORT_X,
+    EL_SP_PORT_Y,
+    EL_SP_PORT_XY,
+    EL_SP_DISK_RED,
+    EL_SP_DISK_YELLOW,
+    EL_SP_CHIP_SINGLE,
+    EL_SP_CHIP_LEFT,
+    EL_SP_CHIP_RIGHT,
+    EL_SP_CHIP_UPPER,
+    EL_SP_CHIP_LOWER,
+    EL_SP_HARD_GRAY,
+    EL_SP_HARD_GREEN,
+    EL_SP_HARD_BLUE,
+    EL_SP_HARD_RED,
+    EL_SP_HARD_YELLOW,
+    EL_SP_HARD_BASE1,
+    EL_SP_HARD_BASE2,
+    EL_SP_HARD_BASE3,
+    EL_SP_HARD_BASE4,
+    EL_SP_HARD_BASE5,
+    EL_SP_HARD_BASE6,
+    EL_SP_EXIT,
+    EL_INVISIBLE_STEEL
   };
   static int ep_inactive_num = sizeof(ep_inactive)/sizeof(int);
 
@@ -1206,7 +1379,12 @@ void InitElementProperties()
     EL_PINGUIN,
     EL_SCHWEIN,
     EL_DRACHE,
-    EL_SONDE
+    EL_SONDE,
+    EL_SP_DISK_RED,
+    EL_SP_DISK_ORANGE,
+    EL_SP_DISK_YELLOW,
+    EL_SP_SNIKSNAK,
+    EL_SP_ELECTRON
   };
   static int ep_explosive_num = sizeof(ep_explosive)/sizeof(int);
 
@@ -1229,7 +1407,10 @@ void InitElementProperties()
     EL_ZEIT_LEER,
     EL_SOKOBAN_FELD_VOLL,
     EL_SOKOBAN_OBJEKT,
-    EL_SONDE
+    EL_SONDE,
+    EL_SP_ZONK,
+    EL_SP_DISK_ORANGE,
+    EL_SP_DISK_YELLOW
   };
   static int ep_pushable_num = sizeof(ep_pushable)/sizeof(int);
 
@@ -1243,6 +1424,69 @@ void InitElementProperties()
   };
   static int ep_player_num = sizeof(ep_player)/sizeof(int);
 
+  static int ep_has_content[] =
+  {
+    EL_MAMPFER,
+    EL_AMOEBE_NASS,
+    EL_AMOEBE_NORM,
+    EL_AMOEBE_VOLL,
+    EL_AMOEBE_BD
+  };
+  static int ep_has_content_num = sizeof(ep_has_content)/sizeof(int);
+
+  static int ep_eatable[] =
+  {
+    EL_ERDREICH,
+    EL_SP_BASE,
+    EL_SP_BUG
+  };
+  static int ep_eatable_num = sizeof(ep_eatable)/sizeof(int);
+
+  static int ep_sp_element[] =
+  {
+    EL_SP_EMPTY,
+    EL_SP_ZONK,
+    EL_SP_BASE,
+    EL_SP_MURPHY,
+    EL_SP_INFOTRON,
+    EL_SP_CHIP_SINGLE,
+    EL_SP_HARD_GRAY,
+    EL_SP_EXIT,
+    EL_SP_DISK_ORANGE,
+    EL_SP_PORT1_RIGHT,
+    EL_SP_PORT1_DOWN,
+    EL_SP_PORT1_LEFT,
+    EL_SP_PORT1_UP,
+    EL_SP_PORT2_RIGHT,
+    EL_SP_PORT2_DOWN,
+    EL_SP_PORT2_LEFT,
+    EL_SP_PORT2_UP,
+    EL_SP_SNIKSNAK,
+    EL_SP_DISK_YELLOW,
+    EL_SP_TERMINAL,
+    EL_SP_DISK_RED,
+    EL_SP_PORT_Y,
+    EL_SP_PORT_X,
+    EL_SP_PORT_XY,
+    EL_SP_ELECTRON,
+    EL_SP_BUG,
+    EL_SP_CHIP_LEFT,
+    EL_SP_CHIP_RIGHT,
+    EL_SP_HARD_BASE1,
+    EL_SP_HARD_GREEN,
+    EL_SP_HARD_BLUE,
+    EL_SP_HARD_RED,
+    EL_SP_HARD_YELLOW,
+    EL_SP_HARD_BASE2,
+    EL_SP_HARD_BASE3,
+    EL_SP_HARD_BASE4,
+    EL_SP_HARD_BASE5,
+    EL_SP_HARD_BASE6,
+    EL_SP_CHIP_UPPER,
+    EL_SP_CHIP_LOWER
+  };
+  static int ep_sp_element_num = sizeof(ep_sp_element)/sizeof(int);
+
   static long ep_bit[] =
   {
     EP_BIT_AMOEBALIVE,
@@ -1250,7 +1494,7 @@ void InitElementProperties()
     EP_BIT_SCHLUESSEL,
     EP_BIT_PFORTE,
     EP_BIT_SOLID,
-    EP_BIT_MASSIV,
+    EP_BIT_MASSIVE,
     EP_BIT_SLIPPERY,
     EP_BIT_ENEMY,
     EP_BIT_MAUER,
@@ -1269,7 +1513,10 @@ void InitElementProperties()
     EP_BIT_EXPLOSIVE,
     EP_BIT_MAMPF3,
     EP_BIT_PUSHABLE,
-    EP_BIT_PLAYER
+    EP_BIT_PLAYER,
+    EP_BIT_HAS_CONTENT,
+    EP_BIT_EATABLE,
+    EP_BIT_SP_ELEMENT
   };
   static int *ep_array[] =
   {
@@ -1278,7 +1525,7 @@ void InitElementProperties()
     ep_schluessel,
     ep_pforte,
     ep_solid,
-    ep_massiv,
+    ep_massive,
     ep_slippery,
     ep_enemy,
     ep_mauer,
@@ -1297,7 +1544,10 @@ void InitElementProperties()
     ep_explosive,
     ep_mampf3,
     ep_pushable,
-    ep_player
+    ep_player,
+    ep_has_content,
+    ep_eatable,
+    ep_sp_element
   };
   static int *ep_num[] =
   {
@@ -1306,7 +1556,7 @@ void InitElementProperties()
     &ep_schluessel_num,
     &ep_pforte_num,
     &ep_solid_num,
-    &ep_massiv_num,
+    &ep_massive_num,
     &ep_slippery_num,
     &ep_enemy_num,
     &ep_mauer_num,
@@ -1325,7 +1575,10 @@ void InitElementProperties()
     &ep_explosive_num,
     &ep_mampf3_num,
     &ep_pushable_num,
-    &ep_player_num
+    &ep_player_num,
+    &ep_has_content_num,
+    &ep_eatable_num,
+    &ep_sp_element_num
   };
   static int num_properties = sizeof(ep_num)/sizeof(int *);
 
index a0483461e302bf798c45bba8f6c3ae446f9a1f53..161f384350a2992c7ff72e6958ab6f1ed7e22588 100644 (file)
@@ -53,6 +53,7 @@ char         *joystick_device_name[MAX_PLAYERS] =
 char          *program_name = NULL;
 
 int            game_status = MAINMENU;
+boolean                level_editor_test_game = FALSE;
 boolean                network_playing = FALSE;
 int            button_status = MB_NOT_PRESSED;
 boolean                motion_status = FALSE;
@@ -88,14 +89,21 @@ int         lev_fieldx,lev_fieldy, scroll_x,scroll_y;
 int            FX = SX, FY = SY, ScrollStepSize = TILEX/8;
 int            ScreenMovDir = MV_NO_MOVING, ScreenMovPos = 0;
 int            ScreenGfxPos = 0;
+int            BorderElement = EL_BETON;
 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            SBX_Left, SBX_Right;
+int            SBY_Upper, SBY_Lower;
 int            ZX,ZY, ExitX,ExitY;
 int            AllPlayersGone;
-int            FrameCounter, TimeFrames, TimeLeft;
-int            MampferNr, SiebAktiv;
+int            FrameCounter, TimeFrames, TimePlayed, TimeLeft;
+int            MampferMax, MampferNr;
+boolean                SiebAktiv;
+int            SiebCount;
+
+int            game_emulation = EMU_NONE;
 
 boolean                network_player_action_received = FALSE;
 
@@ -164,7 +172,16 @@ char *sound_name[NUM_SOUNDS] =
   "voyager",
   "warnton",
   "whoosh",
-  "zisch"
+  "zisch",
+  "base",
+  "infotron",
+  "zonkdown",
+  "zonkpush",
+  "bug",
+  "boom",
+  "booom",
+  "exit",
+  "empty"
 };
 
 /* background music */
@@ -180,6 +197,266 @@ int background_loop[] =
 };
 int num_bg_loops = sizeof(background_loop)/sizeof(int);
 
+char *element_info[] =
+{
+  "empty space",
+  "sand",
+  "normal wall",
+  "round wall",
+  "rock",
+  "key",
+  "emerald",
+  "closed exit",
+  "player",
+  "bug",
+  "spaceship",
+  "yam yam",
+  "robot",
+  "steel wall",
+  "diamond",
+  "dead amoeba",
+  "empty quicksand",
+  "quicksand with rock",
+  "amoeba drop",
+  "bomb",
+  "magic wall",
+  "speed ball",
+  "acid pool",
+  "dropping amoeba",
+  "normal amoeba",
+  "nut with emerald",
+  "life wall",
+  "biomaze",
+  "burning dynamite",
+  "unknown",
+  "magic wheel",
+  "running wire",
+  "red key",
+  "yellow key",
+  "green key",
+  "blue key",
+  "red door",
+  "yellow door",
+  "green door",
+  "blue door",
+  "grey door (opened by red key)",
+  "grey door (opened by yellow key)",
+  "grey door (opened by green key)",
+  "grey door (opened by blue key)",
+  "dynamite",
+  "pac man",
+  "invisible normal wall",
+  "light bulb (dark)",
+  "ligh bulb (glowing)",
+  "wall with emerald",
+  "wall with diamond",
+  "amoeba with content",
+  "amoeba (BD style)",
+  "time orb (full)",
+  "time orb (empty)",
+  "growing wall",
+  "diamond (BD style)",
+  "yellow emerald",
+  "wall with BD style diamond",
+  "wall with yellow emerald",
+  "dark yam yam",
+  "magic wall (BD style)",
+  "invisible steel wall",
+  "dynabomb",
+  "increases number of bombs",
+  "increases explosion size",
+  "increases power of explosion",
+  "sokoban object",
+  "sokoban empty field",
+  "sokoban field with object",
+  "butterfly (starts moving right)",
+  "butterfly (starts moving up)",
+  "butterfly (starts moving left)",
+  "butterfly (starts moving down)",
+  "firefly (starts moving right)",
+  "firefly (starts moving up)",
+  "firefly (starts moving left)",
+  "firefly (starts moving down)",
+  "butterfly",
+  "firefly",
+  "yellow player",
+  "red player",
+  "green player",
+  "blue player",
+  "bug (starts moving right)",
+  "bug (starts moving up)",
+  "bug (starts moving left)",
+  "bug (starts moving down)",
+  "spaceship (starts moving right)",
+  "spaceship (starts moving up)",
+  "spaceship (starts moving left)",
+  "spaceship (starts moving down)",
+  "pac man (starts moving right)",
+  "pac man (starts moving up)",
+  "pac man (starts moving left)",
+  "pac man (starts moving down)",
+  "red emerald",
+  "violet emerald",
+  "wall with red emerald",
+  "wall with violet emerald",
+  "unknown",
+  "unknown",
+  "unknown",
+  "unknown",
+  "unknown",
+  "unknown",
+  "unknown",
+  "open exit",
+  "unknown",
+  "amoeba",
+  "mole",
+  "penguin",
+  "satellite",
+  "arrow left",
+  "arrow right",
+  "arrow up",
+  "arrow down",
+  "pig",
+  "fire breathing dragon",
+  "unknown",
+  "letter ' '",
+  "letter '!'",
+  "letter '\"'",
+  "letter '#'",
+  "letter '$'",
+  "letter '%'",
+  "letter '&'",
+  "letter '''",
+  "letter '('",
+  "letter ')'",
+  "letter '*'",
+  "letter '+'",
+  "letter ','",
+  "letter '-'",
+  "letter '.'",
+  "letter '/'",
+  "letter '0'",
+  "letter '1'",
+  "letter '2'",
+  "letter '3'",
+  "letter '4'",
+  "letter '5'",
+  "letter '6'",
+  "letter '7'",
+  "letter '8'",
+  "letter '9'",
+  "letter ':'",
+  "letter ';'",
+  "letter '<'",
+  "letter '='",
+  "letter '>'",
+  "letter '?'",
+  "letter '@'",
+  "letter 'A'",
+  "letter 'B'",
+  "letter 'C'",
+  "letter 'D'",
+  "letter 'E'",
+  "letter 'F'",
+  "letter 'G'",
+  "letter 'H'",
+  "letter 'I'",
+  "letter 'J'",
+  "letter 'K'",
+  "letter 'L'",
+  "letter 'M'",
+  "letter 'N'",
+  "letter 'O'",
+  "letter 'P'",
+  "letter 'Q'",
+  "letter 'R'",
+  "letter 'S'",
+  "letter 'T'",
+  "letter 'U'",
+  "letter 'V'",
+  "letter 'W'",
+  "letter 'X'",
+  "letter 'Y'",
+  "letter 'Z'",
+  "letter 'Ä'",
+  "letter 'Ö'",
+  "letter 'Ãœ'",
+  "letter '^'",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "growing wall (horizontally)",
+  "growing wall (vertically)",
+  "growing wall (all directions)",
+  "unused",
+  "unused",
+  "unused",
+  "unused",
+  "unused",
+  "unused",
+  "unused",
+  "empty space",
+  "zonk",
+  "base",
+  "murphy",
+  "infotron",
+  "chip (single)",
+  "hardware",
+  "exit",
+  "orange disk",
+  "port (leading right)",
+  "port (leading down)",
+  "port (leading left)",
+  "port (leading up)",
+  "port (leading right)",
+  "port (leading down)",
+  "port (leading left)",
+  "port (leading up)",
+  "snik snak",
+  "yellow disk",
+  "terminal",
+  "red disk",
+  "port (vertically)",
+  "port (horizontally)",
+  "port (all directions)",
+  "electron",
+  "buggy base",
+  "chip (left half)",
+  "chip (right half)",
+  "hardware",
+  "hardware",
+  "hardware",
+  "hardware",
+  "hardware",
+  "hardware",
+  "hardware",
+  "hardware",
+  "hardware",
+  "hardware",
+  "chip (upper half)",
+  "chip (lower half)",
+  "unknown",
+  "unknown",
+  "unknown",
+  "unknown",
+  "unknown",
+  "unknown"
+};
+
 int main(int argc, char *argv[])
 {
   program_name = (strrchr(argv[0],'/') ? strrchr(argv[0],'/') + 1 : argv[0]);
index fed17ca6a049a68e12801eb688aaf460c4420ad8..f25774b42b9632f72b1c7d1f1f4231f53e7e624c 100644 (file)
@@ -65,9 +65,8 @@ typedef unsigned char byte;
 #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 3
+#define MIN_LEV_FIELDY 3
 #define STD_LEV_FIELDX 64
 #define STD_LEV_FIELDY 32
 #define MAX_LEV_FIELDX 128
@@ -96,39 +95,42 @@ typedef unsigned char byte;
 #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_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)
-#define EP_BIT_CAN_MOVE                (1<<12)
-#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_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 EP_BIT_AMOEBALIVE      (1 << 0)
+#define EP_BIT_AMOEBOID                (1 << 1)
+#define EP_BIT_SCHLUESSEL      (1 << 2)
+#define EP_BIT_PFORTE          (1 << 3)
+#define EP_BIT_SOLID           (1 << 4)
+#define EP_BIT_MASSIVE         (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)
+#define EP_BIT_CAN_MOVE                (1 << 12)
+#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_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 EP_BIT_HAS_CONTENT     (1 << 26)
+#define EP_BIT_EATABLE         (1 << 27)
+#define EP_BIT_SP_ELEMENT      (1 << 28)
 
 #define IS_AMOEBALIVE(e)       (Elementeigenschaften[e] & EP_BIT_AMOEBALIVE)
 #define IS_AMOEBOID(e)         (Elementeigenschaften[e] & EP_BIT_AMOEBOID)
 #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_MASSIVE(e)          (Elementeigenschaften[e] & EP_BIT_MASSIVE)
 #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)
@@ -149,6 +151,9 @@ typedef unsigned char byte;
 #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 HAS_CONTENT(e)         (Elementeigenschaften[e] & EP_BIT_HAS_CONTENT)
+#define IS_EATABLE(e)          (Elementeigenschaften[e] & EP_BIT_EATABLE)
+#define IS_SP_ELEMENT(e)       (Elementeigenschaften[e] & EP_BIT_SP_ELEMENT)
 
 #define IS_PLAYER(x,y)         (ELEM_IS_PLAYER(StorePlayer[x][y]))
 
@@ -168,7 +173,6 @@ typedef unsigned char byte;
                                 EL_FELSBROCKEN)
 #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)
 
@@ -179,19 +183,21 @@ typedef unsigned char byte;
 #define PIX_DOOR               1
 #define PIX_HEROES             2
 #define PIX_TOONS              3
-#define        PIX_BIGFONT             4
-#define PIX_SMALLFONT          5
+#define PIX_MORE               4
+#define        PIX_BIGFONT             5
+#define PIX_SMALLFONT          6
 /* Pixmaps without them */
-#define PIX_DB_BACK            6
-#define PIX_DB_DOOR            7
-#define PIX_DB_FIELD           8
+#define PIX_DB_BACK            7
+#define PIX_DB_DOOR            8
+#define PIX_DB_FIELD           9
 
-#define NUM_PICTURES           6
-#define NUM_PIXMAPS            9
+#define NUM_PICTURES           7
+#define NUM_PIXMAPS            10
 
 /* boundaries of arrays etc. */
 #define MAX_NAMELEN            (10+1)
-#define MAX_LEVNAMLEN          32
+#define MAX_LEVEL_NAME_LEN     32
+#define MAX_LEVEL_AUTHOR_LEN   32
 #define MAX_TAPELEN            (1000 * 50)     /* max. time * framerate */
 #define MAX_LEVDIR_ENTRIES     100
 #define MAX_SCORE_ENTRIES      100
@@ -204,6 +210,9 @@ typedef unsigned char byte;
 #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)
+#define GADGET_FRAME_DELAY     150     /* delay between gadget actions */
+#define MICROLEVEL_SCROLL_DELAY        50      /* delay for scrolling micro level */
+#define MICROLEVEL_LABEL_DELAY 250     /* delay for micro level label */
 
 struct HiScore
 {
@@ -326,23 +335,29 @@ struct LevelInfo
   int fieldy;
   int time;
   int edelsteine;
-  char name[MAX_LEVNAMLEN];
+  char name[MAX_LEVEL_NAME_LEN + 1];
+  char author[MAX_LEVEL_AUTHOR_LEN + 1];
   int score[LEVEL_SCORE_ELEMENTS];
-  int mampfer_inhalt[4][3][3];
+  int mampfer_inhalt[8][3][3];
   int tempo_amoebe;
   int dauer_sieb;
   int dauer_ablenk;
   int amoebe_inhalt;
+  boolean double_speed;
 };
 
 struct LevelDirInfo
 {
   char *filename;
   char *name;
+  char *author;
   int levels;
+  int first_level;
+  int last_level;
   int sort_priority;
   boolean user_defined;
   boolean readonly;
+  int color;
 };
 
 struct TapeInfo
@@ -390,6 +405,7 @@ extern char        *joystick_device_name[];
 extern char           *program_name;
 
 extern int             game_status;
+extern boolean         level_editor_test_game;
 extern boolean         network_playing;
 extern int             button_status;
 extern boolean         motion_status;
@@ -423,14 +439,21 @@ extern int                lev_fieldx,lev_fieldy, scroll_x,scroll_y;
 
 extern int             FX,FY, ScrollStepSize;
 extern int             ScreenMovDir, ScreenMovPos, ScreenGfxPos;
+extern int             BorderElement;
 extern int             GameFrameDelay;
 extern int             FfwdFrameDelay;
 extern int             MoveSpeed;
 extern int             BX1,BY1, BX2,BY2;
+extern int             SBX_Left, SBX_Right;
+extern int             SBY_Upper, SBY_Lower;
 extern int             ZX,ZY, ExitX,ExitY;
 extern int             AllPlayersGone;
-extern int             FrameCounter, TimeFrames, TimeLeft;
-extern int             MampferNr, SiebAktiv;
+extern int             FrameCounter, TimeFrames, TimePlayed, TimeLeft;
+extern int             MampferMax, MampferNr;
+extern boolean         SiebAktiv;
+extern int             SiebCount;
+
+extern int             game_emulation;
 
 extern boolean         network_player_action_received;
 
@@ -449,40 +472,45 @@ extern struct SetupFileList       *level_setup_list;
 extern char            *sound_name[];
 extern int             background_loop[];
 extern int             num_bg_loops;
+extern char            *element_info[];
 
 
 /* often used screen positions */
 #define SX                     8
 #define SY                     8
-#define REAL_SX                        (SX-2)
-#define REAL_SY                        (SY-2)
+#define REAL_SX                        (SX - 2)
+#define REAL_SY                        (SY - 2)
 #define DX                     566
 #define DY                     60
 #define VX                     DX
 #define VY                     400
+#define EX                     DX
+#define EY                     (VY - 44)
 #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 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 EXSIZE                 DXSIZE
+#define EYSIZE                 (VXSIZE + 44)
+#define FULL_SXSIZE            (2 + SXSIZE + 2)
+#define FULL_SYSIZE            (2 + SYSIZE + 2)
+#define MICROLEV_XSIZE         ((STD_LEV_FIELDX + 2) * MICRO_TILEX)
+#define MICROLEV_YSIZE         ((STD_LEV_FIELDY + 2) * MICRO_TILEY)
+#define MICROLEV_XPOS          (SX + (SXSIZE - MICROLEV_XSIZE) / 2)
+#define MICROLEV_YPOS          (SX + 12 * TILEY - MICRO_TILEY)
+#define MICROLABEL_YPOS                (MICROLEV_YPOS + MICROLEV_YSIZE + 7)
 #define FONT1_XSIZE            32
 #define FONT1_YSIZE            32
 #define FONT2_XSIZE            14
@@ -502,6 +530,13 @@ extern int         num_bg_loops;
 #define MINI_GFX_PER_LINE      32
 #define MICRO_GFX_PER_LINE     128
 #define HEROES_PER_LINE                16
+#define MINI_MORE_STARTX       0
+#define MINI_MORE_STARTY       224
+#define MICRO_MORE_STARTX      0
+#define MICRO_MORE_STARTY      336
+#define MORE_PER_LINE          16
+#define MINI_MORE_PER_LINE     16
+#define MICRO_MORE_PER_LINE    16
 #define FONT_CHARS_PER_LINE    16
 #define FONT_LINES_PER_FONT    4
 
@@ -530,8 +565,8 @@ extern int          num_bg_loops;
 #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_SIEB_INAKTIV                20
+#define EL_SPEED_PILL          21
 #define EL_SALZSAEURE          22
 #define EL_AMOEBE_NASS         23
 #define EL_AMOEBE_NORM         24
@@ -571,8 +606,8 @@ extern int          num_bg_loops;
 #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_SIEB2_INAKTIV       61
+#define EL_INVISIBLE_STEEL     62
 #define EL_DYNABOMB            63
 #define EL_DYNABOMB_NR         64
 #define EL_DYNABOMB_SZ         65
@@ -623,10 +658,14 @@ extern int                num_bg_loops;
 #define EL_BADEWANNE3          102
 #define EL_BADEWANNE4          103
 #define EL_BADEWANNE5          104
-#define EL_SIEB_TOT            105
-#define EL_AUSGANG_ACT         106
+
+#define EL_UNUSED_105          105
+#define EL_UNUSED_106          106
+
 #define EL_AUSGANG_AUF         107
-#define EL_SIEB2_TOT           108
+
+#define EL_BLACK_ORB           108
+
 #define EL_AMOEBA2DIAM         109
 #define EL_MAULWURF            110
 #define EL_PINGUIN             111
@@ -644,11 +683,14 @@ extern int                num_bg_loops;
 #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_RAUTE          (EL_CHAR_ASCII0+35)
 #define EL_CHAR_DOLLAR         (EL_CHAR_ASCII0+36)
 #define EL_CHAR_PROZ           (EL_CHAR_ASCII0+37)
+#define EL_CHAR_AMPERSAND      (EL_CHAR_ASCII0+38)
 #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_MULT           (EL_CHAR_ASCII0+42)
 #define EL_CHAR_PLUS           (EL_CHAR_ASCII0+43)
 #define EL_CHAR_KOMMA          (EL_CHAR_ASCII0+44)
 #define EL_CHAR_MINUS          (EL_CHAR_ASCII0+45)
@@ -671,39 +713,112 @@ extern int               num_bg_loops;
 #define EL_CHAR_COPY           (EL_CHAR_ASCII0+94)
 #define EL_CHAR_END            (EL_CHAR_START+79)
 
+#define EL_CHAR(x)             ((x) == 'Ä' ? EL_CHAR_AE : \
+                                (x) == 'Ö' ? EL_CHAR_OE : \
+                                (x) == 'Ãœ' ? EL_CHAR_UE : \
+                                EL_CHAR_A + (x) - 'A')
+
 #define EL_MAUER_X             200
 #define EL_MAUER_Y             201
 #define EL_MAUER_XY            202
 
-#define EL_UNUSED_200          203
-/* ... */
+#define EL_UNUSED_203          203
+#define EL_UNUSED_204          204
+#define EL_UNUSED_205          205
+#define EL_UNUSED_206          206
+#define EL_UNUSED_207          207
+#define EL_UNUSED_208          208
+#define EL_UNUSED_209          209
+
+#define EL_SP_START            210
+#define EL_SP_EMPTY            (EL_SP_START + 0)
+#define EL_SP_ZONK             (EL_SP_START + 1)
+#define EL_SP_BASE             (EL_SP_START + 2)
+#define EL_SP_MURPHY           (EL_SP_START + 3)
+#define EL_SP_INFOTRON         (EL_SP_START + 4)
+#define EL_SP_CHIP_SINGLE      (EL_SP_START + 5)
+#define EL_SP_HARD_GRAY                (EL_SP_START + 6)
+#define EL_SP_EXIT             (EL_SP_START + 7)
+#define EL_SP_DISK_ORANGE      (EL_SP_START + 8)
+#define EL_SP_PORT1_RIGHT      (EL_SP_START + 9)
+#define EL_SP_PORT1_DOWN       (EL_SP_START + 10)
+#define EL_SP_PORT1_LEFT       (EL_SP_START + 11)
+#define EL_SP_PORT1_UP         (EL_SP_START + 12)
+#define EL_SP_PORT2_RIGHT      (EL_SP_START + 13)
+#define EL_SP_PORT2_DOWN       (EL_SP_START + 14)
+#define EL_SP_PORT2_LEFT       (EL_SP_START + 15)
+#define EL_SP_PORT2_UP         (EL_SP_START + 16)
+#define EL_SP_SNIKSNAK         (EL_SP_START + 17)
+#define EL_SP_DISK_YELLOW      (EL_SP_START + 18)
+#define EL_SP_TERMINAL         (EL_SP_START + 19)
+#define EL_SP_DISK_RED         (EL_SP_START + 20)
+#define EL_SP_PORT_Y           (EL_SP_START + 21)
+#define EL_SP_PORT_X           (EL_SP_START + 22)
+#define EL_SP_PORT_XY          (EL_SP_START + 23)
+#define EL_SP_ELECTRON         (EL_SP_START + 24)
+#define EL_SP_BUG              (EL_SP_START + 25)
+#define EL_SP_CHIP_LEFT                (EL_SP_START + 26)
+#define EL_SP_CHIP_RIGHT       (EL_SP_START + 27)
+#define EL_SP_HARD_BASE1       (EL_SP_START + 28)
+#define EL_SP_HARD_GREEN       (EL_SP_START + 29)
+#define EL_SP_HARD_BLUE                (EL_SP_START + 30)
+#define EL_SP_HARD_RED         (EL_SP_START + 31)
+#define EL_SP_HARD_YELLOW      (EL_SP_START + 32)
+#define EL_SP_HARD_BASE2       (EL_SP_START + 33)
+#define EL_SP_HARD_BASE3       (EL_SP_START + 34)
+#define EL_SP_HARD_BASE4       (EL_SP_START + 35)
+#define EL_SP_HARD_BASE5       (EL_SP_START + 36)
+#define EL_SP_HARD_BASE6       (EL_SP_START + 37)
+#define EL_SP_CHIP_UPPER       (EL_SP_START + 38)
+#define EL_SP_CHIP_LOWER       (EL_SP_START + 39)
+#define EL_SP_END              (EL_SP_START + 39)
+
+#define EL_UNUSED_250          250
+#define EL_UNUSED_251          251
+#define EL_UNUSED_252          252
+#define EL_UNUSED_253          253
+#define EL_UNUSED_254          254
 #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
-#define EL_MAUERND             306
-#define EL_BURNING             307
-#define EL_PLAYER_IS_LEAVING   308
+/* "real" (and therefore drawable) runtime elements */
+#define EL_SIEB_LEER           300
+#define EL_SIEB2_LEER          301
+#define EL_SIEB_VOLL           302
+#define EL_SIEB2_VOLL          303
+#define EL_SIEB_TOT            304
+#define EL_SIEB2_TOT           305
+#define EL_AUSGANG_ACT         306
+#define EL_SP_TERMINAL_ACTIVE  307
+#define EL_SP_BUG_ACTIVE       308
+
+/* "unreal" (and therefore not drawable) runtime elements */
+#define EL_BLOCKED             400
+#define EL_EXPLODING           401
+#define EL_CRACKINGNUT         402
+#define EL_BLURB_LEFT          403
+#define EL_BLURB_RIGHT         404
+#define EL_AMOEBING            405
+#define EL_MAUERND             406
+#define EL_BURNING             407
+#define EL_PLAYER_IS_LEAVING   408
 
 /* game graphics:
-**       0 - 255: graphics from "RocksScreen"
-**     256 - 511: graphics from "RocksFont"
-**     512 - 767: graphics from "RocksHeroes"
+**       0 -  255: graphics from "RocksScreen"
+**     256 -  511: graphics from "RocksFont"
+**     512 -  767: graphics from "RocksMore"
+**     768 - 1023: 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 GFX_START_ROCKSMORE    512
+#define GFX_END_ROCKSMORE      767
+#define GFX_START_ROCKSHEROES  768
+#define GFX_END_ROCKSHEROES    1023
 
-#define NUM_TILES              768
+#define NUM_TILES              1024
 
 /* graphics from "RocksScreen" */
 /* Zeile 0 (0) */
@@ -722,7 +837,11 @@ extern int         num_bg_loops;
 #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
@@ -796,9 +915,10 @@ extern int         num_bg_loops;
 #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_SIEB_INAKTIV       128
+#define GFX_SIEB_LEER          GFX_SIEB_INAKTIV
+#define GFX_SIEB_VOLL          GFX_SIEB_INAKTIV
+#define GFX_SIEB_TOT           GFX_SIEB_INAKTIV
 #define GFX_ERZ_EDEL           132
 #define GFX_ERZ_DIAM           133
 #define GFX_ERZ_EDEL_ROT       134
@@ -821,6 +941,8 @@ extern int          num_bg_loops;
 #define GFX_EDELSTEIN_ROT      152
 #define GFX_EDELSTEIN_LILA     154
 #define GFX_DYNABOMB_XL                156
+#define GFX_BLACK_ORB          157
+#define GFX_SPEED_PILL         158
 #define GFX_SONDE              159
 /* Zeile 10 (160) */
 #define GFX_EDELSTEIN_BD       163
@@ -831,9 +953,10 @@ extern int         num_bg_loops;
 #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
+#define GFX_SIEB2_INAKTIV      172
+#define GFX_SIEB2_LEER         GFX_SIEB2_INAKTIV
+#define GFX_SIEB2_VOLL         GFX_SIEB2_INAKTIV
+#define GFX_SIEB2_TOT          GFX_SIEB2_INAKTIV
 /* Zeile 11 (176) */
 #define        GFX_AUSGANG_ZU          176
 #define        GFX_AUSGANG_ACT         177
@@ -858,12 +981,25 @@ extern int                num_bg_loops;
 #define GFX_FIREFLY_L          206
 #define GFX_FIREFLY_U          207
 
+/* only available as size MINI_TILE */
+#define GFX_VSTEEL_UPPER_LEFT  208
+#define GFX_VSTEEL_UPPER_RIGHT 209
+#define GFX_VSTEEL_LOWER_LEFT  210
+#define GFX_VSTEEL_LOWER_RIGHT 211
+#define GFX_VSTEEL_HORIZONTAL  212
+#define GFX_VSTEEL_VERTICAL    213
+#define GFX_ISTEEL_UPPER_LEFT  214
+#define GFX_ISTEEL_UPPER_RIGHT 215
+#define GFX_ISTEEL_LOWER_LEFT  216
+#define GFX_ISTEEL_LOWER_RIGHT 217
+#define GFX_ISTEEL_HORIZONTAL  218
+#define GFX_ISTEEL_VERTICAL    219
+
+/* elements with graphics borrowed from other elements */
 #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)
@@ -917,37 +1053,106 @@ extern int              num_bg_loops;
 #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 "RocksMore" */
+#define GFX_SP_EMPTY           (GFX_START_ROCKSMORE +  0 * MORE_PER_LINE +  0)
+#define GFX_SP_ZONK            (GFX_START_ROCKSMORE +  0 * MORE_PER_LINE +  1)
+#define GFX_SP_BASE            (GFX_START_ROCKSMORE +  0 * MORE_PER_LINE +  2)
+#define GFX_SP_MURPHY          (GFX_START_ROCKSMORE +  0 * MORE_PER_LINE +  3)
+#define GFX_SP_INFOTRON                (GFX_START_ROCKSMORE +  0 * MORE_PER_LINE +  4)
+#define GFX_SP_CHIP_SINGLE     (GFX_START_ROCKSMORE +  0 * MORE_PER_LINE +  5)
+#define GFX_SP_HARD_GRAY       (GFX_START_ROCKSMORE +  0 * MORE_PER_LINE +  6)
+#define GFX_SP_EXIT            (GFX_START_ROCKSMORE +  0 * MORE_PER_LINE +  7)
+#define GFX_SP_DISK_ORANGE     (GFX_START_ROCKSMORE +  1 * MORE_PER_LINE +  0)
+#define GFX_SP_PORT1_RIGHT     (GFX_START_ROCKSMORE +  1 * MORE_PER_LINE +  1)
+#define GFX_SP_PORT1_DOWN      (GFX_START_ROCKSMORE +  1 * MORE_PER_LINE +  2)
+#define GFX_SP_PORT1_LEFT      (GFX_START_ROCKSMORE +  1 * MORE_PER_LINE +  3)
+#define GFX_SP_PORT1_UP                (GFX_START_ROCKSMORE +  1 * MORE_PER_LINE +  4)
+#define GFX_SP_PORT2_RIGHT     (GFX_START_ROCKSMORE +  1 * MORE_PER_LINE +  5)
+#define GFX_SP_PORT2_DOWN      (GFX_START_ROCKSMORE +  1 * MORE_PER_LINE +  6)
+#define GFX_SP_PORT2_LEFT      (GFX_START_ROCKSMORE +  1 * MORE_PER_LINE +  7)
+#define GFX_SP_PORT2_UP                (GFX_START_ROCKSMORE +  2 * MORE_PER_LINE +  0)
+#define GFX_SP_SNIKSNAK                (GFX_START_ROCKSMORE +  2 * MORE_PER_LINE +  1)
+#define GFX_SP_DISK_YELLOW     (GFX_START_ROCKSMORE +  2 * MORE_PER_LINE +  2)
+#define GFX_SP_TERMINAL                (GFX_START_ROCKSMORE +  2 * MORE_PER_LINE +  3)
+#define GFX_SP_DISK_RED                (GFX_START_ROCKSMORE +  2 * MORE_PER_LINE +  4)
+#define GFX_SP_PORT_Y          (GFX_START_ROCKSMORE +  2 * MORE_PER_LINE +  5)
+#define GFX_SP_PORT_X          (GFX_START_ROCKSMORE +  2 * MORE_PER_LINE +  6)
+#define GFX_SP_PORT_XY         (GFX_START_ROCKSMORE +  2 * MORE_PER_LINE +  7)
+#define GFX_SP_ELECTRON                (GFX_START_ROCKSMORE +  3 * MORE_PER_LINE +  0)
+#define GFX_SP_BUG             (GFX_START_ROCKSMORE +  3 * MORE_PER_LINE +  1)
+#define GFX_SP_CHIP_LEFT       (GFX_START_ROCKSMORE +  3 * MORE_PER_LINE +  2)
+#define GFX_SP_CHIP_RIGHT      (GFX_START_ROCKSMORE +  3 * MORE_PER_LINE +  3)
+#define GFX_SP_HARD_BASE1      (GFX_START_ROCKSMORE +  3 * MORE_PER_LINE +  4)
+#define GFX_SP_HARD_GREEN      (GFX_START_ROCKSMORE +  3 * MORE_PER_LINE +  5)
+#define GFX_SP_HARD_BLUE       (GFX_START_ROCKSMORE +  3 * MORE_PER_LINE +  6)
+#define GFX_SP_HARD_RED                (GFX_START_ROCKSMORE +  3 * MORE_PER_LINE +  7)
+#define GFX_SP_HARD_YELLOW     (GFX_START_ROCKSMORE +  4 * MORE_PER_LINE +  0)
+#define GFX_SP_HARD_BASE2      (GFX_START_ROCKSMORE +  4 * MORE_PER_LINE +  1)
+#define GFX_SP_HARD_BASE3      (GFX_START_ROCKSMORE +  4 * MORE_PER_LINE +  2)
+#define GFX_SP_HARD_BASE4      (GFX_START_ROCKSMORE +  4 * MORE_PER_LINE +  3)
+#define GFX_SP_HARD_BASE5      (GFX_START_ROCKSMORE +  4 * MORE_PER_LINE +  4)
+#define GFX_SP_HARD_BASE6      (GFX_START_ROCKSMORE +  4 * MORE_PER_LINE +  5)
+#define GFX_SP_CHIP_UPPER      (GFX_START_ROCKSMORE +  4 * MORE_PER_LINE +  6)
+#define GFX_SP_CHIP_LOWER      (GFX_START_ROCKSMORE +  4 * MORE_PER_LINE +  7)
+
+#define GFX_INVISIBLE_STEEL    (GFX_START_ROCKSMORE +  5 * MORE_PER_LINE +  3)
+#define GFX_UNSICHTBAR         (GFX_START_ROCKSMORE +  5 * MORE_PER_LINE +  7)
+
+#define GFX_MURPHY_GO_LEFT     (GFX_START_ROCKSMORE +  0 * MORE_PER_LINE +  8)
+#define GFX_MURPHY_ANY_LEFT    (GFX_START_ROCKSMORE +  0 * MORE_PER_LINE +  9)
+#define GFX_MURPHY_GO_RIGHT    (GFX_START_ROCKSMORE +  0 * MORE_PER_LINE + 11)
+#define GFX_MURPHY_ANY_RIGHT   (GFX_START_ROCKSMORE +  0 * MORE_PER_LINE + 12)
+#define GFX_MURPHY_SNAP_UP     (GFX_START_ROCKSMORE +  0 * MORE_PER_LINE + 14)
+#define GFX_MURPHY_SNAP_DOWN   (GFX_START_ROCKSMORE +  0 * MORE_PER_LINE + 15)
+#define GFX_MURPHY_SNAP_RIGHT  (GFX_START_ROCKSMORE +  1 * MORE_PER_LINE +  8)
+#define GFX_MURPHY_SNAP_LEFT   (GFX_START_ROCKSMORE +  1 * MORE_PER_LINE +  9)
+#define GFX_MURPHY_PUSH_RIGHT  (GFX_START_ROCKSMORE +  1 * MORE_PER_LINE + 10)
+#define GFX_MURPHY_PUSH_LEFT   (GFX_START_ROCKSMORE +  1 * MORE_PER_LINE + 11)
+
+#define GFX_SP_BUG_WARNING     (GFX_START_ROCKSMORE +  2 * MORE_PER_LINE + 15)
+#define GFX_SP_EXPLODE_EMPTY   (GFX_START_ROCKSMORE +  3 * MORE_PER_LINE +  8)
+#define GFX_SP_EXPLODE_INFOTRON        (GFX_START_ROCKSMORE +  4 * MORE_PER_LINE +  8)
+#define GFX_SP_BUG_ACTIVE      (GFX_START_ROCKSMORE +  6 * MORE_PER_LINE +  8)
+#define GFX_SP_SNIKSNAK_LEFT   (GFX_START_ROCKSMORE +  8 * MORE_PER_LINE +  8)
+#define GFX_SP_SNIKSNAK_RIGHT  (GFX_START_ROCKSMORE +  8 * MORE_PER_LINE + 12)
+#define GFX_SP_SNIKSNAK_UP     (GFX_START_ROCKSMORE +  9 * MORE_PER_LINE +  8)
+#define GFX_SP_SNIKSNAK_DOWN   (GFX_START_ROCKSMORE +  9 * MORE_PER_LINE + 12)
+
+#define GFX2_SP_ELECTRON       (GFX_START_ROCKSMORE + 10 * MORE_PER_LINE +  8)
+#define GFX2_SP_TERMINAL       (GFX_START_ROCKSMORE + 11 * MORE_PER_LINE +  8)
+#define GFX2_SP_TERMINAL_ACTIVE        (GFX_START_ROCKSMORE + 12 * MORE_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)
+#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
@@ -1002,8 +1207,17 @@ extern int               num_bg_loops;
 #define SND_WARNTON            49
 #define SND_WHOOSH             50
 #define SND_ZISCH              51
-
-#define NUM_SOUNDS             52
+#define SND_SP_BASE            52
+#define SND_SP_INFOTRON                53
+#define SND_SP_ZONKDOWN                54
+#define SND_SP_ZONKPUSH                55
+#define SND_SP_BUG             56
+#define SND_SP_BOOM            57
+#define SND_SP_BOOOM           58
+#define SND_SP_EXIT            59
+#define SND_EMPTY              60
+
+#define NUM_SOUNDS             61
 
 /* default input keys */
 #define KEY_UNDEFINDED         XK_VoidSymbol
@@ -1018,10 +1232,10 @@ extern int              num_bg_loops;
 
 /* directions for moving */
 #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)
+#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
@@ -1059,14 +1273,21 @@ extern int              num_bg_loops;
 #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"
+#define PROGRAM_VERSION_STRING "1.3.0"
+#define PROGRAM_TITLE_STRING   "Rocks'n'Diamonds"
+#define PROGRAM_AUTHOR_STRING  "Holger Schemel"
+#define WINDOW_TITLE_STRING    PROGRAM_TITLE_STRING " " PROGRAM_VERSION_STRING
+#define COPYRIGHT_STRING       "Copyright ^1995-99 by " PROGRAM_AUTHOR_STRING
 
 /* default name for empty highscore entry */
 #define EMPTY_PLAYER_NAME      "no name"
 
+/* default name for unknown player names */
+#define ANONYMOUS_NAME         "anonymous"
+
+/* default name for new levels */
+#define NAMELESS_LEVEL_NAME    "nameless level"
+
 /* values for button_status */
 #define MB_NOT_PRESSED         FALSE
 #define MB_RELEASED            FALSE
@@ -1075,28 +1296,30 @@ extern int              num_bg_loops;
 #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_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_THRESHOLD  SCR_FIELDX*SCR_FIELDY/2
+#define REDRAW_ALL             (1 << 0)
+#define REDRAW_FIELD           (1 << 1)
+#define REDRAW_TILES           (1 << 2)
+#define REDRAW_DOOR_1          (1 << 3)
+#define REDRAW_VIDEO_1         (1 << 4)
+#define REDRAW_VIDEO_2         (1 << 5)
+#define REDRAW_VIDEO_3         (1 << 6)
+#define REDRAW_MICROLEVEL      (1 << 7)
+#define REDRAW_FROM_BACKBUFFER (1 << 8)
+#define REDRAW_DOOR_2          (REDRAW_VIDEO_1 | \
+                                REDRAW_VIDEO_2 | \
+                                REDRAW_VIDEO_3)
+#define REDRAW_DOOR_3          (1 << 9)
+#define REDRAW_DOORS           (REDRAW_DOOR_1 | \
+                                REDRAW_DOOR_2 | \
+                                REDRAW_DOOR_3)
+#define REDRAW_MAIN            (REDRAW_FIELD | \
+                                REDRAW_TILES | \
+                                REDRAW_MICROLEVEL)
+#define REDRAWTILES_THRESHOLD  (SCR_FIELDX * SCR_FIELDY / 2)
 
 /* areas in pixmap PIX_DOOR */
 /* meaning in PIX_DB_DOOR: (3 PAGEs)
@@ -1112,12 +1335,20 @@ extern int              num_bg_loops;
 #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_PAGEX7                (6 * DOOR_GFX_PAGESIZE)
+#define DOOR_GFX_PAGEX8                (7 * DOOR_GFX_PAGESIZE)
 #define DOOR_GFX_PAGEY1                0
 #define DOOR_GFX_PAGEY2                DYSIZE
 
 /* for DrawGraphicAnimation() [tools.c] and AnimateToon() [cartoons.c] */
-#define ANIM_NORMAL    0
-#define ANIM_OSCILLATE 1
-#define ANIM_REVERSE   2
+#define ANIM_NORMAL            0
+#define ANIM_OSCILLATE         1
+#define ANIM_REVERSE           2
+
+/* values for game_emulation */
+#define EMU_NONE               0
+#define EMU_BOULDERDASH                1
+#define EMU_SOKOBAN            2
+#define EMU_SUPAPLEX           3
 
 #endif /* MAIN_H */
index 39d1fdc45864f5c8022f6891efdb08ca9a49b51e..fd85aeddac9e433635bff3586077c11e6fa5ad2f 100644 (file)
@@ -235,12 +235,49 @@ char *getLoginName()
 {
   struct passwd *pwd;
 
-  if (!(pwd = getpwuid(getuid())))
-    return "ANONYMOUS";
+  if ((pwd = getpwuid(getuid())) == NULL)
+    return ANONYMOUS_NAME;
   else
     return pwd->pw_name;
 }
 
+char *getRealName()
+{
+#ifndef MSDOS
+  struct passwd *pwd;
+
+  if ((pwd = getpwuid(getuid())) == NULL || strlen(pwd->pw_gecos) == 0)
+    return ANONYMOUS_NAME;
+  else
+  {
+    static char real_name[1024];
+    char *from_ptr = pwd->pw_gecos, *to_ptr = real_name;
+
+    if (strchr(pwd->pw_gecos, 'ß') == NULL)
+      return pwd->pw_gecos;
+
+    /* the user's real name contains a 'ß' character (german sharp s),
+       which has no equivalent in upper case letters (which our fonts use) */
+    while (*from_ptr != '\0' && (long)(to_ptr - real_name) < 1024 - 2)
+    {
+      if (*from_ptr != 'ß')
+       *to_ptr++ = *from_ptr++;
+      else
+      {
+       from_ptr++;
+       *to_ptr++ = 's';
+       *to_ptr++ = 's';
+      }
+    }
+    *to_ptr = '\0';
+
+    return real_name;
+  }
+#else
+  return ANONYMOUS_NAME;
+#endif
+}
+
 char *getHomeDir()
 {
 #ifndef MSDOS
@@ -315,6 +352,25 @@ void MarkTileDirty(int x, int y)
   redraw_mask |= REDRAW_TILES;
 }
 
+void SetBorderElement()
+{
+  int x, y;
+
+  BorderElement = EL_LEERRAUM;
+
+  for(y=0; y<lev_fieldy && BorderElement == EL_LEERRAUM; y++)
+  {
+    for(x=0; x<lev_fieldx; x++)
+    {
+      if (!IS_MASSIVE(Feld[x][y]))
+       BorderElement = EL_BETON;
+
+      if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
+       x = lev_fieldx - 2;
+    }
+  }
+}
+
 void GetOptions(char *argv[])
 {
   char **options_left = &argv[1];
@@ -581,27 +637,53 @@ void translate_keyname(KeySym *keysym, char **x11name, char **name, int mode)
     { 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",        "ü" },
+
+    /* ASCII 0x20 to 0x40 keys (except numbers) */
+    { XK_space,                "XK_space",             "space" },
+    { XK_exclam,       "XK_exclam",            "!" },
+    { XK_quotedbl,     "XK_quotedbl",          "\"" },
+    { XK_numbersign,   "XK_numbersign",        "#" },
+    { XK_dollar,       "XK_dollar",            "$" },
+    { XK_percent,      "XK_percent",           "%" },
+    { XK_ampersand,    "XK_ampersand",         "&" },
     { XK_apostrophe,   "XK_apostrophe",        "'" },
+    { XK_parenleft,    "XK_parenleft",         "(" },
+    { XK_parenright,   "XK_parenright",        ")" },
+    { XK_asterisk,     "XK_asterisk",          "*" },
     { XK_plus,         "XK_plus",              "+" },
-    { XK_minus,                "XK_minus",             "-" },
-    { XK_equal,                "XK_equal",             "equal" },
     { XK_comma,                "XK_comma",             "," },
+    { XK_minus,                "XK_minus",             "-" },
     { XK_period,       "XK_period",            "." },
-    { XK_colon,                "XK_colon",             ";" },
     { XK_slash,                "XK_slash",             "/" },
-    { XK_numbersign,   "XK_numbersign",        "#" },
+    { XK_colon,                "XK_colon",             ":" },
+    { XK_semicolon,    "XK_semicolon",         ";" },
+    { XK_less,         "XK_less",              "<" },
+    { XK_equal,                "XK_equal",             "=" },
+    { XK_greater,      "XK_greater",           ">" },
+    { XK_question,     "XK_question",          "?" },
+    { XK_at,           "XK_at",                "@" },
+
+    /* more ASCII keys */
+    { XK_bracketleft,  "XK_bracketleft",       "[" },
     { XK_backslash,    "XK_backslash",         "backslash" },
+    { XK_bracketright, "XK_bracketright",      "]" },
+    { XK_asciicircum,  "XK_asciicircum",       "circumflex" },
+    { XK_underscore,   "XK_underscore",        "_" },
+    { XK_grave,                "XK_grave",             "grave" },
+    { XK_quoteleft,    "XK_quoteleft",         "quote left" },
     { XK_braceleft,    "XK_braceleft",         "brace left" },
+    { XK_bar,          "XK_bar",               "bar" },
     { XK_braceright,   "XK_braceright",        "brace right" },
-    { XK_less,         "XK_less",              "less" },
-    { XK_greater,      "XK_greater",           "greater" },
-    { XK_asciicircum,  "XK_asciicircum",       "circumflex" },
+    { XK_asciitilde,   "XK_asciitilde",        "ascii tilde" },
+
+    /* special (non-ASCII) keys */
+    { XK_Adiaeresis,   "XK_Adiaeresis",        "Ä" },
+    { XK_Odiaeresis,   "XK_Odiaeresis",        "Ö" },
+    { XK_Udiaeresis,   "XK_Udiaeresis",        "Ãœ" },
+    { XK_adiaeresis,   "XK_adiaeresis",        "ä" },
+    { XK_odiaeresis,   "XK_odiaeresis",        "ö" },
+    { XK_udiaeresis,   "XK_udiaeresis",        "ü" },
     { XK_ssharp,       "XK_ssharp",            "sharp s" },
 
     /* end-of-array identifier */
@@ -793,6 +875,21 @@ KeySym getKeySymFromX11KeyName(char *x11name)
   return keysym;
 }
 
+char getCharFromKeySym(KeySym keysym)
+{
+  char *keyname = getKeyNameFromKeySym(keysym);
+  char letter = 0;
+
+  if (strlen(keyname) == 1)
+    letter = keyname[0];
+  else if (strcmp(keyname, "space") == 0)
+    letter = ' ';
+  else if (strcmp(keyname, "circumflex") == 0)
+    letter = '^';
+
+  return letter;
+}
+
 #define TRANSLATE_JOYSYMBOL_TO_JOYNAME 0
 #define TRANSLATE_JOYNAME_TO_JOYSYMBOL 1
 
index ae8ae79e9ad85e266d8778c503a43e25d735d5d7..9793cb733f54b697dc884747d15074e6cfccca5e 100644 (file)
@@ -45,12 +45,14 @@ unsigned int SimpleRND(unsigned int);
 unsigned int RND(unsigned int);
 unsigned int InitRND(long);
 char *getLoginName(void);
+char *getRealName(void);
 char *getHomeDir(void);
 char *getPath2(char *, char *);
 char *getPath3(char *, char *, char*);
 char *getStringCopy(char *);
 char *getStringToLower(char *);
 void MarkTileDirty(int, int);
+void SetBorderElement();
 void GetOptions(char **);
 void Error(int, char *, ...);
 void *checked_malloc(unsigned long);
@@ -58,6 +60,7 @@ void *checked_calloc(unsigned long);
 char *getKeyNameFromKeySym(KeySym);
 char *getX11KeyNameFromKeySym(KeySym);
 KeySym getKeySymFromX11KeyName(char *);
+char getCharFromKeySym(KeySym);
 char *getJoyNameFromJoySymbol(int);
 int getJoySymbolFromJoyName(char *);
 int getJoystickNrFromDeviceName(char *);
index efcd80205918fd491e3524139bab1884c4fd071f..5090c55c1603ed779928c6373ef0538714583206 100644 (file)
@@ -37,6 +37,7 @@ extern JOYSTICK_INFO joy[];
 extern int i_love_bill;
 
 /* internal variables of msdos.c */
+static boolean keyboard_auto_repeat = TRUE;
 static int key_press_state[MAX_SCANCODES];
 static XEvent event_buffer[MAX_EVENT_BUFFER];
 static int pending_events;
@@ -250,6 +251,42 @@ void XMapWindow(Display *display, Window window)
     unhide_mouse(display);
 }
 
+static unsigned long AllocColorCell(int r, int g, int b)
+{
+  byte pixel_mapping = 0;
+  int i;
+
+  r >>= 10;
+  g >>= 10;
+  b >>= 10;
+
+  /* try to use existing colors from the global colormap */
+  for (i=0; i<global_colormap_entries_used; i++)
+  {
+    if (r == global_colormap[i].r &&
+       g == global_colormap[i].g &&
+       b == global_colormap[i].b)              /* color found */
+    {
+      pixel_mapping = i;
+      break;
+    }
+  }
+
+  if (i == global_colormap_entries_used)       /* color not found */
+  {
+    if (global_colormap_entries_used < MAX_COLORS)
+      global_colormap_entries_used++;
+
+    global_colormap[i].r = r;
+    global_colormap[i].g = g;
+    global_colormap[i].b = b;
+
+    pixel_mapping = i;
+  }
+
+  return pixel_mapping;
+}
+
 Display *XOpenDisplay(char *display_name)
 {
   Screen *screen;
@@ -271,8 +308,13 @@ Display *XOpenDisplay(char *display_name)
 
   screen[0].cmap = 0;
   screen[0].root = 0;
+#if 0
   screen[0].white_pixel = 0xFF;
   screen[0].black_pixel = 0x00;
+#else
+  screen[0].white_pixel = AllocColorCell(0xFFFF, 0xFFFF, 0xFFFF);
+  screen[0].black_pixel = AllocColorCell(0x0000, 0x0000, 0x0000);
+#endif
   screen[0].video_bitmap = NULL;
 
   display->default_screen = 0;
@@ -306,6 +348,11 @@ Window XCreateSimpleWindow(Display *display, Window parent, int x, int y,
   display->screens[display->default_screen].height = YRES;
 
   set_mouse_sprite(display->mouse_ptr);
+
+#if 0
+  set_mouse_sprite_focus(1, 1);
+#endif
+
   set_mouse_speed(1, 1);
   set_mouse_range(display->screens[display->default_screen].x + 1,
                  display->screens[display->default_screen].y + 1,
@@ -385,7 +432,7 @@ void XFillRectangle(Display *display, Drawable d, GC gc, int x, int y,
     mouse_off = hide_mouse(display, x, y, width, height);
   }
 
-  rectfill((BITMAP *)d, x, y, x + width, y + height,
+  rectfill((BITMAP *)d, x, y, x + width - 1, y + height - 1,
           ((XGCValues *)gc)->foreground);
 
   if (mouse_off)
@@ -460,7 +507,12 @@ static BITMAP *Image_to_AllegroBitmap(Image *image)
   byte *src_ptr = image->data;
   byte pixel_mapping[MAX_COLORS];
   unsigned int depth = 8;
+
+#if 0
   int i, j, x, y;
+#else
+  int i, x, y;
+#endif
 
   /* allocate new allegro bitmap structure */
   if ((bitmap = create_bitmap_ex(depth, image->width, image->height)) == NULL)
@@ -471,11 +523,16 @@ static BITMAP *Image_to_AllegroBitmap(Image *image)
   /* try to use existing colors from the global colormap */
   for (i=0; i<MAX_COLORS; i++)
   {
+
+#if 0
     int r, g, b;
+#endif
 
     if (!image->rgb.color_used[i])
       continue;
 
+
+#if 0
     r = image->rgb.red[i] >> 10;
     g = image->rgb.green[i] >> 10;
     b = image->rgb.blue[i] >> 10;
@@ -502,6 +559,12 @@ static BITMAP *Image_to_AllegroBitmap(Image *image)
 
       pixel_mapping[i] = j;
     }
+#else
+    pixel_mapping[i] = AllocColorCell(image->rgb.red[i],
+                                     image->rgb.green[i],
+                                     image->rgb.blue[i]);
+#endif
+
   }
 
   /* copy bitmap data */
@@ -519,13 +582,19 @@ static BITMAP *Read_PCX_to_AllegroBitmap(char *filename)
 
   /* read the graphic file in PCX format to internal image structure */
   if ((image = Read_PCX_to_Image(filename)) == NULL)
+  {
+    Error(ERR_RETURN, "Read_PCX_to_Image failed");
     return NULL;
+  }
 
   /* convert internal image structure to allegro bitmap structure */
   if ((bitmap = Image_to_AllegroBitmap(image)) == NULL)
+  {
+    Error(ERR_RETURN, "Image_to_AllegroBitmap failed");
     return NULL;
+  }
 
-  set_pallete(global_colormap);
+  set_palette(global_colormap);
 
   return bitmap;
 }
@@ -633,7 +702,7 @@ static void NewKeyEvent(int key_press_state, KeySym keysym)
   xkey->state = (unsigned int)keysym;
 }
 
-#define HANDLE_RAW_KB_ALL_KEYS         0
+#define HANDLE_RAW_KB_ALL_KEYS                 0
 #define HANDLE_RAW_KB_MODIFIER_KEYS_ONLY       1
 
 static int modifier_scancode[] =
@@ -701,6 +770,26 @@ static void HandleKeyboardEvent()
       else if (ascii == '.')
        keysym = XK_KP_Separator;
     }
+    else if (ascii >= ' ' && ascii <= 'Z')
+      keysym = XK_space + (KeySym)(ascii - ' ');
+    else if (ascii == '^')
+      keysym = XK_asciicircum;
+    else if (ascii == '_')
+      keysym = XK_underscore;
+    else if (ascii == 'Ä')
+      keysym = XK_Adiaeresis;
+    else if (ascii == 'Ö')
+      keysym = XK_Odiaeresis;
+    else if (ascii == 'Ãœ')
+      keysym = XK_Udiaeresis;
+    else if (ascii == 'ä')
+      keysym = XK_adiaeresis;
+    else if (ascii == 'ö')
+      keysym = XK_odiaeresis;
+    else if (ascii == 'ü')
+      keysym = XK_udiaeresis;
+    else if (ascii == 'ß')
+      keysym = XK_ssharp;
 
     NewKeyEvent(KeyPress, keysym);
   }
@@ -720,15 +809,22 @@ int XPending(Display *display)
   XMotionEvent *xmotion;
   int i;
 
+  /* When using 'HandleKeyboardRaw()', keyboard input is also stored in
+     the allegro keyboard input buffer and would be available a second
+     time by calling 'HandleKeyboardEvent()'. To avoid double keyboard
+     events, the allegro function 'clear_keybuf()' must be called each
+     time when switching from calling 'HandleKeyboardRaw()' to calling
+     'HandleKeyboardEvent()' to get keyboard input, which is actually
+     done by 'XAutoRepeatOn()' which sets keyboard_auto_repeat to TRUE. */
+
   /* keyboard event */
-  if (game_status == PLAYING)
-    HandleKeyboardRaw(HANDLE_RAW_KB_ALL_KEYS);
-  else
+  if (keyboard_auto_repeat)
     HandleKeyboardEvent();
+  else
+    HandleKeyboardRaw(HANDLE_RAW_KB_ALL_KEYS);
 
   /* mouse motion event */
-  /* generate mouse motion event only if any mouse buttons are pressed */
-  if (mouse_pos != last_mouse_pos && mouse_b)
+  if (mouse_pos != last_mouse_pos)
   {
     last_mouse_pos = mouse_pos;
     pending_events++;
@@ -768,6 +864,71 @@ KeySym XLookupKeysym(XKeyEvent *key_event, int index)
   return key_event->state;
 }
 
+int XLookupString(XKeyEvent *key_event, char *buffer, int buffer_size,
+                 KeySym *key, XComposeStatus *compose)
+{
+  *key = key_event->state;
+  return 0;
+}
+
+void XSetForeground(Display *display, GC gc, unsigned long pixel)
+{
+  XGCValues *gcv = (XGCValues *)gc;
+
+  gcv->foreground = pixel;
+}
+
+void XDrawLine(Display *display, Drawable d, GC gc,
+              int x1, int y1, int x2, int y2)
+{
+  XGCValues *gcv = (XGCValues *)gc;
+  boolean mouse_off = FALSE;
+
+  if ((BITMAP *)d == video_bitmap)
+  {
+    x1 += display->screens[display->default_screen].x;
+    y1 += display->screens[display->default_screen].y;
+    x2 += display->screens[display->default_screen].x;
+    y2 += display->screens[display->default_screen].y;
+    freeze_mouse_flag = TRUE;
+    mouse_off = hide_mouse(display, MIN(x1, x2), MIN(y1, y2),
+                          MAX(x1, x2) - MIN(x1, x2),
+                          MAX(y1, y2) - MIN(y1, y2));
+  }
+
+  line((BITMAP *)d, x1, y1, x2, y2, gcv->foreground);
+
+  if (mouse_off)
+    unhide_mouse(display);
+
+  freeze_mouse_flag = FALSE;
+}
+
+void XDestroyImage(XImage *ximage)
+{
+}
+
+Bool XQueryPointer(Display *display, Window window,
+                  Window *root, Window *child, int *root_x, int *root_y,
+                  int *win_x, int *win_y, unsigned int *mask)
+{
+  *win_x = mouse_x - display->screens[display->default_screen].x;
+  *win_y = mouse_y - display->screens[display->default_screen].y;
+
+  return True;
+}
+
+void XAutoRepeatOn(Display *display)
+{
+  keyboard_auto_repeat = TRUE;
+  clear_keybuf();
+}
+
+void XAutoRepeatOff(Display *display)
+{
+  keyboard_auto_repeat = FALSE;
+}
+
 void NetworkServer(int port, int serveronly)
 {
   Error(ERR_WARN, "networking not supported in DOS version");
index c344414ed379eeeffb461de91d0d437e2016fbbc..508bb84463a5970dbbea345bc2aa392da7f1cd79 100644 (file)
 
 #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)
@@ -550,6 +548,7 @@ typedef long XVisualInfo;
 typedef long Atom;
 typedef int Status;
 typedef int Bool;
+typedef int XComposeStatus;    /* we don't need the real type */
 
 typedef struct _XGC
 {
@@ -706,4 +705,12 @@ void XCloseDisplay(Display *);
 void XNextEvent(Display *, XEvent *);
 int XPending(Display *);
 KeySym XLookupKeysym(XKeyEvent *, int);
+int XLookupString(XKeyEvent *, char *, int, KeySym *, XComposeStatus *);
+void XSetForeground(Display *, GC, unsigned long);
+void XDrawLine(Display *, Drawable, GC, int, int, int, int);
+void XDestroyImage(XImage *);
+Bool XQueryPointer(Display *, Window, Window *, Window *, int *, int *,
+                  int *, int *, unsigned int *);
+void XAutoRepeatOn(Display *);
+void XAutoRepeatOff(Display *);
 void NetworkServer(int, int);
index 514d30de6041f7683a86e96cffed79e474767357..1c63396a5f04751d8da4f029c6c82366c9597cb7 100644 (file)
--- a/src/pcx.c
+++ b/src/pcx.c
@@ -139,8 +139,9 @@ Image *Read_PCX_to_Image(char *filename)
   file_length = ftell(file);
   rewind(file);
 
-  if (file_length < PCX_HEADER_SIZE + PCX_COLORMAP_SIZE)
+  if (file_length < PCX_HEADER_SIZE)
   {
+    /* PCX file is too short to contain a valid PCX header */
     fclose(file);
     return NULL;
   }
@@ -185,6 +186,7 @@ Image *Read_PCX_to_Image(char *filename)
           filename, pcx.xmax, pcx.ymax,
           pcx.color_planes);
     printf("depth: %d\n", pcx.bits_per_pixel);
+    printf("color_planes: %d\n", pcx.color_planes);
     printf("bytes_per_line: %d\n", pcx.bytes_per_line);
     printf("palette type: %s\n",
           (pcx.palette_type == 1 ? "color" :
@@ -205,6 +207,13 @@ Image *Read_PCX_to_Image(char *filename)
     return NULL;
   }
 
+  if (file_length < PCX_HEADER_SIZE + PCX_COLORMAP_SIZE)
+  {
+    /* PCX file is too short to contain a valid 256 colors colormap */
+    fclose(file);
+    return NULL;
+  }
+
   /* read colormap data */
   if (!PCX_ReadColormap(image, buffer_ptr, buffer_last))
   {
index c0a3b8c64a93b851a9b24380d3cde2c3786cc0b5..a27dcba819af1910a6d8a9afc81a167fb7d6059b 100644 (file)
@@ -47,9 +47,9 @@ extern unsigned char get_ascii(KeySym);
 
 void DrawHeadline()
 {
-  int x = SX + (SXSIZE - strlen(GAMETITLE_STRING) * FONT1_XSIZE) / 2;
+  int x = SX + (SXSIZE - strlen(PROGRAM_TITLE_STRING) * FONT1_XSIZE) / 2;
 
-  DrawText(x, SY + 8, GAMETITLE_STRING, FS_BIG, FC_YELLOW);
+  DrawText(x, SY + 8, PROGRAM_TITLE_STRING, FS_BIG, FC_YELLOW);
   DrawTextFCentered(46, FC_RED, COPYRIGHT_STRING);
 }
 
@@ -58,7 +58,21 @@ void DrawMainMenu()
   int i;
   char *name_text = (!options.network && setup.team_mode ? "Team:" : "Name:");
 
+  UnmapAllGadgets();
   FadeSounds();
+  XAutoRepeatOn(display);
+
+  /* needed if last screen was the playing screen, invoked from level editor */
+  if (level_editor_test_game)
+  {
+    game_status = LEVELED;
+    DrawLevelEd();
+    return;
+  }
+
+  /* map gadgets for main menu screen */
+  MapTapeButtons();
+
   GetPlayerConfig();
   LoadLevel(level_nr);
 
@@ -76,15 +90,23 @@ void DrawMainMenu()
   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);
+  DrawMicroLevel(MICROLEV_XPOS, MICROLEV_YPOS, TRUE);
+
+  DrawTextF(7*32 + 6, 3*32 + 9, FC_RED, "%d-%d",
+           leveldir[leveldir_nr].first_level,
+           leveldir[leveldir_nr].last_level);
+
+  if (leveldir[leveldir_nr].readonly)
+  {
+    DrawTextF(15*32 + 6, 3*32 + 9 - 7, FC_RED, "READ");
+    DrawTextF(15*32 + 6, 3*32 + 9 + 7, FC_RED, "ONLY");
+  }
 
   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);
 
@@ -109,7 +131,6 @@ void DrawMainMenu()
   OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2);
 
   ClearEventQueue();
-  XAutoRepeatOn(display);
 }
 
 void HandleMainMenu(int mx, int my, int dx, int dy, int button)
@@ -154,8 +175,8 @@ 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[leveldir_nr].levels - 1)) &&
+  if (y == 4 && ((x == 11 && level_nr > leveldir[leveldir_nr].first_level) ||
+                (x == 15 && level_nr < leveldir[leveldir_nr].last_level)) &&
       button)
   {
     static unsigned long level_delay = 0;
@@ -164,12 +185,13 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
     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 (new_level_nr < leveldir[leveldir_nr].first_level)
+      new_level_nr = leveldir[leveldir_nr].first_level;
+    if (new_level_nr > leveldir[leveldir_nr].last_level)
+      new_level_nr = leveldir[leveldir_nr].last_level;
 
-    if (old_level_nr == new_level_nr || !DelayReached(&level_delay, 150))
+    if (old_level_nr == new_level_nr ||
+       !DelayReached(&level_delay, GADGET_FRAME_DELAY))
       goto out;
 
     level_nr = new_level_nr;
@@ -180,7 +202,7 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
                int2str(level_nr, 3), FS_BIG, font_color);
 
     LoadLevel(level_nr);
-    DrawMicroLevel(MICROLEV_XPOS, MICROLEV_YPOS);
+    DrawMicroLevel(MICROLEV_XPOS, MICROLEV_YPOS, TRUE);
 
     TapeErase();
     LoadTape(level_nr);
@@ -225,7 +247,8 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
       }
       else if (y == 6)
       {
-       if (leveldir[leveldir_nr].readonly)
+       if (leveldir[leveldir_nr].readonly &&
+           strcmp(setup.player_name, "Artsoft") != 0)
          Request("This level is read only !", REQ_CONFIRM);
        game_status = LEVELED;
        DrawLevelEd();
@@ -270,7 +293,10 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
   out:
 
   if (game_status == MAINMENU)
+  {
+    DrawMicroLevel(MICROLEV_XPOS, MICROLEV_YPOS, FALSE);
     DoAnimation();
+  }
 }
 
 #define MAX_HELPSCREEN_ELS     10
@@ -294,8 +320,10 @@ static int helpscreen_action[] =
   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_MAUER_L1,  3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10,
+  GFX_MAUER_R1,  3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10,
+  GFX_MAUER_UP,  3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10,
+  GFX_MAUER_DOWN,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,
@@ -374,13 +402,17 @@ static int helpscreen_action[] =
   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_SIEB_INAKTIV,4,2,                                                HA_NEXT,
+  GFX_SIEB2_INAKTIV,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,
+  GFX_SOKOBAN_OBJEKT,1,100,                                    HA_NEXT,
+  GFX_SOKOBAN_FELD_LEER,1,100,                                 HA_NEXT,
+  GFX_SOKOBAN_FELD_VOLL,1,100,                                 HA_NEXT,
+  GFX_SPEED_PILL,1,100,                                                HA_NEXT,
   HA_END
 };
 static char *helpscreen_eltext[][2] =
@@ -391,7 +423,7 @@ static char *helpscreen_eltext[][2] =
  {"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"},
+ {"Growing Wall: Grows in several di-",        "rections 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"},
@@ -442,6 +474,10 @@ static char *helpscreen_eltext[][2] =
  {"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"},
+ {"Sokoban element: Object which must", "be pushed to an empty field"},
+ {"Sokoban element: Empty field where", "a Sokoban object can be placed on"},
+ {"Sokoban element: Field with object", "which can be pushed away"},
+ {"Speed pill: Lets the player run",    "twice as fast as normally"},
 };
 static int num_helpscreen_els = sizeof(helpscreen_eltext)/(2*sizeof(char *));
 
@@ -640,6 +676,7 @@ void DrawHelpScreen()
 {
   int i;
 
+  UnmapAllGadgets();
   CloseDoor(DOOR_CLOSE_2);
 
   for(i=0;i<MAX_HELPSCREEN_ELS;i++)
@@ -759,6 +796,7 @@ void HandleTypeName(int newxpos, KeySym key)
 
 void DrawChooseLevel()
 {
+  UnmapAllGadgets();
   CloseDoor(DOOR_CLOSE_2);
 
   FadeToFront();
@@ -778,7 +816,8 @@ static void drawChooseLevelList(int first_entry, int num_page_entries)
   {
     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);
+    DrawText(SX + 32, SY + (i + 2) * 32, buffer,
+            FS_BIG, leveldir[first_entry + i].color);
     DrawGraphic(0, i + 2, GFX_KUGEL_BLAU);
   }
 
@@ -791,10 +830,17 @@ static void drawChooseLevelList(int first_entry, int num_page_entries)
 
 static void drawChooseLevelInfo(int leveldir_nr)
 {
+  int x, last_redraw_mask = redraw_mask;
+
   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");
+
+  /* let BackToFront() redraw only what is needed */
+  redraw_mask = last_redraw_mask | REDRAW_TILES;
+  for (x=0; x<SCR_FIELDX; x++)
+    MarkTileDirty(x, 1);
 }
 
 void HandleChooseLevel(int mx, int my, int dx, int dy, int button)
@@ -850,7 +896,7 @@ void HandleChooseLevel(int mx, int my, int dx, int dy, int button)
   if (x == 1 && y == 2)
   {
     if (first_entry > 0 &&
-       (dy || DelayReached(&choose_delay, 150)))
+       (dy || DelayReached(&choose_delay, GADGET_FRAME_DELAY)))
     {
 #if 0
       first_entry--;
@@ -868,7 +914,7 @@ void HandleChooseLevel(int mx, int my, int dx, int dy, int button)
   else if (x == 1 && y > num_page_entries + 2)
   {
     if (first_entry + num_page_entries < num_leveldirs &&
-       (dy || DelayReached(&choose_delay, 150)))
+       (dy || DelayReached(&choose_delay, GADGET_FRAME_DELAY)))
     {
 #if 0
       first_entry++;
@@ -927,6 +973,7 @@ void DrawHallOfFame(int highlight_position)
 {
   int i;
 
+  UnmapAllGadgets();
   CloseDoor(DOOR_CLOSE_2);
 
   if (highlight_position < 0) 
@@ -994,9 +1041,11 @@ void DrawSetupScreen()
     { NULL,                    "Save and exit" }
   };
 
+  UnmapAllGadgets();
   CloseDoor(DOOR_CLOSE_2);
   ClearWindow();
-  DrawText(SX+16, SY+16, "SETUP",FS_BIG,FC_YELLOW);
+
+  DrawText(SX + 16, SY + 16, "SETUP",FS_BIG,FC_YELLOW);
 
   for(i=SETUP_SCREEN_POS_START;i<=SETUP_SCREEN_POS_END;i++)
   {
@@ -1410,7 +1459,7 @@ void HandleSetupInputScreen(int mx, int my, int dx, int dy, int button)
   {
     static unsigned long delay = 0;
 
-    if (!DelayReached(&delay, 150))
+    if (!DelayReached(&delay, GADGET_FRAME_DELAY))
       goto out;
 
     player_nr = (player_nr + (x == 11 ? -1 : +1) + MAX_PLAYERS) % MAX_PLAYERS;
@@ -2077,6 +2126,11 @@ void HandleGameActions()
 
 void HandleVideoButtons(int mx, int my, int button)
 {
+  return;
+
+
+
+
   if (game_status != MAINMENU && game_status != PLAYING)
     return;
 
@@ -2181,6 +2235,13 @@ void HandleVideoButtons(int mx, int my, int button)
 
 void HandleSoundButtons(int mx, int my, int button)
 {
+
+
+
+  return;
+
+
+
   if (game_status != PLAYING)
     return;
 
@@ -2242,6 +2303,13 @@ void HandleSoundButtons(int mx, int my, int button)
 
 void HandleGameButtons(int mx, int my, int button)
 {
+
+
+
+  return;
+
+
+
   if (game_status != PLAYING)
     return;
 
index 39e301538a40d1e97bad2979932d154f29bf8266..ccb6cd50b0e7139d58642dcdfa1a6288f870f32c 100644 (file)
@@ -37,8 +37,5 @@ 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 /* SCREENS_H */
index 340ec942b8ebc30d25e62107bdf02a28e30498c5..51a75c88dbe5e6a9d7d5f9d1d63e462cd7cc4ff7 100644 (file)
@@ -39,9 +39,11 @@ static unsigned char playing_buffer[SND_BLOCKSIZE];
 static void SoundServer_InsertNewSound(struct SoundControl);
 #endif
 #ifndef VOXWARE
+#ifndef MSDOS
 static unsigned char linear_to_ulaw(int);
 static int ulaw_to_linear(unsigned char);
 #endif
+#endif
 #ifdef HPUX_AUDIO
 static void HPUX_Audio_Control();
 #endif
@@ -132,7 +134,10 @@ void SoundServer()
       long sample_size;
       static long max_sample_size = 0;
       static long fragment_size = 0;
-      boolean stereo;
+      /* Even if the stereo flag is used as being boolean, it must be
+        defined as an integer, else 'ioctl()' will fail! */
+      int stereo = TRUE;
+      int sample_rate = 8000;
 
       if (playing_sounds || (sound_device=open(sound_device_name,O_WRONLY))>=0)
       {
@@ -141,12 +146,35 @@ void SoundServer()
          /* 2 buffers / 512 bytes, giving 1/16 second resolution */
          /* (with stereo the effective buffer size will shrink to 256) */
          fragment_size = 0x00020009;
-         ioctl(sound_device, SNDCTL_DSP_SETFRAGMENT, &fragment_size);
+
+         if (ioctl(sound_device, SNDCTL_DSP_SETFRAGMENT, &fragment_size) < 0)
+           Error(ERR_EXIT_SOUND_SERVER,
+                 "cannot set fragment size of /dev/dsp - no sounds");
+
          /* try if we can use stereo sound */
-         stereo = TRUE;
-         ioctl(sound_device, SNDCTL_DSP_STEREO, &stereo);
+         if (ioctl(sound_device, SNDCTL_DSP_STEREO, &stereo) < 0)
+         {
+#ifdef DEBUG
+           static boolean reported = FALSE;
+
+           if (!reported)
+           {
+             Error(ERR_RETURN, "cannot get stereo sound on /dev/dsp");
+             reported = TRUE;
+           }
+#endif
+           stereo = FALSE;
+         }
+
+         if (ioctl(sound_device, SNDCTL_DSP_SPEED, &sample_rate) < 0)
+           Error(ERR_EXIT_SOUND_SERVER,
+                 "cannot set sample rate of /dev/dsp - no sounds");
+
          /* get the real fragmentation size; this should return 512 */
-         ioctl(sound_device, SNDCTL_DSP_GETBLKSIZE, &fragment_size);
+         if (ioctl(sound_device, SNDCTL_DSP_GETBLKSIZE, &fragment_size) < 0)
+           Error(ERR_EXIT_SOUND_SERVER,
+                 "cannot get fragment size of /dev/dsp - no sounds");
+
          max_sample_size = fragment_size / (stereo ? 2 : 1);
        }
 
@@ -498,6 +526,7 @@ void SoundServer_FadeSound(int nr)
 }
 */
 
+#ifdef MSDOS
 static void SoundServer_StopSound(int nr)
 {
   int i;
@@ -540,6 +569,7 @@ static void SoundServer_StopAllSounds()
   close(sound_device);
 #endif
 }
+#endif /* MSDOS */
 
 #ifdef HPUX_AUDIO
 static void HPUX_Audio_Control()
@@ -564,6 +594,8 @@ static void HPUX_Audio_Control()
 }
 #endif /* HPUX_AUDIO */
 
+#ifndef VOXWARE
+#ifndef MSDOS
 /* these two are stolen from "sox"... :) */
 
 /*
@@ -667,6 +699,8 @@ static int ulaw_to_linear(unsigned char ulawbyte)
 
   return(sample);
 }
+#endif /* !MSDOS */
+#endif /* !VOXWARE */
 
 /*** THE STUFF ABOVE IS ONLY USED BY THE SOUND SERVER CHILD PROCESS ***/
 
index ca97ff0a39f735a7cccb6ce700b8ee949cb20f71..0811e5a21bb2c00f50f9149bea01400f1897be5c 100644 (file)
 #ifndef VOXWARE
 #define VOXWARE
 #endif
+
+#if 0
 /* where is the right declaration for 'ioctl'? */
 extern void ioctl(long, long, void *);
+#else
+#include <sys/ioctl.h>
+#endif
+
 #endif
 
 #ifdef __FreeBSD__
index 2cc0b39962eba632c9606af412956264e3a64f64..15a8dc2098973ee82bf3654264fac7f5e4a7657d 100644 (file)
 #include "misc.h"
 #include "game.h"
 #include "buttons.h"
+#include "tools.h"
+#include "files.h"
+#include "network.h"
+
+/* tape button identifiers */
+#define TAPE_CTRL_ID_EJECT             0
+#define TAPE_CTRL_ID_STOP              1
+#define TAPE_CTRL_ID_PAUSE             2
+#define TAPE_CTRL_ID_RECORD            3
+#define TAPE_CTRL_ID_PLAY              4
+
+#define NUM_TAPE_BUTTONS               5
+
+/* forward declaration for internal use */
+static void HandleTapeButtons(struct GadgetInfo *);
+
+static struct GadgetInfo *tape_gadget[NUM_TAPE_BUTTONS];
 
 void TapeStartRecording()
 {
@@ -109,16 +126,20 @@ void TapeRecordDelay()
 
 void TapeTogglePause()
 {
+  unsigned long state;
+
   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);
+
+  state = (tape.pausing ? VIDEO_STATE_PAUSE_ON : VIDEO_STATE_PAUSE_OFF);
+  if (tape.playing)
+    state |= VIDEO_STATE_PBEND_OFF;
+
+  DrawVideoDisplay(state, 0);
 }
 
 void TapeStartPlaying()
@@ -199,7 +220,7 @@ boolean TapePlayDelay()
        DrawVideoDisplay(VIDEO_STATE_PBEND_OFF, VIDEO_DISPLAY_LABEL_ONLY);
     }
 
-    if (level.time-TimeLeft > tape.length_seconds - PAUSE_SECONDS_BEFORE_DEATH)
+    if (TimePlayed > tape.length_seconds - PAUSE_SECONDS_BEFORE_DEATH)
     {
       TapeTogglePause();
       return(FALSE);
@@ -252,3 +273,210 @@ unsigned int GetTapeLength()
 
   return(tape_length * GAME_FRAME_DELAY / 1000);
 }
+
+/* ---------- new tape button stuff ---------------------------------------- */
+
+/* graphic position values for tape buttons */
+#define TAPE_BUTTON_XSIZE      18
+#define TAPE_BUTTON_YSIZE      18
+#define TAPE_BUTTON_XPOS       5
+#define TAPE_BUTTON_YPOS       77
+
+#define TAPE_BUTTON_EJECT_XPOS (TAPE_BUTTON_XPOS + 0 * TAPE_BUTTON_XSIZE)
+#define TAPE_BUTTON_STOP_XPOS  (TAPE_BUTTON_XPOS + 1 * TAPE_BUTTON_XSIZE)
+#define TAPE_BUTTON_PAUSE_XPOS (TAPE_BUTTON_XPOS + 2 * TAPE_BUTTON_XSIZE)
+#define TAPE_BUTTON_RECORD_XPOS        (TAPE_BUTTON_XPOS + 3 * TAPE_BUTTON_XSIZE)
+#define TAPE_BUTTON_PLAY_XPOS  (TAPE_BUTTON_XPOS + 4 * TAPE_BUTTON_XSIZE)
+
+static struct
+{
+  int x, y;
+  int gadget_id;
+  char *infotext;
+} tapebutton_info[NUM_TAPE_BUTTONS] =
+{
+  {
+    TAPE_BUTTON_EJECT_XPOS,    TAPE_BUTTON_YPOS,
+    TAPE_CTRL_ID_EJECT,
+    "eject tape"
+  },
+  {
+    TAPE_BUTTON_STOP_XPOS,     TAPE_BUTTON_YPOS,
+    TAPE_CTRL_ID_STOP,
+    "stop tape"
+  },
+  {
+    TAPE_BUTTON_PAUSE_XPOS,    TAPE_BUTTON_YPOS,
+    TAPE_CTRL_ID_PAUSE,
+    "pause tape"
+  },
+  {
+    TAPE_BUTTON_RECORD_XPOS,   TAPE_BUTTON_YPOS,
+    TAPE_CTRL_ID_RECORD,
+    "record tape"
+  },
+  {
+    TAPE_BUTTON_PLAY_XPOS,     TAPE_BUTTON_YPOS,
+    TAPE_CTRL_ID_PLAY,
+    "play tape"
+  }
+};
+
+void CreateTapeButtons()
+{
+  int i;
+
+  for (i=0; i<NUM_TAPE_BUTTONS; i++)
+  {
+    Pixmap gd_pixmap = pix[PIX_DOOR];
+    struct GadgetInfo *gi;
+    int gd_xoffset, gd_yoffset;
+    int gd_x1, gd_x2, gd_y;
+    int id = i;
+
+    gd_xoffset = tapebutton_info[i].x;
+    gd_yoffset = tapebutton_info[i].y;
+    gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
+    gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
+    gd_y  = DOOR_GFX_PAGEY2 + gd_yoffset;
+
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_INFO_TEXT, tapebutton_info[i].infotext,
+                     GDI_X, VX + gd_xoffset,
+                     GDI_Y, VY + gd_yoffset,
+                     GDI_WIDTH, TAPE_BUTTON_XSIZE,
+                     GDI_HEIGHT, TAPE_BUTTON_YSIZE,
+                     GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
+                     GDI_STATE, GD_BUTTON_UNPRESSED,
+                     GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y,
+                     GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y,
+                     GDI_EVENT_MASK, GD_EVENT_RELEASED,
+                     GDI_CALLBACK_ACTION, HandleTapeButtons,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    tape_gadget[id] = gi;
+  }
+}
+
+void MapTapeButtons()
+{
+  int i;
+
+  for (i=0; i<NUM_TAPE_BUTTONS; i++)
+    MapGadget(tape_gadget[i]);
+}
+
+void UnmapTapeButtons()
+{
+  int i;
+
+  for (i=0; i<NUM_TAPE_BUTTONS; i++)
+    UnmapGadget(tape_gadget[i]);
+}
+
+static void HandleTapeButtons(struct GadgetInfo *gi)
+{
+  int id = gi->custom_id;
+
+  if (game_status != MAINMENU && game_status != PLAYING)
+    return;
+
+  switch (id)
+  {
+    case TAPE_CTRL_ID_EJECT:
+      TapeStop();
+      if (TAPE_IS_EMPTY(tape))
+      {
+       LoadTape(level_nr);
+       if (TAPE_IS_EMPTY(tape))
+         Request("No tape for this level !", REQ_CONFIRM);
+      }
+      else
+      {
+       if (tape.changed)
+         SaveTape(tape.level_nr);
+       TapeErase();
+      }
+      DrawCompleteVideoDisplay();
+      break;
+
+    case TAPE_CTRL_ID_STOP:
+      TapeStop();
+      break;
+
+    case TAPE_CTRL_ID_PAUSE:
+      TapeTogglePause();
+      break;
+
+    case TAPE_CTRL_ID_RECORD:
+      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 TAPE_CTRL_ID_PLAY:
+      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;
+  }
+}
index 31151088d2d04681ae011921aff9781824e590d4..1f2a4d8fe6a5b09cf03161ff10d766cf164b9753 100644 (file)
@@ -31,4 +31,8 @@ void TapeStop(void);
 void TapeErase(void);
 unsigned int GetTapeLength(void);
 
+void CreateTapeButtons();
+void MapTapeButtons();
+void UnmapTapeButtons();
+
 #endif
index d2645960674a93a8112d1c9b9db28fe0bf379cbe..df5c62d5ff27726e6630bf812b483ff78923b2a8 100644 (file)
 extern boolean wait_for_vsync;
 #endif
 
+/* tool button identifiers */
+#define TOOL_CTRL_ID_YES       0
+#define TOOL_CTRL_ID_NO                1
+#define TOOL_CTRL_ID_CONFIRM   2
+#define TOOL_CTRL_ID_PLAYER_1  3
+#define TOOL_CTRL_ID_PLAYER_2  4
+#define TOOL_CTRL_ID_PLAYER_3  5
+#define TOOL_CTRL_ID_PLAYER_4  6
+
+#define NUM_TOOL_BUTTONS       7
+
+/* forward declaration for internal use */
+static int getGraphicAnimationPhase(int, int, int);
+static void UnmapToolButtons();
+static void HandleToolButtons(struct GadgetInfo *);
+
+static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
+static int request_gadget_id = -1;
+
 void SetDrawtoField(int mode)
 {
   if (mode == DRAW_BUFFERED && setup.soft_scrolling)
@@ -99,18 +118,20 @@ void BackToFront()
 
   if (redraw_mask & REDRAW_ALL)
   {
-    XCopyArea(display,backbuffer,window,gc,
-             0,0, WIN_XSIZE,WIN_YSIZE,
-             0,0);
+    XCopyArea(display, backbuffer, window, gc,
+             0, 0, WIN_XSIZE, WIN_YSIZE,
+             0, 0);
     redraw_mask = 0;
   }
 
   if (redraw_mask & REDRAW_FIELD)
   {
     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);
+    {
+      XCopyArea(display, backbuffer, window, gc,
+               REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
+               REAL_SX, REAL_SY);
+    }
     else
     {
       int fx = FX, fy = FY;
@@ -122,10 +143,26 @@ void BackToFront()
       }
 
       if (setup.soft_scrolling ||
-         ABS(ScreenGfxPos) + ScrollStepSize == TILEX ||
-         ABS(ScreenGfxPos) == ScrollStepSize ||
+         ABS(ScreenMovPos) + ScrollStepSize == TILEX ||
+         ABS(ScreenMovPos) == ScrollStepSize ||
          redraw_tiles > REDRAWTILES_THRESHOLD)
+      {
        XCopyArea(display, buffer, window, gc, fx, fy, SXSIZE, SYSIZE, SX, SY);
+
+#ifdef DEBUG
+#if 0
+       printf("redrawing all (ScreenGfxPos == %d) because %s\n",
+              ScreenGfxPos,
+              (setup.soft_scrolling ?
+               "setup.soft_scrolling" :
+               ABS(ScreenGfxPos) + ScrollStepSize == TILEX ?
+               "ABS(ScreenGfxPos) + ScrollStepSize == TILEX" :
+               ABS(ScreenGfxPos) == ScrollStepSize ?
+               "ABS(ScreenGfxPos) == ScrollStepSize" :
+               "redraw_tiles > REDRAWTILES_THRESHOLD"));
+#endif
+#endif
+      }
     }
     redraw_mask &= ~REDRAW_MAIN;
   }
@@ -133,9 +170,9 @@ void BackToFront()
   if (redraw_mask & REDRAW_DOORS)
   {
     if (redraw_mask & REDRAW_DOOR_1)
-      XCopyArea(display,backbuffer,window,gc,
-               DX,DY, DXSIZE,DYSIZE,
-               DX,DY);
+      XCopyArea(display, backbuffer, window, gc,
+               DX, DY, DXSIZE, DYSIZE,
+               DX, DY);
     if (redraw_mask & REDRAW_DOOR_2)
     {
       if ((redraw_mask & REDRAW_DOOR_2) == REDRAW_DOOR_2)
@@ -161,18 +198,22 @@ void BackToFront()
                    VX+VIDEO_CONTROL_XPOS,VY+VIDEO_CONTROL_YPOS);
       }
     }
+    if (redraw_mask & REDRAW_DOOR_3)
+      XCopyArea(display, backbuffer, window, gc,
+               EX, EY, EXSIZE, EYSIZE,
+               EX, EY);
     redraw_mask &= ~REDRAW_DOORS;
   }
 
-  if (redraw_mask & REDRAW_MICROLEV)
+  if (redraw_mask & REDRAW_MICROLEVEL)
   {
     XCopyArea(display,backbuffer,window,gc,
-             MICROLEV_XPOS,MICROLEV_YPOS, MICROLEV_XSIZE,MICROLEV_YSIZE,
-             MICROLEV_XPOS,MICROLEV_YPOS);
+             MICROLEV_XPOS, MICROLEV_YPOS, MICROLEV_XSIZE, MICROLEV_YSIZE,
+             MICROLEV_XPOS, MICROLEV_YPOS);
     XCopyArea(display,backbuffer,window,gc,
-             SX,MICROLABEL_YPOS, SXSIZE,FONT4_YSIZE,
-             SX,MICROLABEL_YPOS);
-    redraw_mask &= ~REDRAW_MICROLEV;
+             SX, MICROLABEL_YPOS, SXSIZE, FONT4_YSIZE,
+             SX, MICROLABEL_YPOS);
+    redraw_mask &= ~REDRAW_MICROLEVEL;
   }
 
   if (redraw_mask & REDRAW_TILES)
@@ -180,9 +221,9 @@ void BackToFront()
     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);
+         XCopyArea(display, buffer, window, gc,
+                   FX + x * TILEX, FX + y * TILEY, TILEX, TILEY,
+                   SX + x * TILEX, SY + y * TILEY);
   }
 
   XFlush(display);
@@ -329,6 +370,7 @@ void DrawTextExt(Drawable d, GC gc, int x, int y,
 {
   int font_width, font_height, font_start;
   int font_pixmap;
+  boolean print_inverse = FALSE;
 
   if (font_size != FS_SMALL && font_size != FS_BIG)
     font_size = FS_SMALL;
@@ -344,10 +386,16 @@ void DrawTextExt(Drawable d, GC gc, int x, int y,
   font_start = (font_type * (font_size == FS_BIG ? FONT1_YSIZE : FONT2_YSIZE) *
                FONT_LINES_PER_FONT);
 
-  while(*text)
+  while (*text)
   {
     char c = *text++;
 
+    if (c == '~' && font_size == FS_SMALL && font_type <= FC_YELLOW)
+    {
+      print_inverse = TRUE;
+      continue;
+    }
+
     if (c >= 'a' && c <= 'z')
       c = 'A' + (c - 'a');
     else if (c == 'ä' || c == 'Ä')
@@ -358,10 +406,27 @@ void DrawTextExt(Drawable d, GC gc, int x, int y,
       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);
+    {
+      int src_x = ((c - 32) % FONT_CHARS_PER_LINE) * font_width;
+      int src_y = ((c - 32) / FONT_CHARS_PER_LINE) * font_height + font_start;
+      int dest_x = x, dest_y = y;
+
+      if (print_inverse)
+      {
+       XCopyArea(display, pix[font_pixmap], d, gc,
+                 FONT_CHARS_PER_LINE * font_width,
+                 3 * font_height + font_start,
+                 font_width, font_height, x, y);
+
+       XSetClipOrigin(display, clip_gc[font_pixmap],
+                      dest_x - src_x, dest_y - src_y);
+       XCopyArea(display, pix[font_pixmap], d, clip_gc[font_pixmap],
+                 0, 0, font_width, font_height, dest_x, dest_y);
+      }
+      else
+       XCopyArea(display, pix[font_pixmap], d, gc,
+                 src_x, src_y, font_width, font_height, dest_x, dest_y);
+    }
 
     x += font_width;
   }
@@ -415,7 +480,7 @@ void DrawPlayer(struct PlayerInfo *player)
 
   if (last_jx != jx || last_jy != jy)
   {
-    if (Store[last_jx][last_jy])
+    if (Store[last_jx][last_jy] && IS_DRAWABLE(Feld[last_jx][last_jy]))
     {
       DrawLevelElement(last_jx, last_jy, Store[last_jx][last_jy]);
       DrawLevelFieldThruMask(last_jx, last_jy);
@@ -454,17 +519,70 @@ void DrawPlayer(struct PlayerInfo *player)
 
   /* 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;
+  if (game_emulation == EMU_SUPAPLEX)
+  {
+    static int last_dir = MV_LEFT;
+    boolean action_moving =
+      ((player->action & (MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN)) &&
+       !(player->action & ~(MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN)));
+
+    graphic = GFX_SP_MURPHY;
+
+    if (player->Pushing)
+    {
+      if (player->MovDir == MV_LEFT)
+       graphic = GFX_MURPHY_PUSH_LEFT;
+      else if (player->MovDir == MV_RIGHT)
+       graphic = GFX_MURPHY_PUSH_RIGHT;
+      else if (player->MovDir & (MV_UP | MV_DOWN) && last_dir == MV_LEFT)
+       graphic = GFX_MURPHY_PUSH_LEFT;
+      else if (player->MovDir & (MV_UP | MV_DOWN) && last_dir == MV_RIGHT)
+       graphic = GFX_MURPHY_PUSH_RIGHT;
+    }
+    else if (player->snapped)
+    {
+      if (player->MovDir == MV_LEFT)
+       graphic = GFX_MURPHY_SNAP_LEFT;
+      else if (player->MovDir == MV_RIGHT)
+       graphic = GFX_MURPHY_SNAP_RIGHT;
+      else if (player->MovDir == MV_UP)
+       graphic = GFX_MURPHY_SNAP_UP;
+      else if (player->MovDir == MV_DOWN)
+       graphic = GFX_MURPHY_SNAP_DOWN;
+    }
+    else if (action_moving)
+    {
+      if (player->MovDir == MV_LEFT)
+       graphic = GFX_MURPHY_ANY_LEFT;
+      else if (player->MovDir == MV_RIGHT)
+       graphic = GFX_MURPHY_ANY_RIGHT;
+      else if (player->MovDir & (MV_UP | MV_DOWN) && last_dir == MV_LEFT)
+       graphic = GFX_MURPHY_ANY_LEFT;
+      else if (player->MovDir & (MV_UP | MV_DOWN) && last_dir == MV_RIGHT)
+       graphic = GFX_MURPHY_ANY_RIGHT;
+
+      graphic -= getGraphicAnimationPhase(2, 4, ANIM_NORMAL);
+    }
+
+    if (player->MovDir == MV_LEFT || player->MovDir == MV_RIGHT)
+      last_dir = player->MovDir;
+  }
+  else
+  {
+    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;
+    graphic += player->index_nr * 3 * HEROES_PER_LINE;
+    graphic += player->Frame;
+  }
 
   if (player->GfxPos)
   {
@@ -523,7 +641,10 @@ void DrawPlayer(struct PlayerInfo *player)
        phase = 7 - phase;
     }
 
-    DrawGraphicThruMask(sx, sy, graphic + phase);
+    if (game_emulation == EMU_SUPAPLEX)
+      DrawGraphic(sx, sy, GFX_SP_DISK_RED);
+    else
+      DrawGraphicThruMask(sx, sy, graphic + phase);
   }
 
   if ((last_jx != jx || last_jy != jy) &&
@@ -597,6 +718,45 @@ void DrawGraphicAnimationThruMask(int x, int y, int graphic,
   DrawGraphicAnimationExt(x, y, graphic, frames, delay, mode, USE_MASKING);
 }
 
+void getGraphicSource(int graphic, int *pixmap_nr, int *x, int *y)
+{
+  if (graphic >= GFX_START_ROCKSSCREEN && graphic <= GFX_END_ROCKSSCREEN)
+  {
+    graphic -= GFX_START_ROCKSSCREEN;
+    *pixmap_nr = PIX_BACK;
+    *x = SX + (graphic % GFX_PER_LINE) * TILEX;
+    *y = SY + (graphic / GFX_PER_LINE) * TILEY;
+  }
+  else if (graphic >= GFX_START_ROCKSMORE && graphic <= GFX_END_ROCKSMORE)
+  {
+    graphic -= GFX_START_ROCKSMORE;
+    *pixmap_nr = PIX_MORE;
+    *x = (graphic % MORE_PER_LINE) * TILEX;
+    *y = (graphic / MORE_PER_LINE) * TILEY;
+  }
+  else if (graphic >= GFX_START_ROCKSHEROES && graphic <= GFX_END_ROCKSHEROES)
+  {
+    graphic -= GFX_START_ROCKSHEROES;
+    *pixmap_nr = PIX_HEROES;
+    *x = (graphic % HEROES_PER_LINE) * TILEX;
+    *y = (graphic / HEROES_PER_LINE) * TILEY;
+  }
+  else if (graphic >= GFX_START_ROCKSFONT && graphic <= GFX_END_ROCKSFONT)
+  {
+    graphic -= GFX_START_ROCKSFONT;
+    *pixmap_nr = PIX_BIGFONT;
+    *x = (graphic % FONT_CHARS_PER_LINE) * TILEX;
+    *y = ((graphic / FONT_CHARS_PER_LINE) * TILEY +
+         FC_SPECIAL1 * FONT_LINES_PER_FONT * TILEY);
+  }
+  else
+  {
+    *pixmap_nr = PIX_MORE;
+    *x = 0;
+    *y = 0;
+  }
+}
+
 void DrawGraphic(int x, int y, int graphic)
 {
 #if DEBUG
@@ -614,6 +774,18 @@ void DrawGraphic(int x, int y, int graphic)
 
 void DrawGraphicExt(Drawable d, GC gc, int x, int y, int graphic)
 {
+
+#if 1
+
+  int pixmap_nr;
+  int src_x, src_y;
+
+  getGraphicSource(graphic, &pixmap_nr, &src_x, &src_y);
+  XCopyArea(display, pix[pixmap_nr], d, gc,
+           src_x, src_y, TILEX, TILEY, x, y);
+
+#else
+
   if (graphic >= GFX_START_ROCKSSCREEN && graphic <= GFX_END_ROCKSSCREEN)
   {
     graphic -= GFX_START_ROCKSSCREEN;
@@ -622,6 +794,14 @@ void DrawGraphicExt(Drawable d, GC gc, int x, int y, int graphic)
              SY + (graphic / GFX_PER_LINE) * TILEY,
              TILEX, TILEY, x, y);
   }
+  else if (graphic >= GFX_START_ROCKSMORE && graphic <= GFX_END_ROCKSMORE)
+  {
+    graphic -= GFX_START_ROCKSMORE;
+    XCopyArea(display, pix[PIX_MORE], d, gc,
+             (graphic % MORE_PER_LINE) * TILEX,
+             (graphic / MORE_PER_LINE) * TILEY,
+             TILEX, TILEY, x, y);
+  }
   else if (graphic >= GFX_START_ROCKSHEROES && graphic <= GFX_END_ROCKSHEROES)
   {
     graphic -= GFX_START_ROCKSHEROES;
@@ -641,6 +821,9 @@ void DrawGraphicExt(Drawable d, GC gc, int x, int y, int graphic)
   }
   else
     XFillRectangle(display, d, gc, x, y, TILEX, TILEY);
+
+#endif
+
 }
 
 void DrawGraphicThruMask(int x, int y, int graphic)
@@ -660,6 +843,24 @@ void DrawGraphicThruMask(int x, int y, int graphic)
 
 void DrawGraphicThruMaskExt(Drawable d, int dest_x, int dest_y, int graphic)
 {
+
+#if 1
+
+  int tile = graphic;
+  int pixmap_nr;
+  int src_x, src_y;
+  Pixmap src_pixmap;
+  GC drawing_gc;
+
+  if (graphic == GFX_LEERRAUM)
+    return;
+
+  getGraphicSource(graphic, &pixmap_nr, &src_x, &src_y);
+  src_pixmap = pix[pixmap_nr];
+  drawing_gc = clip_gc[pixmap_nr];
+
+#else
+
   int src_x, src_y;
   int tile = graphic;
   Pixmap src_pixmap;
@@ -673,6 +874,14 @@ void DrawGraphicThruMaskExt(Drawable d, int dest_x, int dest_y, int graphic)
     src_x  = SX + (graphic % GFX_PER_LINE) * TILEX;
     src_y  = SY + (graphic / GFX_PER_LINE) * TILEY;
   }
+  else if (graphic >= GFX_START_ROCKSMORE && graphic <= GFX_END_ROCKSMORE)
+  {
+    src_pixmap = pix[PIX_MORE];
+    drawing_gc = clip_gc[PIX_MORE];
+    graphic -= GFX_START_ROCKSMORE;
+    src_x  = (graphic % MORE_PER_LINE) * TILEX;
+    src_y  = (graphic / MORE_PER_LINE) * TILEY;
+  }
   else if (graphic >= GFX_START_ROCKSHEROES && graphic <= GFX_END_ROCKSHEROES)
   {
     src_pixmap = pix[PIX_HEROES];
@@ -687,11 +896,14 @@ void DrawGraphicThruMaskExt(Drawable d, int dest_x, int dest_y, int graphic)
     return;
   }
 
+#endif
+
+
   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,
+    XCopyArea(display, src_pixmap, d, tile_clip_gc,
              src_x, src_y, TILEX, TILEY, dest_x, dest_y);
   }
   else
@@ -701,7 +913,7 @@ void DrawGraphicThruMaskExt(Drawable d, int dest_x, int dest_y, int graphic)
 #endif
 
     XSetClipOrigin(display, drawing_gc, dest_x-src_x, dest_y-src_y);
-    XCopyArea(display, src_pixmap, drawto_field, drawing_gc,
+    XCopyArea(display, src_pixmap, d, drawing_gc,
              src_x, src_y, TILEX, TILEY, dest_x, dest_y);
   }
 }
@@ -712,8 +924,52 @@ void DrawMiniGraphic(int x, int y, int graphic)
   MarkTileDirty(x/2, y/2);
 }
 
+void getMiniGraphicSource(int graphic, Pixmap *pixmap, int *x, int *y)
+{
+  if (graphic >= GFX_START_ROCKSSCREEN && graphic <= GFX_END_ROCKSSCREEN)
+  {
+    graphic -= GFX_START_ROCKSSCREEN;
+    *pixmap = pix[PIX_BACK];
+    *x = MINI_GFX_STARTX + (graphic % MINI_GFX_PER_LINE) * MINI_TILEX;
+    *y = MINI_GFX_STARTY + (graphic / MINI_GFX_PER_LINE) * MINI_TILEY;
+  }
+  else if (graphic >= GFX_START_ROCKSMORE && graphic <= GFX_END_ROCKSMORE)
+  {
+    graphic -= GFX_START_ROCKSMORE;
+    *pixmap = pix[PIX_MORE];
+    *x = MINI_MORE_STARTX + (graphic % MINI_MORE_PER_LINE) * MINI_TILEX;
+    *y = MINI_MORE_STARTY + (graphic / MINI_MORE_PER_LINE) * MINI_TILEY;
+  }
+  else if (graphic >= GFX_START_ROCKSFONT && graphic <= GFX_END_ROCKSFONT)
+  {
+    graphic -= GFX_START_ROCKSFONT;
+    *pixmap = pix[PIX_SMALLFONT];
+    *x = (graphic % FONT_CHARS_PER_LINE) * FONT4_XSIZE;
+    *y = ((graphic / FONT_CHARS_PER_LINE) * FONT4_YSIZE +
+             FC_SPECIAL2 * FONT2_YSIZE * FONT_LINES_PER_FONT);
+  }
+  else
+  {
+    *pixmap = pix[PIX_MORE];
+    *x = MINI_MORE_STARTX;
+    *y = MINI_MORE_STARTY;
+  }
+}
+
 void DrawMiniGraphicExt(Drawable d, GC gc, int x, int y, int graphic)
 {
+
+#if 1
+
+  Pixmap pixmap;
+  int src_x, src_y;
+
+  getMiniGraphicSource(graphic, &pixmap, &src_x, &src_y);
+  XCopyArea(display, pixmap, d, gc,
+           src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
+
+#else
+
   if (graphic >= GFX_START_ROCKSSCREEN && graphic <= GFX_END_ROCKSSCREEN)
   {
     graphic -= GFX_START_ROCKSSCREEN;
@@ -722,6 +978,14 @@ void DrawMiniGraphicExt(Drawable d, GC gc, int x, int y, int graphic)
              MINI_GFX_STARTY + (graphic / MINI_GFX_PER_LINE) * MINI_TILEY,
              MINI_TILEX, MINI_TILEY, x, y);
   }
+  else if (graphic >= GFX_START_ROCKSMORE && graphic <= GFX_END_ROCKSMORE)
+  {
+    graphic -= GFX_START_ROCKSMORE;
+    XCopyArea(display, pix[PIX_MORE], d, gc,
+             MINI_MORE_STARTX + (graphic % MINI_MORE_PER_LINE) * MINI_TILEX,
+             MINI_MORE_STARTY + (graphic / MINI_MORE_PER_LINE) * MINI_TILEY,
+             MINI_TILEX, MINI_TILEY, x, y);
+  }
   else if (graphic >= GFX_START_ROCKSFONT && graphic <= GFX_END_ROCKSFONT)
   {
     graphic -= GFX_START_ROCKSFONT;
@@ -733,6 +997,9 @@ void DrawMiniGraphicExt(Drawable d, GC gc, int x, int y, int graphic)
   }
   else
     XFillRectangle(display, d, gc, x, y, MINI_TILEX, MINI_TILEY);
+
+#endif
+
 }
 
 void DrawGraphicShifted(int x,int y, int dx,int dy, int graphic,
@@ -823,6 +1090,14 @@ void DrawGraphicShifted(int x,int y, int dx,int dy, int graphic,
     src_x  = SX + (graphic % GFX_PER_LINE) * TILEX + cx;
     src_y  = SY + (graphic / GFX_PER_LINE) * TILEY + cy;
   }
+  else if (graphic >= GFX_START_ROCKSMORE && graphic <= GFX_END_ROCKSMORE)
+  {
+    src_pixmap = pix[PIX_MORE];
+    drawing_gc = clip_gc[PIX_MORE];
+    graphic -= GFX_START_ROCKSMORE;
+    src_x  = (graphic % MORE_PER_LINE) * TILEX + cx;
+    src_y  = (graphic / MORE_PER_LINE) * TILEY + cy;
+  }
   else if (graphic >= GFX_START_ROCKSHEROES && graphic <= GFX_END_ROCKSHEROES)
   {
     src_pixmap = pix[PIX_HEROES];
@@ -884,13 +1159,14 @@ void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
 {
   int ux = LEVELX(x), uy = LEVELY(y);
   int graphic = el2gfx(element);
-  int phase4 = ABS(MovPos[ux][uy]) / (TILEX / 4);
-  int phase  = phase4 / 2;
+  int phase8 = ABS(MovPos[ux][uy]) / (TILEX / 8);
+  int phase4 = phase8 / 2;
+  int phase2  = phase8 / 4;
   int dir = MovDir[ux][uy];
 
   if (element == EL_PACMAN || element == EL_KAEFER || element == EL_FLIEGER)
   {
-    graphic += 4*!phase;
+    graphic += 4 * !phase2;
 
     if (dir == MV_UP)
       graphic += 1;
@@ -899,6 +1175,23 @@ void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
     else if (dir == MV_DOWN)
       graphic += 3;
   }
+  else if (element == EL_SP_SNIKSNAK)
+  {
+    if (dir == MV_LEFT)
+      graphic = GFX_SP_SNIKSNAK_LEFT;
+    else if (dir == MV_RIGHT)
+      graphic = GFX_SP_SNIKSNAK_RIGHT;
+    else if (dir == MV_UP)
+      graphic = GFX_SP_SNIKSNAK_UP;
+    else
+      graphic = GFX_SP_SNIKSNAK_DOWN;
+
+    graphic += (phase8 < 4 ? phase8 : 7 - phase8);
+  }
+  else if (element == EL_SP_ELECTRON)
+  {
+    graphic = GFX2_SP_ELECTRON + getGraphicAnimationPhase(8, 2, ANIM_NORMAL);
+  }
   else if (element == EL_MAULWURF || element == EL_PINGUIN ||
           element == EL_SCHWEIN || element == EL_DRACHE)
   {
@@ -931,16 +1224,17 @@ void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
   }
   else if (element == EL_BUTTERFLY || element == EL_FIREFLY)
   {
-    graphic += !phase;
+    graphic += !phase2;
   }
   else if ((element == EL_FELSBROCKEN || IS_GEM(element)) && !cut_mode)
   {
-    graphic += phase * (element == EL_FELSBROCKEN ? 2 : 1);
+    if (element != EL_SP_INFOTRON)
+      graphic += phase2 * (element == EL_FELSBROCKEN ? 2 : 1);
   }
-  else if ((element == EL_SIEB_LEER || element == EL_SIEB2_LEER ||
-           element == EL_SIEB_VOLL || element == EL_SIEB2_VOLL) && SiebAktiv)
+  else if (element == EL_SIEB_LEER || element == EL_SIEB2_LEER ||
+          element == EL_SIEB_VOLL || element == EL_SIEB2_VOLL)
   {
-    graphic += 3 - (SiebAktiv % 8) / 2;
+    graphic += 3 + getGraphicAnimationPhase(4, 4, ANIM_REVERSE);
   }
   else if (IS_AMOEBOID(element))
   {
@@ -1131,7 +1425,12 @@ void DrawScreenField(int x, int y)
 
   if (!IN_LEV_FIELD(ux, uy))
   {
-    DrawScreenElement(x, y, EL_BETON);
+    if (ux < -1 || ux > lev_fieldx || uy < -1 || uy > lev_fieldy)
+      element = EL_LEERRAUM;
+    else
+      element = BorderElement;
+
+    DrawScreenElement(x, y, element);
     return;
   }
 
@@ -1240,10 +1539,50 @@ void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int 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
+  else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
     DrawMiniElement(sx, sy, Feld[x][y]);
+  else
+  {
+    int steel_type, steel_position;
+    int border[6][2] =
+    {
+      { GFX_VSTEEL_UPPER_LEFT, GFX_ISTEEL_UPPER_LEFT  },
+      { GFX_VSTEEL_UPPER_RIGHT,        GFX_ISTEEL_UPPER_RIGHT },
+      { GFX_VSTEEL_LOWER_LEFT, GFX_ISTEEL_LOWER_LEFT  },
+      { GFX_VSTEEL_LOWER_RIGHT,        GFX_ISTEEL_LOWER_RIGHT },
+      { GFX_VSTEEL_VERTICAL,   GFX_ISTEEL_VERTICAL    },
+      { GFX_VSTEEL_HORIZONTAL, GFX_ISTEEL_HORIZONTAL  }
+    };
+
+    steel_type = (BorderElement == EL_BETON ? 0 : 1);
+    steel_position = (x == -1 && y == -1                       ? 0 :
+                     x == lev_fieldx && y == -1                ? 1 :
+                     x == -1 && y == lev_fieldy                ? 2 :
+                     x == lev_fieldx && y == lev_fieldy        ? 3 :
+                     x == -1 || x == lev_fieldx                ? 4 :
+                     y == -1 || y == lev_fieldy                ? 5 : -1);
+
+    if (steel_position != -1)
+      DrawMiniGraphic(sx, sy, border[steel_position][steel_type]);
+
+
+#if 0
+    if (x == -1 && y == -1)
+      DrawMiniGraphic(sx, sy, GFX_STEEL_UPPER_LEFT);
+    else if (x == lev_fieldx && y == -1)
+      DrawMiniGraphic(sx, sy, GFX_STEEL_UPPER_RIGHT);
+    else if (x == -1 && y == lev_fieldy)
+      DrawMiniGraphic(sx, sy, GFX_STEEL_LOWER_LEFT);
+    else if (x == lev_fieldx && y == lev_fieldy)
+      DrawMiniGraphic(sx, sy, GFX_STEEL_LOWER_RIGHT);
+    else if (x == -1 || x == lev_fieldx)
+      DrawMiniGraphic(sx, sy, GFX_STEEL_VERTICAL);
+    else if (y == -1 || y == lev_fieldy)
+      DrawMiniGraphic(sx, sy, GFX_STEEL_HORIZONTAL);
+#endif
+
+
+  }
 }
 
 void DrawMicroElement(int xpos, int ypos, int element)
@@ -1255,10 +1594,19 @@ void DrawMicroElement(int xpos, int ypos, int element)
 
   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);
+  if (graphic >= GFX_START_ROCKSMORE && graphic <= GFX_END_ROCKSMORE)
+  {
+    graphic -= GFX_START_ROCKSMORE;
+    XCopyArea(display, pix[PIX_MORE], drawto, gc,
+             MICRO_MORE_STARTX + (graphic % MICRO_MORE_PER_LINE) *MICRO_TILEX,
+             MICRO_MORE_STARTY + (graphic / MICRO_MORE_PER_LINE) *MICRO_TILEY,
+             MICRO_TILEX, MICRO_TILEY, xpos, ypos);
+  }
+  else
+    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()
@@ -1278,53 +1626,157 @@ void DrawLevel()
   redraw_mask |= (REDRAW_FIELD | REDRAW_FROM_BACKBUFFER);
 }
 
-void DrawMiniLevel(int scroll_x, int scroll_y)
+void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
 {
   int x,y;
 
-  ClearWindow();
-
-  for(x=0; x<2*SCR_FIELDX; x++)
-    for(y=0; y<2*SCR_FIELDY; y++)
+  for(x=0; x<size_x; x++)
+    for(y=0; y<size_y; y++)
       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
 
   redraw_mask |= REDRAW_FIELD;
 }
 
-void DrawMicroLevel(int xpos, int ypos)
+static void DrawMicroLevelExt(int xpos, int ypos, int from_x, int from_y)
 {
-  int x,y;
+  int x, y;
+
+  /* determine border element for this level */
+  SetBorderElement();
 
   XFillRectangle(display, drawto, gc,
-                xpos - MICRO_TILEX, ypos - MICRO_TILEY,
-                MICRO_TILEX * (STD_LEV_FIELDX + 2),
-                MICRO_TILEY * (STD_LEV_FIELDY + 2));
+                xpos, ypos, MICROLEV_XSIZE, MICROLEV_YSIZE);
+
   if (lev_fieldx < STD_LEV_FIELDX)
-    xpos += (STD_LEV_FIELDX - lev_fieldx)/2 * MICRO_TILEX;
+    xpos += (STD_LEV_FIELDX - lev_fieldx) / 2 * MICRO_TILEX;
   if (lev_fieldy < STD_LEV_FIELDY)
-    ypos += (STD_LEV_FIELDY - lev_fieldy)/2 * MICRO_TILEY;
+    ypos += (STD_LEV_FIELDY - lev_fieldy) / 2 * MICRO_TILEY;
+
+  xpos += MICRO_TILEX;
+  ypos += 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)
+    {
+      int lx = from_x + x, ly = from_y + y;
+
+      if (lx >= 0 && lx < lev_fieldx && ly >= 0 && ly < 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)
+                        Ur[lx][ly]);
+      else if (lx >= -1 && lx < lev_fieldx+1 && ly >= -1 && ly < lev_fieldy+1)
        DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
-                        EL_BETON);
+                        BorderElement);
+    }
+  }
+
+  redraw_mask |= REDRAW_MICROLEVEL;
+}
+
+static void DrawMicroLevelLabelExt(int mode)
+{
+  char label_text[100];
+
+  XFillRectangle(display, drawto,gc,
+                SX, MICROLABEL_YPOS, SXSIZE, FONT4_YSIZE);
 
-  XFillRectangle(display, drawto,gc, SX, MICROLABEL_YPOS, SXSIZE, FONT4_YSIZE);
+  strcpy(label_text, (mode == 1 ? level.name :
+                     mode == 2 ? "created by" :
+                     mode == 3 ? level.author : ""));
 
-  if (level.name)
+  if (strlen(label_text) > 0)
   {
-    int len = strlen(level.name);
-    int lxpos = SX + (SXSIZE - len * FONT4_XSIZE) / 2;
-    int lypos = MICROLABEL_YPOS;
+    int size, lxpos, lypos;
+
+    label_text[SXSIZE / FONT4_XSIZE] = '\0';
 
-    DrawText(lxpos, lypos, level.name, FS_SMALL, FC_SPECIAL2);
+    size = strlen(label_text);
+    lxpos = SX + (SXSIZE - size * FONT4_XSIZE) / 2;
+    lypos = MICROLABEL_YPOS;
+
+    DrawText(lxpos, lypos, label_text, FS_SMALL, FC_SPECIAL2);
   }
 
-  redraw_mask |= REDRAW_MICROLEV;
+  redraw_mask |= REDRAW_MICROLEVEL;
+}
+
+void DrawMicroLevel(int xpos, int ypos, boolean restart)
+{
+  static unsigned long scroll_delay = 0;
+  static unsigned long label_delay = 0;
+  static int from_x, from_y, scroll_direction;
+  static int label_state, label_counter;
+
+  if (restart)
+  {
+    from_x = from_y = 0;
+    scroll_direction = MV_RIGHT;
+    label_state = 1;
+    label_counter = 0;
+
+    DrawMicroLevelExt(xpos, ypos, from_x, from_y);
+    DrawMicroLevelLabelExt(label_state);
+
+    /* initialize delay counters */
+    DelayReached(&scroll_delay, 0);
+    DelayReached(&label_delay, 0);
+
+    return;
+  }
+
+  /* scroll micro level, if needed */
+  if ((lev_fieldx > STD_LEV_FIELDX || lev_fieldy > STD_LEV_FIELDY) &&
+      DelayReached(&scroll_delay, MICROLEVEL_SCROLL_DELAY))
+  {
+    switch (scroll_direction)
+    {
+      case MV_LEFT:
+       if (from_x > 0)
+         from_x--;
+       else
+         scroll_direction = MV_UP;
+       break;
+
+      case MV_RIGHT:
+       if (from_x < lev_fieldx - STD_LEV_FIELDX)
+         from_x++;
+       else
+         scroll_direction = MV_DOWN;
+       break;
+
+      case MV_UP:
+       if (from_y > 0)
+         from_y--;
+       else
+         scroll_direction = MV_RIGHT;
+       break;
+
+      case MV_DOWN:
+       if (from_y < lev_fieldy - STD_LEV_FIELDY)
+         from_y++;
+       else
+         scroll_direction = MV_LEFT;
+       break;
+
+      default:
+       break;
+    }
+
+    DrawMicroLevelExt(xpos, ypos, from_x, from_y);
+  }
+
+  /* redraw micro level label, if needed */
+  if (strcmp(level.name, NAMELESS_LEVEL_NAME) != 0 &&
+      strcmp(level.author, ANONYMOUS_NAME) != 0 &&
+      strcmp(level.author, leveldir[leveldir_nr].name) != 0 &&
+      DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
+  {
+    label_counter = (label_counter + 1) % 23;
+    label_state = (label_counter >= 0 && label_counter <= 7 ? 1 :
+                  label_counter >= 9 && label_counter <= 12 ? 2 :
+                  label_counter >= 14 && label_counter <= 21 ? 3 : 0);
+    DrawMicroLevelLabelExt(label_state);
+  }
 }
 
 int REQ_in_range(int x, int y)
@@ -1354,17 +1806,24 @@ boolean Request(char *text, unsigned int req_state)
 
   old_door_state = GetDoorState();
 
+  UnmapAllGadgets();
+
   CloseDoor(DOOR_CLOSE_1);
 
-  /* Alten Türinhalt sichern */
+  /* save old door content */
   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 */
+  /* clear door drawing field */
+#if 0
   XFillRectangle(display, pix[PIX_DB_DOOR], gc,
                 DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE);
+#else
+  XFillRectangle(display, drawto, gc, DX, DY, DXSIZE, DYSIZE);
+#endif
 
+  /* write text for request */
   for(ty=0; ty<13; ty++)
   {
     int tx, tl, tc;
@@ -1387,12 +1846,19 @@ boolean Request(char *text, unsigned int req_state)
     }
     sprintf(txt, text); 
     txt[tl] = 0;
+#if 0
     DrawTextExt(pix[PIX_DB_DOOR], gc,
                DOOR_GFX_PAGEX1 + 51 - (tl * 14)/2, SY + ty * 16,
                txt, FS_SMALL, FC_YELLOW);
+#else
+    DrawTextExt(drawto, gc,
+               DX + 51 - (tl * 14)/2, DY + 8 + ty * 16,
+               txt, FS_SMALL, FC_YELLOW);
+#endif
     text += tl + (tc == 32 ? 1 : 0);
   }
 
+#if 0
   if (req_state & REQ_ASK)
   {
     DrawYesNoButton(BUTTON_OK, DB_INIT);
@@ -1409,6 +1875,31 @@ boolean Request(char *text, unsigned int req_state)
     DrawPlayerButton(BUTTON_PLAYER_3, DB_INIT);
     DrawPlayerButton(BUTTON_PLAYER_4, DB_INIT);
   }
+#else
+
+  if (req_state & REQ_ASK)
+  {
+    MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
+    MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
+  }
+  else if (req_state & REQ_CONFIRM)
+  {
+    MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
+  }
+  else if (req_state & REQ_PLAYER)
+  {
+    MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
+    MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
+    MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
+    MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
+  }
+
+  /* copy request gadgets to door backbuffer */
+  XCopyArea(display, drawto, pix[PIX_DB_DOOR], gc,
+           DX, DY, DXSIZE, DYSIZE,
+           DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+
+#endif
 
   OpenDoor(DOOR_OPEN_1);
   ClearEventQueue();
@@ -1421,6 +1912,8 @@ boolean Request(char *text, unsigned int req_state)
 
   button_status = MB_RELEASED;
 
+  request_gadget_id = -1;
+
   while(result < 0)
   {
     if (XPending(display))
@@ -1435,10 +1928,25 @@ boolean Request(char *text, unsigned int req_state)
        case ButtonRelease:
        case MotionNotify:
        {
+
+#if 0
          int choice;
+#endif
 
          if (event.type == MotionNotify)
          {
+           Window root, child;
+           int root_x, root_y;
+           int win_x, win_y;
+           unsigned int mask;
+
+           if (!XQueryPointer(display, window, &root, &child,
+                              &root_x, &root_y, &win_x, &win_y, &mask))
+             continue;
+
+           if (!button_status)
+             continue;
+
            motion_status = TRUE;
            mx = ((XMotionEvent *) &event)->x;
            my = ((XMotionEvent *) &event)->y;
@@ -1454,6 +1962,9 @@ boolean Request(char *text, unsigned int req_state)
              button_status = MB_RELEASED;
          }
 
+
+
+#if 0
          if (req_state & REQ_ASK)
            choice = CheckYesNoButtons(mx,my,button_status);
          else if (req_state & REQ_CONFIRM)
@@ -1489,6 +2000,41 @@ boolean Request(char *text, unsigned int req_state)
            default:
              break;
          }
+#else
+
+         /* this sets 'request_gadget_id' */
+         HandleGadgets(mx, my, button_status);
+
+         switch(request_gadget_id)
+         {
+           case TOOL_CTRL_ID_YES:
+             result = TRUE;
+             break;
+           case TOOL_CTRL_ID_NO:
+             result = FALSE;
+             break;
+           case TOOL_CTRL_ID_CONFIRM:
+             result = TRUE | FALSE;
+             break;
+
+           case TOOL_CTRL_ID_PLAYER_1:
+             result = 1;
+             break;
+           case TOOL_CTRL_ID_PLAYER_2:
+             result = 2;
+             break;
+           case TOOL_CTRL_ID_PLAYER_3:
+             result = 3;
+             break;
+           case TOOL_CTRL_ID_PLAYER_4:
+             result = 4;
+             break;
+
+           default:
+             break;
+         }
+#endif
+
          break;
        }
 
@@ -1539,6 +2085,8 @@ boolean Request(char *text, unsigned int req_state)
   if (game_status != MAINMENU)
     StopAnimation();
 
+  UnmapToolButtons();
+
   if (!(req_state & REQ_STAY_OPEN))
   {
     CloseDoor(DOOR_CLOSE_1);
@@ -1552,6 +2100,8 @@ boolean Request(char *text, unsigned int req_state)
     }
   }
 
+  RemapAllGadgets();
+
 #ifndef MSDOS
   /* continue network game after request */
   if (options.network &&
@@ -1712,7 +2262,15 @@ unsigned int MoveDoor(unsigned int door_state)
        redraw_mask |= REDRAW_DOOR_2;
       }
 
+
+
+#if 1
       BackToFront();
+#else
+      XCopyArea(display, drawto, window, gc, DX, DY, DXSIZE, DYSIZE, DX, DY);
+#endif
+
+
 
       if (game_status == MAINMENU)
        DoAnimation();
@@ -1732,10 +2290,284 @@ unsigned int MoveDoor(unsigned int door_state)
 
 int ReadPixel(Drawable d, int x, int y)
 {
-  static XImage *pixelimage;
+  XImage *pixel_image;
+  unsigned long pixel_value;
+
+  pixel_image = XGetImage(display, d, x, y, 1, 1, AllPlanes, ZPixmap);
+  pixel_value = XGetPixel(pixel_image, 0, 0);
+
+  XDestroyImage(pixel_image);
+
+  return pixel_value;
+}
+
+/* ---------- new tool button stuff ---------------------------------------- */
+
+/* graphic position values for tool buttons */
+#define TOOL_BUTTON_YES_XPOS           2
+#define TOOL_BUTTON_YES_YPOS           250
+#define TOOL_BUTTON_YES_GFX_YPOS       0
+#define TOOL_BUTTON_YES_XSIZE          46
+#define TOOL_BUTTON_YES_YSIZE          28
+#define TOOL_BUTTON_NO_XPOS            52
+#define TOOL_BUTTON_NO_YPOS            TOOL_BUTTON_YES_YPOS
+#define TOOL_BUTTON_NO_GFX_YPOS                TOOL_BUTTON_YES_GFX_YPOS
+#define TOOL_BUTTON_NO_XSIZE           TOOL_BUTTON_YES_XSIZE
+#define TOOL_BUTTON_NO_YSIZE           TOOL_BUTTON_YES_YSIZE
+#define TOOL_BUTTON_CONFIRM_XPOS       TOOL_BUTTON_YES_XPOS
+#define TOOL_BUTTON_CONFIRM_YPOS       TOOL_BUTTON_YES_YPOS
+#define TOOL_BUTTON_CONFIRM_GFX_YPOS   30
+#define TOOL_BUTTON_CONFIRM_XSIZE      96
+#define TOOL_BUTTON_CONFIRM_YSIZE      TOOL_BUTTON_YES_YSIZE
+#define TOOL_BUTTON_PLAYER_XSIZE       30
+#define TOOL_BUTTON_PLAYER_YSIZE       30
+#define TOOL_BUTTON_PLAYER_GFX_XPOS    5
+#define TOOL_BUTTON_PLAYER_GFX_YPOS    185
+#define TOOL_BUTTON_PLAYER_XPOS                (5 + TOOL_BUTTON_PLAYER_XSIZE / 2)
+#define TOOL_BUTTON_PLAYER_YPOS                (215 - TOOL_BUTTON_PLAYER_YSIZE / 2)
+#define TOOL_BUTTON_PLAYER1_XPOS       (TOOL_BUTTON_PLAYER_XPOS \
+                                        + 0 * TOOL_BUTTON_PLAYER_XSIZE)
+#define TOOL_BUTTON_PLAYER2_XPOS       (TOOL_BUTTON_PLAYER_XPOS \
+                                        + 1 * TOOL_BUTTON_PLAYER_XSIZE)
+#define TOOL_BUTTON_PLAYER3_XPOS       (TOOL_BUTTON_PLAYER_XPOS \
+                                        + 0 * TOOL_BUTTON_PLAYER_XSIZE)
+#define TOOL_BUTTON_PLAYER4_XPOS       (TOOL_BUTTON_PLAYER_XPOS \
+                                        + 1 * TOOL_BUTTON_PLAYER_XSIZE)
+#define TOOL_BUTTON_PLAYER1_YPOS       (TOOL_BUTTON_PLAYER_YPOS \
+                                        + 0 * TOOL_BUTTON_PLAYER_YSIZE)
+#define TOOL_BUTTON_PLAYER2_YPOS       (TOOL_BUTTON_PLAYER_YPOS \
+                                        + 0 * TOOL_BUTTON_PLAYER_YSIZE)
+#define TOOL_BUTTON_PLAYER3_YPOS       (TOOL_BUTTON_PLAYER_YPOS \
+                                        + 1 * TOOL_BUTTON_PLAYER_YSIZE)
+#define TOOL_BUTTON_PLAYER4_YPOS       (TOOL_BUTTON_PLAYER_YPOS \
+                                        + 1 * TOOL_BUTTON_PLAYER_YSIZE)
+
+static struct
+{
+  int xpos, ypos;
+  int x, y;
+  int width, height;
+  int gadget_id;
+  char *infotext;
+} toolbutton_info[NUM_TOOL_BUTTONS] =
+{
+  {
+    TOOL_BUTTON_YES_XPOS,      TOOL_BUTTON_YES_GFX_YPOS,
+    TOOL_BUTTON_YES_XPOS,      TOOL_BUTTON_YES_YPOS,
+    TOOL_BUTTON_YES_XSIZE,     TOOL_BUTTON_YES_YSIZE,
+    TOOL_CTRL_ID_YES,
+    "yes"
+  },
+  {
+    TOOL_BUTTON_NO_XPOS,       TOOL_BUTTON_NO_GFX_YPOS,
+    TOOL_BUTTON_NO_XPOS,       TOOL_BUTTON_NO_YPOS,
+    TOOL_BUTTON_NO_XSIZE,      TOOL_BUTTON_NO_YSIZE,
+    TOOL_CTRL_ID_NO,
+    "no"
+  },
+  {
+    TOOL_BUTTON_CONFIRM_XPOS,  TOOL_BUTTON_CONFIRM_GFX_YPOS,
+    TOOL_BUTTON_CONFIRM_XPOS,  TOOL_BUTTON_CONFIRM_YPOS,
+    TOOL_BUTTON_CONFIRM_XSIZE, TOOL_BUTTON_CONFIRM_YSIZE,
+    TOOL_CTRL_ID_CONFIRM,
+    "confirm"
+  },
+  {
+    TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
+    TOOL_BUTTON_PLAYER1_XPOS,  TOOL_BUTTON_PLAYER1_YPOS,
+    TOOL_BUTTON_PLAYER_XSIZE,  TOOL_BUTTON_PLAYER_YSIZE,
+    TOOL_CTRL_ID_PLAYER_1,
+    "player 1"
+  },
+  {
+    TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
+    TOOL_BUTTON_PLAYER2_XPOS,  TOOL_BUTTON_PLAYER2_YPOS,
+    TOOL_BUTTON_PLAYER_XSIZE,  TOOL_BUTTON_PLAYER_YSIZE,
+    TOOL_CTRL_ID_PLAYER_2,
+    "player 2"
+  },
+  {
+    TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
+    TOOL_BUTTON_PLAYER3_XPOS,  TOOL_BUTTON_PLAYER3_YPOS,
+    TOOL_BUTTON_PLAYER_XSIZE,  TOOL_BUTTON_PLAYER_YSIZE,
+    TOOL_CTRL_ID_PLAYER_3,
+    "player 3"
+  },
+  {
+    TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
+    TOOL_BUTTON_PLAYER4_XPOS,  TOOL_BUTTON_PLAYER4_YPOS,
+    TOOL_BUTTON_PLAYER_XSIZE,  TOOL_BUTTON_PLAYER_YSIZE,
+    TOOL_CTRL_ID_PLAYER_4,
+    "player 4"
+  }
+};
+
+void CreateToolButtons()
+{
+  int i;
+
+  for (i=0; i<NUM_TOOL_BUTTONS; i++)
+  {
+    Pixmap gd_pixmap = pix[PIX_DOOR];
+    Pixmap deco_pixmap = None;
+    int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
+    struct GadgetInfo *gi;
+    unsigned long event_mask;
+    int gd_xoffset, gd_yoffset;
+    int gd_x1, gd_x2, gd_y;
+    int id = i;
+
+    event_mask = GD_EVENT_RELEASED;
+
+    gd_xoffset = toolbutton_info[i].xpos;
+    gd_yoffset = toolbutton_info[i].ypos;
+    gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
+    gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
+    gd_y = DOOR_GFX_PAGEY1 + gd_yoffset;
+
+    if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
+    {
+      getMiniGraphicSource(GFX_SPIELER1 + id - TOOL_CTRL_ID_PLAYER_1,
+                          &deco_pixmap, &deco_x, &deco_y);
+      deco_xpos = (toolbutton_info[i].width - MINI_TILEX) / 2;
+      deco_ypos = (toolbutton_info[i].height - MINI_TILEY) / 2;
+    }
+
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_INFO_TEXT, toolbutton_info[i].infotext,
+                     GDI_X, DX + toolbutton_info[i].x,
+                     GDI_Y, DY + toolbutton_info[i].y,
+                     GDI_WIDTH, toolbutton_info[i].width,
+                     GDI_HEIGHT, toolbutton_info[i].height,
+                     GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
+                     GDI_STATE, GD_BUTTON_UNPRESSED,
+                     GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y,
+                     GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y,
+                     GDI_DECORATION_DESIGN, deco_pixmap, deco_x, deco_y,
+                     GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
+                     GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
+                     GDI_DECORATION_SHIFTING, 1, 1,
+                     GDI_EVENT_MASK, event_mask,
+                     GDI_CALLBACK_ACTION, HandleToolButtons,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    tool_gadget[id] = gi;
+  }
+}
+
+static void UnmapToolButtons()
+{
+  int i;
+
+  for (i=0; i<NUM_TOOL_BUTTONS; i++)
+    UnmapGadget(tool_gadget[i]);
+}
+
+static void HandleToolButtons(struct GadgetInfo *gi)
+{
+  request_gadget_id = gi->custom_id;
+
+
+#if 0
+  int id = gi->custom_id;
+
+  if (game_status != PLAYING)
+    return;
+
+  switch (id)
+  {
+    case GAME_CTRL_ID_STOP:
+      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 GAME_CTRL_ID_PAUSE:
+      if (options.network)
+      {
+#ifndef MSDOS
+       if (tape.pausing)
+         SendToServer_ContinuePlaying();
+       else
+         SendToServer_PausePlaying();
+#endif
+      }
+      else
+       TapeTogglePause();
+      break;
+
+    case GAME_CTRL_ID_PLAY:
+      if (tape.pausing)
+      {
+#ifndef MSDOS
+       if (options.network)
+         SendToServer_ContinuePlaying();
+       else
+#endif
+       {
+         tape.pausing = FALSE;
+         DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
+       }
+      }
+      break;
+
+    case SOUND_CTRL_ID_MUSIC:
+      if (setup.sound_music)
+      { 
+       setup.sound_music = FALSE;
+       FadeSound(background_loop[level_nr % num_bg_loops]);
+      }
+      else if (sound_loops_allowed)
+      { 
+       setup.sound = setup.sound_music = TRUE;
+       PlaySoundLoop(background_loop[level_nr % num_bg_loops]);
+      }
+      break;
+
+    case SOUND_CTRL_ID_LOOPS:
+      if (setup.sound_loops)
+       setup.sound_loops = FALSE;
+      else if (sound_loops_allowed)
+       setup.sound = setup.sound_loops = TRUE;
+      break;
+
+    case SOUND_CTRL_ID_SIMPLE:
+      if (setup.sound_simple)
+       setup.sound_simple = FALSE;
+      else if (sound_status==SOUND_AVAILABLE)
+       setup.sound = setup.sound_simple = TRUE;
+      break;
+
+    default:
+      break;
+  }
+#endif
+
+
 
-  pixelimage = XGetImage(display, d, x, y, 1, 1, AllPlanes, ZPixmap);
-  return(XGetPixel(pixelimage, 0, 0));
 }
 
 int el2gfx(int element)
@@ -1785,6 +2617,7 @@ int el2gfx(int element)
     case EL_MORAST_VOLL:       return GFX_MORAST_VOLL;
     case EL_TROPFEN:           return GFX_TROPFEN;
     case EL_BOMBE:             return GFX_BOMBE;
+    case EL_SIEB_INAKTIV:      return GFX_SIEB_INAKTIV;
     case EL_SIEB_LEER:         return GFX_SIEB_LEER;
     case EL_SIEB_VOLL:         return GFX_SIEB_VOLL;
     case EL_SIEB_TOT:          return GFX_SIEB_TOT;
@@ -1845,6 +2678,7 @@ int el2gfx(int element)
     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_INAKTIV:     return GFX_SIEB2_INAKTIV;
     case EL_SIEB2_LEER:                return GFX_SIEB2_LEER;
     case EL_SIEB2_VOLL:                return GFX_SIEB2_VOLL;
     case EL_SIEB2_TOT:         return GFX_SIEB2_TOT;
@@ -1864,10 +2698,26 @@ int el2gfx(int element)
     case EL_PFEIL_R:           return GFX_PFEIL_R;
     case EL_PFEIL_O:           return GFX_PFEIL_O;
     case EL_PFEIL_U:           return GFX_PFEIL_U;
+    case EL_SPEED_PILL:                return GFX_SPEED_PILL;
+    case EL_SP_TERMINAL_ACTIVE:        return GFX_SP_TERMINAL;
+    case EL_SP_BUG_ACTIVE:     return GFX_SP_BUG_ACTIVE;
+    case EL_INVISIBLE_STEEL:   return GFX_INVISIBLE_STEEL;
+    case EL_BLACK_ORB:         return GFX_BLACK_ORB;
+
     default:
     {
       if (IS_CHAR(element))
        return GFX_CHAR_START + (element - EL_CHAR_START);
+      else if (element >= EL_SP_START && element <= EL_SP_END)
+      {
+       int nr_element = element - EL_SP_START;
+       int gfx_per_line = 8;
+       int nr_graphic =
+         (nr_element / gfx_per_line) * MORE_PER_LINE +
+         (nr_element % gfx_per_line);
+
+       return GFX_START_ROCKSMORE + nr_graphic;
+      }
       else
        return -1;
     }
index 6d70852092b5617f9eb67adb6195019e6eda5978..7878a28309a5feb492ced86e1c808e3e3368e914 100644 (file)
@@ -38,8 +38,8 @@
 #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_OPEN_ALL          (DOOR_OPEN_1 | DOOR_OPEN_2)
+#define DOOR_CLOSE_ALL         (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)
@@ -72,11 +72,13 @@ 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 getGraphicSource(int, int *, int *, int *);
 void DrawGraphic(int, int, int);
 void DrawGraphicExt(Drawable, GC, int, int, int);
 void DrawGraphicThruMask(int, int, int);
 void DrawGraphicThruMaskExt(Drawable, int, int, int);
 void DrawMiniGraphic(int, int, int);
+void getMiniGraphicSource(int, Pixmap *, int *, int *);
 void DrawMiniGraphicExt(Drawable, GC, int, int, int);
 void DrawGraphicShifted(int, int, int, int, int, int, int);
 void DrawGraphicShiftedThruMask(int, int, int, int, int, int);
@@ -96,14 +98,17 @@ void DrawMiniElement(int, int, int);
 void DrawMiniElementOrWall(int, int, int, int);
 void DrawMicroElement(int, int, int);
 void DrawLevel(void);
-void DrawMiniLevel(int, int);
-void DrawMicroLevel(int, int);
+void DrawMiniLevel(int, int, int, int);
+void DrawMicroLevel(int, int, boolean);
 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 CreateToolButtons();
+
 int el2gfx(int);
 
 #endif