added and adjusted source files for Mirror Magic game engine
authorHolger Schemel <info@artsoft.org>
Tue, 7 Feb 2017 20:29:46 +0000 (21:29 +0100)
committerHolger Schemel <info@artsoft.org>
Fri, 23 Mar 2018 22:21:07 +0000 (23:21 +0100)
12 files changed:
src/game_mm/Makefile [new file with mode: 0644]
src/game_mm/export.h [new file with mode: 0644]
src/game_mm/game_mm.h [new file with mode: 0644]
src/game_mm/main_mm.h [new file with mode: 0644]
src/game_mm/mm_files.c [new file with mode: 0644]
src/game_mm/mm_game.c [new file with mode: 0644]
src/game_mm/mm_game.h [new file with mode: 0644]
src/game_mm/mm_init.c [new file with mode: 0644]
src/game_mm/mm_main.c [new file with mode: 0644]
src/game_mm/mm_main.h [new file with mode: 0644]
src/game_mm/mm_tools.c [new file with mode: 0644]
src/game_mm/mm_tools.h [new file with mode: 0644]

diff --git a/src/game_mm/Makefile b/src/game_mm/Makefile
new file mode 100644 (file)
index 0000000..270ab7d
--- /dev/null
@@ -0,0 +1,62 @@
+# =============================================================================
+# Rocks'n'Diamonds - McDuffin Strikes Back!
+# -----------------------------------------------------------------------------
+# (c) 1989-2017 by Artsoft Entertainment
+#                  Holger Schemel
+#                  info@artsoft.org
+#                  http://www.artsoft.org/
+# -----------------------------------------------------------------------------
+# The native Mirror Magic game engine is based on:
+# - Mirror Magic II by Holger Schemel (Linux/DOS/Windows version, 1995)
+# - Mindbender by Holger Schemel (Amiga version, 1989)
+# -----------------------------------------------------------------------------
+# src/game_mm/Makefile
+# =============================================================================
+
+# -----------------------------------------------------------------------------
+# configuration
+# -----------------------------------------------------------------------------
+
+SRCS = mm_init.c               \
+       mm_main.c               \
+       mm_game.c               \
+       mm_files.c              \
+       mm_tools.c
+
+OBJS = mm_init.o               \
+       mm_main.o               \
+       mm_game.o               \
+       mm_files.o              \
+       mm_tools.o
+
+GAME_MM = game_mm.a
+
+
+# -----------------------------------------------------------------------------
+# build targets
+# -----------------------------------------------------------------------------
+
+all: $(GAME_MM)
+
+$(GAME_MM): $(OBJS)
+       $(AR) cru $(GAME_MM) $(OBJS)
+       $(RANLIB) $(GAME_MM)
+
+.c.o:
+       $(CC) $(PROFILING) $(CFLAGS) -c $*.c
+
+clean:
+       $(RM) $(OBJS)
+       $(RM) $(GAME_MM)
+
+
+# -----------------------------------------------------------------------------
+# development only
+# -----------------------------------------------------------------------------
+
+depend:
+       for i in $(SRCS); do $(CPP) $(CFLAGS) -M $$i; done > .depend
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/src/game_mm/export.h b/src/game_mm/export.h
new file mode 100644 (file)
index 0000000..2bbd5dc
--- /dev/null
@@ -0,0 +1,193 @@
+#ifndef GAME_MM_EXPORT_H
+#define GAME_MM_EXPORT_H
+
+/* ========================================================================= */
+/* functions and definitions exported from game_mm to main program           */
+/* ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+/* constant definitions                                                      */
+/* ------------------------------------------------------------------------- */
+
+#define MM_MAX_PLAYFIELD_WIDTH         MAX_PLAYFIELD_WIDTH
+#define MM_MAX_PLAYFIELD_HEIGHT                MAX_PLAYFIELD_HEIGHT
+
+#define MM_STD_PLAYFIELD_WIDTH         16
+#define MM_STD_PLAYFIELD_HEIGHT                12
+
+#define MM_MAX_PLAYFIELD_SIZE          (MM_MAX_PLAYFIELD_WIDTH *       \
+                                        MM_MAX_PLAYFIELD_HEIGHT)
+
+#define MAX_NUM_AMOEBA         100
+#define MAX_NUM_BEAMERS                8
+
+#define MAX_LASER_LEN          256
+#define MAX_LASER_ENERGY       100
+#define MAX_LASER_OVERLOAD     100
+
+#define MM_LEVEL_SCORE_ELEMENTS        16
+
+#define MM_MAX_LEVEL_NAME_LEN  32
+#define MM_MAX_LEVEL_AUTHOR_LEN        32
+
+
+/* ------------------------------------------------------------------------- */
+/* data structure definitions                                                */
+/* ------------------------------------------------------------------------- */
+
+struct GlobalInfo_MM
+{
+};
+
+struct CycleList
+{
+  int x, y;
+  int steps;
+};
+
+struct MovingList
+{
+  int x, y;
+  int dir;
+};
+
+struct DamageList
+{
+  int x, y;
+  int edge, angle;
+  boolean is_mirror;
+};
+
+struct BeamerInfo
+{
+  int x, y;
+  int num;
+};
+
+struct PacMan
+{
+  int XP, YP;
+  int Dr;
+};
+
+struct LaserInfo
+{
+  struct XY start_edge;
+  int start_angle;
+
+  int current_angle;
+
+  struct DamageList damage[MAX_LASER_LEN + 10];
+  int num_damages;
+
+  struct XY edge[MAX_LASER_LEN + 10];
+  int num_edges;
+
+  struct BeamerInfo beamer[MAX_NUM_BEAMERS][2];
+  int beamer_edge[MAX_NUM_BEAMERS];
+  int beamer_nr[MAX_NUM_BEAMERS];
+  int num_beamers;
+
+  boolean overloaded;
+  int overload_value;
+
+  boolean fuse_off;
+  int fuse_x, fuse_y;
+
+  int dest_element;
+  boolean stops_inside_element;
+
+  boolean redraw;
+
+  int wall_mask;
+};
+
+struct GameInfo_MM
+{
+  boolean LevelSolved;
+  boolean GameOver;
+
+  struct CycleList cycle[MM_MAX_PLAYFIELD_SIZE];
+  int num_cycle;
+
+  struct MovingList pacman[MM_MAX_PLAYFIELD_SIZE];
+  int num_pacman;
+
+  int score;
+  int energy_left;
+  int kettles_still_needed;
+  int lights_still_needed;
+  int num_keys;
+
+  boolean level_solved;
+  boolean game_over;
+  int game_over_cause;
+
+  boolean cheat_no_overload;
+  boolean cheat_no_explosion;
+};
+
+struct LevelInfo_MM
+{
+  int file_version;            /* version of file the level was stored with */
+  int game_version;            /* version of game engine to play this level */
+  boolean encoding_16bit_field;                /* level contains 16-bit elements */
+
+  int fieldx;
+  int fieldy;
+  int time;
+  int kettles_needed;
+  boolean auto_count_kettles;
+  boolean laser_red, laser_green, laser_blue;
+  char name[MM_MAX_LEVEL_NAME_LEN + 1];
+  char author[MM_MAX_LEVEL_AUTHOR_LEN + 1];
+  int score[MM_LEVEL_SCORE_ELEMENTS];
+  int amoeba_speed;
+  int time_fuse;
+
+  short field[MAX_PLAYFIELD_WIDTH][MAX_PLAYFIELD_HEIGHT];
+};
+
+struct GraphicInfo_MM
+{
+};
+
+struct EngineSnapshotInfo_MM
+{
+};
+
+
+/* ------------------------------------------------------------------------- */
+/* exported functions                                                        */
+/* ------------------------------------------------------------------------- */
+
+extern struct GlobalInfo_MM global_mm_info;
+extern struct GameInfo_MM game_mm;
+extern struct LevelInfo_MM native_mm_level;
+extern struct EngineSnapshotInfo_MM engine_snapshot_mm;
+
+extern void mm_open_all();
+extern void mm_close_all();
+
+extern void InitGfxBuffers_MM();
+
+extern void InitGameEngine_MM();
+extern void GameActions_MM(byte *, boolean);
+
+extern unsigned int InitEngineRandom_MM(int);
+
+extern void setLevelInfoToDefaults_MM();
+extern void copyInternalEngineVars_MM();
+extern boolean LoadNativeLevel_MM(char *, boolean);
+extern void SaveNativeLevel_MM(char *);
+
+extern int getFieldbufferOffsetX_MM();
+extern int getFieldbufferOffsetY_MM();
+
+extern void BlitScreenToBitmap_MM(Bitmap *);
+extern void RedrawPlayfield_MM();
+
+extern void LoadEngineSnapshotValues_MM();
+extern void SaveEngineSnapshotValues_MM(ListNode **);
+
+#endif /* GAME_MM_EXPORT_H */
diff --git a/src/game_mm/game_mm.h b/src/game_mm/game_mm.h
new file mode 100644 (file)
index 0000000..eab1a75
--- /dev/null
@@ -0,0 +1,19 @@
+// ============================================================================
+// Mirror Magic -- McDuffin's Revenge
+// ----------------------------------------------------------------------------
+// (c) 1994-2017 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// game_mm.h
+// ============================================================================
+
+#ifndef GAME_MM_H
+#define GAME_MM_H
+
+#define GAME_MM_VERSION_1_0_0
+
+#include "export.h"
+
+#endif /* GAME_MM_H */
diff --git a/src/game_mm/main_mm.h b/src/game_mm/main_mm.h
new file mode 100644 (file)
index 0000000..4eb98fc
--- /dev/null
@@ -0,0 +1,80 @@
+#ifndef MAIN_MM_H
+#define MAIN_MM_H
+
+/* ========================================================================= */
+/* external functions and definitions imported from main program to game_mm  */
+/* ========================================================================= */
+
+#include "../engines.h"
+#include "../conf_gfx.h"
+
+
+/* ========================================================================= */
+/* functions and definitions that are exported from game_mm to main program  */
+/* ========================================================================= */
+
+#include "export.h"
+
+
+/* ========================================================================= */
+/* internal functions and definitions that are not exported to main program  */
+/* ========================================================================= */
+
+
+/* ------------------------------------------------------------------------- */
+/* constant definitions                                                      */
+/* ------------------------------------------------------------------------- */
+
+/* screen sizes and positions for MM engine */
+
+extern int                     TILESIZE_VAR;
+
+#define TILESIZE               32
+#define TILEX                  TILESIZE
+#define TILEY                  TILESIZE
+#define TILEX_VAR              TILESIZE_VAR
+#define TILEY_VAR              TILESIZE_VAR
+
+extern int                     SCR_FIELDX, SCR_FIELDY;
+
+#define MAX_BUF_XSIZE          SCR_FIELDX
+#define MAX_BUF_YSIZE          SCR_FIELDY
+
+/* often used screen positions */
+
+extern int                     SX, SY;
+
+#define SXSIZE                 (SCR_FIELDX * TILEX_VAR)
+#define SYSIZE                 (SCR_FIELDY * TILEY_VAR)
+#define FXSIZE                 (MAX_BUF_XSIZE * TILEX_VAR)
+#define FYSIZE                 (MAX_BUF_YSIZE * TILEY_VAR)
+
+extern int                     REAL_SX, REAL_SY;
+
+#define FULL_SXSIZE            (2 + SXSIZE + 2)
+#define FULL_SYSIZE            (2 + SYSIZE + 2)
+
+
+/* ------------------------------------------------------------------------- */
+/* data structure definitions                                                */
+/* ------------------------------------------------------------------------- */
+
+/* ------------------------------------------------------------------------- */
+/* exported variables                                                        */
+/* ------------------------------------------------------------------------- */
+
+extern struct LevelInfo_MM native_mm_level;
+
+extern Bitmap *bitmap_db_field_mm;
+
+extern int GfxElementLast[MM_MAX_PLAYFIELD_WIDTH][MM_MAX_PLAYFIELD_HEIGHT];
+extern int GfxGraphicLast[MM_MAX_PLAYFIELD_WIDTH][MM_MAX_PLAYFIELD_HEIGHT];
+extern int GfxGraphic[MM_MAX_PLAYFIELD_WIDTH][MM_MAX_PLAYFIELD_HEIGHT];
+extern int GfxFrame[MM_MAX_PLAYFIELD_WIDTH][MM_MAX_PLAYFIELD_HEIGHT];
+
+
+/* ------------------------------------------------------------------------- */
+/* exported functions                                                        */
+/* ------------------------------------------------------------------------- */
+
+#endif /* MAIN_MM_H */
diff --git a/src/game_mm/mm_files.c b/src/game_mm/mm_files.c
new file mode 100644 (file)
index 0000000..277a2fb
--- /dev/null
@@ -0,0 +1,418 @@
+// ============================================================================
+// Mirror Magic -- McDuffin's Revenge
+// ----------------------------------------------------------------------------
+// (c) 1994-2017 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// mm_files.c
+// ============================================================================
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <dirent.h>
+
+#include "main_mm.h"
+
+#include "mm_main.h"
+
+#define CHUNK_ID_LEN           4       /* IFF style chunk id length */
+#define CHUNK_SIZE_UNDEFINED   0       /* undefined chunk size == 0  */
+#define CHUNK_SIZE_NONE                -1      /* do not write chunk size    */
+#define FILE_VERS_CHUNK_SIZE   8       /* size of file version chunk */
+#define LEVEL_HEADER_SIZE      80      /* size of level file header */
+#define LEVEL_HEADER_UNUSED    19      /* unused level header bytes */
+
+/* file identifier strings */
+#define LEVEL_COOKIE_TMPL      "MIRRORMAGIC_LEVEL_FILE_VERSION_x.x"
+#define SCORE_COOKIE           "MIRRORMAGIC_SCORE_FILE_VERSION_1.4"
+
+
+/* ========================================================================= */
+/* level file functions                                                      */
+/* ========================================================================= */
+
+static void ReadChunk_MM_VERS(File *file, int *file_version, int *game_version)
+{
+  int file_version_major, file_version_minor, file_version_patch;
+  int game_version_major, game_version_minor, game_version_patch;
+
+  file_version_major = getFile8Bit(file);
+  file_version_minor = getFile8Bit(file);
+  file_version_patch = getFile8Bit(file);
+  getFile8Bit(file);           /* not used */
+
+  game_version_major = getFile8Bit(file);
+  game_version_minor = getFile8Bit(file);
+  game_version_patch = getFile8Bit(file);
+  getFile8Bit(file);           /* not used */
+
+  *file_version = MM_VERSION_IDENT(file_version_major,
+                                  file_version_minor,
+                                  file_version_patch);
+
+  *game_version = MM_VERSION_IDENT(game_version_major,
+                                  game_version_minor,
+                                  game_version_patch);
+}
+
+static void WriteChunk_MM_VERS(FILE *file, int file_version, int game_version)
+{
+  int file_version_major = MM_VERSION_MAJOR(file_version);
+  int file_version_minor = MM_VERSION_MINOR(file_version);
+  int file_version_patch = MM_VERSION_PATCH(file_version);
+  int game_version_major = MM_VERSION_MAJOR(game_version);
+  int game_version_minor = MM_VERSION_MINOR(game_version);
+  int game_version_patch = MM_VERSION_PATCH(game_version);
+
+  fputc(file_version_major, file);
+  fputc(file_version_minor, file);
+  fputc(file_version_patch, file);
+  fputc(0, file);      /* not used */
+
+  fputc(game_version_major, file);
+  fputc(game_version_minor, file);
+  fputc(game_version_patch, file);
+  fputc(0, file);      /* not used */
+}
+
+void setLevelInfoToDefaults_MM()
+{
+  int i, x, y;
+
+  native_mm_level.file_version = MM_FILE_VERSION_ACTUAL;
+  native_mm_level.game_version = MM_GAME_VERSION_ACTUAL;
+
+  native_mm_level.encoding_16bit_field = FALSE;        /* default: only 8-bit elements */
+
+  lev_fieldx = native_mm_level.fieldx = STD_LEV_FIELDX;
+  lev_fieldy = native_mm_level.fieldy = STD_LEV_FIELDY;
+
+  for(x=0; x<MAX_LEV_FIELDX; x++)
+    for(y=0; y<MAX_LEV_FIELDY; y++)
+      Feld[x][y] = Ur[x][y] = EL_EMPTY;
+
+  native_mm_level.time = 100;
+  native_mm_level.kettles_needed = 0;
+  native_mm_level.auto_count_kettles = TRUE;
+  native_mm_level.amoeba_speed = 0;
+  native_mm_level.time_fuse = 0;
+  native_mm_level.laser_red = FALSE;
+  native_mm_level.laser_green = FALSE;
+  native_mm_level.laser_blue = TRUE;
+
+  for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
+    native_mm_level.name[i] = '\0';
+  for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
+    native_mm_level.author[i] = '\0';
+
+  strcpy(native_mm_level.name, NAMELESS_LEVEL_NAME);
+  strcpy(native_mm_level.author, ANONYMOUS_NAME);
+
+  for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
+    native_mm_level.score[i] = 10;
+
+  Feld[0][0] = Ur[0][0] = EL_MCDUFFIN_RIGHT;
+  Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
+    Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_EXIT_CLOSED;
+}
+
+static int checkLevelElement(int element)
+{
+  if (element >= EL_FIRST_RUNTIME_EL)
+  {
+    Error(ERR_WARN, "invalid level element %d", element);
+    element = EL_CHAR_FRAGE;
+  }
+
+  return element;
+}
+
+static int LoadLevel_MM_VERS(File *file, int chunk_size, struct LevelInfo_MM *level)
+{
+  ReadChunk_MM_VERS(file, &(level->file_version), &(level->game_version));
+
+  return chunk_size;
+}
+
+static int LoadLevel_MM_HEAD(File *file, int chunk_size, struct LevelInfo_MM *level)
+{
+  int i;
+  int laser_color;
+
+  lev_fieldx = level->fieldx = getFile8Bit(file);
+  lev_fieldy = level->fieldy = getFile8Bit(file);
+
+  level->time           = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
+  level->kettles_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
+
+  for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
+    level->name[i] = getFile8Bit(file);
+  level->name[MAX_LEVEL_NAME_LEN] = 0;
+
+  for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
+    level->score[i] = getFile8Bit(file);
+
+  level->auto_count_kettles    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+  level->amoeba_speed          = getFile8Bit(file);
+  level->time_fuse             = getFile8Bit(file);
+
+  laser_color                  = getFile8Bit(file);
+  level->laser_red             = (laser_color >> 2) & 0x01;
+  level->laser_green           = (laser_color >> 1) & 0x01;
+  level->laser_blue            = (laser_color >> 0) & 0x01;
+
+  level->encoding_16bit_field  = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+
+  ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
+
+  return chunk_size;
+}
+
+static int LoadLevel_MM_AUTH(File *file, int chunk_size, struct LevelInfo_MM *level)
+{
+  int i;
+
+  for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
+    level->author[i] = getFile8Bit(file);
+  level->author[MAX_LEVEL_NAME_LEN] = 0;
+
+  return chunk_size;
+}
+
+static int LoadLevel_MM_BODY(File *file, int chunk_size, struct LevelInfo_MM *level)
+{
+  int x, y;
+  int chunk_size_expected = level->fieldx * level->fieldy;
+
+  /* Note: "chunk_size" was wrong before version 2.0 when elements are
+     stored with 16-bit encoding (and should be twice as big then).
+     Even worse, playfield data was stored 16-bit when only yamyam content
+     contained 16-bit elements and vice versa. */
+
+  if (level->encoding_16bit_field && level->file_version >= MM_FILE_VERSION_2_0)
+    chunk_size_expected *= 2;
+
+  if (chunk_size_expected != chunk_size)
+  {
+    ReadUnusedBytesFromFile(file, chunk_size);
+    return chunk_size_expected;
+  }
+
+  for(y=0; y<level->fieldy; y++)
+    for(x=0; x<level->fieldx; x++)
+      Feld[x][y] = Ur[x][y] =
+       checkLevelElement(level->encoding_16bit_field ?
+                         getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
+                         getFile8Bit(file));
+  return chunk_size;
+}
+
+boolean LoadNativeLevel_MM(char *filename, boolean level_info_only)
+{
+  char cookie[MAX_LINE_LEN];
+  char chunk_name[CHUNK_ID_LEN + 1];
+  int chunk_size;
+  File *file;
+
+  static struct
+  {
+    char *name;
+    int size;
+    int (*loader)(File *, int, struct LevelInfo_MM *);
+  }
+  chunk_info[] =
+  {
+    { "VERS", FILE_VERS_CHUNK_SIZE,    LoadLevel_MM_VERS },
+    { "HEAD", LEVEL_HEADER_SIZE,       LoadLevel_MM_HEAD },
+    { "AUTH", MAX_LEVEL_AUTHOR_LEN,    LoadLevel_MM_AUTH },
+    { "BODY", -1,                      LoadLevel_MM_BODY },
+    {  NULL,  0,                       NULL }
+  };
+
+  /* always start with reliable default values */
+  setLevelInfoToDefaults_MM();
+
+  if (!(file = openFile(filename, MODE_READ)))
+  {
+    if (!level_info_only)
+      Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
+
+    return FALSE;
+  }
+
+  getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
+  if (strcmp(chunk_name, "MMII") == 0)
+  {
+    getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);  /* not used */
+
+    getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
+    if (strcmp(chunk_name, "CAVE") != 0)
+    {
+      Error(ERR_WARN, "unknown format of level file '%s'", filename);
+      closeFile(file);
+
+      return FALSE;
+    }
+  }
+  else /* check for pre-2.0 file format with cookie string */
+  {
+    strcpy(cookie, chunk_name);
+    getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4);
+    if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
+      cookie[strlen(cookie) - 1] = '\0';
+
+    if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
+    {
+      Error(ERR_WARN, "unknown format of level file '%s'", filename);
+      closeFile(file);
+
+      return FALSE;
+    }
+
+    if ((native_mm_level.file_version = getFileVersionFromCookieString(cookie)) == -1)
+    {
+      Error(ERR_WARN, "unsupported version of level file '%s'", filename);
+      closeFile(file);
+
+      return FALSE;
+    }
+  }
+
+  while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
+  {
+    int i = 0;
+
+    while (chunk_info[i].name != NULL &&
+          strcmp(chunk_name, chunk_info[i].name) != 0)
+      i++;
+
+    if (chunk_info[i].name == NULL)
+    {
+      Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
+           chunk_name, filename);
+      ReadUnusedBytesFromFile(file, chunk_size);
+    }
+    else if (chunk_info[i].size != -1 &&
+            chunk_info[i].size != chunk_size)
+    {
+      Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
+           chunk_size, chunk_name, filename);
+      ReadUnusedBytesFromFile(file, chunk_size);
+    }
+    else
+    {
+      /* call function to load this level chunk */
+      int chunk_size_expected =
+       (chunk_info[i].loader)(file, chunk_size, &native_mm_level);
+
+      /* the size of some chunks cannot be checked before reading other
+        chunks first (like "HEAD" and "BODY") that contain some header
+        information, so check them here */
+      if (chunk_size_expected != chunk_size)
+      {
+       Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
+             chunk_size, chunk_name, filename);
+      }
+    }
+  }
+
+  closeFile(file);
+
+  return TRUE;
+}
+
+static void SaveLevel_MM_HEAD(FILE *file, struct LevelInfo_MM *level)
+{
+  int i;
+  int laser_color;
+
+  fputc(level->fieldx, file);
+  fputc(level->fieldy, file);
+
+  putFile16BitInteger(file, level->time,           BYTE_ORDER_BIG_ENDIAN);
+  putFile16BitInteger(file, level->kettles_needed, BYTE_ORDER_BIG_ENDIAN);
+
+  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);
+
+  fputc((level->auto_count_kettles ? 1 : 0), file);
+  fputc(level->amoeba_speed, file);
+  fputc(level->time_fuse, file);
+
+  laser_color = ((level->laser_red   << 2) |
+                (level->laser_green << 1) |
+                (level->laser_blue  << 0));
+  fputc(laser_color, file);
+
+  fputc((level->encoding_16bit_field ? 1 : 0), file);
+
+  WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
+}
+
+static void SaveLevel_MM_AUTH(FILE *file, struct LevelInfo_MM *level)
+{
+  int i;
+
+  for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
+    fputc(level->author[i], file);
+}
+
+static void SaveLevel_MM_BODY(FILE *file, struct LevelInfo_MM *level)
+{
+  int x, y;
+
+  for(y=0; y<level->fieldy; y++)
+    for(x=0; x<level->fieldx; x++)
+      if (level->encoding_16bit_field)
+       putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
+      else
+       fputc(Ur[x][y], file);
+}
+
+void SaveNativeLevel_MM(char *filename)
+{
+  int x, y;
+  int body_chunk_size;
+  FILE *file;
+
+  if (!(file = fopen(filename, MODE_WRITE)))
+  {
+    Error(ERR_WARN, "cannot save level file '%s'", filename);
+    return;
+  }
+
+  /* check level field for 16-bit elements */
+  native_mm_level.encoding_16bit_field = FALSE;
+  for(y=0; y<native_mm_level.fieldy; y++)
+    for(x=0; x<native_mm_level.fieldx; x++)
+      if (Ur[x][y] > 255)
+       native_mm_level.encoding_16bit_field = TRUE;
+
+  body_chunk_size =
+    native_mm_level.fieldx * native_mm_level.fieldy * (native_mm_level.encoding_16bit_field ? 2 : 1);
+
+  putFileChunk(file, "MMII", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
+  putFileChunk(file, "CAVE", CHUNK_SIZE_NONE,      BYTE_ORDER_BIG_ENDIAN);
+
+  putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
+  WriteChunk_MM_VERS(file, MM_FILE_VERSION_ACTUAL, MM_GAME_VERSION_ACTUAL);
+
+  putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
+  SaveLevel_MM_HEAD(file, &native_mm_level);
+
+  putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
+  SaveLevel_MM_AUTH(file, &native_mm_level);
+
+  putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
+  SaveLevel_MM_BODY(file, &native_mm_level);
+
+  fclose(file);
+
+  SetFilePermissions(filename, PERMS_PRIVATE);
+}
diff --git a/src/game_mm/mm_game.c b/src/game_mm/mm_game.c
new file mode 100644 (file)
index 0000000..5e42cea
--- /dev/null
@@ -0,0 +1,3895 @@
+// ============================================================================
+// Mirror Magic -- McDuffin's Revenge
+// ----------------------------------------------------------------------------
+// (c) 1994-2017 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// mm_game.c
+// ============================================================================
+
+#include "main_mm.h"
+
+#include "mm_main.h"
+#include "mm_game.h"
+#include "mm_tools.h"
+
+/* graphic position values for game controls */
+#define ENERGY_XSIZE           32
+#define ENERGY_YSIZE           MAX_LASER_ENERGY
+#define OVERLOAD_XSIZE         ENERGY_XSIZE
+#define OVERLOAD_YSIZE         MAX_LASER_OVERLOAD
+
+/* values for Explode() */
+#define EX_PHASE_START         0
+#define EX_NORMAL              0
+#define EX_KETTLE              1
+#define EX_SHORT               2
+
+/* special positions in the game control window (relative to control window) */
+#define XX_LEVEL               36
+#define YY_LEVEL               23
+#define XX_KETTLES             29
+#define YY_KETTLES             63
+#define XX_SCORE               22
+#define YY_SCORE               101
+#define XX_ENERGY              8
+#define YY_ENERGY              158
+#define XX_OVERLOAD            60
+#define YY_OVERLOAD            YY_ENERGY
+
+/* special positions in the game control window (relative to main window) */
+#define DX_LEVEL               (DX + XX_LEVEL)
+#define DY_LEVEL               (DY + YY_LEVEL)
+#define DX_KETTLES             (DX + XX_KETTLES)
+#define DY_KETTLES             (DY + YY_KETTLES)
+#define DX_SCORE               (DX + XX_SCORE)
+#define DY_SCORE               (DY + YY_SCORE)
+#define DX_ENERGY              (DX + XX_ENERGY)
+#define DY_ENERGY              (DY + YY_ENERGY)
+#define DX_OVERLOAD            (DX + XX_OVERLOAD)
+#define DY_OVERLOAD            (DY + YY_OVERLOAD)
+
+#define IS_LOOP_SOUND(s)       ((s) == SND_FUEL)
+#define IS_MUSIC_SOUND(s)      ((s) == SND_TYGER || (s) == SND_VOYAGER)
+
+/* game button identifiers */
+#define GAME_CTRL_ID_LEFT              0
+#define GAME_CTRL_ID_MIDDLE            1
+#define GAME_CTRL_ID_RIGHT             2
+
+#define NUM_GAME_BUTTONS               3
+
+/* values for DrawLaser() */
+#define DL_LASER_DISABLED              0
+#define DL_LASER_ENABLED               1
+
+/* values for 'click_delay_value' in ClickElement() */
+#define CLICK_DELAY_SHORT      125
+#define CLICK_DELAY_LONG       250
+#define AUTO_ROTATE_DELAY      CLICK_DELAY_SHORT
+
+/* forward declaration for internal use */
+
+
+void GetPlayerConfig()
+{
+  if (!audio.sound_available)
+    setup.sound = FALSE;
+
+  if (!audio.loops_available)
+  {
+    setup.sound_loops = FALSE;
+    setup.sound_music = FALSE;
+  }
+
+  if (!video.fullscreen_available)
+    setup.fullscreen = FALSE;
+
+  setup.sound_simple = setup.sound;
+
+  SetAudioMode(setup.sound);
+}
+
+static int get_element_angle(int element)
+{
+  int element_phase = get_element_phase(element);
+
+  if (IS_MIRROR_FIXED(element) ||
+      IS_MCDUFFIN(element) ||
+      IS_LASER(element) ||
+      IS_RECEIVER(element))
+    return 4 * element_phase;
+  else
+    return element_phase;
+}
+
+static int get_opposite_angle(int angle)
+{
+  int opposite_angle = angle + ANG_RAY_180;
+
+  /* make sure "opposite_angle" is in valid interval [0, 15] */
+  return (opposite_angle + 16) % 16;
+}
+
+static int get_mirrored_angle(int laser_angle, int mirror_angle)
+{
+  int reflected_angle = 16 - laser_angle + mirror_angle;
+
+  /* make sure "reflected_angle" is in valid interval [0, 15] */
+  return (reflected_angle + 16) % 16;
+}
+
+void InitMovDir(int x, int y)
+{
+  int element = Feld[x][y];
+  static int direction[3][4] =
+  {
+    { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
+    { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
+    { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
+  };
+
+  switch(element)
+  {
+    case EL_PACMAN_RIGHT:
+    case EL_PACMAN_UP:
+    case EL_PACMAN_LEFT:
+    case EL_PACMAN_DOWN:
+      Feld[x][y] = EL_PACMAN;
+      MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
+      break;
+
+    default:
+      break;
+  }
+}
+
+static void InitField(int x, int y, boolean init_game)
+{
+  int element = Feld[x][y];
+
+  switch (element)
+  {
+    case EL_DF_EMPTY:
+      Feld[x][y] = EL_EMPTY;
+      break;
+
+    case EL_KETTLE:
+    case EL_CELL:
+      if (native_mm_level.auto_count_kettles)
+       game_mm.kettles_still_needed++;
+      break;
+
+    case EL_LIGHTBULB_OFF:
+      game_mm.lights_still_needed++;
+      break;
+
+    default:
+      if (IS_MIRROR(element) ||
+         IS_BEAMER_OLD(element) ||
+         IS_BEAMER(element) ||
+         IS_POLAR(element) ||
+         IS_POLAR_CROSS(element) ||
+         IS_DF_MIRROR(element) ||
+         IS_DF_MIRROR_AUTO(element) ||
+         IS_GRID_STEEL_AUTO(element) ||
+         IS_GRID_WOOD_AUTO(element) ||
+         IS_FIBRE_OPTIC(element))
+      {
+       if (IS_BEAMER_OLD(element))
+       {
+         Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
+         element = Feld[x][y];
+       }
+
+       if (!IS_FIBRE_OPTIC(element))
+       {
+         static int steps_grid_auto = 0;
+
+         if (game_mm.num_cycle == 0)   /* initialize cycle steps for grids */
+           steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
+
+         if (IS_GRID_STEEL_AUTO(element) ||
+             IS_GRID_WOOD_AUTO(element))
+           game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
+         else
+           game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
+
+         game_mm.cycle[game_mm.num_cycle].x = x;
+         game_mm.cycle[game_mm.num_cycle].y = y;
+         game_mm.num_cycle++;
+       }
+
+       if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
+       {
+         int beamer_nr = BEAMER_NR(element);
+         int nr = laser.beamer[beamer_nr][0].num;
+
+         laser.beamer[beamer_nr][nr].x = x;
+         laser.beamer[beamer_nr][nr].y = y;
+         laser.beamer[beamer_nr][nr].num = 1;
+       }
+      }
+      else if (IS_PACMAN(element))
+      {
+#if 0
+       int phase = element - EL_PACMAN_RIGHT;
+
+       game_mm.pacman[game_mm.num_pacman].x = x;
+       game_mm.pacman[game_mm.num_pacman].y = y;
+       game_mm.pacman[game_mm.num_pacman].dir = phase + ((phase + 1) % 2) * 2;
+       game_mm.num_pacman++;
+#else
+       InitMovDir(x, y);
+#endif
+      }
+      else if (IS_MCDUFFIN(element) || IS_LASER(element))
+      {
+       laser.start_edge.x = x;
+       laser.start_edge.y = y;
+       laser.start_angle = get_element_angle(element);
+      }
+
+      break;
+  }
+}
+
+static void InitCycleElements()
+{
+  int i, j;
+
+  if (game_mm.num_cycle == 0)  /* no elements to cycle */
+    return;
+
+  for(i=0; i<16; i++)
+  {
+    for(j=0; j<game_mm.num_cycle; j++)
+    {
+      int x = game_mm.cycle[j].x;
+      int y = game_mm.cycle[j].y;
+      int step = SIGN(game_mm.cycle[j].steps);
+      int last_element = Feld[x][y];
+      int next_element = get_rotated_element(last_element, step);
+
+      if (!game_mm.cycle[j].steps)
+       continue;
+
+      Feld[x][y] = next_element;
+
+      DrawField_MM(x, y);
+      game_mm.cycle[j].steps -= step;
+    }
+
+    BackToFront();
+    ColorCycling();
+
+#ifdef DEBUG
+    if (setup.quick_doors)
+      continue;
+#endif
+
+    Delay(AUTO_ROTATE_DELAY);
+  }
+}
+
+static void InitLaser()
+{
+  int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
+  int step = (IS_LASER(start_element) ? 4 : 0);
+
+  LX = laser.start_edge.x * TILEX;
+  if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
+    LX += 14;
+  else
+    LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
+
+  LY = laser.start_edge.y * TILEY;
+  if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
+    LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
+  else
+    LY += 14;
+
+  XS = 2 * Step[laser.start_angle].x;
+  YS = 2 * Step[laser.start_angle].y;
+
+  laser.current_angle = laser.start_angle;
+
+  laser.num_damages = 0;
+  laser.num_edges = 0;
+  laser.num_beamers = 0;
+  laser.beamer_edge[0] = 0;
+
+  AddLaserEdge(LX, LY);                /* set laser starting edge */
+
+  pen_ray = GetPixelFromRGB(window,
+                           native_mm_level.laser_red   * 0xFF,
+                           native_mm_level.laser_green * 0xFF,
+                           native_mm_level.laser_blue  * 0xFF);
+}
+
+void InitGame()
+{
+  int i, x, y;
+
+  /* set global editor control values */
+  editor.draw_walls_masked = FALSE;
+
+  /* set global game control values */
+  game_mm.num_cycle = 0;
+  game_mm.num_pacman = 0;
+
+  game_mm.score = 0;
+  game_mm.energy_left = native_mm_level.time;
+  game_mm.kettles_still_needed =
+    (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
+  game_mm.lights_still_needed = 0;
+  game_mm.num_keys = 0;
+
+  game_mm.level_solved = FALSE;
+  game_mm.game_over = FALSE;
+  game_mm.game_over_cause = 0;
+
+  /* set global laser control values (must be set before "InitLaser()") */
+  laser.start_edge.x = 0;
+  laser.start_edge.y = 0;
+  laser.start_angle = 0;
+
+  for (i=0; i<MAX_NUM_BEAMERS; i++)
+    laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
+
+  laser.overloaded = FALSE;
+  laser.overload_value = 0;
+  laser.fuse_off = FALSE;
+  laser.fuse_x = laser.fuse_y = -1;
+
+  laser.dest_element = EL_EMPTY;
+  laser.wall_mask = 0;
+
+  CT = Ct = 0;
+
+  for (x=0; x<lev_fieldx; x++)
+  {
+    for (y=0; y<lev_fieldy; y++)
+    {
+      Feld[x][y] = Ur[x][y];
+      Hit[x][y] = Box[x][y] = 0;
+      Angle[x][y] = 0;
+      MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
+      Store[x][y] = Store2[x][y] = 0;
+      Frame[x][y] = 0;
+      Stop[x][y] = FALSE;
+
+      InitField(x, y, TRUE);
+    }
+  }
+
+  CloseDoor(DOOR_CLOSE_1);
+
+  DrawLevel_MM();
+  InitCycleElements();
+  InitLaser();
+
+  /* copy default game door content to main double buffer */
+  BlitBitmap(pix[PIX_DOOR], drawto,
+            DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
+
+#if 0
+  DrawText(DX_LEVEL, DY_LEVEL,
+          int2str(level_nr, 2), FONT_TEXT_2);
+  DrawText(DX_KETTLES, DY_KETTLES,
+          int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
+  DrawText(DX_SCORE, DY_SCORE,
+          int2str(game_mm.score, 4), FONT_TEXT_2);
+#endif
+
+  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();
+
+  /* copy actual game door content to door double buffer for OpenDoor() */
+  BlitBitmap(drawto, pix[PIX_DB_DOOR],
+            DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+
+  OpenDoor(DOOR_OPEN_ALL);
+
+  if (setup.sound_loops)
+    PlaySoundExt(SND_FUEL, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
+
+  for(i=0; i<=game_mm.energy_left; i+=2)
+  {
+    if (!setup.sound_loops)
+      PlaySoundStereo(SND_FUEL, SOUND_MAX_RIGHT);
+
+    BlitBitmap(pix[PIX_DOOR], drawto,
+              DOOR_GFX_PAGEX4 + XX_ENERGY,
+              DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
+              ENERGY_XSIZE, i,
+              DX_ENERGY, DY_ENERGY + ENERGY_YSIZE - i);
+
+    redraw_mask |= REDRAW_DOOR_1;
+    BackToFront();
+
+    ColorCycling();
+
+#ifdef DEBUG
+    if (setup.quick_doors)
+      continue;
+#endif
+
+    Delay(20);
+  }
+
+  if (setup.sound_loops)
+    StopSound(SND_FUEL);
+
+  if (setup.sound_music && num_bg_loops)
+    PlayMusic(level_nr % num_bg_loops);
+
+  ScanLaser();
+}
+
+void AddLaserEdge(int lx, int ly)
+{
+  if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
+  {
+    Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
+    return;
+  }
+
+  laser.edge[laser.num_edges].x = SX + 2 + lx;
+  laser.edge[laser.num_edges].y = SY + 2 + ly;
+  laser.num_edges++;
+
+  laser.redraw = TRUE;
+}
+
+void AddDamagedField(int ex, int ey)
+{
+  laser.damage[laser.num_damages].is_mirror = FALSE;
+  laser.damage[laser.num_damages].angle = laser.current_angle;
+  laser.damage[laser.num_damages].edge = laser.num_edges;
+  laser.damage[laser.num_damages].x = ex;
+  laser.damage[laser.num_damages].y = ey;
+  laser.num_damages++;
+}
+
+boolean StepBehind()
+{
+  if (laser.num_edges)
+  {
+    int x = LX - XS;
+    int y = LY - YS;
+    int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
+    int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
+
+    return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
+  }
+  else
+    return FALSE;
+}
+
+static int getMaskFromElement(int element)
+{
+  if (IS_GRID(element))
+    return GFX_MASK_GRID_00 + get_element_phase(element);
+  else if (IS_MCDUFFIN(element))
+    return GFX_MASK_MCDUFFIN_00 + get_element_phase(element);
+  else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
+    return GFX_MASK_RECTANGLE;
+  else
+    return GFX_MASK_CIRCLE;
+}
+
+int ScanPixel()
+{
+  int hit_mask = 0;
+
+#if 0
+  printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
+        LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
+#endif
+
+  /* follow laser beam until it hits something (at least the screen border) */
+  while (hit_mask == HIT_MASK_NO_HIT)
+  {
+    int i;
+
+#if 0
+    /* for security */
+    if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
+       SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
+    {
+      printf("ScanPixel: touched screen border!\n");
+
+      return HIT_MASK_ALL;
+    }
+#endif
+
+    for (i=0; i<4; i++)
+    {
+      Pixel pixel;
+      int px, py, lx, ly;
+
+      px = SX + LX + (i % 2) * 2;
+      py = SY + LY + (i / 2) * 2;
+      lx = (px - SX + TILEX) / TILEX - 1;  /* ...+TILEX...-1 to get correct */
+      ly = (py - SY + TILEY) / TILEY - 1;  /* negative values!              */
+
+      if (IN_LEV_FIELD(lx, ly))
+      {
+       int element = Feld[lx][ly];
+
+       if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
+         pixel = 0;
+       else if (IS_WALL(element) || IS_WALL_CHANGING(element))
+       {
+         int pos =
+           ((py - SY - ly * TILEY) / MINI_TILEX) * 2 +
+           (px - SX - lx * TILEX) / MINI_TILEY;
+
+         pixel = ((element & (1 << pos)) ? 1 : 0);
+       }
+       else
+       {
+         int graphic_mask = getMaskFromElement(element);
+         int mask_x, mask_y;
+         int dx = px - lx * TILEX;
+         int dy = py - ly * TILEY;
+
+         mask_x = (graphic_mask % GFX_PER_LINE) * TILEX + dx;
+         mask_y = (graphic_mask / GFX_PER_LINE) * TILEY + dy;
+
+         pixel = (ReadPixel(pix[PIX_BACK], mask_x, mask_y) ? 1 : 0);
+       }
+      }
+      else
+      {
+       if (px < REAL_SX || px >= REAL_SX + FULL_SXSIZE ||
+           py < REAL_SY || py >= REAL_SY + FULL_SYSIZE)
+         pixel = 1;
+       else
+         pixel = 0;
+      }
+
+      if ((Sign[laser.current_angle] & (1 << i)) && pixel)
+       hit_mask |= (1 << i);
+    }
+
+    if (hit_mask == HIT_MASK_NO_HIT)
+    {
+      /* hit nothing -- go on with another step */
+      LX += XS;
+      LY += YS;
+    }
+  }
+
+  return hit_mask;
+}
+
+void ScanLaser()
+{
+  int element;
+  int end = 0, rf = laser.num_edges;
+#if 0
+  unsigned short color_scale = 0xFFFF / 15;
+#endif
+#if 0
+  int testx, testy;
+#endif
+
+  laser.overloaded = FALSE;
+  laser.stops_inside_element = FALSE;
+
+#if 0
+  if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
+    SetRGB(pen_ray,
+          (laser.overload_value / 6) * color_scale, 0x0000,
+          (15 - (laser.overload_value / 6)) * color_scale);
+#endif
+
+  DrawLaser(0, DL_LASER_ENABLED);
+
+#if 0
+  printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
+        LX, LY, XS, YS);
+#endif
+
+  while (1)
+  {
+    int hit_mask;
+
+    if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
+    {
+      end = 1;
+      laser.overloaded = TRUE;
+      break;
+    }
+
+    hit_mask = ScanPixel();
+
+#if 0
+    printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
+          LX, LY, XS, YS);
+#endif
+
+    /* hit something -- check out what it was */
+    ELX = (LX + XS) / TILEX;
+    ELY = (LY + YS) / TILEY;
+
+#if 0
+    printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
+          hit_mask, LX, LY, ELX, ELY);
+#endif
+
+    if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
+    {
+      element = EL_EMPTY;
+      laser.dest_element = element;
+
+      break;
+    }
+
+    if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
+    {
+      /* we have hit the top-right and bottom-left element --
+        choose the bottom-left one */
+      /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
+        ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
+        THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
+      ELX = (LX - 2) / TILEX;
+      ELY = (LY + 2) / TILEY;
+    }
+
+    if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
+    {
+      /* we have hit the top-left and bottom-right element --
+        choose the top-left one */
+      /* !!! SEE ABOVE !!! */
+      ELX = (LX - 2) / TILEX;
+      ELY = (LY - 2) / TILEY;
+    }
+
+
+#if 0
+    printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
+          hit_mask, LX, LY, ELX, ELY);
+#endif
+
+    element = Feld[ELX][ELY];
+    laser.dest_element = element;
+
+#if 0
+    printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
+          element, ELX, ELY,
+          LX, LY,
+          LX % TILEX, LY % TILEY,
+          hit_mask);
+#endif
+
+#if 0
+    if (!IN_LEV_FIELD(ELX, ELY))
+      printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
+#endif
+
+#if 0
+    testx = ELX;
+    testy = ELY;
+#endif
+
+    if (element == EL_EMPTY)
+    {
+      if (!HitOnlyAnEdge(element, hit_mask))
+       break;
+    }
+    else if (element == EL_FUSE_ON)
+    {
+      if (HitPolarizer(element, hit_mask))
+       break;
+    }
+    else if (IS_GRID(element) || IS_DF_GRID(element))
+    {
+      if (HitPolarizer(element, hit_mask))
+       break;
+    }
+    else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
+            element == EL_GATE_STONE || element == EL_GATE_WOOD)
+    {
+      if (HitBlock(element, hit_mask))
+      {
+       rf = 1;
+       break;
+      }
+    }
+    else if (IS_MCDUFFIN(element))
+    {
+      if (HitLaserSource(element, hit_mask))
+       break;
+    }
+    else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
+            IS_RECEIVER(element))
+    {
+      if (HitLaserDestination(element, hit_mask))
+       break;
+    }
+    else if (IS_WALL(element))
+    {
+      if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
+      {
+       if (HitReflectingWalls(element, hit_mask))
+         break;
+      }
+      else
+      {
+       if (HitAbsorbingWalls(element, hit_mask))
+         break;
+      }
+    }
+    else
+    {
+      if (HitElement(element, hit_mask))
+       break;
+    }
+
+    if (rf)
+      DrawLaser(rf - 1, DL_LASER_ENABLED);
+    rf = laser.num_edges;
+  }
+
+  /*
+    element = Feld[ELX][ELY];
+    laser.dest_element = element;
+  */
+
+
+
+
+#if 0
+  if (laser.dest_element != Feld[ELX][ELY])
+  {
+    printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
+          laser.dest_element, Feld[ELX][ELY]);
+  }
+#endif
+
+
+  if (!end && !laser.stops_inside_element && !StepBehind())
+  {
+#if 0
+    printf("ScanLaser: Go one step back\n");
+#endif
+
+    LX -= XS;
+    LY -= YS;
+    AddLaserEdge(LX, LY);
+  }
+
+  if (rf)
+    DrawLaser(rf - 1, DL_LASER_ENABLED);
+
+  Ct = CT = Counter();
+
+
+#if 0
+    if (!IN_LEV_FIELD(ELX, ELY))
+      printf("WARNING! (2) %d, %d\n", ELX, ELY);
+#endif
+
+
+#if 0
+  printf("(%d, %d) == %d  [(%d, %d) == %d]\n",
+        testx, testy, laser.dest_element,
+        ELX, ELY, (IN_SCR_FIELD(ELX,ELY) ? Feld[ELX][ELY] : -1));
+#endif
+
+}
+
+void DrawLaserExt(int start_edge, int num_edges, int mode)
+{
+  int element;
+  int elx, ely;
+
+#if 0
+  printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
+        start_edge, num_edges, mode);
+#endif
+
+  if (start_edge < 0)
+  {
+    Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
+    return;
+  }
+
+  if (num_edges < 0)
+  {
+    Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
+    return;
+  }
+
+#if 0
+  if (mode == DL_LASER_DISABLED)
+  {
+    printf("DrawLaser: Delete laser from edge %d\n", start_edge);
+  }
+#endif
+
+  /* now draw the laser to the backbuffer and (if enabled) to the screen */
+  DrawLines(drawto, &laser.edge[start_edge], num_edges,
+           (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
+
+  redraw_mask |= REDRAW_FIELD;
+
+  if (mode == DL_LASER_ENABLED)
+    return;
+
+  /* after the laser was deleted, the "damaged" graphics must be restored */
+  if (laser.num_damages)
+  {
+    int damage_start = 0;
+    int i;
+
+    /* determine the starting edge, from which graphics need to be restored */
+    if (start_edge > 0)
+    {
+      for(i=0; i<laser.num_damages; i++)
+      {
+       if (laser.damage[i].edge == start_edge + 1)
+       {
+         damage_start = i;
+         break;
+       }
+      }
+    }
+
+    /* restore graphics from this starting edge to the end of damage list */
+    for(i=damage_start; i<laser.num_damages; i++)
+    {
+      int lx = laser.damage[i].x;
+      int ly = laser.damage[i].y;
+      int element = Feld[lx][ly];
+
+      if (Hit[lx][ly] == laser.damage[i].edge)
+       if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
+              i == damage_start))
+         Hit[lx][ly] = 0;
+      if (Box[lx][ly] == laser.damage[i].edge)
+       Box[lx][ly] = 0;
+
+      if (IS_DRAWABLE(element))
+       DrawField_MM(lx, ly);
+    }
+
+    elx = laser.damage[damage_start].x;
+    ely = laser.damage[damage_start].y;
+    element = Feld[elx][ely];
+
+
+#if 0
+    if (IS_BEAMER(element))
+    {
+      int i;
+
+      for (i=0; i<laser.num_beamers; i++)
+       printf("-> %d\n", laser.beamer_edge[i]);
+      printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
+            mode, elx, ely, Hit[elx][ely], start_edge);
+      printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
+            get_element_angle(element), laser.damage[damage_start].angle);
+    }
+#endif
+
+    if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
+       laser.num_beamers > 0 &&
+       start_edge == laser.beamer_edge[laser.num_beamers - 1])
+    {
+      /* element is outgoing beamer */
+      laser.num_damages = damage_start + 1;
+      if (IS_BEAMER(element))
+       laser.current_angle = get_element_angle(element);
+    }
+    else
+    {
+      /* element is incoming beamer or other element */
+      laser.num_damages = damage_start;
+      laser.current_angle = laser.damage[laser.num_damages].angle;
+    }
+  }
+  else
+  {
+    /* no damages but McDuffin himself (who needs to be redrawn anyway) */
+
+    elx = laser.start_edge.x;
+    ely = laser.start_edge.y;
+    element = Feld[elx][ely];
+  }
+
+  laser.num_edges = start_edge + 1;
+  if (start_edge == 0)
+    laser.current_angle = laser.start_angle;
+  LX = laser.edge[start_edge].x - (SX + 2);
+  LY = laser.edge[start_edge].y - (SY + 2);
+  XS = 2 * Step[laser.current_angle].x;
+  YS = 2 * Step[laser.current_angle].y;
+
+#if 0
+  printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
+        LX, LY, element);
+#endif
+
+  if (start_edge > 0)
+  {
+    if (IS_BEAMER(element) ||
+       IS_FIBRE_OPTIC(element) ||
+       IS_PACMAN(element) ||
+       IS_POLAR(element) ||
+       IS_POLAR_CROSS(element) ||
+       element == EL_FUSE_ON)
+    {
+      int step_size;
+
+#if 0
+      printf("element == %d\n", element);
+#endif
+
+      if (IS_22_5_ANGLE(laser.current_angle))  /* neither 90° nor 45° angle */
+       step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
+      else
+       step_size = 8;
+
+      if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
+         ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
+          (laser.num_beamers == 0 ||
+           start_edge != laser.beamer_edge[laser.num_beamers - 1])))
+      {
+       /* element is incoming beamer or other element */
+       step_size = -step_size;
+       laser.num_edges--;
+      }
+
+#if 0
+      if (IS_BEAMER(element))
+      {
+       printf("start_edge == %d, laser.beamer_edge == %d\n",
+              start_edge, laser.beamer_edge);
+      }
+#endif
+
+      LX += step_size * XS;
+      LY += step_size * YS;
+    }
+    else if (element != EL_EMPTY)
+    {
+      LX -= 3 * XS;
+      LY -= 3 * YS;
+      laser.num_edges--;
+    }
+  }
+
+#if 0
+  printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
+        LX, LY, element);
+#endif
+}
+
+void DrawLaser(int start_edge, int mode)
+{
+  if (laser.num_edges - start_edge < 0)
+  {
+    Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
+    return;
+  }
+
+  /* check if laser is interrupted by beamer element */
+  if (laser.num_beamers > 0 &&
+      start_edge < laser.beamer_edge[laser.num_beamers - 1])
+  {
+    if (mode == DL_LASER_ENABLED)
+    {
+      int i;
+      int tmp_start_edge = start_edge;
+
+      /* draw laser segments forward from the start to the last beamer */
+      for (i=0; i<laser.num_beamers; i++)
+      {
+       int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
+
+       if (tmp_num_edges <= 0)
+         continue;
+
+#if 0
+       printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
+              i, laser.beamer_edge[i], tmp_start_edge);
+#endif
+
+       DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
+       tmp_start_edge = laser.beamer_edge[i];
+      }
+
+      /* draw last segment from last beamer to the end */
+      DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
+                  DL_LASER_ENABLED);
+    }
+    else
+    {
+      int i;
+      int last_num_edges = laser.num_edges;
+      int num_beamers = laser.num_beamers;
+
+      /* delete laser segments backward from the end to the first beamer */
+      for (i=num_beamers-1; i>=0; i--)
+      {
+       int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
+
+       if (laser.beamer_edge[i] - start_edge <= 0)
+         break;
+
+       DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
+       last_num_edges = laser.beamer_edge[i];
+       laser.num_beamers--;
+      }
+
+#if 0
+      if (last_num_edges - start_edge <= 0)
+       printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
+              last_num_edges, start_edge);
+#endif
+
+      /* delete first segment from start to the first beamer */
+      DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
+    }
+  }
+  else
+    DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
+}
+
+boolean HitElement(int element, int hit_mask)
+{
+  if (HitOnlyAnEdge(element, hit_mask))
+    return FALSE;
+
+  if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
+    element = MovingOrBlocked2Element(ELX, ELY);
+
+#if 0
+  printf("HitElement (1): element == %d\n", element);
+#endif
+
+#if 0
+  if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
+    printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
+  else
+    printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
+#endif
+
+  AddDamagedField(ELX, ELY);
+
+#if 0
+  if (ELX != (LX + 5 * XS) / TILEX ||
+      ELY != (LY + 5 * YS) / TILEY)
+  {
+    LX += 2 * XS;
+    LY += 2 * YS;
+
+    return FALSE;
+  }
+
+#else
+
+  /* this is more precise: check if laser would go through the center */
+  if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
+  {
+    /* skip the whole element before continuing the scan */
+    do
+    {
+      LX += XS;
+      LY += YS;
+    }
+    while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
+
+    if (LX/TILEX > ELX || LY/TILEY > ELY)
+    {
+      /* skipping scan positions to the right and down skips one scan
+        position too much, because this is only the top left scan position
+        of totally four scan positions (plus one to the right, one to the
+        bottom and one to the bottom right) */
+
+      LX -= XS;
+      LY -= YS;
+    }
+
+    return FALSE;
+  }
+#endif
+
+#if 0
+  printf("HitElement (2): element == %d\n", element);
+#endif
+
+  if (LX + 5 * XS < 0 ||
+      LY + 5 * YS < 0)
+  {
+    LX += 2 * XS;
+    LY += 2 * YS;
+
+    return FALSE;
+  }
+
+#if 0
+  printf("HitElement (3): element == %d\n", element);
+#endif
+
+  if (IS_POLAR(element) &&
+      ((element - EL_POLAR_START) % 2 ||
+       (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
+  {
+    PlaySoundStereo(SND_KINK, ST(ELX));
+    laser.num_damages--;
+
+    return TRUE;
+  }
+
+  if (IS_POLAR_CROSS(element) &&
+      (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
+  {
+    PlaySoundStereo(SND_KINK, ST(ELX));
+    laser.num_damages--;
+
+    return TRUE;
+  }
+
+  if (!IS_BEAMER(element) &&
+      !IS_FIBRE_OPTIC(element) &&
+      !IS_GRID_WOOD(element) &&
+      element != EL_FUEL_EMPTY)
+  {
+#if 0
+    if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
+      printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
+    else
+      printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
+#endif
+
+    LX = ELX * TILEX + 14;
+    LY = ELY * TILEY + 14;
+    AddLaserEdge(LX, LY);
+  }
+
+  if (IS_MIRROR(element) ||
+      IS_MIRROR_FIXED(element) ||
+      IS_POLAR(element) ||
+      IS_POLAR_CROSS(element) ||
+      IS_DF_MIRROR(element) ||
+      IS_DF_MIRROR_AUTO(element) ||
+      element == EL_PRISM ||
+      element == EL_REFRACTOR)
+  {
+    int current_angle = laser.current_angle;
+    int step_size;
+
+    laser.num_damages--;
+    AddDamagedField(ELX, ELY);
+    laser.damage[laser.num_damages - 1].is_mirror = TRUE;
+
+    if (!Hit[ELX][ELY])
+      Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
+
+    if (IS_MIRROR(element) ||
+       IS_MIRROR_FIXED(element) ||
+       IS_DF_MIRROR(element) ||
+       IS_DF_MIRROR_AUTO(element))
+      laser.current_angle = get_mirrored_angle(laser.current_angle,
+                                              get_element_angle(element));
+
+    if (element == EL_PRISM || element == EL_REFRACTOR)
+      laser.current_angle = RND(16);
+
+    XS = 2 * Step[laser.current_angle].x;
+    YS = 2 * Step[laser.current_angle].y;
+
+    if (!IS_22_5_ANGLE(laser.current_angle))   /* 90° or 45° angle */
+      step_size = 8;
+    else
+      step_size = 4;
+
+    LX += step_size * XS;
+    LY += step_size * YS;
+
+#if 0
+    /* draw sparkles on mirror */
+    if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
+       current_angle != laser.current_angle)
+    {
+      MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
+    }
+#endif
+
+    if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
+       current_angle != laser.current_angle)
+      PlaySoundStereo(SND_LASER, ST(ELX));
+
+    laser.overloaded =
+      (get_opposite_angle(laser.current_angle) ==
+       laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
+
+    return (laser.overloaded ? TRUE : FALSE);
+  }
+
+  if (element == EL_FUEL_FULL)
+  {
+    laser.stops_inside_element = TRUE;
+
+    return TRUE;
+  }
+
+  if (element == EL_BOMB || element == EL_MINE)
+  {
+    PlaySoundStereo(SND_KINK, ST(ELX));
+
+    if (element == EL_MINE)
+      laser.overloaded = TRUE;
+  }
+
+  if (element == EL_KETTLE ||
+      element == EL_CELL ||
+      element == EL_KEY ||
+      element == EL_LIGHTBALL ||
+      element == EL_PACMAN ||
+      IS_PACMAN(element))
+  {
+    if (!IS_PACMAN(element))
+      Bang(ELX, ELY);
+
+    if (element == EL_PACMAN)
+      Bang(ELX, ELY);
+
+    if (element == EL_KETTLE || element == EL_CELL)
+    {
+#if 0
+      if (game_mm.kettles_still_needed)
+       DrawText(DX_KETTLES, DY_KETTLES,
+                int2str(--game_mm.kettles_still_needed, 3), FONT_TEXT_2);
+#endif
+      RaiseScore(10);
+
+      if (game_mm.kettles_still_needed == 0)
+      {
+       int x, y;
+       static int xy[4][2] =
+       {
+         { +1,  0 },
+         {  0, -1 },
+         { -1,  0 },
+         {  0, +1 }
+       };
+
+        PlaySoundStereo(SND_KLING, ST(ELX));
+
+       for(y=0; y<lev_fieldy; y++)
+       {
+         for(x=0; x<lev_fieldx; x++)
+         {
+           /* initiate opening animation of exit door */
+           if (Feld[x][y] == EL_EXIT_CLOSED)
+             Feld[x][y] = EL_EXIT_OPENING;
+
+           /* remove field that blocks receiver */
+           if (IS_RECEIVER(Feld[x][y]))
+           {
+             int phase = Feld[x][y] - EL_RECEIVER_START;
+             int blocking_x, blocking_y;
+
+             blocking_x = x + xy[phase][0];
+             blocking_y = y + xy[phase][1];
+
+             if (IN_LEV_FIELD(blocking_x, blocking_y))
+             {
+               Feld[blocking_x][blocking_y] = EL_EMPTY;
+               DrawField_MM(blocking_x, blocking_y);
+             }
+           }
+         }
+       }
+
+       DrawLaser(0, DL_LASER_ENABLED);
+      }
+    }
+    else if (element == EL_KEY)
+      game_mm.num_keys++;
+    else if (element == EL_LIGHTBALL)
+      RaiseScore(10);
+    else if (IS_PACMAN(element))
+    {
+      DeletePacMan(ELX, ELY);
+      RaiseScore(50);
+    }
+
+    return FALSE;
+  }
+
+  if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
+  {
+    PlaySoundStereo(SND_KINK, ST(ELX));
+
+    DrawLaser(0, DL_LASER_ENABLED);
+
+    if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
+    {
+      Feld[ELX][ELY] = EL_LIGHTBULB_ON;
+      game_mm.lights_still_needed--;
+    }
+    else
+    {
+      Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
+      game_mm.lights_still_needed++;
+    }
+
+    DrawField_MM(ELX, ELY);
+    DrawLaser(0, DL_LASER_ENABLED);
+
+    /*
+    BackToFront();
+    */
+    laser.stops_inside_element = TRUE;
+
+    return TRUE;
+  }
+
+#if 0
+  printf("HitElement (4): element == %d\n", element);
+#endif
+
+  if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
+      laser.num_beamers < MAX_NUM_BEAMERS &&
+      laser.beamer[BEAMER_NR(element)][1].num)
+  {
+    int beamer_angle = get_element_angle(element);
+    int beamer_nr = BEAMER_NR(element);
+    int step_size;
+
+#if 0
+  printf("HitElement (BEAMER): element == %d\n", element);
+#endif
+
+    laser.num_damages--;
+
+    if (IS_FIBRE_OPTIC(element) ||
+       laser.current_angle == get_opposite_angle(beamer_angle))
+    {
+      int pos;
+
+      LX = ELX * TILEX + 14;
+      LY = ELY * TILEY + 14;
+      AddLaserEdge(LX, LY);
+      AddDamagedField(ELX, ELY);
+      laser.damage[laser.num_damages - 1].is_mirror = TRUE;
+
+      if (!Hit[ELX][ELY])
+       Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
+
+      pos = (ELX == laser.beamer[beamer_nr][0].x &&
+            ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
+      ELX = laser.beamer[beamer_nr][pos].x;
+      ELY = laser.beamer[beamer_nr][pos].y;
+      LX = ELX * TILEX + 14;
+      LY = ELY * TILEY + 14;
+
+      if (IS_BEAMER(element))
+      {
+       laser.current_angle = get_element_angle(Feld[ELX][ELY]);
+       XS = 2 * Step[laser.current_angle].x;
+       YS = 2 * Step[laser.current_angle].y;
+      }
+
+      laser.beamer_edge[laser.num_beamers] = laser.num_edges;
+      AddLaserEdge(LX, LY);
+      AddDamagedField(ELX, ELY);
+      laser.damage[laser.num_damages - 1].is_mirror = TRUE;
+
+      if (!Hit[ELX][ELY])
+       Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
+
+      if (laser.current_angle == (laser.current_angle >> 1) << 1)
+       step_size = 8;
+      else
+       step_size = 4;
+
+      LX += step_size * XS;
+      LY += step_size * YS;
+
+      laser.num_beamers++;
+
+      return FALSE;
+    }
+  }
+
+  return TRUE;
+}
+
+boolean HitOnlyAnEdge(int element, int hit_mask)
+{
+  /* check if the laser hit only the edge of an element and, if so, go on */
+
+#if 0
+  printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
+#endif
+
+  if ((hit_mask == HIT_MASK_TOPLEFT ||
+       hit_mask == HIT_MASK_TOPRIGHT ||
+       hit_mask == HIT_MASK_BOTTOMLEFT ||
+       hit_mask == HIT_MASK_BOTTOMRIGHT) &&
+      laser.current_angle % 4)                 /* angle is not 90° */
+  {
+    int dx, dy;
+
+    if (hit_mask == HIT_MASK_TOPLEFT)
+    {
+      dx = -1;
+      dy = -1;
+    }
+    else if (hit_mask == HIT_MASK_TOPRIGHT)
+    {
+      dx = +1;
+      dy = -1;
+    }
+    else if (hit_mask == HIT_MASK_BOTTOMLEFT)
+    {
+      dx = -1;
+      dy = +1;
+    }
+    else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
+    {
+      dx = +1;
+      dy = +1;
+    }
+
+    AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
+    LX += XS;
+    LY += YS;
+
+#if 0
+    printf("[HitOnlyAnEdge() == TRUE]\n");
+#endif
+
+    return TRUE;
+  }
+
+#if 0
+    printf("[HitOnlyAnEdge() == FALSE]\n");
+#endif
+
+  return FALSE;
+}
+
+boolean HitPolarizer(int element, int hit_mask)
+{
+  if (HitOnlyAnEdge(element, hit_mask))
+    return FALSE;
+
+  if (IS_DF_GRID(element))
+  {
+    int grid_angle = get_element_angle(element);
+
+#if 0
+    printf("HitPolarizer: angle: grid == %d, laser == %d\n",
+          grid_angle, laser.current_angle);
+#endif
+
+    AddLaserEdge(LX, LY);
+    AddDamagedField(ELX, ELY);
+
+    if (!Hit[ELX][ELY])
+      Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
+
+    if (laser.current_angle == grid_angle ||
+       laser.current_angle == get_opposite_angle(grid_angle))
+    {
+#if 0
+      int step_size;
+
+      if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
+       step_size = 8;
+      else
+       step_size = 4;
+
+      LX += step_size * XS;
+      LY += step_size * YS;
+#else
+
+      /* skip the whole element before continuing the scan */
+      do
+      {
+       LX += XS;
+       LY += YS;
+      }
+      while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
+
+      if (LX/TILEX > ELX || LY/TILEY > ELY)
+      {
+       /* skipping scan positions to the right and down skips one scan
+          position too much, because this is only the top left scan position
+          of totally four scan positions (plus one to the right, one to the
+          bottom and one to the bottom right) */
+
+       LX -= XS;
+       LY -= YS;
+      }
+#endif
+
+      AddLaserEdge(LX, LY);
+
+      LX += XS;
+      LY += YS;
+
+#if 0
+      printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
+            LX, LY,
+            LX / TILEX, LY / TILEY,
+            LX % TILEX, LY % TILEY);
+#endif
+
+      return FALSE;
+    }
+    else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
+      return HitReflectingWalls(element, hit_mask);
+    else
+      return HitAbsorbingWalls(element, hit_mask);
+  }
+  else if (IS_GRID_STEEL(element))
+    return HitReflectingWalls(element, hit_mask);
+  else /* IS_GRID_WOOD */
+    return HitAbsorbingWalls(element, hit_mask);
+
+  return TRUE;
+}
+
+boolean HitBlock(int element, int hit_mask)
+{
+  boolean check = FALSE;
+
+  if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
+      game_mm.num_keys == 0)
+    check = TRUE;
+
+  if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
+  {
+    int i, x, y;
+    int ex = ELX * TILEX + 14;
+    int ey = ELY * TILEY + 14;
+
+    check = TRUE;
+
+    for(i=1; i<32; i++)
+    {
+      x = LX + i * XS;
+      y = LY + i * YS;
+
+      if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
+       check = FALSE;
+    }
+  }
+
+  if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
+    return HitAbsorbingWalls(element, hit_mask);
+
+  if (check)
+  {
+    AddLaserEdge(LX - XS, LY - YS);
+    AddDamagedField(ELX, ELY);
+
+    if (!Box[ELX][ELY])
+      Box[ELX][ELY] = laser.num_edges;
+
+    return HitReflectingWalls(element, hit_mask);
+  }
+
+  if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
+  {
+    int xs = XS / 2, ys = YS / 2;
+    int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
+    int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
+
+    if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
+       (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
+    {
+      laser.overloaded = (element == EL_GATE_STONE);
+      return TRUE;
+    }
+
+    if (ABS(xs) == 1 && ABS(ys) == 1 &&
+       (hit_mask == HIT_MASK_TOP ||
+        hit_mask == HIT_MASK_LEFT ||
+        hit_mask == HIT_MASK_RIGHT ||
+        hit_mask == HIT_MASK_BOTTOM))
+      AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
+                                 hit_mask == HIT_MASK_BOTTOM),
+                     ELY - ys * (hit_mask == HIT_MASK_LEFT ||
+                                 hit_mask == HIT_MASK_RIGHT));
+    AddLaserEdge(LX, LY);
+
+    Bang(ELX, ELY);
+
+    game_mm.num_keys--;
+    if (element == EL_GATE_STONE && Box[ELX][ELY])
+    {
+      DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
+      /*
+      BackToFront();
+      */
+      ScanLaser();
+
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
+  {
+    int xs = XS / 2, ys = YS / 2;
+    int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
+    int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
+
+    if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
+       (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
+    {
+      laser.overloaded = (element == EL_BLOCK_STONE);
+
+      return TRUE;
+    }
+
+    if (ABS(xs) == 1 && ABS(ys) == 1 &&
+       (hit_mask == HIT_MASK_TOP ||
+        hit_mask == HIT_MASK_LEFT ||
+        hit_mask == HIT_MASK_RIGHT ||
+        hit_mask == HIT_MASK_BOTTOM))
+      AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
+                                 hit_mask == HIT_MASK_BOTTOM),
+                     ELY - ys * (hit_mask == HIT_MASK_LEFT ||
+                                 hit_mask == HIT_MASK_RIGHT));
+    AddDamagedField(ELX, ELY);
+
+    LX = ELX * TILEX + 14;
+    LY = ELY * TILEY + 14;
+    AddLaserEdge(LX, LY);
+
+    laser.stops_inside_element = TRUE;
+
+    return TRUE;
+  }
+
+  return TRUE;
+}
+
+boolean HitLaserSource(int element, int hit_mask)
+{
+   if (HitOnlyAnEdge(element, hit_mask))
+     return FALSE;
+
+   PlaySoundStereo(SND_AUTSCH, ST(ELX));
+   laser.overloaded = TRUE;
+
+   return TRUE;
+}
+
+boolean HitLaserDestination(int element, int hit_mask)
+{
+  if (HitOnlyAnEdge(element, hit_mask))
+    return FALSE;
+
+  if (element != EL_EXIT_OPEN &&
+      !(IS_RECEIVER(element) &&
+       game_mm.kettles_still_needed == 0 &&
+       laser.current_angle == get_opposite_angle(get_element_angle(element))))
+  {
+    PlaySoundStereo(SND_HOLZ, ST(ELX));
+    return TRUE;
+  }
+
+  if (IS_RECEIVER(element) ||
+      (IS_22_5_ANGLE(laser.current_angle) &&
+       (ELX != (LX + 6 * XS) / TILEX ||
+       ELY != (LY + 6 * YS) / TILEY ||
+       LX + 6 * XS < 0 ||
+       LY + 6 * YS < 0)))
+  {
+    LX -= XS;
+    LY -= YS;
+  }
+  else
+  {
+    LX = ELX * TILEX + 14;
+    LY = ELY * TILEY + 14;
+
+    laser.stops_inside_element = TRUE;
+  }
+
+  AddLaserEdge(LX, LY);
+  AddDamagedField(ELX, ELY);
+
+  if (game_mm.lights_still_needed == 0)
+    game_mm.level_solved = TRUE;
+
+  return TRUE;
+}
+
+boolean HitReflectingWalls(int element, int hit_mask)
+{
+  /* check if laser hits side of a wall with an angle that is not 90° */
+  if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
+                                           hit_mask == HIT_MASK_LEFT ||
+                                           hit_mask == HIT_MASK_RIGHT ||
+                                           hit_mask == HIT_MASK_BOTTOM))
+  {
+    PlaySoundStereo(SND_HUI, ST(ELX));
+    LX -= XS;
+    LY -= YS;
+    if (!IS_DF_GRID(element))
+      AddLaserEdge(LX, LY);
+
+    /* check if laser hits wall with an angle of 45° */
+    if (!IS_22_5_ANGLE(laser.current_angle))
+    {
+      if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
+      {
+       LX += 2 * XS;
+       laser.current_angle = get_mirrored_angle(laser.current_angle,
+                                                ANG_MIRROR_0);
+      }
+      else     /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
+      {
+       LY += 2 * YS;
+       laser.current_angle = get_mirrored_angle(laser.current_angle,
+                                                ANG_MIRROR_90);
+      }
+
+      AddLaserEdge(LX, LY);
+      XS = 2 * Step[laser.current_angle].x;
+      YS = 2 * Step[laser.current_angle].y;
+
+      return FALSE;
+    }
+    else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
+    {
+      laser.current_angle = get_mirrored_angle(laser.current_angle,
+                                              ANG_MIRROR_0);
+      if (ABS(XS) == 4)
+      {
+       LX += 2 * XS;
+       if (!IS_DF_GRID(element))
+         AddLaserEdge(LX, LY);
+      }
+      else
+      {
+       LX += XS;
+       if (!IS_DF_GRID(element))
+         AddLaserEdge(LX, LY + YS / 2);
+
+       LX += XS;
+       if (!IS_DF_GRID(element))
+         AddLaserEdge(LX, LY);
+      }
+
+      YS = 2 * Step[laser.current_angle].y;
+
+      return FALSE;
+    }
+    else       /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
+    {
+      laser.current_angle = get_mirrored_angle(laser.current_angle,
+                                              ANG_MIRROR_90);
+      if (ABS(YS) == 4)
+      {
+       LY += 2 * YS;
+       if (!IS_DF_GRID(element))
+         AddLaserEdge(LX, LY);
+      }
+      else
+      {
+       LY += YS;
+       if (!IS_DF_GRID(element))
+         AddLaserEdge(LX + XS / 2, LY);
+
+       LY += YS;
+       if (!IS_DF_GRID(element))
+         AddLaserEdge(LX, LY);
+      }
+
+      XS = 2 * Step[laser.current_angle].x;
+
+      return FALSE;
+    }
+  }
+
+  /* reflection at the edge of reflecting DF style wall */
+  if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
+  {
+    if (((laser.current_angle == 1 || laser.current_angle == 3) &&
+        hit_mask == HIT_MASK_TOPRIGHT) ||
+       ((laser.current_angle == 5 || laser.current_angle == 7) &&
+        hit_mask == HIT_MASK_TOPLEFT) ||
+       ((laser.current_angle == 9 || laser.current_angle == 11) &&
+        hit_mask == HIT_MASK_BOTTOMLEFT) ||
+       ((laser.current_angle == 13 || laser.current_angle == 15) &&
+        hit_mask == HIT_MASK_BOTTOMRIGHT))
+    {
+      int mirror_angle =
+       (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
+        ANG_MIRROR_135 : ANG_MIRROR_45);
+
+      PlaySoundStereo(SND_HUI, ST(ELX));
+      AddDamagedField(ELX, ELY);
+      AddLaserEdge(LX, LY);
+
+      laser.current_angle = get_mirrored_angle(laser.current_angle,
+                                              mirror_angle);
+      XS = 8 / -XS;
+      YS = 8 / -YS;
+
+      LX += XS;
+      LY += YS;
+      AddLaserEdge(LX, LY);
+
+      return FALSE;
+    }
+  }
+
+  /* reflection inside an edge of reflecting DF style wall */
+  if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
+  {
+    if (((laser.current_angle == 1 || laser.current_angle == 3) &&
+        hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
+       ((laser.current_angle == 5 || laser.current_angle == 7) &&
+        hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
+       ((laser.current_angle == 9 || laser.current_angle == 11) &&
+        hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
+       ((laser.current_angle == 13 || laser.current_angle == 15) &&
+        hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
+    {
+      int mirror_angle =
+       (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
+        hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
+        ANG_MIRROR_135 : ANG_MIRROR_45);
+
+      PlaySoundStereo(SND_HUI, ST(ELX));
+      /*
+      AddDamagedField(ELX, ELY);
+      */
+      AddLaserEdge(LX - XS, LY - YS);
+      AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
+                  LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
+
+      laser.current_angle = get_mirrored_angle(laser.current_angle,
+                                              mirror_angle);
+      XS = 8 / -XS;
+      YS = 8 / -YS;
+
+      LX += XS;
+      LY += YS;
+      AddLaserEdge(LX, LY);
+
+      return FALSE;
+    }
+  }
+
+  /* check if laser hits DF style wall with an angle of 90° */
+  if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
+  {
+    if ((IS_HORIZ_ANGLE(laser.current_angle) &&
+        (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
+       (IS_VERT_ANGLE(laser.current_angle) &&
+        (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
+    {
+      static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
+
+      /* laser at last step touched nothing or the same side of the wall */
+      if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
+      {
+       AddDamagedField(ELX, ELY);
+       LX += 8 * XS;
+       LY += 8 * YS;
+
+       last_LX = LX;
+       last_LY = LY;
+       last_hit_mask = hit_mask;
+
+       return FALSE;
+      }
+    }
+  }
+
+  if (!HitOnlyAnEdge(element, hit_mask))
+  {
+    laser.overloaded = TRUE;
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+boolean HitAbsorbingWalls(int element, int hit_mask)
+{
+  if (HitOnlyAnEdge(element, hit_mask))
+    return FALSE;
+
+  if (ABS(XS) == 4 &&
+      (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
+  {
+    AddLaserEdge(LX - XS, LY - YS);
+    LX = LX + XS / 2;
+    LY = LY + YS;
+  }
+
+  if (ABS(YS) == 4 &&
+      (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
+  {
+    AddLaserEdge(LX - XS, LY - YS);
+    LX = LX + XS;
+    LY = LY + YS / 2;
+  }
+
+  if (IS_WALL_WOOD(element) ||
+      IS_DF_WALL_WOOD(element) ||
+      IS_GRID_WOOD(element) ||
+      IS_GRID_WOOD_FIXED(element) ||
+      IS_GRID_WOOD_AUTO(element) ||
+      element == EL_FUSE_ON ||
+      element == EL_BLOCK_WOOD ||
+      element == EL_GATE_WOOD)
+  {
+    PlaySoundStereo(SND_HOLZ, ST(ELX));
+    return TRUE;
+  }
+
+  if (IS_WALL_ICE(element))
+  {
+    int mask;
+
+    mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1;    /* Quadrant (horizontal) */
+    mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2;  /* || (vertical) */
+
+    /* check if laser hits wall with an angle of 90° */
+    if (IS_90_ANGLE(laser.current_angle))
+      mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
+
+    if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
+    {
+      int i;
+
+      for(i=0; i<4; i++)
+      {
+       if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
+         mask = 15 - (8 >> i);
+       else if (ABS(XS) == 4 &&
+                mask == (1 << i) &&
+                (XS > 0) == (i % 2) &&
+                (YS < 0) == (i / 2))
+         mask = 3 + (i / 2) * 9;
+       else if (ABS(YS) == 4 &&
+                mask == (1 << i) &&
+                (XS < 0) == (i % 2) &&
+                (YS > 0) == (i / 2))
+         mask = 5 + (i % 2) * 5;
+      }
+    }
+
+    laser.wall_mask = mask;
+  }
+  else if (IS_WALL_AMOEBA(element))
+  {
+    int elx = (LX - 2 * XS) / TILEX;
+    int ely = (LY - 2 * YS) / TILEY;
+    int element2 = Feld[elx][ely];
+    int mask;
+
+    if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
+    {
+      laser.dest_element = EL_EMPTY;
+      return TRUE;
+    }
+
+    ELX = elx;
+    ELY = ely;
+
+    mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
+    mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
+
+    if (IS_90_ANGLE(laser.current_angle))
+      mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
+
+    laser.dest_element = element2 | EL_WALL_AMOEBA;
+
+    laser.wall_mask = mask;
+  }
+
+  return TRUE;
+}
+
+void OpenExit(int x, int y)
+{
+  int delay = 6;
+
+  if (!MovDelay[x][y])         /* next animation frame */
+    MovDelay[x][y] = 4 * delay;
+
+  if (MovDelay[x][y])          /* wait some time before next frame */
+  {
+    int phase;
+
+    MovDelay[x][y]--;
+    phase = MovDelay[x][y] / delay;
+    if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
+      DrawGraphic_MM(x, y, EL_EXIT_OPEN - phase);
+
+    if (!MovDelay[x][y])
+    {
+      Feld[x][y] = EL_EXIT_OPEN;
+      DrawField_MM(x, y);
+    }
+  }
+}
+
+void OpenSurpriseBall(int x, int y)
+{
+  int delay = 2;
+
+  if (!MovDelay[x][y])         /* next animation frame */
+    MovDelay[x][y] = 50 * delay;
+
+  if (MovDelay[x][y])          /* wait some time before next frame */
+  {
+#if 0
+    int phase;
+#endif
+
+    MovDelay[x][y]--;
+#if 0
+    phase = MovDelay[x][y] / delay;
+#endif
+    if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
+    {
+      Bitmap *bitmap;
+      int graphic = el2gfx(Store[x][y]);
+      int gx, gy;
+      int dx = RND(26), dy = RND(26);
+
+      getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
+      BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
+                SX + x * TILEX + dx, SY + y * TILEY + dy);
+      MarkTileDirty(x, y);
+    }
+
+    if (!MovDelay[x][y])
+    {
+      Feld[x][y] = Store[x][y];
+      Store[x][y] = 0;
+      DrawField_MM(x, y);
+
+      ScanLaser();
+    }
+  }
+}
+
+void MeltIce(int x, int y)
+{
+  int frames = 5;
+  int delay = 5;
+
+  if (!MovDelay[x][y])         /* next animation frame */
+    MovDelay[x][y] = frames * delay;
+
+  if (MovDelay[x][y])          /* wait some time before next frame */
+  {
+    int phase;
+    int wall_mask = Store2[x][y];
+    int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
+
+    MovDelay[x][y]--;
+    phase = frames - MovDelay[x][y] / delay - 1;
+
+    if (!MovDelay[x][y])
+    {
+      int i;
+
+      Feld[x][y] = real_element & (wall_mask ^ 0xFF);
+      Store[x][y] = Store2[x][y] = 0;
+
+      DrawWalls_MM(x, y, Feld[x][y]);
+
+      if (Feld[x][y] == EL_WALL_ICE)
+       Feld[x][y] = EL_EMPTY;
+
+      for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
+       if (laser.damage[i].is_mirror)
+         break;
+
+      if (i > 0)
+       DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
+      else
+       DrawLaser(0, DL_LASER_DISABLED);
+
+      ScanLaser();
+    }
+    else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
+    {
+      DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
+
+      laser.redraw = TRUE;
+    }
+  }
+}
+
+void GrowAmoeba(int x, int y)
+{
+  int frames = 5;
+  int delay = 1;
+
+  if (!MovDelay[x][y])         /* next animation frame */
+    MovDelay[x][y] = frames * delay;
+
+  if (MovDelay[x][y])          /* wait some time before next frame */
+  {
+    int phase;
+    int wall_mask = Store2[x][y];
+    int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
+
+    MovDelay[x][y]--;
+    phase = MovDelay[x][y] / delay;
+
+    if (!MovDelay[x][y])
+    {
+      Feld[x][y] = real_element;
+      Store[x][y] = Store2[x][y] = 0;
+
+      DrawWalls_MM(x, y, Feld[x][y]);
+      DrawLaser(0, DL_LASER_ENABLED);
+    }
+    else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
+      DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
+  }
+}
+
+void Explode(int x, int y, int phase, int mode)
+{
+  int num_phase = 9, delay = 2;
+  int last_phase = num_phase * delay;
+  int half_phase = (num_phase / 2) * delay;
+#if 0
+  int first_phase_after_start = EX_PHASE_START + 1;
+#endif
+
+  laser.redraw = TRUE;
+
+  if (phase == EX_PHASE_START)         /* initialize 'Store[][]' field */
+  {
+    int center_element = Feld[x][y];
+
+    if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
+    {
+      /* put moving element to center field (and let it explode there) */
+      center_element = MovingOrBlocked2Element(x, y);
+      RemoveMovingField(x, y);
+      Feld[x][y] = center_element;
+    }
+
+    if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
+      Store[x][y] = center_element;
+    else
+      Store[x][y] = EL_EMPTY;
+    Store2[x][y] = mode;
+    Feld[x][y] = EL_EXPLODING_OPAQUE;
+    MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
+    Frame[x][y] = 1;
+
+    return;
+  }
+
+  Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
+
+  if (phase == half_phase)
+  {
+    Feld[x][y] = EL_EXPLODING_TRANSP;
+
+    if (x == ELX && y == ELY)
+      ScanLaser();
+  }
+
+  if (phase == last_phase)
+  {
+#if 0
+    int element;
+#endif
+
+    if (Store[x][y] == EL_BOMB)
+    {
+      laser.num_damages--;
+      DrawLaser(0, DL_LASER_DISABLED);
+      laser.num_edges = 0;
+
+      Bang(laser.start_edge.x, laser.start_edge.y);
+      Store[x][y] = EL_EMPTY;
+    }
+    else if (IS_MCDUFFIN(Store[x][y]))
+    {
+      game_mm.game_over = TRUE;
+      game_mm.game_over_cause = GAME_OVER_BOMB;
+      Store[x][y] = EL_EMPTY;
+    }
+
+#if 0
+    element = Feld[x][y] = Store[x][y];
+#endif
+    Store[x][y] = Store2[x][y] = 0;
+    MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
+    InitField(x, y, FALSE);
+    DrawField_MM(x, y);
+  }
+  else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+  {
+    int graphic = GFX_EXPLOSION_START;
+    int graphic_phase = (phase / delay - 1);
+
+    if (Store2[x][y] == EX_KETTLE)
+    {
+      if (graphic_phase < 3)
+       graphic = GFX_EXPLOSION_KETTLE;
+      else if (graphic_phase < 5)
+      {
+       graphic = GFX_EXPLOSION_LAST;
+       graphic_phase -= graphic_phase;
+      }
+      else
+      {
+       graphic = GFX_EMPTY;
+       graphic_phase = 0;
+      }
+    }
+    else if (Store2[x][y] == EX_SHORT)
+    {
+      if (graphic_phase < 4)
+       graphic = GFX_EXPLOSION_SHORT;
+      else
+      {
+       graphic = GFX_EMPTY;
+       graphic_phase = 0;
+      }
+    }
+
+    DrawGraphic_MM(x, y, graphic + graphic_phase);
+  }
+}
+
+void Bang(int x, int y)
+{
+  int element = Feld[x][y];
+  int mode = EX_NORMAL;
+
+#if 0
+  DrawLaser(0, DL_LASER_ENABLED);
+#endif
+
+  switch(element)
+  {
+    case EL_KETTLE:
+      mode = EX_KETTLE;
+      break;
+
+    case EL_GATE_STONE:
+    case EL_GATE_WOOD:
+      mode = EX_SHORT;
+      break;
+
+    default:
+      mode = EX_NORMAL;
+      break;
+  }
+
+  if (IS_PACMAN(element))
+    PlaySoundStereo(SND_QUIEK, ST(x));
+  else if (element == EL_BOMB || IS_MCDUFFIN(element))
+    PlaySoundStereo(SND_ROAAAR, ST(x));
+  else if (element == EL_KEY)
+    PlaySoundStereo(SND_KLING, ST(x));
+  else
+    PlaySoundStereo((mode == EX_SHORT ? SND_WHOOSH : SND_KABUMM), ST(x));
+
+  Explode(x, y, EX_PHASE_START, mode);
+}
+
+void TurnRound(int x, int y)
+{
+  static struct
+  {
+    int x, y;
+  } move_xy[] =
+  {
+    { 0, 0 },
+    {-1, 0 },
+    {+1, 0 },
+    { 0, 0 },
+    { 0, -1 },
+    { 0, 0 }, { 0, 0 }, { 0, 0 },
+    { 0, +1 }
+  };
+  static struct
+  {
+    int left, right, back;
+  } turn[] =
+  {
+    { 0,       0,              0 },
+    { MV_DOWN, MV_UP,          MV_RIGHT },
+    { MV_UP,   MV_DOWN,        MV_LEFT },
+    { 0,       0,              0 },
+    { MV_LEFT, MV_RIGHT,       MV_DOWN },
+    { 0,0,0 }, { 0,0,0 },      { 0,0,0 },
+    { MV_RIGHT,        MV_LEFT,        MV_UP }
+  };
+
+  int element = Feld[x][y];
+  int old_move_dir = MovDir[x][y];
+#if 0
+  int left_dir = turn[old_move_dir].left;
+#endif
+  int right_dir = turn[old_move_dir].right;
+  int back_dir = turn[old_move_dir].back;
+
+#if 0
+  int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
+#endif
+  int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
+
+#if 0
+  int left_x = x+left_dx, left_y = y+left_dy;
+#endif
+  int right_x = x+right_dx, right_y = y+right_dy;
+
+  if (element == EL_PACMAN)
+  {
+#if 0
+    boolean can_turn_left = FALSE;
+#endif
+    boolean can_turn_right = FALSE;
+
+#if 0
+    if (IN_LEV_FIELD(left_x, left_y) &&
+       IS_EATABLE4PACMAN(Feld[left_x][left_y]))
+      can_turn_left = TRUE;
+#endif
+    if (IN_LEV_FIELD(right_x, right_y) &&
+       IS_EATABLE4PACMAN(Feld[right_x][right_y]))
+      can_turn_right = TRUE;
+
+    if (can_turn_right)
+      MovDir[x][y] = right_dir;
+    else
+      MovDir[x][y] = back_dir;
+
+    MovDelay[x][y] = 0;
+  }
+}
+
+void StartMoving(int x, int y)
+{
+  int element = Feld[x][y];
+
+  if (Stop[x][y])
+    return;
+
+  if (CAN_MOVE(element))
+  {
+    int newx, newy;
+
+    if (MovDelay[x][y])                /* wait some time before next movement */
+    {
+      MovDelay[x][y]--;
+
+      if (MovDelay[x][y])
+       return;
+    }
+
+    /* now make next step */
+
+    Moving2Blocked(x, y, &newx, &newy);        /* get next screen position */
+
+    if (element == EL_PACMAN &&
+       IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
+       !ObjHit(newx, newy, HIT_POS_CENTER))
+    {
+      Store[newx][newy] = Feld[newx][newy];
+      Feld[newx][newy] = EL_EMPTY;
+      DrawField_MM(newx, newy);
+    }
+    else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
+            ObjHit(newx, newy, HIT_POS_CENTER))
+    {
+      /* object was running against a wall */
+
+      TurnRound(x, y);
+
+      return;
+    }
+
+    InitMovingField(x, y, MovDir[x][y]);
+  }
+
+  if (MovDir[x][y])
+    ContinueMoving(x, y);
+}
+
+void ContinueMoving(int x, int y)
+{
+  int element = Feld[x][y];
+  int direction = MovDir[x][y];
+  int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
+  int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
+  int horiz_move = (dx!=0);
+  int newx = x + dx, newy = y + dy;
+  int step = (horiz_move ? dx : dy) * TILEX / 8;
+
+  MovPos[x][y] += step;
+
+  if (ABS(MovPos[x][y]) >= TILEX)      /* object reached its destination */
+  {
+    Feld[x][y] = EL_EMPTY;
+    Feld[newx][newy] = element;
+
+    MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
+    MovDelay[newx][newy] = 0;
+
+    if (!CAN_MOVE(element))
+      MovDir[newx][newy] = 0;
+
+    DrawField_MM(x, y);
+    DrawField_MM(newx, newy);
+
+    Stop[newx][newy] = TRUE;
+
+    if (element == EL_PACMAN)
+    {
+      if (Store[newx][newy] == EL_BOMB)
+       Bang(newx, newy);
+
+      if (IS_WALL_AMOEBA(Store[newx][newy]) &&
+         (LX + 2 * XS) / TILEX == newx &&
+         (LY + 2 * YS) / TILEY == newy)
+      {
+       laser.num_edges--;
+       ScanLaser();
+      }
+    }
+  }
+  else                         /* still moving on */
+    DrawField_MM(x, y);
+
+  laser.redraw = TRUE;
+}
+
+void ClickElement(int mx, int my, int button)
+{
+  static unsigned int click_delay = 0;
+  static int click_delay_value = CLICK_DELAY_SHORT;
+  static boolean new_button = TRUE;
+  int element;
+  int x = (mx - SX) / TILEX, y = (my - SY) / TILEY;
+
+  if (button == MB_RELEASED)
+  {
+    new_button = TRUE;
+    click_delay_value = CLICK_DELAY_SHORT;
+
+    /* release eventually hold auto-rotating mirror */
+    RotateMirror(x, y, MB_RELEASED);
+
+    return;
+  }
+
+  if (!DelayReached(&click_delay, click_delay_value) && !new_button)
+    return;
+
+  if (button == MB_MIDDLEBUTTON)       /* middle button has no function */
+    return;
+
+  if (!IN_PIX_FIELD(mx - SX, my - SY))
+    return;
+
+  if (Feld[x][y] == EL_EMPTY)
+    return;
+
+  element = Feld[x][y];
+
+  if (IS_MIRROR(element) ||
+      IS_BEAMER(element) ||
+      IS_POLAR(element) ||
+      IS_POLAR_CROSS(element) ||
+      IS_DF_MIRROR(element) ||
+      IS_DF_MIRROR_AUTO(element))
+  {
+    RotateMirror(x, y, button);
+  }
+  else if (IS_MCDUFFIN(element))
+  {
+    if (!laser.fuse_off)
+    {
+      DrawLaser(0, DL_LASER_DISABLED);
+      /*
+      BackToFront();
+      */
+    }
+
+    element = get_rotated_element(element, BUTTON_ROTATION(button));
+    laser.start_angle = get_element_angle(element);
+
+    InitLaser();
+
+    Feld[x][y] = element;
+    DrawField_MM(x, y);
+    /*
+    BackToFront();
+    */
+    if (!laser.fuse_off)
+      ScanLaser();
+  }
+  else if (element == EL_FUSE_ON && laser.fuse_off)
+  {
+    if (x != laser.fuse_x || y != laser.fuse_y)
+      return;
+
+    laser.fuse_off = FALSE;
+    laser.fuse_x = laser.fuse_y = -1;
+
+    DrawGraphic_MM(x, y, GFX_FUSE_ON);
+    ScanLaser();
+  }
+  else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
+  {
+    laser.fuse_off = TRUE;
+    laser.fuse_x = x;
+    laser.fuse_y = y;
+    laser.overloaded = FALSE;
+
+    DrawLaser(0, DL_LASER_DISABLED);
+    DrawGraphic_MM(x, y, GFX_FUSE_OFF);
+  }
+  else if (element == EL_LIGHTBALL)
+  {
+    Bang(x, y);
+    RaiseScore(10);
+    DrawLaser(0, DL_LASER_ENABLED);
+  }
+
+  click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT);
+  new_button = FALSE;
+}
+
+void RotateMirror(int x, int y, int button)
+{
+  static int hold_x = -1, hold_y = -1;
+
+  if (button == MB_RELEASED)
+  {
+    /* release eventually hold auto-rotating mirror */
+    hold_x = -1;
+    hold_y = -1;
+
+    return;
+  }
+
+  if (IS_MIRROR(Feld[x][y]) ||
+      IS_POLAR_CROSS(Feld[x][y]) ||
+      IS_POLAR(Feld[x][y]) ||
+      IS_BEAMER(Feld[x][y]) ||
+      IS_DF_MIRROR(Feld[x][y]) ||
+      IS_GRID_STEEL_AUTO(Feld[x][y]) ||
+      IS_GRID_WOOD_AUTO(Feld[x][y]))
+  {
+    Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
+  }
+  else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
+  {
+    if (button == MB_LEFTBUTTON)
+    {
+      /* left mouse button only for manual adjustment, no auto-rotating;
+        freeze mirror for until mouse button released */
+      hold_x = x;
+      hold_y = y;
+    }
+    else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
+      Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
+  }
+
+  if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
+  {
+    int edge = Hit[x][y];
+
+    DrawField_MM(x, y);
+
+    if (edge > 0)
+    {
+      DrawLaser(edge - 1, DL_LASER_DISABLED);
+      ScanLaser();
+    }
+  }
+  else if (ObjHit(x, y, HIT_POS_CENTER))
+  {
+    int edge = Hit[x][y];
+
+    if (edge == 0)
+    {
+      Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
+      edge = 1;
+    }
+
+    DrawLaser(edge - 1, DL_LASER_DISABLED);
+    ScanLaser();
+  }
+  else
+  {
+    int check = 1;
+
+    if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
+      check = 2;
+
+    DrawField_MM(x, y);
+
+    if ((IS_BEAMER(Feld[x][y]) ||
+        IS_POLAR(Feld[x][y]) ||
+        IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
+    {
+      check = 0;
+
+      if (IS_BEAMER(Feld[x][y]))
+      {
+#if 0
+       printf("TEST (%d, %d) [%d] [%d]\n",
+              LX, LY,
+              laser.beamer_edge, laser.beamer[1].num);
+#endif
+
+       laser.num_edges--;
+      }
+
+      ScanLaser();
+    }
+
+    if (check == 2)
+      DrawLaser(0, DL_LASER_ENABLED);
+  }
+}
+
+void AutoRotateMirrors()
+{
+  static unsigned int rotate_delay = 0;
+  int x, y;
+
+  if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY))
+    return;
+
+  for (x=0; x<lev_fieldx; x++)
+  {
+    for (y=0; y<lev_fieldy; y++)
+    {
+      int element = Feld[x][y];
+
+      if (IS_DF_MIRROR_AUTO(element) ||
+         IS_GRID_WOOD_AUTO(element) ||
+         IS_GRID_STEEL_AUTO(element) ||
+         element == EL_REFRACTOR)
+       RotateMirror(x, y, MB_RIGHTBUTTON);
+    }
+  }
+}
+
+boolean ObjHit(int obx, int oby, int bits)
+{
+  int i;
+
+  obx *= TILEX;
+  oby *= TILEY;
+
+  if (bits & HIT_POS_CENTER)
+  {
+    if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
+      return TRUE;
+  }
+
+  if (bits & HIT_POS_EDGE)
+  {
+    for(i=0; i<4; i++)
+      if (ReadPixel(drawto,
+                   SX + obx + 31 * (i % 2),
+                   SY + oby + 31 * (i / 2)) == pen_ray)
+       return TRUE;
+  }
+
+  if (bits & HIT_POS_BETWEEN)
+  {
+    for(i=0; i<4; i++)
+      if (ReadPixel(drawto,
+                   SX + 4 + obx + 22 * (i % 2),
+                   SY + 4 + oby + 22 * (i / 2)) == pen_ray)
+       return TRUE;
+  }
+
+  return FALSE;
+}
+
+void DeletePacMan(int px, int py)
+{
+  int i, j;
+
+  Bang(px, py);
+
+  if (game_mm.num_pacman <= 1)
+  {
+    game_mm.num_pacman = 0;
+    return;
+  }
+
+  for(i=0; i<game_mm.num_pacman; i++)
+    if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
+      break;
+
+  game_mm.num_pacman--;
+
+  for(j=i; j<game_mm.num_pacman; j++)
+  {
+    game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
+    game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
+    game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
+  }
+}
+
+void ColorCycling(void)
+{
+  static int CC, Cc = 0;
+
+  static int color, old = 0xF00, new = 0x010, mult = 1;
+  static unsigned short red, green, blue;
+
+  if (color_status == STATIC_COLORS)
+    return;
+
+  CC = Counter();
+
+  if (CC < Cc || CC > Cc + 50)
+  {
+    Cc = CC;
+
+    color = old + new * mult;
+    if (mult > 0)
+      mult++;
+    else
+      mult--;
+
+    if (ABS(mult) == 16)
+    {
+      mult =- mult / 16;
+      old = color;
+      new = new << 4;
+      if (new > 0x100)
+       new = 0x001;
+    }
+
+    red   = 0x0e00 * ((color & 0xF00) >> 8);
+    green = 0x0e00 * ((color & 0x0F0) >> 4);
+    blue  = 0x0e00 * ((color & 0x00F));
+    SetRGB(pen_magicolor[0], red, green, blue);
+
+    red   = 0x1111 * ((color & 0xF00) >> 8);
+    green = 0x1111 * ((color & 0x0F0) >> 4);
+    blue  = 0x1111 * ((color & 0x00F));
+    SetRGB(pen_magicolor[1], red, green, blue);
+  }
+}
+
+void GameActions()
+{
+  static unsigned int action_delay = 0;
+  static unsigned int pacman_delay = 0;
+  static unsigned int energy_delay = 0;
+  static unsigned int overload_delay = 0;
+#if 0
+  unsigned short color_scale = 0xFFFF / 15;
+#endif
+  int element;
+  int x, y, i;
+
+  int r, d;
+
+#if 1
+  WaitUntilDelayReached(&action_delay, GameFrameDelay);
+#else
+  if (!DelayReached(&action_delay, GameFrameDelay))
+  {
+    if (!PendingEvent())       /* delay only if no pending events */
+      Delay(10);
+    return;
+  }
+#endif
+
+  for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+    Stop[x][y] = FALSE;
+
+  for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+  {
+    element = Feld[x][y];
+
+    if (!IS_MOVING(x, y) && CAN_MOVE(element))
+      StartMoving(x, y);
+    else if (IS_MOVING(x, y))
+      ContinueMoving(x, y);
+    else if (IS_EXPLODING(element))
+      Explode(x, y, Frame[x][y], EX_NORMAL);
+    else if (element == EL_EXIT_OPENING)
+      OpenExit(x, y);
+    else if (element == EL_GRAY_BALL_OPENING)
+      OpenSurpriseBall(x, y);
+    else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
+      MeltIce(x, y);
+    else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
+      GrowAmoeba(x, y);
+  }
+
+  AutoRotateMirrors();
+
+#if 1
+  /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
+
+  /* redraw after Explode() ... */
+  if (laser.redraw)
+    DrawLaser(0, DL_LASER_ENABLED);
+  laser.redraw = FALSE;
+#endif
+
+  CT = Counter();
+
+  if (game_mm.num_pacman && DelayReached(&pacman_delay, 250))
+  {
+    MovePacMen();
+
+    if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
+    {
+      DrawLaser(0, DL_LASER_DISABLED);
+      ScanLaser();
+    }
+  }
+
+  if (DelayReached(&energy_delay, 4000))
+  {
+    game_mm.energy_left--;
+    if (game_mm.energy_left >= 0)
+    {
+      BlitBitmap(pix[PIX_DOOR], drawto,
+                DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
+                ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
+                DX_ENERGY, DY_ENERGY);
+      redraw_mask |= REDRAW_DOOR_1;
+    }
+    else if (setup.time_limit)
+    {
+      int i;
+
+      for(i=15; i>=0; i--)
+      {
+#if 0
+       SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
+#endif
+       pen_ray = GetPixelFromRGB(window,
+                                 native_mm_level.laser_red   * 0x11 * i,
+                                 native_mm_level.laser_green * 0x11 * i,
+                                 native_mm_level.laser_blue  * 0x11 * i);
+       DrawLaser(0, DL_LASER_ENABLED);
+       BackToFront();
+       Delay(50);
+      }
+
+      StopSound(SND_WARNTON);
+      FadeMusic();
+
+      DrawLaser(0, DL_LASER_DISABLED);
+      game_mm.game_over = TRUE;
+      game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
+
+#if 0
+      if (Request("Out of magic energy ! Play it again ?",
+                 REQ_ASK | REQ_STAY_CLOSED))
+      {
+       InitGame();
+      }
+      else
+      {
+       game_status = MAINMENU;
+       DrawMainMenu();
+      }
+#endif
+
+      return;
+    }
+  }
+
+  element = laser.dest_element;
+
+#if 0
+  if (element != Feld[ELX][ELY])
+  {
+    printf("element == %d, Feld[ELX][ELY] == %d\n",
+          element, Feld[ELX][ELY]);
+  }
+#endif
+
+  if (!laser.overloaded && laser.overload_value == 0 &&
+      element != EL_BOMB &&
+      element != EL_MINE &&
+      element != EL_BALL_GRAY &&
+      element != EL_BLOCK_STONE &&
+      element != EL_BLOCK_WOOD &&
+      element != EL_FUSE_ON &&
+      element != EL_FUEL_FULL &&
+      !IS_WALL_ICE(element) &&
+      !IS_WALL_AMOEBA(element))
+    return;
+
+  if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
+       (!laser.overloaded && laser.overload_value > 0)) &&
+      DelayReached(&overload_delay, 60 + !laser.overloaded * 120))
+  {
+    if (laser.overloaded)
+      laser.overload_value++;
+    else
+      laser.overload_value--;
+
+    if (game_mm.cheat_no_overload)
+    {
+      laser.overloaded = FALSE;
+      laser.overload_value = 0;
+    }
+
+    if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
+    {
+      int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
+      int color_down = 0xFF - color_up;
+
+#if 0
+      SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
+            (15 - (laser.overload_value / 6)) * color_scale);
+#endif
+      pen_ray = GetPixelFromRGB(window,
+                               (native_mm_level.laser_red  ? 0xFF : color_up),
+                               (native_mm_level.laser_green ? color_down : 0x00),
+                               (native_mm_level.laser_blue  ? color_down : 0x00));
+      DrawLaser(0, DL_LASER_ENABLED);
+      BackToFront();
+    }
+
+    if (laser.overloaded)
+    {
+      if (setup.sound_loops)
+       PlaySoundExt(SND_WARNTON, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
+      else
+       PlaySoundStereo(SND_WARNTON, SOUND_MAX_RIGHT);
+    }
+
+    if (!laser.overloaded)
+      StopSound(SND_WARNTON);
+
+    if (laser.overloaded)
+    {
+      BlitBitmap(pix[PIX_DOOR], drawto,
+                DOOR_GFX_PAGEX4 + XX_OVERLOAD,
+                DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
+                - laser.overload_value,
+                OVERLOAD_XSIZE, laser.overload_value,
+                DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
+                - laser.overload_value);
+      redraw_mask |= REDRAW_DOOR_1;
+    }
+    else
+    {
+      BlitBitmap(pix[PIX_DOOR], drawto,
+                DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
+                OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
+                DX_OVERLOAD, DY_OVERLOAD);
+      redraw_mask |= REDRAW_DOOR_1;
+    }
+
+    if (laser.overload_value == MAX_LASER_OVERLOAD)
+    {
+      int i;
+
+      for(i=15; i>=0; i--)
+      {
+#if 0
+       SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
+#endif
+
+       pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
+       DrawLaser(0, DL_LASER_ENABLED);
+       BackToFront();
+       Delay(50);
+      }
+
+      DrawLaser(0, DL_LASER_DISABLED);
+      game_mm.game_over = TRUE;
+      game_mm.game_over_cause = GAME_OVER_OVERLOADED;
+
+#if 0
+      if (Request("Magic spell hit Mc Duffin ! Play it again ?",
+                 REQ_ASK | REQ_STAY_CLOSED))
+      {
+       InitGame();
+      }
+      else
+      {
+       game_status = MAINMENU;
+       DrawMainMenu();
+      }
+#endif
+
+      return;
+    }
+  }
+
+  if (laser.fuse_off)
+    return;
+
+  CT -= Ct;
+
+  if (element == EL_BOMB && CT > 1500)
+  {
+    if (game_mm.cheat_no_explosion)
+      return;
+
+#if 0
+    laser.num_damages--;
+    DrawLaser(0, DL_LASER_DISABLED);
+    laser.num_edges = 0;
+#endif
+
+    Bang(ELX, ELY);
+
+    laser.dest_element = EL_EXPLODING_OPAQUE;
+
+#if 0
+    Bang(ELX, ELY);
+    laser.num_damages--;
+    DrawLaser(0, DL_LASER_DISABLED);
+
+    laser.num_edges = 0;
+    Bang(laser.start_edge.x, laser.start_edge.y);
+
+    if (Request("Bomb killed Mc Duffin ! Play it again ?",
+               REQ_ASK | REQ_STAY_CLOSED))
+    {
+      InitGame();
+    }
+    else
+    {
+      game_status = MAINMENU;
+      DrawMainMenu();
+    }
+#endif
+
+    return;
+  }
+
+  if (element == EL_FUSE_ON && CT > 500)
+  {
+    laser.fuse_off = TRUE;
+    laser.fuse_x = ELX;
+    laser.fuse_y = ELY;
+    DrawLaser(0, DL_LASER_DISABLED);
+    DrawGraphic_MM(ELX, ELY, GFX_FUSE_OFF);
+  }
+
+  if (element == EL_BALL_GRAY && CT > 1500)
+  {
+    static int new_elements[] =
+    {
+      EL_MIRROR_START,
+      EL_MIRROR_FIXED_START,
+      EL_POLAR_START,
+      EL_POLAR_CROSS_START,
+      EL_PACMAN_START,
+      EL_KETTLE,
+      EL_BOMB,
+      EL_PRISM
+    };
+    int num_new_elements = sizeof(new_elements) / sizeof(int);
+    int new_element = new_elements[RND(num_new_elements)];
+
+    Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
+    Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
+
+    /* !!! CHECK AGAIN: Laser on Polarizer !!! */
+    ScanLaser();
+
+    return;
+
+#if 0
+    int graphic;
+
+    switch (RND(5))
+    {
+      case 0:
+        element = EL_MIRROR_START + RND(16);
+       break;
+      case 1:
+       {
+         int rnd = RND(3);
+
+         element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
+       }
+       break;
+      default:
+       {
+         int rnd = RND(3);
+
+         element = (rnd == 0 ? EL_FUSE_ON :
+                    rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
+                    rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
+                    rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
+                    EL_MIRROR_FIXED_START + rnd - 25);
+       }
+       break;
+    }
+
+    graphic = el2gfx(element);
+
+    for(i=0; i<50; i++)
+    {
+      int x = RND(26);
+      int y = RND(26);
+
+      BlitBitmap(pix[PIX_BACK], drawto,
+                SX + (graphic % GFX_PER_LINE) * TILEX + x,
+                SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
+                SX + ELX * TILEX + x,
+                SY + ELY * TILEY + y);
+      MarkTileDirty(ELX, ELY);
+      BackToFront();
+
+      DrawLaser(0, DL_LASER_ENABLED);
+
+      Delay(50);
+    }
+
+    Feld[ELX][ELY] = element;
+    DrawField_MM(ELX, ELY);
+
+#if 0
+    printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
+#endif
+
+    /* above stuff: GRAY BALL -> PRISM !!! */
+/*
+    LX = ELX * TILEX + 14;
+    LY = ELY * TILEY + 14;
+    if (laser.current_angle == (laser.current_angle >> 1) << 1)
+      OK = 8;
+    else
+      OK = 4;
+    LX -= OK * XS;
+    LY -= OK * YS;
+
+    laser.num_edges -= 2;
+    laser.num_damages--;
+*/
+
+#if 0
+    for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
+      if (laser.damage[i].is_mirror)
+       break;
+
+    if (i > 0)
+      DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
+    else
+      DrawLaser(0, DL_LASER_DISABLED);
+#else
+    DrawLaser(0, DL_LASER_DISABLED);
+#endif
+
+    ScanLaser();
+
+    /*
+    printf("TEST ELEMENT: %d\n", Feld[0][0]);
+    */
+#endif
+
+    return;
+  }
+
+  if (IS_WALL_ICE(element) && CT > 1000)
+  {
+    PlaySoundStereo(SND_SLURP, ST(ELX));
+
+
+
+    {
+      Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
+      Store[ELX][ELY] = EL_WALL_ICE;
+      Store2[ELX][ELY] = laser.wall_mask;
+
+      laser.dest_element = Feld[ELX][ELY];
+
+      return;
+    }
+
+
+
+
+    for(i=0; i<5; i++)
+    {
+      int phase = i + 1;
+
+      if (i == 4)
+      {
+       Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
+       phase = 0;
+      }
+
+      DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
+      BackToFront();
+      Delay(100);
+    }
+
+    if (Feld[ELX][ELY] == EL_WALL_ICE)
+      Feld[ELX][ELY] = EL_EMPTY;
+
+/*
+    laser.num_edges--;
+    LX = laser.edge[laser.num_edges].x - (SX + 2);
+    LY = laser.edge[laser.num_edges].y - (SY + 2);
+*/
+
+    for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
+      if (laser.damage[i].is_mirror)
+       break;
+
+    if (i > 0)
+      DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
+    else
+      DrawLaser(0, DL_LASER_DISABLED);
+
+    ScanLaser();
+
+    return;
+  }
+
+  if (IS_WALL_AMOEBA(element) && CT > 1200)
+  {
+    int k1, k2, k3, dx, dy, de, dm;
+    int element2 = Feld[ELX][ELY];
+
+    if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
+      return;
+
+    for (i = laser.num_damages - 1; i>=0; i--)
+      if (laser.damage[i].is_mirror)
+       break;
+
+    r = laser.num_edges;
+    d = laser.num_damages;
+    k1 = i;
+
+    if (k1 > 0)
+    {
+      int x, y;
+
+      DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
+
+      laser.num_edges++;
+      DrawLaser(0, DL_LASER_ENABLED);
+      laser.num_edges--;
+
+      x = laser.damage[k1].x;
+      y = laser.damage[k1].y;
+      DrawField_MM(x, y);
+    }
+
+    for(i=0; i<4; i++)
+    {
+      if (laser.wall_mask & (1 << i))
+      {
+       if (ReadPixel(drawto,
+                     SX + ELX * TILEX + 14 + (i % 2) * 2,
+                     SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
+         break;
+       if (ReadPixel(drawto,
+                     SX + ELX * TILEX + 31 * (i % 2),
+                     SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
+         break;
+      }
+    }
+
+    k2 = i;
+
+    for(i=0; i<4; i++)
+    {
+      if (laser.wall_mask & (1 << i))
+      {
+       if (ReadPixel(drawto,
+                     SX + ELX * TILEX + 31 * (i % 2),
+                     SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
+         break;
+      }
+    }
+
+    k3 = i;
+
+    if (laser.num_beamers > 0 ||
+       k1 < 1 || k2 < 4 || k3 < 4 ||
+       ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
+       == pen_ray)
+    {
+      laser.num_edges = r;
+      laser.num_damages = d;
+      DrawLaser(0, DL_LASER_DISABLED);
+    }
+
+    Feld[ELX][ELY] = element | laser.wall_mask;
+    dx = ELX;
+    dy = ELY;
+    de = Feld[ELX][ELY];
+    dm = laser.wall_mask;
+
+
+
+#if 1
+    {
+      int x = ELX, y = ELY;
+      int wall_mask = laser.wall_mask;
+
+
+      ScanLaser();
+      DrawLaser(0, DL_LASER_ENABLED);
+
+      PlaySoundStereo(SND_AMOEBE, ST(dx));
+
+
+
+      Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
+      Store[x][y] = EL_WALL_AMOEBA;
+      Store2[x][y] = wall_mask;
+
+      return;
+    }
+#endif
+
+
+
+    DrawWallsAnimation_MM(dx, dy, de, 4, dm);
+    ScanLaser();
+    DrawLaser(0, DL_LASER_ENABLED);
+
+    PlaySoundStereo(SND_AMOEBE, ST(dx));
+
+    for(i=4; i>=0; i--)
+    {
+      DrawWallsAnimation_MM(dx, dy, de, i, dm);
+      BackToFront();
+      Delay(20);
+    }
+
+    DrawLaser(0, DL_LASER_ENABLED);
+
+    return;
+  }
+
+  if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
+      laser.stops_inside_element && CT > 1500)
+  {
+    int x, y;
+    int k;
+
+    if (ABS(XS) > ABS(YS))
+      k = 0;
+    else
+      k = 1;
+    if (XS < YS)
+      k += 2;
+
+    for(i=0; i<4; i++)
+    {
+      if (i)
+       k++;
+      if (k > 3)
+       k=0;
+
+      x = ELX + Step[k * 4].x;
+      y = ELY + Step[k * 4].y;
+
+      if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
+       continue;
+
+      if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
+       continue;
+
+      break;
+    }
+
+    if (i > 3)
+    {
+      laser.overloaded = (element == EL_BLOCK_STONE);
+      return;
+    }
+
+    PlaySoundStereo(SND_BONG, ST(ELX));
+
+    Feld[ELX][ELY] = 0;
+    Feld[x][y] = element;
+
+    DrawGraphic_MM(ELX, ELY, -1);
+    DrawField_MM(x, y);
+
+    if (element == EL_BLOCK_STONE && Box[ELX][ELY])
+    {
+      DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
+      DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
+    }
+
+    ScanLaser();
+
+    return;
+  }
+
+  if (element == EL_FUEL_FULL && CT > 200)
+  {
+    for(i=game_mm.energy_left; i<=MAX_LASER_ENERGY; i+=2)
+    {
+      BlitBitmap(pix[PIX_DOOR], drawto,
+                DOOR_GFX_PAGEX4 + XX_ENERGY,
+                DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
+                ENERGY_XSIZE, i, DX_ENERGY,
+                DY_ENERGY + ENERGY_YSIZE - i);
+
+      redraw_mask |= REDRAW_DOOR_1;
+      BackToFront();
+
+      Delay(20);
+    }
+
+    game_mm.energy_left = MAX_LASER_ENERGY;
+    Feld[ELX][ELY] = EL_FUEL_EMPTY;
+    DrawField_MM(ELX, ELY);
+
+    DrawLaser(0, DL_LASER_ENABLED);
+
+    return;
+  }
+
+  return;
+}
+
+void MovePacMen()
+{
+  static int p = -1;
+  int mx, my, ox, oy, nx, ny;
+  int g, element;
+  int l;
+
+  if (++p >= game_mm.num_pacman)
+    p = 0;
+  game_mm.pacman[p].dir--;
+
+  for(l=1; l<5; l++)
+  {
+    game_mm.pacman[p].dir++;
+
+    if (game_mm.pacman[p].dir > 4)
+      game_mm.pacman[p].dir = 1;
+
+    if (game_mm.pacman[p].dir % 2)
+    {
+      mx = 0;
+      my = game_mm.pacman[p].dir - 2;
+    }
+    else
+    {
+      my = 0;
+      mx = 3 - game_mm.pacman[p].dir;
+    }
+
+    ox = game_mm.pacman[p].x;
+    oy = game_mm.pacman[p].y;
+    nx = ox + mx;
+    ny = oy + my;
+    element = Feld[nx][ny];
+    if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
+      continue;
+
+    if (!IS_EATABLE4PACMAN(element))
+      continue;
+
+    if (ObjHit(nx, ny, HIT_POS_CENTER))
+      continue;
+
+    Feld[ox][oy] = EL_EMPTY;
+    Feld[nx][ny] =
+      EL_PACMAN_RIGHT - 1 +
+      (game_mm.pacman[p].dir - 1 +
+       (game_mm.pacman[p].dir % 2) * 2);
+
+    game_mm.pacman[p].x = nx;
+    game_mm.pacman[p].y = ny;
+    g = Feld[nx][ny] - EL_PACMAN_RIGHT;
+    DrawGraphic_MM(ox, oy, GFX_EMPTY);
+
+    if (element != EL_EMPTY)
+    {
+      int i;
+
+      CT = Counter();
+      ox = SX + ox * TILEX;
+      oy = SY + oy * TILEY;
+
+      for(i=1; i<33; i+=2)
+      {
+       BlitBitmap(pix[PIX_BACK], window,
+                  SX + g * TILEX, SY + 4 * TILEY, TILEX, TILEY,
+                  ox + i * mx, oy + i * my);
+#if 0
+       FlushDisplay();
+       Delay(1);
+#endif
+      }
+      Ct = Ct + Counter() - CT;
+    }
+    DrawField_MM(nx, ny);
+    BackToFront();
+
+    if (!laser.fuse_off)
+    {
+      DrawLaser(0, DL_LASER_ENABLED);
+
+      if (ObjHit(nx, ny, HIT_POS_BETWEEN))
+      {
+       AddDamagedField(nx, ny);
+       laser.damage[laser.num_damages - 1].edge = 0;
+      }
+    }
+
+    if (element == EL_BOMB)
+    {
+      DeletePacMan(nx, ny);
+    }
+
+    if (IS_WALL_AMOEBA(element) &&
+       (LX + 2 * XS) / TILEX == nx &&
+       (LY + 2 * YS) / TILEY == ny)
+    {
+      laser.num_edges--;
+      ScanLaser();
+    }
+    break;
+  }
+}
+
+void GameWon()
+{
+  int hi_pos;
+  boolean raise_level = FALSE;
+
+#if 0
+  if (local_player->MovPos)
+    return;
+
+  local_player->LevelSolved = FALSE;
+#endif
+
+  if (game_mm.energy_left)
+  {
+    if (setup.sound_loops)
+      PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
+
+    while(game_mm.energy_left > 0)
+    {
+      if (!setup.sound_loops)
+       PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
+
+      /*
+      if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
+       RaiseScore(native_mm_level.score[SC_ZEITBONUS]);
+      */
+
+      RaiseScore(5);
+
+      game_mm.energy_left--;
+      if (game_mm.energy_left >= 0)
+      {
+       BlitBitmap(pix[PIX_DOOR], drawto,
+                  DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
+                  ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
+                  DX_ENERGY, DY_ENERGY);
+       redraw_mask |= REDRAW_DOOR_1;
+      }
+
+      BackToFront();
+      Delay(10);
+    }
+
+    if (setup.sound_loops)
+      StopSound(SND_SIRR);
+  }
+  else if (native_mm_level.time == 0)          /* level without time limit */
+  {
+    if (setup.sound_loops)
+      PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
+
+    while(TimePlayed < 999)
+    {
+      if (!setup.sound_loops)
+       PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
+      if (TimePlayed < 999 && !(TimePlayed % 10))
+       RaiseScore(native_mm_level.score[SC_ZEITBONUS]);
+      if (TimePlayed < 900 && !(TimePlayed % 10))
+       TimePlayed += 10;
+      else
+       TimePlayed++;
+
+      /*
+      DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
+      */
+
+      BackToFront();
+      Delay(10);
+    }
+
+    if (setup.sound_loops)
+      StopSound(SND_SIRR);
+  }
+
+#if 0
+  FadeSounds();
+#endif
+
+  CloseDoor(DOOR_CLOSE_1);
+
+  Request("Level solved !", REQ_CONFIRM);
+
+  if (level_nr == leveldir_current->handicap_level)
+  {
+    leveldir_current->handicap_level++;
+    SaveLevelSetup_SeriesInfo();
+  }
+
+  if (level_editor_test_game)
+    game_mm.score = -1;                /* no highscore when playing from editor */
+  else if (level_nr < leveldir_current->last_level)
+    raise_level = TRUE;                /* advance to next level */
+
+  if ((hi_pos = NewHiScore()) >= 0)
+  {
+    game_status = HALLOFFAME;
+    // DrawHallOfFame(hi_pos);
+    if (raise_level)
+      level_nr++;
+  }
+  else
+  {
+    game_status = MAINMENU;
+    if (raise_level)
+      level_nr++;
+    // DrawMainMenu();
+  }
+
+  BackToFront();
+}
+
+int NewHiScore()
+{
+  int k, l;
+  int position = -1;
+
+  // LoadScore(level_nr);
+
+  if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
+      game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
+    return -1;
+
+  for (k=0; k<MAX_SCORE_ENTRIES; k++)
+  {
+    if (game_mm.score > highscore[k].Score)
+    {
+      /* player has made it to the hall of fame */
+
+      if (k < MAX_SCORE_ENTRIES - 1)
+      {
+       int m = MAX_SCORE_ENTRIES - 1;
+
+#ifdef ONE_PER_NAME
+       for (l=k; l<MAX_SCORE_ENTRIES; l++)
+         if (!strcmp(setup.player_name, highscore[l].Name))
+           m = l;
+       if (m == k)     /* player's new highscore overwrites his old one */
+         goto put_into_list;
+#endif
+
+       for (l=m; l>k; l--)
+       {
+         strcpy(highscore[l].Name, highscore[l - 1].Name);
+         highscore[l].Score = highscore[l - 1].Score;
+       }
+      }
+
+#ifdef ONE_PER_NAME
+      put_into_list:
+#endif
+      strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
+      highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
+      highscore[k].Score = game_mm.score;
+      position = k;
+      break;
+    }
+
+#ifdef ONE_PER_NAME
+    else if (!strncmp(setup.player_name, highscore[k].Name,
+                     MAX_PLAYER_NAME_LEN))
+      break;   /* player already there with a higher score */
+#endif
+
+  }
+
+  // if (position >= 0)
+  //   SaveScore(level_nr);
+
+  return position;
+}
+
+void InitMovingField(int x, int y, int direction)
+{
+  int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
+  int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
+
+  MovDir[x][y] = direction;
+  MovDir[newx][newy] = direction;
+  if (Feld[newx][newy] == EL_EMPTY)
+    Feld[newx][newy] = EL_BLOCKED;
+}
+
+void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
+{
+  int direction = MovDir[x][y];
+  int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
+  int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
+
+  *goes_to_x = newx;
+  *goes_to_y = newy;
+}
+
+void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
+{
+  int oldx = x, oldy = y;
+  int direction = MovDir[x][y];
+
+  if (direction == MV_LEFT)
+    oldx++;
+  else if (direction == MV_RIGHT)
+    oldx--;
+  else if (direction == MV_UP)
+    oldy++;
+  else if (direction == MV_DOWN)
+    oldy--;
+
+  *comes_from_x = oldx;
+  *comes_from_y = oldy;
+}
+
+int MovingOrBlocked2Element(int x, int y)
+{
+  int element = Feld[x][y];
+
+  if (element == EL_BLOCKED)
+  {
+    int oldx, oldy;
+
+    Blocked2Moving(x, y, &oldx, &oldy);
+    return Feld[oldx][oldy];
+  }
+  else
+    return element;
+}
+
+#if 0
+static void RemoveField(int x, int y)
+{
+  Feld[x][y] = EL_EMPTY;
+  MovPos[x][y] = 0;
+  MovDir[x][y] = 0;
+  MovDelay[x][y] = 0;
+}
+#endif
+
+void RemoveMovingField(int x, int y)
+{
+  int oldx = x, oldy = y, newx = x, newy = y;
+
+  if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
+    return;
+
+  if (IS_MOVING(x, y))
+  {
+    Moving2Blocked(x, y, &newx, &newy);
+    if (Feld[newx][newy] != EL_BLOCKED)
+      return;
+  }
+  else if (Feld[x][y] == EL_BLOCKED)
+  {
+    Blocked2Moving(x, y, &oldx, &oldy);
+    if (!IS_MOVING(oldx, oldy))
+      return;
+  }
+
+  Feld[oldx][oldy] = EL_EMPTY;
+  Feld[newx][newy] = EL_EMPTY;
+  MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
+  MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
+
+  DrawLevelField_MM(oldx, oldy);
+  DrawLevelField_MM(newx, newy);
+}
+
+void PlaySoundLevel(int x, int y, int sound_nr)
+{
+  int sx = SCREENX(x), sy = SCREENY(y);
+  int volume, stereo;
+  int silence_distance = 8;
+
+  if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
+      (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
+    return;
+
+  if (!IN_LEV_FIELD(x, y) ||
+      sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
+      sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
+    return;
+
+  volume = SOUND_MAX_VOLUME;
+
+#ifndef MSDOS
+  stereo = (sx - SCR_FIELDX/2) * 12;
+#else
+  stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
+  if (stereo > SOUND_MAX_RIGHT)
+    stereo = SOUND_MAX_RIGHT;
+  if (stereo < SOUND_MAX_LEFT)
+    stereo = SOUND_MAX_LEFT;
+#endif
+
+  if (!IN_SCR_FIELD(sx, sy))
+  {
+    int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
+    int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
+
+    volume -= volume * (dx > dy ? dx : dy) / silence_distance;
+  }
+
+  PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
+}
+
+void RaiseScore(int value)
+{
+  game_mm.score += value;
+#if 0
+  DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
+          FONT_TEXT_2);
+#endif
+}
+
+void RaiseScoreElement(int element)
+{
+  switch(element)
+  {
+    case EL_PACMAN:
+      RaiseScore(native_mm_level.score[SC_PACMAN]);
+      break;
+    case EL_KEY:
+      RaiseScore(native_mm_level.score[SC_KEY]);
+      break;
+    default:
+      break;
+  }
+}
diff --git a/src/game_mm/mm_game.h b/src/game_mm/mm_game.h
new file mode 100644 (file)
index 0000000..2098506
--- /dev/null
@@ -0,0 +1,119 @@
+// ============================================================================
+// Mirror Magic -- McDuffin's Revenge
+// ----------------------------------------------------------------------------
+// (c) 1994-2017 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// mm_game.h
+// ============================================================================
+
+#ifndef MM_GAME_H
+#define MM_GAME_H
+
+#include "main_mm.h"
+
+/* score for elements (also used by editor.c) */
+#define SC_EDELSTEIN           0
+#define SC_DIAMANT             1
+#define SC_KAEFER              2
+#define SC_FLIEGER             3
+#define SC_MAMPFER             4
+#define SC_ROBOT               5
+#define SC_PACMAN              6
+#define SC_KOKOSNUSS           7
+#define SC_DYNAMIT             8
+#define SC_KEY                 9
+#define SC_ZEITBONUS           10
+
+void GetPlayerConfig(void);
+void InitGame(void);
+void InitMovDir(int, int);
+void InitAmoebaNr(int, int);
+void GameWon(void);
+int NewHiScore(void);
+void InitMovingField(int, int, int);
+void Moving2Blocked(int, int, int *, int *);
+void Blocked2Moving(int, int, int *, int *);
+int MovingOrBlocked2Element(int, int);
+void RemoveMovingField(int, int);
+
+
+
+
+
+void Blurb(int, int);
+void Impact(int, int);
+void TurnRound(int, int);
+void StartMoving(int, int);
+void ContinueMoving(int, int);
+int AmoebeNachbarNr(int, int);
+void AmoebeUmwandeln(int, int);
+void AmoebeUmwandelnBD(int, int, int);
+void AmoebeWaechst(int, int);
+void AmoebeAbleger(int, int);
+void Life(int, int);
+void Ablenk(int, int);
+void Blubber(int, int);
+void NussKnacken(int, int);
+void SiebAktivieren(int, int, int);
+void AusgangstuerPruefen(int, int);
+void AusgangstuerOeffnen(int, int);
+void AusgangstuerBlinken(int, int);
+void EdelsteinFunkeln(int, int);
+void MauerWaechst(int, int);
+void MauerAbleger(int, int);
+
+
+
+boolean MoveFigureOneStep(struct PlayerInfo *, int, int, int, int);
+boolean MoveFigure(struct PlayerInfo *, int, int);
+void ScrollFigure(struct PlayerInfo *, int);
+void ScrollScreen(struct PlayerInfo *, int);
+
+void TestIfGoodThingHitsBadThing(int, int);
+void TestIfBadThingHitsGoodThing(int, int);
+void TestIfHeroHitsBadThing(int, int);
+void TestIfBadThingHitsHero(int, int);
+void TestIfFriendHitsBadThing(int, int);
+void TestIfBadThingHitsFriend(int, int);
+void TestIfBadThingHitsOtherBadThing(int, int);
+void KillHero(struct PlayerInfo *);
+void BuryHero(struct PlayerInfo *);
+void RemoveHero(struct PlayerInfo *);
+int DigField(struct PlayerInfo *, int, int, int, int, int);
+boolean SnapField(struct PlayerInfo *, int, int);
+boolean PlaceBomb(struct PlayerInfo *);
+void PlaySoundLevel(int, int, int);
+void RaiseScore(int);
+void RaiseScoreElement(int);
+
+void CreateGameButtons();
+void UnmapGameButtons();
+
+
+void InitGame(void);
+void AddLaserEdge(int, int);
+void AddDamagedField(int, int);
+void ScanLaser(void);
+void DrawLaser(int, int);
+boolean HitElement(int, int);
+boolean HitOnlyAnEdge(int, int);
+boolean HitPolarizer(int, int);
+boolean HitBlock(int, int);
+boolean HitLaserSource(int, int);
+boolean HitLaserDestination(int, int);
+boolean HitReflectingWalls(int, int);
+boolean HitAbsorbingWalls(int, int);
+void Bang(int, int);
+void ClickElement(int, int, int);
+void RotateMirror(int, int, int);
+boolean ObjHit(int, int, int);
+void DeletePacMan(int, int);
+
+void ColorCycling(void);
+void GameActions(void);
+void MovePacMen(void);
+
+#endif
diff --git a/src/game_mm/mm_init.c b/src/game_mm/mm_init.c
new file mode 100644 (file)
index 0000000..eec9ee1
--- /dev/null
@@ -0,0 +1,17 @@
+// ============================================================================
+// Mirror Magic -- McDuffin's Revenge
+// ----------------------------------------------------------------------------
+// (c) 1994-2017 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// mm_init.c
+// ============================================================================
+
+#include "main_mm.h"
+
+unsigned int InitEngineRandom_MM(int seed)
+{
+  return InitEngineRandom(seed);
+}
diff --git a/src/game_mm/mm_main.c b/src/game_mm/mm_main.c
new file mode 100644 (file)
index 0000000..44ca0ad
--- /dev/null
@@ -0,0 +1,520 @@
+// ============================================================================
+// Mirror Magic -- McDuffin's Revenge
+// ----------------------------------------------------------------------------
+// (c) 1994-2017 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// mm_main.c
+// ============================================================================
+
+#include "main_mm.h"
+
+#include "mm_main.h"
+
+
+struct GameInfo_MM game_mm;
+struct LevelInfo_MM native_mm_level;
+
+short          Ur[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+short          Hit[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+short          Box[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+short          Angle[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+
+unsigned int   Elementeigenschaften[MAX_ELEMENTS];
+
+short          LX,LY, XS,YS, ELX,ELY;
+short          CT,Ct;
+
+Pixel          pen_fg, pen_bg, pen_ray, pen_magicolor[2];
+int            color_status;
+
+struct XY      Step[16] =
+{
+  {  1,  0 },
+  {  2, -1 },
+  {  1, -1 },
+  {  1, -2 },
+  {  0, -1 },
+  { -1, -2 },
+  { -1, -1 },
+  { -2, -1 },
+  { -1,  0 },
+  { -2,  1 },
+  { -1,  1 },
+  { -1,  2 },
+  {  0,  1 },
+  {  1,  2 },
+  {  1,  1 },
+  {  2,  1 }
+};
+
+/* "Sign" has the following structure:
+
+   each 4-bit-value represents the values d*8 + c*4 + b*2 + a*1
+   at the pixel positions
+
+   a b     1 2
+   c d     4 8
+
+   so the value "0xA" (=> (d=1)*8 + (c=0)*4 + (b=1)*2 + (a=0)*1)
+   would result in the pixel pattern
+
+   0 1        _ x
+   0 1    or  _ x
+
+   x x    x x    x x    x x    x x
+   x _    x x    _ _    x x    _ x
+      6                      2
+   x x     5      4      3     x x
+   x x  7                   1  x x
+
+   x _                         _ x
+   x _  8                   0  _ x
+
+   x x  9                  15  x x
+   x x     11    12     13     x x
+      10                     14
+   x _    x x    _ _    x x    _ x
+   x x    x x    x x    x x    x x
+
+ */
+
+short Sign[16] =
+{
+  0xA,0xF,0xB,0xF,
+  0x3,0xF,0x7,0xF,
+  0x5,0xF,0xD,0xF,
+  0xC,0xF,0xE,0xF
+};
+
+char *element_info_mm[] =
+{
+  "empty space",                               /* 0 */
+  "mirror (0\xb0)",
+  "mirror (11.25\xb0)",
+  "mirror (22.5\xb0)",
+  "mirror (33.75\xb0)",
+  "mirror (45\xb0)",
+  "mirror (56.25\xb0)",
+  "mirror (67.5\xb0)",
+  "mirror (78.75\xb0)",
+  "mirror (90\xb0)",
+  "mirror (101.25\xb0)",                       /* 10 */
+  "mirror (112.5\xb0)",
+  "mirror (123.75\xb0)",
+  "mirror (135\xb0)",
+  "mirror (146.25\xb0)",
+  "mirror (157.5\xb0)",
+  "mirror (168.75\xb0)",
+  "fixed steel polarisator (0\xb0)",
+  "fixed steel polarisator (90\xb0)",
+  "fixed steel polarisator (45\xb0)",
+  "fixed steel polarisator (135\xb0)",         /* 20 */
+  "Gregor McDuffin (looking right)",
+  "Gregor McDuffin (looking up)",
+  "Gregor McDuffin (looking left)",
+  "Gregor McDuffin (looking down)",
+  "closed exit",
+  "opening exit",
+  "opening exit",
+  "open exit",
+  "magic kettle",
+  "bomb",                                      /* 30 */
+  "prism",
+  "steel wall",
+  "steel wall",
+  "steel wall",
+  "steel wall",
+  "steel wall",
+  "steel wall",
+  "steel wall",
+  "steel wall",
+  "steel wall",                                        /* 40 */
+  "steel wall",
+  "steel wall",
+  "steel wall",
+  "steel wall",
+  "steel wall",
+  "steel wall",
+  "steel wall",
+  "wooden wall",
+  "wooden wall",
+  "wooden wall",                               /* 50 */
+  "wooden wall",
+  "wooden wall",
+  "wooden wall",
+  "wooden wall",
+  "wooden wall",
+  "wooden wall",
+  "wooden wall",
+  "wooden wall",
+  "wooden wall",
+  "wooden wall",                               /* 60 */
+  "wooden wall",
+  "wooden wall",
+  "wooden wall",
+  "ice wall",
+  "ice wall",
+  "ice wall",
+  "ice wall",
+  "ice wall",
+  "ice wall",
+  "ice wall",                                  /* 70 */
+  "ice wall",
+  "ice wall",
+  "ice wall",
+  "ice wall",
+  "ice wall",
+  "ice wall",
+  "ice wall",
+  "ice wall",
+  "ice wall",
+  "amoeba wall",                               /* 80 */
+  "amoeba wall",
+  "amoeba wall",
+  "amoeba wall",
+  "amoeba wall",
+  "amoeba wall",
+  "amoeba wall",
+  "amoeba wall",
+  "amoeba wall",
+  "amoeba wall",
+  "amoeba wall",                               /* 90 */
+  "amoeba wall",
+  "amoeba wall",
+  "amoeba wall",
+  "amoeba wall",
+  "amoeba wall",
+  "wooden block",
+  "gray ball",
+  "beamer (0\xb0)",
+  "beamer (22.5\xb0)",
+  "beamer (45\xb0)",                           /* 100 */
+  "beamer (67.5\xb0)",
+  "beamer (90\xb0)",
+  "beamer (112.5\xb0)",
+  "beamer (135\xb0)",
+  "beamer (157.5\xb0)",
+  "beamer (180\xb0)",
+  "beamer (202.5\xb0)",
+  "beamer (225\xb0)",
+  "beamer (247.5\xb0)",
+  "beamer (270\xb0)",                          /* 110 */
+  "beamer (292.5\xb0)",
+  "beamer (315\xb0)",
+  "beamer (337.5\xb0)",
+  "fuse",
+  "pac man (starts moving right)",
+  "pac man (starts moving up)",
+  "pac man (starts moving left)",
+  "pac man (starts moving down)",
+  "polarisator (0\xb0)",
+  "polarisator (11.25\xb0)",                   /* 120 */
+  "polarisator (22.5\xb0)",
+  "polarisator (33.75\xb0)",
+  "polarisator (45\xb0)",
+  "polarisator (56.25\xb0)",
+  "polarisator (67.5\xb0)",
+  "polarisator (78.75\xb0)",
+  "polarisator (90\xb0)",
+  "polarisator (101.25\xb0)",
+  "polarisator (112.5\xb0)",
+  "polarisator (123.75\xb0)",                  /* 130 */
+  "polarisator (135\xb0)",
+  "polarisator (146.25\xb0)",
+  "polarisator (157.5\xb0)",
+  "polarisator (168.75\xb0)",
+  "two-way polarisator (0\xb0)",
+  "two-way polarisator (22.5\xb0)",
+  "two-way polarisator (45\xb0)",
+  "two-way polarisator (67.5\xb0)",
+  "fixed mirror (0\xb0)",
+  "fixed mirror (45\xb0)",                     /* 140 */
+  "fixed mirror (90\xb0)",
+  "fixed mirror (135\xb0)",
+  "reflecting stone lock",
+  "key",
+  "light bulb (dark)",
+  "ligh bulb (glowing)",
+  "bonus ball",
+  "reflecting stone block",
+  "wooden lock",
+  "extra energy ball (full)",                  /* 150 */
+  "fixed wooden polarisator (0\xb0)",
+  "fixed wooden polarisator (90\xb0)",
+  "fixed wooden polarisator (45\xb0)",
+  "fixed wooden polarisator (135\xb0)",
+  "extra energy ball (empty)",
+  "unused",
+  "unused",
+  "unused",
+  "unused",
+  "letter ' '",                                        /* 160 */
+  "letter '!'",
+  "letter '\"'",
+  "letter '#'",
+  "letter '$'",
+  "letter '%'",
+  "letter '&'",
+  "letter '''",
+  "letter '('",
+  "letter ')'",
+  "letter '*'",                                        /* 170 */
+  "letter '+'",
+  "letter ','",
+  "letter '-'",
+  "letter '.'",
+  "letter '/'",
+  "letter '0'",
+  "letter '1'",
+  "letter '2'",
+  "letter '3'",
+  "letter '4'",                                        /* 180 */
+  "letter '5'",
+  "letter '6'",
+  "letter '7'",
+  "letter '8'",
+  "letter '9'",
+  "letter ':'",
+  "letter ';'",
+  "letter '<'",
+  "letter '='",
+  "letter '>'",                                        /* 190 */
+  "letter '?'",
+  "letter '@'",
+  "letter 'A'",
+  "letter 'B'",
+  "letter 'C'",
+  "letter 'D'",
+  "letter 'E'",
+  "letter 'F'",
+  "letter 'G'",
+  "letter 'H'",                                        /* 200 */
+  "letter 'I'",
+  "letter 'J'",
+  "letter 'K'",
+  "letter 'L'",
+  "letter 'M'",
+  "letter 'N'",
+  "letter 'O'",
+  "letter 'P'",
+  "letter 'Q'",
+  "letter 'R'",                                        /* 210 */
+  "letter 'S'",
+  "letter 'T'",
+  "letter 'U'",
+  "letter 'V'",
+  "letter 'W'",
+  "letter 'X'",
+  "letter 'Y'",
+  "letter 'Z'",
+  "letter '\xc4'",
+  "letter '\xd6'",                             /* 220 */
+  "letter '\xdc'",
+  "letter '^'",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",                                 /* 230 */
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "letter ''",
+  "mirror (0\xb0)",                            /* 240 */
+  "mirror (11.25\xb0)",
+  "mirror (22.5\xb0)",
+  "mirror (33.75\xb0)",
+  "mirror (45\xb0)",
+  "mirror (56.25\xb0)",
+  "mirror (67.5\xb0)",
+  "mirror (78.75\xb0)",
+  "mirror (90\xb0)",
+  "mirror (101.25\xb0)",
+  "mirror (112.5\xb0)",                                /* 250 */
+  "mirror (123.75\xb0)",
+  "mirror (135\xb0)",
+  "mirror (146.25\xb0)",
+  "mirror (157.5\xb0)",
+  "mirror (168.75\xb0)",
+  "fixed wooden polarisator (0\xb0)",
+  "fixed wooden polarisator (22.5\xb0)",
+  "fixed wooden polarisator (45\xb0)",
+  "fixed wooden polarisator (67.5\xb0)",
+  "fixed wooden polarisator (90\xb0)",         /* 260 */
+  "fixed wooden polarisator (112.5\xb0)",
+  "fixed wooden polarisator (135\xb0)",
+  "fixed wooden polarisator (157.5\xb0)",
+  "fixed steel polarisator (0\xb0)",
+  "fixed steel polarisator (22.5\xb0)",
+  "fixed steel polarisator (45\xb0)",
+  "fixed steel polarisator (67.5\xb0)",
+  "fixed steel polarisator (90\xb0)",
+  "fixed steel polarisator (112.5\xb0)",
+  "fixed steel polarisator (135\xb0)",         /* 270 */
+  "fixed steel polarisator (157.5\xb0)",
+  "deflektor style wooden wall",
+  "deflektor style wooden wall",
+  "deflektor style wooden wall",
+  "deflektor style wooden wall",
+  "deflektor style wooden wall",
+  "deflektor style wooden wall",
+  "deflektor style wooden wall",
+  "deflektor style wooden wall",
+  "deflektor style wooden wall",               /* 280 */
+  "deflektor style wooden wall",
+  "deflektor style wooden wall",
+  "deflektor style wooden wall",
+  "deflektor style wooden wall",
+  "deflektor style wooden wall",
+  "deflektor style wooden wall",
+  "deflektor style wooden wall",
+  "deflektor style steel wall",
+  "deflektor style steel wall",
+  "deflektor style steel wall",                        /* 290 */
+  "deflektor style steel wall",
+  "deflektor style steel wall",
+  "deflektor style steel wall",
+  "deflektor style steel wall",
+  "deflektor style steel wall",
+  "deflektor style steel wall",
+  "deflektor style steel wall",
+  "deflektor style steel wall",
+  "deflektor style steel wall",
+  "deflektor style steel wall",                        /* 300 */
+  "deflektor style steel wall",
+  "deflektor style steel wall",
+  "deflektor style steel wall",
+  "empty space",
+  "cell",
+  "mine",
+  "refractor",
+  "laser cannon (shooting right)",
+  "laser cannon (shooting up)",
+  "laser cannon (shooting left)",              /* 310 */
+  "laser cannon (shooting down)",
+  "laser receiver (directed right)",
+  "laser receiver (directed up)",
+  "laser receiver (directed left)",
+  "laser receiver (directed down)",
+  "fibre optic (1a)",
+  "fibre optic (1b)",
+  "fibre optic (2a)",
+  "fibre optic (2b)",
+  "fibre optic (3a)",                          /* 320 */
+  "fibre optic (3b)",
+  "fibre optic (4a)",
+  "fibre optic (4b)",
+  "rotating mirror (0\xb0)",
+  "rotating mirror (11.25\xb0)",
+  "rotating mirror (22.5\xb0)",
+  "rotating mirror (33.75\xb0)",
+  "rotating mirror (45\xb0)",
+  "rotating mirror (56.25\xb0)",
+  "rotating mirror (67.5\xb0)",                        /* 330 */
+  "rotating mirror (78.75\xb0)",
+  "rotating mirror (90\xb0)",
+  "rotating mirror (101.25\xb0)",
+  "rotating mirror (112.5\xb0)",
+  "rotating mirror (123.75\xb0)",
+  "rotating mirror (135\xb0)",
+  "rotating mirror (146.25\xb0)",
+  "rotating mirror (157.5\xb0)",
+  "rotating mirror (168.75\xb0)",
+  "rotating wooden polarisator (0\xb0)",       /* 340 */
+  "rotating wooden polarisator (22.5\xb0)",
+  "rotating wooden polarisator (45\xb0)",
+  "rotating wooden polarisator (67.5\xb0)",
+  "rotating wooden polarisator (90\xb0)",
+  "rotating wooden polarisator (112.5\xb0)",
+  "rotating wooden polarisator (135\xb0)",
+  "rotating wooden polarisator (157.5\xb0)",
+  "rotating steel polarisator (0\xb0)",
+  "rotating steel polarisator (22.5\xb0)",
+  "rotating steel polarisator (45\xb0)",       /* 350 */
+  "rotating steel polarisator (67.5\xb0)",
+  "rotating steel polarisator (90\xb0)",
+  "rotating steel polarisator (112.5\xb0)",
+  "rotating steel polarisator (135\xb0)",
+  "rotating steel polarisator (157.5\xb0)",
+  "red beamer (0\xb0)",
+  "red beamer (22.5\xb0)",
+  "red beamer (45\xb0)",
+  "red beamer (67.5\xb0)",
+  "red beamer (90\xb0)",                       /* 360 */
+  "red beamer (112.5\xb0)",
+  "red beamer (135\xb0)",
+  "red beamer (157.5\xb0)",
+  "red beamer (180\xb0)",
+  "red beamer (202.5\xb0)",
+  "red beamer (225\xb0)",
+  "red beamer (247.5\xb0)",
+  "red beamer (270\xb0)",
+  "red beamer (292.5\xb0)",
+  "red beamer (315\xb0)",                      /* 370 */
+  "red beamer (337.5\xb0)",
+  "yellow beamer (0\xb0)",
+  "yellow beamer (22.5\xb0)",
+  "yellow beamer (45\xb0)",
+  "yellow beamer (67.5\xb0)",
+  "yellow beamer (90\xb0)",
+  "yellow beamer (112.5\xb0)",
+  "yellow beamer (135\xb0)",
+  "yellow beamer (157.5\xb0)",
+  "yellow beamer (180\xb0)",                   /* 380 */
+  "yellow beamer (202.5\xb0)",
+  "yellow beamer (225\xb0)",
+  "yellow beamer (247.5\xb0)",
+  "yellow beamer (270\xb0)",
+  "yellow beamer (292.5\xb0)",
+  "yellow beamer (315\xb0)",
+  "yellow beamer (337.5\xb0)",
+  "green beamer (0\xb0)",
+  "green beamer (22.5\xb0)",
+  "green beamer (45\xb0)",                     /* 390 */
+  "green beamer (67.5\xb0)",
+  "green beamer (90\xb0)",
+  "green beamer (112.5\xb0)",
+  "green beamer (135\xb0)",
+  "green beamer (157.5\xb0)",
+  "green beamer (180\xb0)",
+  "green beamer (202.5\xb0)",
+  "green beamer (225\xb0)",
+  "green beamer (247.5\xb0)",
+  "green beamer (270\xb0)",                    /* 400 */
+  "green beamer (292.5\xb0)",
+  "green beamer (315\xb0)",
+  "green beamer (337.5\xb0)",
+  "blue beamer (0\xb0)",
+  "blue beamer (22.5\xb0)",
+  "blue beamer (45\xb0)",
+  "blue beamer (67.5\xb0)",
+  "blue beamer (90\xb0)",
+  "blue beamer (112.5\xb0)",
+  "blue beamer (135\xb0)",                     /* 410 */
+  "blue beamer (157.5\xb0)",
+  "blue beamer (180\xb0)",
+  "blue beamer (202.5\xb0)",
+  "blue beamer (225\xb0)",
+  "blue beamer (247.5\xb0)",
+  "blue beamer (270\xb0)",
+  "blue beamer (292.5\xb0)",
+  "blue beamer (315\xb0)",
+  "blue beamer (337.5\xb0)",
+  "unknown",                                   /* 420 */
+
+  /*
+  "-------------------------------",
+  */
+};
+int num_element_info_mm = sizeof(element_info_mm)/sizeof(char *);
diff --git a/src/game_mm/mm_main.h b/src/game_mm/mm_main.h
new file mode 100644 (file)
index 0000000..98950ed
--- /dev/null
@@ -0,0 +1,1108 @@
+// ============================================================================
+// Mirror Magic -- McDuffin's Revenge
+// ----------------------------------------------------------------------------
+// (c) 1994-2017 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// mm_main.h
+// ============================================================================
+
+#ifndef MM_MAIN_H
+#define MM_MAIN_H
+
+#define STD_LEV_FIELDX  MM_STD_PLAYFIELD_WIDTH
+#define STD_LEV_FIELDY  MM_STD_PLAYFIELD_HEIGHT
+#define MAX_LEV_FIELDX MM_MAX_PLAYFIELD_WIDTH
+#define MAX_LEV_FIELDY MM_MAX_PLAYFIELD_HEIGHT
+
+#define SCREENX(a)      (a)
+#define SCREENY(a)      (a)
+#define LEVELX(a)       (a)
+#define LEVELY(a)       (a)
+#define IN_VIS_FIELD(x,y) ((x)>=0 && (x)<SCR_FIELDX && (y)>=0 &&(y)<SCR_FIELDY)
+#define IN_SCR_FIELD(x,y) ((x)>=BX1 && (x)<=BX2 && (y)>=BY1 &&(y)<=BY2)
+#define IN_LEV_FIELD(x,y) ((x)>=0 && (x)<lev_fieldx && (y)>=0 &&(y)<lev_fieldy)
+#define IN_PIX_FIELD(x,y) ((x)>=0 && (x)<SXSIZE && (y)>=0 && (y)<SYSIZE)
+
+/* values for 'Elementeigenschaften' */
+#define EP_BIT_GRID            (1 << 0)
+#define EP_BIT_MCDUFFIN                (1 << 1)
+#define EP_BIT_RECTANGLE       (1 << 2)
+#define EP_BIT_MIRROR          (1 << 3)
+#define EP_BIT_MIRROR_FIXED    (1 << 4)
+#define EP_BIT_POLAR           (1 << 5)
+#define EP_BIT_POLAR_CROSS     (1 << 6)
+#define EP_BIT_BEAMER          (1 << 7)
+#define EP_BIT_CHAR            (1 << 8)
+#define EP_BIT_REFLECTING      (1 << 9)
+#define EP_BIT_ABSORBING       (1 << 10)
+#define EP_BIT_INACTIVE                (1 << 11)
+#define EP_BIT_WALL            (1 << 12)
+#define EP_BIT_PACMAN          (1 << 13)
+
+#define IS_GRID(e)             (Elementeigenschaften[e] & EP_BIT_GRID)
+#define IS_MCDUFFIN(e)         (Elementeigenschaften[e] & EP_BIT_MCDUFFIN)
+#define IS_RECTANGLE(e)                (Elementeigenschaften[e] & EP_BIT_RECTANGLE)
+#define IS_MIRROR(e)           (Elementeigenschaften[e] & EP_BIT_MIRROR)
+#define IS_MIRROR_FIXED(e)     (Elementeigenschaften[e] & EP_BIT_MIRROR_FIXED)
+#define IS_POLAR(e)            (Elementeigenschaften[e] & EP_BIT_POLAR)
+#define IS_POLAR_CROSS(e)      (Elementeigenschaften[e] & EP_BIT_POLAR_CROSS)
+#define IS_BEAMER_OLD(e)       (Elementeigenschaften[e] & EP_BIT_BEAMER)
+#define IS_CHAR(e)             (Elementeigenschaften[e] & EP_BIT_CHAR)
+#define IS_REFLECTING(e)       (Elementeigenschaften[e] & EP_BIT_REFLECTING)
+#define IS_ABSORBING(e)                (Elementeigenschaften[e] & EP_BIT_ABSORBING)
+#define IS_INACTIVE(e)         (Elementeigenschaften[e] & EP_BIT_INACTIVE)
+#define IS_MM_WALL(e)          (Elementeigenschaften[e] & EP_BIT_WALL)
+#define IS_PACMAN(e)           (Elementeigenschaften[e] & EP_BIT_PACMAN)
+
+#define IS_WALL_STEEL(e)       ((e) >= EL_WALL_STEEL_START &&          \
+                                (e) <= EL_WALL_STEEL_END)
+#define IS_WALL_WOOD(e)                ((e) >= EL_WALL_WOOD_START &&           \
+                                (e) <= EL_WALL_WOOD_END)
+#define IS_WALL_ICE(e)         ((e) >= EL_WALL_ICE_START &&            \
+                                (e) <= EL_WALL_ICE_END)
+#define IS_WALL_AMOEBA(e)      ((e) >= EL_WALL_AMOEBA_START &&         \
+                                (e) <= EL_WALL_AMOEBA_END)
+#define IS_DF_WALL_STEEL(e)    ((e) >= EL_DF_WALL_STEEL_START &&       \
+                                (e) <= EL_DF_WALL_STEEL_END)
+#define IS_DF_WALL_WOOD(e)     ((e) >= EL_DF_WALL_WOOD_START &&        \
+                                (e) <= EL_DF_WALL_WOOD_END)
+#define IS_DF_WALL(e)          ((e) >= EL_DF_WALL_START &&             \
+                                (e) <= EL_DF_WALL_END)
+#define IS_WALL(e)             (IS_MM_WALL(e) || IS_DF_WALL(e))
+#define IS_WALL_CHANGING(e)    ((e) >= EL_WALL_CHANGING_START &&       \
+                                (e) <= EL_WALL_CHANGING_END)
+#define IS_GRID_STEEL(e)       ((e) >= EL_GRID_STEEL_START &&          \
+                                (e) <= EL_GRID_STEEL_END)
+#define IS_GRID_WOOD(e)                ((e) >= EL_GRID_WOOD_START &&           \
+                                (e) <= EL_GRID_WOOD_END)
+#define IS_GRID_WOOD_FIXED(e)  ((e) >= EL_GRID_WOOD_FIXED_START &&     \
+                                (e) <= EL_GRID_WOOD_FIXED_END)
+#define IS_GRID_STEEL_FIXED(e) ((e) >= EL_GRID_STEEL_FIXED_START &&    \
+                                (e) <= EL_GRID_STEEL_FIXED_END)
+#define IS_GRID_WOOD_AUTO(e)   ((e) >= EL_GRID_WOOD_AUTO_START &&      \
+                                (e) <= EL_GRID_WOOD_AUTO_END)
+#define IS_GRID_STEEL_AUTO(e)  ((e) >= EL_GRID_STEEL_AUTO_START &&     \
+                                (e) <= EL_GRID_STEEL_AUTO_END)
+#define IS_DF_GRID(e)          (IS_GRID_WOOD_FIXED(e) ||               \
+                                IS_GRID_STEEL_FIXED(e) ||              \
+                                IS_GRID_WOOD_AUTO(e) ||                \
+                                IS_GRID_STEEL_AUTO(e))
+#define IS_DF_MIRROR(e)                ((e) >= EL_DF_MIRROR_START &&           \
+                                (e) <= EL_DF_MIRROR_END)
+#define IS_DF_MIRROR_AUTO(e)   ((e) >= EL_DF_MIRROR_AUTO_START &&      \
+                                (e) <= EL_DF_MIRROR_AUTO_END)
+#define IS_LASER(e)            ((e) >= EL_LASER_START &&               \
+                                (e) <= EL_LASER_END)
+#define IS_RECEIVER(e)         ((e) >= EL_RECEIVER_START &&            \
+                                (e) <= EL_RECEIVER_END)
+#define IS_FIBRE_OPTIC(e)      ((e) >= EL_FIBRE_OPTIC_START &&         \
+                                (e) <= EL_FIBRE_OPTIC_END)
+#define FIBRE_OPTIC_NR(e)      (((e) - EL_FIBRE_OPTIC_START) / 2)
+#define IS_BEAMER(e)           ((e) >= EL_BEAMER_RED_START &&          \
+                                (e) <= EL_BEAMER_BLUE_END)
+#define BEAMER_CLASSIC_NR(e)   (((e) - EL_BEAMER_RED_START) / 16)
+#define BEAMER_NR(e)           (IS_BEAMER(e) ? BEAMER_CLASSIC_NR(e) :  \
+                                IS_FIBRE_OPTIC(e) ? (FIBRE_OPTIC_NR(e)+4) : 0)
+#define IS_EXPLODING(e)                ((e) == EL_EXPLODING_OPAQUE ||          \
+                                (e) == EL_EXPLODING_TRANSP)
+
+#define IS_EATABLE4PACMAN(e)   ((e) == EL_EMPTY ||                     \
+                                (e) == EL_KETTLE ||                    \
+                                (e) == EL_CELL ||                      \
+                                (e) == EL_BOMB ||                      \
+                                IS_WALL_AMOEBA(e))
+
+#define CAN_MOVE(e)            ((e) == EL_PACMAN)
+#define IS_FREE(x,y)            (Feld[x][y] == EL_EMPTY)
+
+#define IS_MOVING(x,y)          (MovPos[x][y] != 0)
+#define IS_BLOCKED(x,y)         (Feld[x][y] == EL_BLOCKED)
+#define IS_DRAWABLE(e)          ((e) < EL_BLOCKED)
+#define IS_NOT_DRAWABLE(e)      ((e) >= EL_BLOCKED)
+
+#define PLAYERINFO(x,y)         (&stored_player[StorePlayer[x][y]-EL_SPIELER1])
+
+#define WALL_BASE(e)           ((e) & 0xfff0)
+#define WALL_BITS(e)           ((e) & 0x000f)
+
+/* Bitmaps with graphic file */
+#define PIX_BACK               0
+#define PIX_DOOR               1
+#define PIX_TOONS              2
+#define PIX_DF                 3
+#define        PIX_BIGFONT             4
+#define PIX_SMALLFONT          5
+#define PIX_MEDIUMFONT         6
+/* Bitmaps without graphic file */
+#define PIX_DB_DOOR            7
+
+#define NUM_PICTURES           7
+#define NUM_BITMAPS            8
+
+/* boundaries of arrays etc. */
+#define MAX_PLAYER_NAME_LEN    10
+#define MAX_LEVEL_NAME_LEN     32
+#define MAX_LEVEL_AUTHOR_LEN   32
+#define MAX_SCORE_ENTRIES      100
+#define MAX_ELEMENTS           700             /* 500 static + 200 runtime */
+
+#define LEVEL_SCORE_ELEMENTS   16      /* level elements with score */
+
+#define MICROLEVEL_SCROLL_DELAY        50      /* delay for scrolling micro level */
+#define MICROLEVEL_LABEL_DELAY 250     /* delay for micro level label */
+
+struct HiScore
+{
+  char Name[MAX_PLAYER_NAME_LEN + 1];
+  int Score;
+};
+
+struct EditorInfo
+{
+  boolean draw_walls_masked;
+};
+
+extern Bitmap         *pix[];
+extern DrawBuffer     *fieldbuffer;
+extern DrawBuffer     *drawto_field;
+
+extern int             joystick_device;
+extern char           *joystick_device_name[];
+
+extern int             game_status;
+extern boolean         level_editor_test_game;
+extern boolean         network_playing;
+
+extern int             key_joystick_mapping;
+extern int             global_joystick_status, joystick_status;
+extern int             sound_status;
+extern boolean         sound_loops_allowed;
+
+extern boolean         redraw[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern int             redraw_x1, redraw_y1;
+
+extern short           Feld[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           Ur[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           Hit[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           Box[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           Angle[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+
+extern short           MovPos[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           MovDir[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           MovDelay[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           Store[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           Store2[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           StorePlayer[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           Frame[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern boolean         Stop[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           JustStopped[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           AmoebaNr[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
+extern short           AmoebaCnt[MAX_NUM_AMOEBA], AmoebaCnt2[MAX_NUM_AMOEBA];
+extern unsigned int    Elementeigenschaften[MAX_ELEMENTS];
+
+extern int             level_nr;
+extern int             lev_fieldx,lev_fieldy, scroll_x,scroll_y;
+
+extern int             FX,FY, ScrollStepSize;
+extern int             ScreenMovDir, ScreenMovPos, ScreenGfxPos;
+extern int             GameFrameDelay;
+extern int             FfwdFrameDelay;
+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             TimeFrames, TimePlayed, TimeLeft;
+extern boolean         SiebAktiv;
+extern int             SiebCount;
+
+extern struct LevelInfo_MM     native_mm_level;
+extern struct HiScore          highscore[];
+extern struct TapeInfo         tape;
+extern struct SetupInfo                setup;
+extern struct GameInfo_MM      game_mm;
+extern struct LaserInfo                laser;
+extern struct EditorInfo       editor;
+extern struct GlobalInfo       global;
+
+extern short           LX,LY, XS,YS, ELX,ELY;
+extern short           CT,Ct;
+
+extern Pixel           pen_fg, pen_bg, pen_ray, pen_magicolor[2];
+extern int             color_status;
+
+extern struct XY       Step[];
+extern short           Sign[16];
+
+extern char            *sound_name[];
+extern int             background_loop[];
+extern int             num_bg_loops;
+extern char            *element_info[];
+extern int             num_element_info;
+
+/* often used screen positions */
+#define SX                     8
+#define SY                     8
+#define REAL_SX                        (SX - 2)
+#define REAL_SY                        (SY - 2)
+#define DX                     534
+#define DY                     60
+#define EX                     DX
+#define EY                     (DY + 178)
+#define TILEX                  TILESIZE
+#define TILEY                  TILESIZE
+#define MINI_TILEX             (TILEX / 2)
+#define MINI_TILEY             (TILEY / 2)
+#define MICRO_TILEX            (TILEX / 4)
+#define MICRO_TILEY            (TILEY / 4)
+#define MICRO_WALLX            (TILEX / 8)
+#define MICRO_WALLY            (TILEY / 8)
+#define MIDPOSX                        (SCR_FIELDX / 2)
+#define MIDPOSY                        (SCR_FIELDY / 2)
+#define DXSIZE                 100
+#define DYSIZE                 280
+#define VXSIZE                 DXSIZE
+#define VYSIZE                 100
+#define EXSIZE                 DXSIZE
+#define EYSIZE                 (VXSIZE + 44)
+#define FULL_SXSIZE            (2 + SXSIZE + 2)
+#define FULL_SYSIZE            (2 + SYSIZE + 2)
+#define MICROLEV_XPOS          (SX + 12 * TILEX)
+#define MICROLEV_YPOS          (SY +  6 * TILEY)
+#define MICROLEV_XSIZE         (STD_LEV_FIELDX * MICRO_TILEX)
+#define MICROLEV_YSIZE         (STD_LEV_FIELDY * MICRO_TILEY)
+#define MICROLABEL_XPOS                (SY)
+#define MICROLABEL_YPOS                (SY + 352)
+#define MICROLABEL_XSIZE       (SXSIZE)
+#define MICROLABEL_YSIZE       (FONT4_YSIZE)
+
+#define GFX_STARTX             SX
+#define GFX_STARTY             SY
+#define MINI_GFX_STARTX                (SX + 8 * TILEX)
+#define MINI_GFX_STARTY                (SY + 6 * TILEY)
+#define MICRO_GFX_STARTX       (MINI_GFX_STARTX + 8 * MINI_TILEX)
+#define MICRO_GFX_STARTY       (MINI_GFX_STARTY + 6 * MINI_TILEY)
+#define GFX_PER_LINE           16
+#define MINI_GFX_PER_LINE      GFX_PER_LINE
+#define MICRO_GFX_PER_LINE     GFX_PER_LINE
+
+#define MINI_DF_STARTX         (8 * TILEX)
+#define MINI_DF_STARTY         (8 * TILEY)
+#define MICRO_DF_STARTX                (MINI_DF_STARTX + 8 * MINI_TILEX)
+#define MICRO_DF_STARTY                (MINI_DF_STARTY + 8 * MINI_TILEY)
+#define DF_PER_LINE            16
+#define MINI_DF_PER_LINE       DF_PER_LINE
+#define MICRO_DF_PER_LINE      DF_PER_LINE
+
+#define MICRO_FONT_STARTX      (MICRO_DF_STARTX + 8 * MICRO_TILEX)
+#define MICRO_FONT_STARTY      (MICRO_DF_STARTY + 8 * MICRO_TILEY)
+#define MICRO_FONT_PER_LINE    8
+
+/* wall positions (that can be OR'ed together) */
+#define WALL_TOPLEFT           1
+#define WALL_TOPRIGHT          2
+#define WALL_BOTTOMLEFT                4
+#define WALL_BOTTOMRIGHT       8
+#define WALL_LEFT              (WALL_TOPLEFT    | WALL_BOTTOMLEFT)
+#define WALL_RIGHT             (WALL_TOPRIGHT   | WALL_BOTTOMRIGHT)
+#define WALL_TOP               (WALL_TOPLEFT    | WALL_TOPRIGHT)
+#define WALL_BOTTOM            (WALL_BOTTOMLEFT | WALL_BOTTOMRIGHT)
+
+/* game elements:
+**       0 - 499: real elements, stored in level file
+**      500 - 699: flag elements, only used at runtime
+*/
+/* "real" level elements */
+#define EL_EMPTY               0
+#define EL_MIRROR_START                1
+#define EL_MIRROR_00           (EL_MIRROR_START + 0)
+#define EL_MIRROR_01           (EL_MIRROR_START + 1)
+#define EL_MIRROR_02           (EL_MIRROR_START + 2)
+#define EL_MIRROR_03           (EL_MIRROR_START + 3)
+#define EL_MIRROR_04           (EL_MIRROR_START + 4)
+#define EL_MIRROR_05           (EL_MIRROR_START + 5)
+#define EL_MIRROR_06           (EL_MIRROR_START + 6)
+#define EL_MIRROR_07           (EL_MIRROR_START + 7)
+#define EL_MIRROR_08           (EL_MIRROR_START + 8)
+#define EL_MIRROR_09           (EL_MIRROR_START + 9)
+#define EL_MIRROR_10           (EL_MIRROR_START + 10)
+#define EL_MIRROR_11           (EL_MIRROR_START + 11)
+#define EL_MIRROR_12           (EL_MIRROR_START + 12)
+#define EL_MIRROR_13           (EL_MIRROR_START + 13)
+#define EL_MIRROR_14           (EL_MIRROR_START + 14)
+#define EL_MIRROR_15           (EL_MIRROR_START + 15)
+#define EL_MIRROR_END          EL_MIRROR_15
+#define EL_GRID_STEEL_START    17
+#define EL_GRID_STEEL_00       (EL_GRID_STEEL_START + 0)
+#define EL_GRID_STEEL_01       (EL_GRID_STEEL_START + 1)
+#define EL_GRID_STEEL_02       (EL_GRID_STEEL_START + 2)
+#define EL_GRID_STEEL_03       (EL_GRID_STEEL_START + 3)
+#define EL_GRID_STEEL_END      EL_GRID_STEEL_03
+#define EL_MCDUFFIN_START      21
+#define EL_MCDUFFIN_RIGHT      (EL_MCDUFFIN_START + 0)
+#define EL_MCDUFFIN_UP         (EL_MCDUFFIN_START + 1)
+#define EL_MCDUFFIN_LEFT       (EL_MCDUFFIN_START + 2)
+#define EL_MCDUFFIN_DOWN       (EL_MCDUFFIN_START + 3)
+#define EL_MCDUFFIN_END                EL_MCDUFFIN_DOWN
+#define EL_EXIT_CLOSED         25
+#define EL_EXIT_OPENING_1      26
+#define EL_EXIT_OPENING_2      27
+#define EL_EXIT_OPEN           28
+#define EL_KETTLE              29
+#define EL_BOMB                        30
+#define EL_PRISM               31
+#define EL_WALL_START          32
+#define EL_WALL_EMPTY          EL_WALL_START
+#define EL_WALL_00             EL_WALL_START
+#define EL_WALL_STEEL          EL_WALL_00
+#define EL_WALL_STEEL_START    EL_WALL_00
+#define EL_WALL_15             47
+#define EL_WALL_STEEL_END      EL_WALL_15
+#define EL_WALL_16             48
+#define EL_WALL_WOOD           EL_WALL_16
+#define EL_WALL_WOOD_START     EL_WALL_16
+#define EL_WALL_31             63
+#define EL_WALL_WOOD_END       EL_WALL_31
+#define EL_WALL_32             64
+#define EL_WALL_ICE            EL_WALL_32
+#define EL_WALL_ICE_START      EL_WALL_32
+#define EL_WALL_47             79
+#define EL_WALL_ICE_END                EL_WALL_47
+#define EL_WALL_48             80
+#define EL_WALL_AMOEBA         EL_WALL_48
+#define EL_WALL_AMOEBA_START   EL_WALL_48
+#define EL_WALL_63             95
+#define EL_WALL_AMOEBA_END     EL_WALL_63
+#define EL_WALL_END            EL_WALL_63
+#define EL_BLOCK_WOOD          96
+#define EL_BALL_GRAY           97
+#define EL_BEAMER_START                98
+#define EL_BEAMER_00           (EL_BEAMER_START + 0)
+#define EL_BEAMER_01           (EL_BEAMER_START + 1)
+#define EL_BEAMER_02           (EL_BEAMER_START + 2)
+#define EL_BEAMER_03           (EL_BEAMER_START + 3)
+#define EL_BEAMER_04           (EL_BEAMER_START + 4)
+#define EL_BEAMER_05           (EL_BEAMER_START + 5)
+#define EL_BEAMER_06           (EL_BEAMER_START + 6)
+#define EL_BEAMER_07           (EL_BEAMER_START + 7)
+#define EL_BEAMER_08           (EL_BEAMER_START + 8)
+#define EL_BEAMER_09           (EL_BEAMER_START + 9)
+#define EL_BEAMER_10           (EL_BEAMER_START + 10)
+#define EL_BEAMER_11           (EL_BEAMER_START + 11)
+#define EL_BEAMER_12           (EL_BEAMER_START + 12)
+#define EL_BEAMER_13           (EL_BEAMER_START + 13)
+#define EL_BEAMER_14           (EL_BEAMER_START + 14)
+#define EL_BEAMER_15           (EL_BEAMER_START + 15)
+#define EL_BEAMER_END          EL_BEAMER_15
+#define EL_FUSE_ON             114
+#define EL_PACMAN_START                115
+#define EL_PACMAN_RIGHT                (EL_PACMAN_START + 0)
+#define EL_PACMAN_UP           (EL_PACMAN_START + 1)
+#define EL_PACMAN_LEFT         (EL_PACMAN_START + 2)
+#define EL_PACMAN_DOWN         (EL_PACMAN_START + 3)
+#define EL_PACMAN_END          EL_PACMAN_DOWN
+#define EL_POLAR_START         119
+#define EL_POLAR_00            (EL_POLAR_START + 0)
+#define EL_POLAR_01            (EL_POLAR_START + 1)
+#define EL_POLAR_02            (EL_POLAR_START + 2)
+#define EL_POLAR_03            (EL_POLAR_START + 3)
+#define EL_POLAR_04            (EL_POLAR_START + 4)
+#define EL_POLAR_05            (EL_POLAR_START + 5)
+#define EL_POLAR_06            (EL_POLAR_START + 6)
+#define EL_POLAR_07            (EL_POLAR_START + 7)
+#define EL_POLAR_08            (EL_POLAR_START + 8)
+#define EL_POLAR_09            (EL_POLAR_START + 9)
+#define EL_POLAR_10            (EL_POLAR_START + 10)
+#define EL_POLAR_11            (EL_POLAR_START + 11)
+#define EL_POLAR_12            (EL_POLAR_START + 12)
+#define EL_POLAR_13            (EL_POLAR_START + 13)
+#define EL_POLAR_14            (EL_POLAR_START + 14)
+#define EL_POLAR_15            (EL_POLAR_START + 15)
+#define EL_POLAR_END           EL_POLAR_15
+#define EL_POLAR_CROSS_START   135
+#define EL_POLAR_CROSS_00      (EL_POLAR_CROSS_START + 0)
+#define EL_POLAR_CROSS_01      (EL_POLAR_CROSS_START + 1)
+#define EL_POLAR_CROSS_02      (EL_POLAR_CROSS_START + 2)
+#define EL_POLAR_CROSS_03      (EL_POLAR_CROSS_START + 3)
+#define EL_POLAR_CROSS_END     EL_POLAR_CROSS_03
+#define EL_MIRROR_FIXED_START  139
+#define EL_MIRROR_FIXED_00     (EL_MIRROR_FIXED_START + 0)
+#define EL_MIRROR_FIXED_01     (EL_MIRROR_FIXED_START + 1)
+#define EL_MIRROR_FIXED_02     (EL_MIRROR_FIXED_START + 2)
+#define EL_MIRROR_FIXED_03     (EL_MIRROR_FIXED_START + 3)
+#define EL_MIRROR_FIXED_END    EL_MIRROR_FIXED_03
+#define EL_GATE_STONE          143
+#define EL_KEY                 144
+#define EL_LIGHTBULB_OFF       145
+#define EL_LIGHTBULB_ON                146
+#define EL_LIGHTBALL           147
+#define EL_BLOCK_STONE         148
+#define EL_GATE_WOOD           149
+#define EL_FUEL_FULL           150
+#define EL_GRID_WOOD_START     151
+#define EL_GRID_WOOD_00                (EL_GRID_WOOD_START + 0)
+#define EL_GRID_WOOD_01                (EL_GRID_WOOD_START + 1)
+#define EL_GRID_WOOD_02                (EL_GRID_WOOD_START + 2)
+#define EL_GRID_WOOD_03                (EL_GRID_WOOD_START + 3)
+#define EL_GRID_WOOD_END       EL_GRID_WOOD_03
+#define EL_FUEL_EMPTY          155
+
+#define EL_CHAR_START          160
+#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)
+#define EL_CHAR_PUNKT          (EL_CHAR_ASCII0+46)
+#define EL_CHAR_SLASH          (EL_CHAR_ASCII0+47)
+#define EL_CHAR_0              (EL_CHAR_ASCII0+48)
+#define EL_CHAR_9              (EL_CHAR_ASCII0+57)
+#define EL_CHAR_DOPPEL         (EL_CHAR_ASCII0+58)
+#define EL_CHAR_SEMIKL         (EL_CHAR_ASCII0+59)
+#define EL_CHAR_LT             (EL_CHAR_ASCII0+60)
+#define EL_CHAR_GLEICH         (EL_CHAR_ASCII0+61)
+#define EL_CHAR_GT             (EL_CHAR_ASCII0+62)
+#define EL_CHAR_FRAGE          (EL_CHAR_ASCII0+63)
+#define EL_CHAR_AT             (EL_CHAR_ASCII0+64)
+#define EL_CHAR_A              (EL_CHAR_ASCII0+65)
+#define EL_CHAR_Z              (EL_CHAR_ASCII0+90)
+#define EL_CHAR_AE             (EL_CHAR_ASCII0+91)
+#define EL_CHAR_OE             (EL_CHAR_ASCII0+92)
+#define EL_CHAR_UE             (EL_CHAR_ASCII0+93)
+#define EL_CHAR_COPY           (EL_CHAR_ASCII0+94)
+#define EL_CHAR_END            (EL_CHAR_START+79)
+
+#define EL_CHAR(x)             ((x) == CHAR_BYTE_UMLAUT_A ? EL_CHAR_AE : \
+                                (x) == CHAR_BYTE_UMLAUT_O ? EL_CHAR_OE : \
+                                (x) == CHAR_BYTE_UMLAUT_U ? EL_CHAR_UE : \
+                                EL_CHAR_A + (x) - 'A')
+
+/* elements for "Deflektor" style levels */
+#define EL_DF_START            240
+
+#define EL_DF_MIRROR_START     EL_DF_START
+#define EL_DF_MIRROR_00                (EL_DF_MIRROR_START + 0)
+#define EL_DF_MIRROR_01                (EL_DF_MIRROR_START + 1)
+#define EL_DF_MIRROR_02                (EL_DF_MIRROR_START + 2)
+#define EL_DF_MIRROR_03                (EL_DF_MIRROR_START + 3)
+#define EL_DF_MIRROR_04                (EL_DF_MIRROR_START + 4)
+#define EL_DF_MIRROR_05                (EL_DF_MIRROR_START + 5)
+#define EL_DF_MIRROR_06                (EL_DF_MIRROR_START + 6)
+#define EL_DF_MIRROR_07                (EL_DF_MIRROR_START + 7)
+#define EL_DF_MIRROR_08                (EL_DF_MIRROR_START + 8)
+#define EL_DF_MIRROR_09                (EL_DF_MIRROR_START + 9)
+#define EL_DF_MIRROR_10                (EL_DF_MIRROR_START + 10)
+#define EL_DF_MIRROR_11                (EL_DF_MIRROR_START + 11)
+#define EL_DF_MIRROR_12                (EL_DF_MIRROR_START + 12)
+#define EL_DF_MIRROR_13                (EL_DF_MIRROR_START + 13)
+#define EL_DF_MIRROR_14                (EL_DF_MIRROR_START + 14)
+#define EL_DF_MIRROR_15                (EL_DF_MIRROR_START + 15)
+#define EL_DF_MIRROR_END       EL_DF_MIRROR_15
+
+#define EL_GRID_WOOD_FIXED_START 256
+#define EL_GRID_WOOD_FIXED_00  (EL_GRID_WOOD_FIXED_START + 0)  /*   0.0° */
+#define EL_GRID_WOOD_FIXED_01  (EL_GRID_WOOD_FIXED_START + 1)  /*  22.5° */
+#define EL_GRID_WOOD_FIXED_02  (EL_GRID_WOOD_FIXED_START + 2)  /*  45.0° */
+#define EL_GRID_WOOD_FIXED_03  (EL_GRID_WOOD_FIXED_START + 3)  /*  67.5° */
+#define EL_GRID_WOOD_FIXED_04  (EL_GRID_WOOD_FIXED_START + 4)  /*  90.0° */
+#define EL_GRID_WOOD_FIXED_05  (EL_GRID_WOOD_FIXED_START + 5)  /* 112.5° */
+#define EL_GRID_WOOD_FIXED_06  (EL_GRID_WOOD_FIXED_START + 6)  /* 135.0° */
+#define EL_GRID_WOOD_FIXED_07  (EL_GRID_WOOD_FIXED_START + 7)  /* 157.5° */
+#define EL_GRID_WOOD_FIXED_END EL_GRID_WOOD_FIXED_07
+
+#define EL_GRID_STEEL_FIXED_START 264
+#define EL_GRID_STEEL_FIXED_00 (EL_GRID_STEEL_FIXED_START + 0) /*   0.0° */
+#define EL_GRID_STEEL_FIXED_01 (EL_GRID_STEEL_FIXED_START + 1) /*  22.5° */
+#define EL_GRID_STEEL_FIXED_02 (EL_GRID_STEEL_FIXED_START + 2) /*  45.0° */
+#define EL_GRID_STEEL_FIXED_03 (EL_GRID_STEEL_FIXED_START + 3) /*  67.5° */
+#define EL_GRID_STEEL_FIXED_04 (EL_GRID_STEEL_FIXED_START + 4) /*  90.0° */
+#define EL_GRID_STEEL_FIXED_05 (EL_GRID_STEEL_FIXED_START + 5) /* 112.5° */
+#define EL_GRID_STEEL_FIXED_06 (EL_GRID_STEEL_FIXED_START + 6) /* 135.0° */
+#define EL_GRID_STEEL_FIXED_07 (EL_GRID_STEEL_FIXED_START + 7) /* 157.5° */
+#define EL_GRID_STEEL_FIXED_END        EL_GRID_STEEL_FIXED_07
+
+#define EL_DF_WALL_WOOD                272
+#define EL_DF_WALL_START       EL_DF_WALL_WOOD_START
+#define EL_DF_WALL_WOOD_START  (EL_DF_WALL_WOOD + 0)
+#define EL_DF_WALL_WOOD_END    (EL_DF_WALL_WOOD + 15)
+
+#define EL_DF_WALL_STEEL       288
+#define EL_DF_WALL_STEEL_START (EL_DF_WALL_STEEL + 0)
+#define EL_DF_WALL_STEEL_END   (EL_DF_WALL_STEEL + 15)
+#define EL_DF_WALL_END         EL_DF_WALL_STEEL_END
+
+#define EL_DF_EMPTY            304
+#define EL_CELL                        305
+#define EL_MINE                        306
+#define EL_REFRACTOR           307
+
+#define EL_LASER_START         308
+#define EL_LASER_RIGHT         (EL_LASER_START + 0)
+#define EL_LASER_UP            (EL_LASER_START + 1)
+#define EL_LASER_LEFT          (EL_LASER_START + 2)
+#define EL_LASER_DOWN          (EL_LASER_START + 3)
+#define EL_LASER_END           EL_LASER_DOWN
+
+#define EL_RECEIVER_START      312
+#define EL_RECEIVER_RIGHT      (EL_RECEIVER_START + 0)
+#define EL_RECEIVER_UP         (EL_RECEIVER_START + 1)
+#define EL_RECEIVER_LEFT       (EL_RECEIVER_START + 2)
+#define EL_RECEIVER_DOWN       (EL_RECEIVER_START + 3)
+#define EL_RECEIVER_END                EL_RECEIVER_DOWN
+
+#define EL_FIBRE_OPTIC_START   316
+#define EL_FIBRE_OPTIC_00      (EL_FIBRE_OPTIC_START + 0)
+#define EL_FIBRE_OPTIC_01      (EL_FIBRE_OPTIC_START + 1)
+#define EL_FIBRE_OPTIC_02      (EL_FIBRE_OPTIC_START + 2)
+#define EL_FIBRE_OPTIC_03      (EL_FIBRE_OPTIC_START + 3)
+#define EL_FIBRE_OPTIC_04      (EL_FIBRE_OPTIC_START + 4)
+#define EL_FIBRE_OPTIC_05      (EL_FIBRE_OPTIC_START + 5)
+#define EL_FIBRE_OPTIC_06      (EL_FIBRE_OPTIC_START + 6)
+#define EL_FIBRE_OPTIC_07      (EL_FIBRE_OPTIC_START + 7)
+#define EL_FIBRE_OPTIC_END     EL_FIBRE_OPTIC_07
+
+#define EL_DF_MIRROR_AUTO_START        324
+#define EL_DF_MIRROR_AUTO_00   (EL_DF_MIRROR_AUTO_START + 0)
+#define EL_DF_MIRROR_AUTO_01   (EL_DF_MIRROR_AUTO_START + 1)
+#define EL_DF_MIRROR_AUTO_02   (EL_DF_MIRROR_AUTO_START + 2)
+#define EL_DF_MIRROR_AUTO_03   (EL_DF_MIRROR_AUTO_START + 3)
+#define EL_DF_MIRROR_AUTO_04   (EL_DF_MIRROR_AUTO_START + 4)
+#define EL_DF_MIRROR_AUTO_05   (EL_DF_MIRROR_AUTO_START + 5)
+#define EL_DF_MIRROR_AUTO_06   (EL_DF_MIRROR_AUTO_START + 6)
+#define EL_DF_MIRROR_AUTO_07   (EL_DF_MIRROR_AUTO_START + 7)
+#define EL_DF_MIRROR_AUTO_08   (EL_DF_MIRROR_AUTO_START + 8)
+#define EL_DF_MIRROR_AUTO_09   (EL_DF_MIRROR_AUTO_START + 9)
+#define EL_DF_MIRROR_AUTO_10   (EL_DF_MIRROR_AUTO_START + 10)
+#define EL_DF_MIRROR_AUTO_11   (EL_DF_MIRROR_AUTO_START + 11)
+#define EL_DF_MIRROR_AUTO_12   (EL_DF_MIRROR_AUTO_START + 12)
+#define EL_DF_MIRROR_AUTO_13   (EL_DF_MIRROR_AUTO_START + 13)
+#define EL_DF_MIRROR_AUTO_14   (EL_DF_MIRROR_AUTO_START + 14)
+#define EL_DF_MIRROR_AUTO_15   (EL_DF_MIRROR_AUTO_START + 15)
+#define EL_DF_MIRROR_AUTO_END  EL_DF_MIRROR_AUTO_15
+
+#define EL_GRID_WOOD_AUTO_START 340
+#define EL_GRID_WOOD_AUTO_00   (EL_GRID_WOOD_AUTO_START + 0)
+#define EL_GRID_WOOD_AUTO_01   (EL_GRID_WOOD_AUTO_START + 1)
+#define EL_GRID_WOOD_AUTO_02   (EL_GRID_WOOD_AUTO_START + 2)
+#define EL_GRID_WOOD_AUTO_03   (EL_GRID_WOOD_AUTO_START + 3)
+#define EL_GRID_WOOD_AUTO_04   (EL_GRID_WOOD_AUTO_START + 4)
+#define EL_GRID_WOOD_AUTO_05   (EL_GRID_WOOD_AUTO_START + 5)
+#define EL_GRID_WOOD_AUTO_06   (EL_GRID_WOOD_AUTO_START + 6)
+#define EL_GRID_WOOD_AUTO_07   (EL_GRID_WOOD_AUTO_START + 7)
+#define EL_GRID_WOOD_AUTO_END  EL_GRID_WOOD_AUTO_07
+
+#define EL_GRID_STEEL_AUTO_START 348
+#define EL_GRID_STEEL_AUTO_00  (EL_GRID_STEEL_AUTO_START + 0)
+#define EL_GRID_STEEL_AUTO_01  (EL_GRID_STEEL_AUTO_START + 1)
+#define EL_GRID_STEEL_AUTO_02  (EL_GRID_STEEL_AUTO_START + 2)
+#define EL_GRID_STEEL_AUTO_03  (EL_GRID_STEEL_AUTO_START + 3)
+#define EL_GRID_STEEL_AUTO_04  (EL_GRID_STEEL_AUTO_START + 4)
+#define EL_GRID_STEEL_AUTO_05  (EL_GRID_STEEL_AUTO_START + 5)
+#define EL_GRID_STEEL_AUTO_06  (EL_GRID_STEEL_AUTO_START + 6)
+#define EL_GRID_STEEL_AUTO_07  (EL_GRID_STEEL_AUTO_START + 7)
+#define EL_GRID_STEEL_AUTO_END EL_GRID_STEEL_AUTO_07
+
+#define EL_DF_END              355
+
+#define EL_BEAMER_RED_START    356
+#define EL_BEAMER_RED_END      (EL_BEAMER_RED_START + 15)
+#define EL_BEAMER_YELLOW_START 372
+#define EL_BEAMER_YELLOW_END   (EL_BEAMER_YELLOW_START + 15)
+#define EL_BEAMER_GREEN_START  388
+#define EL_BEAMER_GREEN_END    (EL_BEAMER_GREEN_START + 15)
+#define EL_BEAMER_BLUE_START   404
+#define EL_BEAMER_BLUE_END     (EL_BEAMER_BLUE_START + 15)
+
+/* "real" (and therefore drawable) runtime elements */
+#define EL_FIRST_RUNTIME_EL    500
+#define EL_FUSE_OFF            501
+#define EL_PACMAN              502
+#define EL_EXIT_OPENING                503
+#define EL_GRAY_BALL_OPENING   504
+
+#define EL_WALL_CHANGING       512
+#define EL_WALL_CHANGING_START (EL_WALL_CHANGING + 0)
+#define EL_WALL_CHANGING_END   (EL_WALL_CHANGING + 15)
+
+#if 0
+#define EL_MIRROR              5
+#define EL_GRID_STEEL          5
+#define EL_MCDUFFIN            5
+#define EL_BEAMER              5
+#define EL_POLAR               5
+#define EL_POLAR_CROSS         5
+#define EL_MIRROR_FIXED                5
+#define EL_GRID_WOOD           5
+#define EL_DF_MIRROR           5
+#define EL_GRID_WOOD_FIXED     5
+#define EL_GRID_STEEL_FIXED    5
+#define EL_LASER               5
+#define EL_RECEIVER            5
+#define EL_FIBRE_OPTIC         5
+#define EL_DF_MIRROR_AUTO      5
+#define EL_GRID_WOOD_AUTO      5
+#define EL_GRID_STEEL_AUTO     5
+#endif
+
+/* "unreal" (and therefore not drawable) runtime elements */
+#define EL_BLOCKED             600
+#define EL_EXPLODING_OPAQUE    601
+#define EL_EXPLODING_TRANSP    602
+
+
+/* game graphics:
+**       0 -  191: graphics from "MirrorScreen"
+**     192 -  255: pseudo graphics mapped to "MirrorScreen"
+**     256 -  511: graphics from "MirrorFont"
+**     512 -  767: graphics from "MirrorDF"
+*/
+
+#define GFX_START_MIRRORSCREEN 0
+#define GFX_END_MIRRORSCREEN   191
+#define GFX_START_PSEUDO       192
+#define GFX_END_PSEUDO         255
+#define GFX_START_MIRRORFONT   256
+#define GFX_END_MIRRORFONT     511
+#define GFX_START_MIRRORDF     512
+#define GFX_END_MIRRORDF       767
+
+#define NUM_TILES              512
+
+/* graphics from "MirrorScreen" */
+#define GFX_EMPTY              (-1)
+/* row 0 (0) */
+#define GFX_MIRROR_START       0
+#define GFX_MIRROR             GFX_MIRROR_START
+#define GFX_MIRROR_00          (GFX_MIRROR_START + 0)
+#define GFX_MIRROR_01          (GFX_MIRROR_START + 1)
+#define GFX_MIRROR_02          (GFX_MIRROR_START + 2)
+#define GFX_MIRROR_03          (GFX_MIRROR_START + 3)
+#define GFX_MIRROR_04          (GFX_MIRROR_START + 4)
+#define GFX_MIRROR_05          (GFX_MIRROR_START + 5)
+#define GFX_MIRROR_06          (GFX_MIRROR_START + 6)
+#define GFX_MIRROR_07          (GFX_MIRROR_START + 7)
+#define GFX_MIRROR_08          (GFX_MIRROR_START + 8)
+#define GFX_MIRROR_09          (GFX_MIRROR_START + 9)
+#define GFX_MIRROR_10          (GFX_MIRROR_START + 10)
+#define GFX_MIRROR_11          (GFX_MIRROR_START + 11)
+#define GFX_MIRROR_12          (GFX_MIRROR_START + 12)
+#define GFX_MIRROR_13          (GFX_MIRROR_START + 13)
+#define GFX_MIRROR_14          (GFX_MIRROR_START + 14)
+#define GFX_MIRROR_15          (GFX_MIRROR_START + 15)
+#define GFX_MIRROR_END         GFX_MIRROR_15
+/* row 1 (16) */
+#define GFX_GRID_STEEL_START   16
+#define GFX_GRID_STEEL         GFX_GRID_STEEL_START
+#define GFX_GRID_STEEL_00      (GFX_GRID_STEEL_START + 0)
+#define GFX_GRID_STEEL_01      (GFX_GRID_STEEL_START + 1)
+#define GFX_GRID_STEEL_02      (GFX_GRID_STEEL_START + 2)
+#define GFX_GRID_STEEL_03      (GFX_GRID_STEEL_START + 3)
+#define GFX_MCDUFFIN_START     20
+#define GFX_MCDUFFIN           GFX_MCDUFFIN_START
+#define GFX_MCDUFFIN_RIGHT     (GFX_MCDUFFIN_START + 0)
+#define GFX_MCDUFFIN_UP                (GFX_MCDUFFIN_START + 1)
+#define GFX_MCDUFFIN_LEFT      (GFX_MCDUFFIN_START + 2)
+#define GFX_MCDUFFIN_DOWN      (GFX_MCDUFFIN_START + 3)
+#define GFX_EXIT_CLOSED                24
+#define GFX_EXIT_OPENING_1     25
+#define GFX_EXIT_OPENING_2     26
+#define GFX_EXIT_OPEN          27
+#define GFX_KETTLE             28
+#define GFX_EXPLOSION_KETTLE   29
+/* row 2 (32) */
+#define GFX_PRISM              32
+#define GFX_WALL_SEVERAL       33
+#define GFX_WALL_ANIMATION     34
+#define GFX_BLOCK_WOOD         36
+#define GFX_BOMB               37
+#define GFX_FUSE_ON            38
+#define GFX_FUSE_OFF           39
+#define GFX_GATE_STONE         40
+#define GFX_KEY                        41
+#define GFX_LIGHTBULB_OFF      42
+#define GFX_LIGHTBULB_ON       43
+#define GFX_BALL_RED           44
+#define GFX_BALL_BLUE          45
+#define GFX_BALL_YELLOW                46
+#define GFX_BALL_GRAY          47
+/* row 3 (48) */
+#define GFX_BEAMER_START       48
+#define GFX_BEAMER_END         63
+/* row 4 (64) */
+#define GFX_PACMAN_START       64
+#define GFX_PACMAN             GFX_PACMAN_START
+#define GFX_PACMAN_RIGHT       (GFX_PACMAN_START + 0)
+#define GFX_PACMAN_UP          (GFX_PACMAN_START + 1)
+#define GFX_PACMAN_LEFT                (GFX_PACMAN_START + 2)
+#define GFX_PACMAN_DOWN                (GFX_PACMAN_START + 3)
+#define GFX_EXPLOSION_START    72
+#define GFX_EXPLOSION_SHORT    76
+#define GFX_EXPLOSION_LAST     78
+/* row 5 (80) */
+#define GFX_POLAR_START                80
+#define GFX_POLAR_END          95
+/* row 6 (96) */
+#define GFX_POLAR_CROSS_START  96
+#define GFX_POLAR_CROSS                GFX_POLAR_CROSS_START
+#define GFX_POLAR_CROSS_00     (GFX_POLAR_CROSS_START + 0)
+#define GFX_POLAR_CROSS_01     (GFX_POLAR_CROSS_START + 1)
+#define GFX_POLAR_CROSS_02     (GFX_POLAR_CROSS_START + 2)
+#define GFX_POLAR_CROSS_03     (GFX_POLAR_CROSS_START + 3)
+#define GFX_MIRROR_FIXED_START 100
+#define GFX_MIRROR_FIXED       GFX_MIRROR_FIXED_START
+#define GFX_MIRROR_FIXED_00    (GFX_MIRROR_FIXED_START + 0)
+#define GFX_MIRROR_FIXED_01    (GFX_MIRROR_FIXED_START + 1)
+#define GFX_MIRROR_FIXED_02    (GFX_MIRROR_FIXED_START + 2)
+#define GFX_MIRROR_FIXED_03    (GFX_MIRROR_FIXED_START + 3)
+/* row 7 (112) */
+#define GFX_BLOCK_STONE                112
+#define GFX_GATE_WOOD          113
+#define GFX_FUEL_FULL          114
+#define GFX_FUEL_EMPTY         115
+#define GFX_GRID_WOOD_00       116
+#define GFX_GRID_WOOD_01       117
+#define GFX_GRID_WOOD_02       118
+#define GFX_GRID_WOOD_03       119
+/* row 8 (128) */
+#define GFX_ARROW_BLUE_LEFT    128
+#define GFX_ARROW_BLUE_RIGHT   129
+#define GFX_ARROW_BLUE_UP      130
+#define GFX_ARROW_BLUE_DOWN    131
+#define GFX_ARROW_RED_LEFT     132
+#define GFX_ARROW_RED_RIGHT    133
+#define GFX_ARROW_RED_UP       134
+#define GFX_ARROW_RED_DOWN     135
+/* row 9 (144) */
+#define GFX_SCROLLBAR_BLUE     144
+#define GFX_SCROLLBAR_RED      145
+/* row 10 (160) */
+#define GFX_MASK_CIRCLE                160
+#define GFX_MASK_RECTANGLE     161
+#define GFX_MASK_RECTANGLE2    162
+#define GFX_MASK_RECTANGLE3    163
+#define GFX_MASK_GRID_00       164
+#define GFX_MASK_GRID_01       165
+#define GFX_MASK_GRID_02       166
+#define GFX_MASK_GRID_03       167
+/* row 11 (176) */
+#define GFX_MASK_MCDUFFIN_00   176
+#define GFX_MASK_MCDUFFIN_01   177
+#define GFX_MASK_MCDUFFIN_02   178
+#define GFX_MASK_MCDUFFIN_03   179
+
+/* pseudo-graphics; will be mapped to other graphics */
+#define GFX_WALL_STEEL         192
+#define GFX_WALL_WOOD          193
+#define GFX_WALL_ICE           194
+#define GFX_WALL_AMOEBA                195
+#define GFX_DF_WALL_STEEL      196
+#define GFX_DF_WALL_WOOD       197
+
+#define GFX_KUGEL_ROT          GFX_BALL_RED
+#define GFX_KUGEL_BLAU         GFX_BALL_BLUE
+#define GFX_KUGEL_GELB         GFX_BALL_YELLOW
+#define GFX_KUGEL_GRAU         GFX_BALL_GRAY
+
+/* graphics from "MirrorFont" */
+#define GFX_CHAR_START         (GFX_START_MIRRORFONT)
+#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)
+
+/* graphics from "MirrorDF" */
+#define GFX_DF_MIRROR_00       (GFX_START_MIRRORDF +  0 * DF_PER_LINE +  0)
+#define GFX_DF_MIRROR_01       (GFX_START_MIRRORDF +  0 * DF_PER_LINE +  1)
+#define GFX_DF_MIRROR_02       (GFX_START_MIRRORDF +  0 * DF_PER_LINE +  2)
+#define GFX_DF_MIRROR_03       (GFX_START_MIRRORDF +  0 * DF_PER_LINE +  3)
+#define GFX_DF_MIRROR_04       (GFX_START_MIRRORDF +  0 * DF_PER_LINE +  4)
+#define GFX_DF_MIRROR_05       (GFX_START_MIRRORDF +  0 * DF_PER_LINE +  5)
+#define GFX_DF_MIRROR_06       (GFX_START_MIRRORDF +  0 * DF_PER_LINE +  6)
+#define GFX_DF_MIRROR_07       (GFX_START_MIRRORDF +  0 * DF_PER_LINE +  7)
+#define GFX_DF_MIRROR_08       (GFX_START_MIRRORDF +  0 * DF_PER_LINE +  8)
+#define GFX_DF_MIRROR_09       (GFX_START_MIRRORDF +  0 * DF_PER_LINE +  9)
+#define GFX_DF_MIRROR_10       (GFX_START_MIRRORDF +  0 * DF_PER_LINE + 10)
+#define GFX_DF_MIRROR_11       (GFX_START_MIRRORDF +  0 * DF_PER_LINE + 11)
+#define GFX_DF_MIRROR_12       (GFX_START_MIRRORDF +  0 * DF_PER_LINE + 12)
+#define GFX_DF_MIRROR_13       (GFX_START_MIRRORDF +  0 * DF_PER_LINE + 13)
+#define GFX_DF_MIRROR_14       (GFX_START_MIRRORDF +  0 * DF_PER_LINE + 14)
+#define GFX_DF_MIRROR_15       (GFX_START_MIRRORDF +  0 * DF_PER_LINE + 15)
+
+#define GFX_DF_MIRROR_AUTO_00  (GFX_START_MIRRORDF +  1 * DF_PER_LINE +  0)
+#define GFX_DF_MIRROR_AUTO_01  (GFX_START_MIRRORDF +  1 * DF_PER_LINE +  1)
+#define GFX_DF_MIRROR_AUTO_02  (GFX_START_MIRRORDF +  1 * DF_PER_LINE +  2)
+#define GFX_DF_MIRROR_AUTO_03  (GFX_START_MIRRORDF +  1 * DF_PER_LINE +  3)
+#define GFX_DF_MIRROR_AUTO_04  (GFX_START_MIRRORDF +  1 * DF_PER_LINE +  4)
+#define GFX_DF_MIRROR_AUTO_05  (GFX_START_MIRRORDF +  1 * DF_PER_LINE +  5)
+#define GFX_DF_MIRROR_AUTO_06  (GFX_START_MIRRORDF +  1 * DF_PER_LINE +  6)
+#define GFX_DF_MIRROR_AUTO_07  (GFX_START_MIRRORDF +  1 * DF_PER_LINE +  7)
+#define GFX_DF_MIRROR_AUTO_08  (GFX_START_MIRRORDF +  1 * DF_PER_LINE +  8)
+#define GFX_DF_MIRROR_AUTO_09  (GFX_START_MIRRORDF +  1 * DF_PER_LINE +  9)
+#define GFX_DF_MIRROR_AUTO_10  (GFX_START_MIRRORDF +  1 * DF_PER_LINE + 10)
+#define GFX_DF_MIRROR_AUTO_11  (GFX_START_MIRRORDF +  1 * DF_PER_LINE + 11)
+#define GFX_DF_MIRROR_AUTO_12  (GFX_START_MIRRORDF +  1 * DF_PER_LINE + 12)
+#define GFX_DF_MIRROR_AUTO_13  (GFX_START_MIRRORDF +  1 * DF_PER_LINE + 13)
+#define GFX_DF_MIRROR_AUTO_14  (GFX_START_MIRRORDF +  1 * DF_PER_LINE + 14)
+#define GFX_DF_MIRROR_AUTO_15  (GFX_START_MIRRORDF +  1 * DF_PER_LINE + 15)
+
+#define GFX_GRID_STEEL_FIXED_00        (GFX_START_MIRRORDF +  2 * DF_PER_LINE +  0)
+#define GFX_GRID_STEEL_FIXED_01        (GFX_START_MIRRORDF +  2 * DF_PER_LINE +  1)
+#define GFX_GRID_STEEL_FIXED_02        (GFX_START_MIRRORDF +  2 * DF_PER_LINE +  2)
+#define GFX_GRID_STEEL_FIXED_03        (GFX_START_MIRRORDF +  2 * DF_PER_LINE +  3)
+#define GFX_GRID_STEEL_FIXED_04        (GFX_START_MIRRORDF +  2 * DF_PER_LINE +  4)
+#define GFX_GRID_STEEL_FIXED_05        (GFX_START_MIRRORDF +  2 * DF_PER_LINE +  5)
+#define GFX_GRID_STEEL_FIXED_06        (GFX_START_MIRRORDF +  2 * DF_PER_LINE +  6)
+#define GFX_GRID_STEEL_FIXED_07        (GFX_START_MIRRORDF +  2 * DF_PER_LINE +  7)
+#define GFX_GRID_STEEL_FIXED   GFX_GRID_STEEL_FIXED_00
+
+#define GFX_GRID_WOOD_FIXED_00 (GFX_START_MIRRORDF +  2 * DF_PER_LINE +  8)
+#define GFX_GRID_WOOD_FIXED_01 (GFX_START_MIRRORDF +  2 * DF_PER_LINE +  9)
+#define GFX_GRID_WOOD_FIXED_02 (GFX_START_MIRRORDF +  2 * DF_PER_LINE + 10)
+#define GFX_GRID_WOOD_FIXED_03 (GFX_START_MIRRORDF +  2 * DF_PER_LINE + 11)
+#define GFX_GRID_WOOD_FIXED_04 (GFX_START_MIRRORDF +  2 * DF_PER_LINE + 12)
+#define GFX_GRID_WOOD_FIXED_05 (GFX_START_MIRRORDF +  2 * DF_PER_LINE + 13)
+#define GFX_GRID_WOOD_FIXED_06 (GFX_START_MIRRORDF +  2 * DF_PER_LINE + 14)
+#define GFX_GRID_WOOD_FIXED_07 (GFX_START_MIRRORDF +  2 * DF_PER_LINE + 15)
+#define GFX_GRID_WOOD_FIXED    GFX_GRID_WOOD_FIXED_00
+
+#define GFX_GRID_STEEL_AUTO_00 (GFX_START_MIRRORDF +  3 * DF_PER_LINE +  0)
+#define GFX_GRID_STEEL_AUTO_01 (GFX_START_MIRRORDF +  3 * DF_PER_LINE +  1)
+#define GFX_GRID_STEEL_AUTO_02 (GFX_START_MIRRORDF +  3 * DF_PER_LINE +  2)
+#define GFX_GRID_STEEL_AUTO_03 (GFX_START_MIRRORDF +  3 * DF_PER_LINE +  3)
+#define GFX_GRID_STEEL_AUTO_04 (GFX_START_MIRRORDF +  3 * DF_PER_LINE +  4)
+#define GFX_GRID_STEEL_AUTO_05 (GFX_START_MIRRORDF +  3 * DF_PER_LINE +  5)
+#define GFX_GRID_STEEL_AUTO_06 (GFX_START_MIRRORDF +  3 * DF_PER_LINE +  6)
+#define GFX_GRID_STEEL_AUTO_07 (GFX_START_MIRRORDF +  3 * DF_PER_LINE +  7)
+#define GFX_GRID_STEEL_AUTO    GFX_GRID_STEEL_AUTO_00
+
+#define GFX_GRID_WOOD_AUTO_00  (GFX_START_MIRRORDF +  3 * DF_PER_LINE +  8)
+#define GFX_GRID_WOOD_AUTO_01  (GFX_START_MIRRORDF +  3 * DF_PER_LINE +  9)
+#define GFX_GRID_WOOD_AUTO_02  (GFX_START_MIRRORDF +  3 * DF_PER_LINE + 10)
+#define GFX_GRID_WOOD_AUTO_03  (GFX_START_MIRRORDF +  3 * DF_PER_LINE + 11)
+#define GFX_GRID_WOOD_AUTO_04  (GFX_START_MIRRORDF +  3 * DF_PER_LINE + 12)
+#define GFX_GRID_WOOD_AUTO_05  (GFX_START_MIRRORDF +  3 * DF_PER_LINE + 13)
+#define GFX_GRID_WOOD_AUTO_06  (GFX_START_MIRRORDF +  3 * DF_PER_LINE + 14)
+#define GFX_GRID_WOOD_AUTO_07  (GFX_START_MIRRORDF +  3 * DF_PER_LINE + 15)
+#define GFX_GRID_WOOD_AUTO     GFX_GRID_WOOD_AUTO_00
+
+#define GFX_BEAMER_RED_START   (GFX_START_MIRRORDF +  4 * DF_PER_LINE +  0)
+#define GFX_BEAMER_RED_END     (GFX_START_MIRRORDF +  4 * DF_PER_LINE + 15)
+#define GFX_BEAMER_YELLOW_START        (GFX_START_MIRRORDF +  5 * DF_PER_LINE +  0)
+#define GFX_BEAMER_YELLOW_END  (GFX_START_MIRRORDF +  5 * DF_PER_LINE + 15)
+#define GFX_BEAMER_GREEN_START (GFX_START_MIRRORDF +  6 * DF_PER_LINE +  0)
+#define GFX_BEAMER_GREEN_END   (GFX_START_MIRRORDF +  6 * DF_PER_LINE + 15)
+#define GFX_BEAMER_BLUE_START  (GFX_START_MIRRORDF +  7 * DF_PER_LINE +  0)
+#define GFX_BEAMER_BLUE_END    (GFX_START_MIRRORDF +  7 * DF_PER_LINE + 15)
+
+#define GFX_DF_WALL_SEVERAL    (GFX_START_MIRRORDF +  8 * DF_PER_LINE +  0)
+#define GFX_REFRACTOR          (GFX_START_MIRRORDF +  8 * DF_PER_LINE +  1)
+#define GFX_CELL               (GFX_START_MIRRORDF +  8 * DF_PER_LINE +  2)
+#define GFX_MINE               (GFX_START_MIRRORDF +  8 * DF_PER_LINE +  4)
+
+#define GFX_LASER_RIGHT                (GFX_START_MIRRORDF +  9 * DF_PER_LINE +  0)
+#define GFX_LASER_UP           (GFX_START_MIRRORDF +  9 * DF_PER_LINE +  1)
+#define GFX_LASER_LEFT         (GFX_START_MIRRORDF +  9 * DF_PER_LINE +  2)
+#define GFX_LASER_DOWN         (GFX_START_MIRRORDF +  9 * DF_PER_LINE +  3)
+#define GFX_RECEIVER_RIGHT     (GFX_START_MIRRORDF +  9 * DF_PER_LINE +  4)
+#define GFX_RECEIVER_UP                (GFX_START_MIRRORDF +  9 * DF_PER_LINE +  5)
+#define GFX_RECEIVER_LEFT      (GFX_START_MIRRORDF +  9 * DF_PER_LINE +  6)
+#define GFX_RECEIVER_DOWN      (GFX_START_MIRRORDF +  9 * DF_PER_LINE +  7)
+
+#define GFX_FIBRE_OPTIC_00     (GFX_START_MIRRORDF + 10 * DF_PER_LINE +  0)
+#define GFX_FIBRE_OPTIC_01     (GFX_START_MIRRORDF + 10 * DF_PER_LINE +  1)
+#define GFX_FIBRE_OPTIC_02     (GFX_START_MIRRORDF + 10 * DF_PER_LINE +  2)
+#define GFX_FIBRE_OPTIC_03     (GFX_START_MIRRORDF + 10 * DF_PER_LINE +  3)
+#define GFX_FIBRE_OPTIC_04     (GFX_START_MIRRORDF + 10 * DF_PER_LINE +  4)
+#define GFX_FIBRE_OPTIC_05     (GFX_START_MIRRORDF + 10 * DF_PER_LINE +  5)
+#define GFX_FIBRE_OPTIC_06     (GFX_START_MIRRORDF + 10 * DF_PER_LINE +  6)
+#define GFX_FIBRE_OPTIC_07     (GFX_START_MIRRORDF + 10 * DF_PER_LINE +  7)
+
+#define GFX_FIBRE_OPTIC_ED_00  (GFX_START_MIRRORDF + 11 * DF_PER_LINE +  0)
+#define GFX_FIBRE_OPTIC_ED_01  (GFX_START_MIRRORDF + 11 * DF_PER_LINE +  1)
+#define GFX_FIBRE_OPTIC_ED_02  (GFX_START_MIRRORDF + 11 * DF_PER_LINE +  2)
+#define GFX_FIBRE_OPTIC_ED_03  (GFX_START_MIRRORDF + 11 * DF_PER_LINE +  3)
+#define GFX_FIBRE_OPTIC_ED_04  (GFX_START_MIRRORDF + 11 * DF_PER_LINE +  4)
+#define GFX_FIBRE_OPTIC_ED_05  (GFX_START_MIRRORDF + 11 * DF_PER_LINE +  5)
+#define GFX_FIBRE_OPTIC_ED_06  (GFX_START_MIRRORDF + 11 * DF_PER_LINE +  6)
+#define GFX_FIBRE_OPTIC_ED_07  (GFX_START_MIRRORDF + 11 * DF_PER_LINE +  7)
+
+/* the names of the sounds */
+#define SND_AMOEBE             0
+#define SND_ANTIGRAV           1
+#define SND_AUTSCH             2
+#define SND_BONG               3
+#define SND_FUEL               4
+#define SND_HALLOFFAME         5
+#define SND_HOLZ               6
+#define SND_HUI                        7
+#define SND_KABUMM             8
+#define SND_KINK               9
+#define SND_KLING              10
+#define SND_LASER              11
+#define SND_OEFFNEN            12
+#define SND_QUIEK              13
+#define SND_RHYTHMLOOP         14
+#define SND_ROAAAR             15
+#define SND_SIRR               16
+#define SND_SLURP              17
+#define SND_WARNTON            18
+#define SND_WHOOSH             19
+
+#define NUM_SOUNDS             20
+
+/* laser angles (directions) */
+#define ANG_RAY_RIGHT          0
+#define ANG_RAY_UP             4
+#define ANG_RAY_LEFT           8
+#define ANG_RAY_DOWN           12
+
+/* laser angles (degree) */
+#define ANG_RAY_0              0
+#define ANG_RAY_90             4
+#define ANG_RAY_180            8
+#define ANG_RAY_270            12
+#define IS_22_5_ANGLE(angle)   ((angle) % 2)
+#define IS_90_ANGLE(angle)     (!((angle) % 4))
+#define IS_HORIZ_ANGLE(angle)  (!((angle) % 8))
+#define IS_VERT_ANGLE(angle)   ((angle) % 8)
+
+/* mirror angles */
+#define ANG_MIRROR_0           0
+#define ANG_MIRROR_45          4
+#define ANG_MIRROR_90          8
+#define ANG_MIRROR_135         12
+
+/* positions for checking where laser already hits element */
+#define HIT_POS_CENTER         1
+#define HIT_POS_EDGE           2
+#define HIT_POS_BETWEEN                4
+
+/* masks for scanning elements */
+#define HIT_MASK_NO_HIT                0
+#define HIT_MASK_TOPLEFT       1
+#define HIT_MASK_TOPRIGHT      2
+#define HIT_MASK_BOTTOMLEFT    4
+#define HIT_MASK_BOTTOMRIGHT   8
+#define HIT_MASK_LEFT          (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMLEFT)
+#define HIT_MASK_RIGHT         (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMRIGHT)
+#define HIT_MASK_TOP           (HIT_MASK_TOPLEFT | HIT_MASK_TOPRIGHT)
+#define HIT_MASK_BOTTOM                (HIT_MASK_BOTTOMLEFT | HIT_MASK_BOTTOMRIGHT)
+#define HIT_MASK_ALL           (HIT_MASK_LEFT | HIT_MASK_RIGHT)
+
+/* step values for rotating elements */
+#define ROTATE_NO_ROTATING     0
+#define ROTATE_LEFT            (+1)
+#define ROTATE_RIGHT           (-1)
+#define BUTTON_ROTATION(button)        ((button) == MB_LEFTBUTTON  ? ROTATE_LEFT :  \
+                                (button) == MB_RIGHTBUTTON ? ROTATE_RIGHT : \
+                                ROTATE_NO_ROTATING)
+
+/* game over values */
+#define GAME_OVER_NO_ENERGY    1
+#define GAME_OVER_OVERLOADED   2
+#define GAME_OVER_BOMB         3
+
+/* values for game_status */
+#define EXITGAME               0
+#define MAINMENU               1
+#define PLAYING                        2
+#define LEVELED                        3
+#define HELPSCREEN             4
+#define CHOOSELEVEL            5
+#define TYPENAME               6
+#define HALLOFFAME             7
+#define SETUP                  8
+
+/* return values for GameActions */
+#define ACT_GO_ON      0
+#define ACT_GAME_OVER  1
+#define ACT_NEW_GAME   2
+
+/* values for color_status */
+#define STATIC_COLORS   0
+#define DYNAMIC_COLORS  1
+
+#define PROGRAM_VERSION_MAJOR  2
+#define PROGRAM_VERSION_MINOR  0
+#define PROGRAM_VERSION_PATCH  2
+#define PROGRAM_VERSION_STRING "2.0.2"
+
+#define PROGRAM_TITLE_STRING   "Mirror Magic II"
+#define PROGRAM_AUTHOR_STRING  "Holger Schemel"
+#define PROGRAM_RIGHTS_STRING  "Copyright ^1994-2001"
+#define PROGRAM_DOS_PORT_STRING        "DOS port based on code by Guido Schulz"
+#define PROGRAM_IDENT_STRING   PROGRAM_VERSION_STRING " " TARGET_STRING
+#define WINDOW_TITLE_STRING    PROGRAM_TITLE_STRING " " PROGRAM_IDENT_STRING
+#define WINDOW_SUBTITLE_STRING PROGRAM_RIGHTS_STRING " " PROGRAM_AUTHOR_STRING
+#define ICON_TITLE_STRING      PROGRAM_TITLE_STRING
+#define UNIX_USERDATA_DIRECTORY        ".mirrormagic"
+
+#define X11_ICON_FILENAME      "mirrormagic_icon.xbm"
+#define X11_ICONMASK_FILENAME  "mirrormagic_iconmask.xbm"
+#define MSDOS_POINTER_FILENAME "mouse.pcx"
+
+/* functions for version handling */
+#define MM_VERSION_IDENT(x,y,z)        ((x) * 10000 + (y) * 100 + (z))
+#define MM_VERSION_MAJOR(x)    ((x) / 10000)
+#define MM_VERSION_MINOR(x)    (((x) % 10000) / 100)
+#define MM_VERSION_PATCH(x)    ((x) % 100)
+
+/* file version numbers for resource files (levels, score, setup, etc.)
+** currently supported/known file version numbers:
+**     1.4 (still in use)
+**     2.0 (actual)
+*/
+#define MM_FILE_VERSION_1_4    MM_VERSION_IDENT(1,4,0)
+#define MM_FILE_VERSION_2_0    MM_VERSION_IDENT(2,0,0)
+
+/* file version does not change for every program version, but is changed
+   when new features are introduced that are incompatible with older file
+   versions, so that they can be treated accordingly */
+#define MM_FILE_VERSION_ACTUAL MM_FILE_VERSION_2_0
+
+#define MM_GAME_VERSION_ACTUAL MM_VERSION_IDENT(PROGRAM_VERSION_MAJOR, \
+                                                PROGRAM_VERSION_MINOR, \
+                                                PROGRAM_VERSION_PATCH)
+
+/* sound control */
+
+#define ST(x)           (((x)-8)*16)
+
+#endif /* MM_MAIN_H */
diff --git a/src/game_mm/mm_tools.c b/src/game_mm/mm_tools.c
new file mode 100644 (file)
index 0000000..bcc61e5
--- /dev/null
@@ -0,0 +1,904 @@
+/***********************************************************
+* Mirror Magic -- McDuffin's Revenge                       *
+*----------------------------------------------------------*
+* (c) 1994-2001 Artsoft Entertainment                      *
+*               Holger Schemel                             *
+*               Detmolder Strasse 189                      *
+*               33604 Bielefeld                            *
+*               Germany                                    *
+*               e-mail: info@artsoft.org                   *
+*----------------------------------------------------------*
+* tools.c                                                  *
+***********************************************************/
+
+#include "main_mm.h"
+
+#include "mm_main.h"
+#include "mm_tools.h"
+
+
+/* forward declaration for internal use */
+static int getGraphicAnimationPhase_MM(int, int, int);
+
+void ClearWindow()
+{
+  ClearRectangle(backbuffer, REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
+
+  SetDrawtoField(DRAW_BACKBUFFER);
+
+  redraw_mask |= REDRAW_FIELD;
+}
+
+static int getGraphicAnimationPhase_MM(int frames, int delay, int mode)
+{
+  int phase;
+
+  if (mode == ANIM_PINGPONG)
+  {
+    int max_anim_frames = 2 * frames - 2;
+    phase = (FrameCounter % (delay * max_anim_frames)) / delay;
+    phase = (phase < frames ? phase : max_anim_frames - phase);
+  }
+  else
+    phase = (FrameCounter % (delay * frames)) / delay;
+
+  if (mode == ANIM_REVERSE)
+    phase = -phase;
+
+  return(phase);
+}
+
+void DrawGraphicAnimationExt_MM(int x, int y, int graphic,
+                                int frames, int delay, int mode, int mask_mode)
+{
+  int phase = getGraphicAnimationPhase_MM(frames, delay, mode);
+
+  if (!(FrameCounter % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+  {
+    if (mask_mode == USE_MASKING)
+      DrawGraphicThruMask_MM(SCREENX(x), SCREENY(y), graphic + phase);
+    else
+      DrawGraphic_MM(SCREENX(x), SCREENY(y), graphic + phase);
+  }
+}
+
+void DrawGraphicAnimation_MM(int x, int y, int graphic,
+                         int frames, int delay, int mode)
+{
+  DrawGraphicAnimationExt_MM(x, y, graphic, frames, delay, mode, NO_MASKING);
+}
+
+void DrawGraphicAnimationThruMask_MM(int x, int y, int graphic,
+                                 int frames, int delay, int mode)
+{
+  DrawGraphicAnimationExt_MM(x, y, graphic, frames, delay, mode, USE_MASKING);
+}
+
+void DrawGraphic_MM(int x, int y, int graphic)
+{
+#if DEBUG
+  if (!IN_SCR_FIELD(x,y))
+  {
+    printf("DrawGraphic_MM(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
+    printf("DrawGraphic_MM(): This should never happen!\n");
+
+#if 1
+    {
+      int i=0;
+      i=i/i;
+    }
+#endif
+
+    return;
+  }
+#endif
+
+  DrawGraphicExt_MM(drawto_field, FX + x*TILEX, FY + y*TILEY, graphic);
+  MarkTileDirty(x, y);
+}
+
+void DrawGraphicExt_MM(DrawBuffer *d, int x, int y, int graphic)
+{
+  Bitmap *bitmap;
+  int src_x, src_y;
+
+  getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
+  BlitBitmap(bitmap, d, src_x, src_y, TILEX, TILEY, x, y);
+}
+
+void DrawGraphicThruMask_MM(int x, int y, int graphic)
+{
+#if DEBUG
+  if (!IN_SCR_FIELD(x,y))
+  {
+    printf("DrawGraphicThruMask_MM(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
+    printf("DrawGraphicThruMask_MM(): This should never happen!\n");
+    return;
+  }
+#endif
+
+  DrawGraphicThruMaskExt_MM(drawto_field, FX + x*TILEX, FY + y*TILEY, graphic);
+  MarkTileDirty(x,y);
+}
+
+void DrawGraphicThruMaskExt_MM(DrawBuffer *d, int dest_x, int dest_y, int graphic)
+{
+  int src_x, src_y;
+  Bitmap *src_bitmap;
+
+  if (graphic == GFX_EMPTY)
+    return;
+
+  getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
+
+  BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dest_x, dest_y);
+}
+
+void DrawMiniGraphic_MM(int x, int y, int graphic)
+{
+  DrawMiniGraphicExt_MM(drawto, SX + x*MINI_TILEX, SY + y*MINI_TILEY, graphic);
+  MarkTileDirty(x/2, y/2);
+}
+
+void getMicroGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
+{
+  getSizedGraphicSource(graphic, 0, TILESIZE / 4, bitmap, x, y);
+}
+
+void DrawMiniGraphicExt_MM(DrawBuffer *d, int x, int y, int graphic)
+{
+  Bitmap *bitmap;
+  int src_x, src_y;
+
+  getMiniGraphicSource(graphic, &bitmap, &src_x, &src_y);
+  BlitBitmap(bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
+}
+
+void DrawGraphicShifted_MM(int x,int y, int dx,int dy, int graphic,
+                       int cut_mode, int mask_mode)
+{
+  int width = TILEX, height = TILEY;
+  int cx = 0, cy = 0;
+  int src_x, src_y, dest_x, dest_y;
+  Bitmap *src_bitmap;
+
+  if (graphic < 0)
+  {
+    DrawGraphic_MM(x, y, graphic);
+    return;
+  }
+
+  if (dx || dy)                        /* Verschiebung der Grafik? */
+  {
+    if (x < BX1)               /* Element kommt von links ins Bild */
+    {
+      x = BX1;
+      width = dx;
+      cx = TILEX - dx;
+      dx = 0;
+    }
+    else if (x > BX2)          /* Element kommt von rechts ins Bild */
+    {
+      x = BX2;
+      width = -dx;
+      dx = TILEX + dx;
+    }
+    else if (x==BX1 && dx < 0) /* Element verläßt links das Bild */
+    {
+      width += dx;
+      cx = -dx;
+      dx = 0;
+    }
+    else if (x==BX2 && dx > 0) /* Element verläßt rechts das Bild */
+      width -= dx;
+    else if (dx)               /* allg. Bewegung in x-Richtung */
+      MarkTileDirty(x + SIGN(dx), y);
+
+    if (y < BY1)               /* Element kommt von oben ins Bild */
+    {
+      if (cut_mode==CUT_BELOW) /* Element oberhalb des Bildes */
+       return;
+
+      y = BY1;
+      height = dy;
+      cy = TILEY - dy;
+      dy = 0;
+    }
+    else if (y > BY2)          /* Element kommt von unten ins Bild */
+    {
+      y = BY2;
+      height = -dy;
+      dy = TILEY + dy;
+    }
+    else if (y==BY1 && dy < 0) /* Element verläßt oben das Bild */
+    {
+      height += dy;
+      cy = -dy;
+      dy = 0;
+    }
+    else if (dy > 0 && cut_mode == CUT_ABOVE)
+    {
+      if (y == BY2)            /* Element unterhalb des Bildes */
+       return;
+
+      height = dy;
+      cy = TILEY - dy;
+      dy = TILEY;
+      MarkTileDirty(x, y + 1);
+    }                          /* Element verläßt unten das Bild */
+    else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
+      height -= dy;
+    else if (dy)               /* allg. Bewegung in y-Richtung */
+      MarkTileDirty(x, y + SIGN(dy));
+  }
+
+  getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
+
+  src_x += cx;
+  src_y += cy;
+
+  dest_x = FX + x * TILEX + dx;
+  dest_y = FY + y * TILEY + dy;
+
+#if DEBUG
+  if (!IN_SCR_FIELD(x,y))
+  {
+    printf("DrawGraphicShifted_MM(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
+    printf("DrawGraphicShifted_MM(): This should never happen!\n");
+    return;
+  }
+#endif
+
+  if (mask_mode == USE_MASKING)
+  {
+    BlitBitmapMasked(src_bitmap, drawto_field,
+                    src_x, src_y, TILEX, TILEY, dest_x, dest_y);
+  }
+  else
+    BlitBitmap(src_bitmap, drawto_field,
+              src_x, src_y, width, height, dest_x, dest_y);
+
+  MarkTileDirty(x,y);
+}
+
+void DrawGraphicShiftedThruMask_MM(int x,int y, int dx,int dy, int graphic,
+                               int cut_mode)
+{
+  DrawGraphicShifted_MM(x,y, dx,dy, graphic, cut_mode, USE_MASKING);
+}
+
+void DrawScreenElementExt_MM(int x, int y, int dx, int dy, int element,
+                         int cut_mode, int mask_mode)
+{
+  int ux = LEVELX(x), uy = LEVELY(y);
+  int graphic = el2gfx(element);
+  int phase8 = ABS(MovPos[ux][uy]) / (TILEX / 8);
+  int phase2  = phase8 / 4;
+  int dir = MovDir[ux][uy];
+
+  if (element == EL_PACMAN)
+  {
+    graphic += 4 * !phase2;
+
+    if (dir == MV_UP)
+      graphic += 1;
+    else if (dir == MV_LEFT)
+      graphic += 2;
+    else if (dir == MV_DOWN)
+      graphic += 3;
+  }
+
+  if (dx || dy)
+    DrawGraphicShifted_MM(x, y, dx, dy, graphic, cut_mode, mask_mode);
+  else if (mask_mode == USE_MASKING)
+    DrawGraphicThruMask_MM(x, y, graphic);
+  else
+    DrawGraphic_MM(x, y, graphic);
+}
+
+void DrawLevelElementExt_MM(int x, int y, int dx, int dy, int element,
+                        int cut_mode, int mask_mode)
+{
+  if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+    DrawScreenElementExt_MM(SCREENX(x), SCREENY(y), dx, dy, element,
+                        cut_mode, mask_mode);
+}
+
+void DrawScreenElementShifted_MM(int x, int y, int dx, int dy, int element,
+                             int cut_mode)
+{
+  DrawScreenElementExt_MM(x, y, dx, dy, element, cut_mode, NO_MASKING);
+}
+
+void DrawLevelElementShifted_MM(int x, int y, int dx, int dy, int element,
+                            int cut_mode)
+{
+  DrawLevelElementExt_MM(x, y, dx, dy, element, cut_mode, NO_MASKING);
+}
+
+void DrawScreenElementThruMask_MM(int x, int y, int element)
+{
+  DrawScreenElementExt_MM(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
+}
+
+void DrawLevelElementThruMask_MM(int x, int y, int element)
+{
+  DrawLevelElementExt_MM(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
+}
+
+void DrawLevelFieldThruMask_MM(int x, int y)
+{
+  DrawLevelElementExt_MM(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
+}
+
+void DrawScreenElement_MM(int x, int y, int element)
+{
+  DrawScreenElementExt_MM(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
+}
+
+void DrawLevelElement_MM(int x, int y, int element)
+{
+  if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+    DrawScreenElement_MM(SCREENX(x), SCREENY(y), element);
+}
+
+void DrawScreenField_MM(int x, int y)
+{
+  int element = Feld[x][y];
+
+  if (!IN_LEV_FIELD(x, y))
+    return;
+
+  if (IS_MOVING(x, y))
+  {
+    int horiz_move = (MovDir[x][y] == MV_LEFT || MovDir[x][y] == MV_RIGHT);
+
+    DrawScreenElement_MM(x, y, EL_EMPTY);
+
+    if (horiz_move)
+      DrawScreenElementShifted_MM(x, y, MovPos[x][y], 0, element, NO_CUTTING);
+    else
+      DrawScreenElementShifted_MM(x, y, 0, MovPos[x][y], element, NO_CUTTING);
+  }
+  else if (IS_BLOCKED(x, y))
+  {
+    int oldx, oldy;
+    int sx, sy;
+    int horiz_move;
+
+    Blocked2Moving(x, y, &oldx, &oldy);
+    sx = SCREENX(oldx);
+    sy = SCREENY(oldy);
+    horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
+                 MovDir[oldx][oldy] == MV_RIGHT);
+
+    DrawScreenElement_MM(x, y, EL_EMPTY);
+    element = Feld[oldx][oldy];
+
+    if (horiz_move)
+      DrawScreenElementShifted_MM(sx,sy, MovPos[oldx][oldy],0,element,NO_CUTTING);
+    else
+      DrawScreenElementShifted_MM(sx,sy, 0,MovPos[oldx][oldy],element,NO_CUTTING);
+  }
+  else if (IS_DRAWABLE(element))
+    DrawScreenElement_MM(x, y, element);
+  else
+    DrawScreenElement_MM(x, y, EL_EMPTY);
+}
+
+void DrawLevelField_MM(int x, int y)
+{
+  DrawScreenField_MM(x, y);
+}
+
+void DrawMiniElement_MM(int x, int y, int element)
+{
+  int graphic;
+
+  if (!element)
+  {
+    DrawMiniGraphic_MM(x, y, GFX_EMPTY);
+    return;
+  }
+
+  graphic = el2gfx(element);
+  DrawMiniGraphic_MM(x, y, graphic);
+}
+
+void DrawMiniElementOrWall_MM(int sx, int sy, int scroll_x, int scroll_y)
+{
+  int x = sx + scroll_x, y = sy + scroll_y;
+
+  if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
+    DrawMiniElement_MM(sx, sy, EL_EMPTY);
+  else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
+    DrawMiniElement_MM(sx, sy, Feld[x][y]);
+}
+
+void DrawField_MM(int x, int y)
+{
+  int element = Feld[x][y];
+
+  DrawElement_MM(x, y, element);
+}
+
+void DrawLevel_MM()
+{
+  int x,y;
+
+  ClearWindow();
+
+  for (x=0; x<lev_fieldx; x++)
+    for (y=0; y<lev_fieldy; y++)
+      DrawField_MM(x, y);
+
+  redraw_mask |= REDRAW_FIELD;
+}
+
+void DrawWallsExt_MM(int x, int y, int element, int draw_mask)
+{
+  Bitmap *bitmap;
+  int graphic = el2gfx(WALL_BASE(element));
+  int gx, gy;
+  int i;
+
+  getMiniGraphicSource(graphic, &bitmap, &gx, &gy);
+
+  if (game_status != LEVELED || !editor.draw_walls_masked)
+    DrawGraphic_MM(x, y, GFX_EMPTY);
+
+  /*
+  if (IS_WALL_WOOD(element) || IS_WALL_AMOEBA(element) ||
+      IS_DF_WALL_WOOD(element))
+    gx += MINI_TILEX;
+  if (IS_WALL_ICE(element) || IS_WALL_AMOEBA(element))
+    gy += MINI_TILEY;
+  */
+
+  for(i=0; i<4; i++)
+  {
+    int dest_x = SX + x * TILEX + MINI_TILEX * (i % 2);
+    int dest_y = SY + y * TILEY + MINI_TILEY * (i / 2);
+
+    if (!((1 << i) & draw_mask))
+      continue;
+
+    if (element & (1 << i))
+      BlitBitmap(bitmap, drawto, gx, gy, MINI_TILEX, MINI_TILEY,
+                dest_x, dest_y);
+    else if (!editor.draw_walls_masked)
+      ClearRectangle(drawto, dest_x, dest_y, MINI_TILEX, MINI_TILEY);
+  }
+
+  MarkTileDirty(x, y);
+}
+
+void DrawWalls_MM(int x, int y, int element)
+{
+  DrawWallsExt_MM(x, y, element, HIT_MASK_ALL);
+}
+
+void DrawWallsAnimation_MM(int x, int y, int element, int phase, int bit_mask)
+{
+  int graphic = GFX_WALL_SEVERAL;
+  int graphic_anim = graphic + (phase + 1) / 2;
+  int dx = (IS_WALL_AMOEBA(element) ? MINI_TILEX : 0);
+  int dy = MINI_TILEY;
+  int dx_anim = dx;
+  int dy_anim = ((phase + 1) % 2) * MINI_TILEY;
+  int i;
+
+  Bitmap *bitmap, *bitmap_anim;
+  int src_x, src_y;
+  int src_x_anim, src_y_anim;
+
+  getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
+  getGraphicSource(graphic_anim, 0, &bitmap_anim, &src_x_anim, &src_y_anim);
+
+  if (phase == 0)
+  {
+    DrawWalls_MM(x, y, element);
+    return;
+  }
+
+  for(i=0; i<4; i++)
+  {
+    if (element & (1 << i))
+    {
+      int dest_x = SX + x * TILEX + MINI_TILEX * (i % 2);
+      int dest_y = SY + y * TILEY + MINI_TILEY * (i / 2);
+      int gx, gy;
+
+      if (bit_mask & (1 << i))
+      {
+       gx = src_x_anim + dx_anim;
+       gy = src_y_anim + dy_anim;
+
+       BlitBitmap(bitmap_anim, drawto, gx, gy, MINI_TILEX, MINI_TILEY,
+                  dest_x, dest_y);
+      }
+      else
+      {
+       gx = src_x + dx;
+       gy = src_y + dy;
+
+       BlitBitmap(bitmap, drawto, gx, gy, MINI_TILEX, MINI_TILEY,
+                  dest_x, dest_y);
+      }
+    }
+  }
+
+  MarkTileDirty(x, y);
+}
+
+void DrawElement_MM(int x, int y, int element)
+{
+  if (element == EL_EMPTY)
+    DrawGraphic_MM(x, y, GFX_EMPTY);
+  else if (IS_WALL(element))
+    DrawWalls_MM(x, y, element);
+#if 0
+  else if (IS_WALL_CHANGING(element) && IS_WALL_CHANGING(Feld[x][y]))
+  {
+    int wall_element = Feld[x][y] - EL_WALL_CHANGING + Store[x][y];
+
+    DrawWalls_MM(x, y, wall_element);
+  }
+#endif
+  else if (element == EL_PACMAN)
+    DrawLevelField_MM(x, y);
+  else
+    DrawGraphic_MM(x, y, el2gfx(element));
+}
+
+void DrawMicroWalls_MM(int x, int y, int element)
+{
+  Bitmap *bitmap;
+  int graphic = el2gfx(WALL_BASE(element));
+  int gx, gy;
+  int i;
+
+  getMicroGraphicSource(graphic, &bitmap, &gx, &gy);
+
+  for (i=0; i<4; i++)
+  {
+    int xpos = MICROLEV_XPOS + x * MICRO_TILEX + MICRO_WALLX * (i % 2);
+    int ypos = MICROLEV_YPOS + y * MICRO_TILEY + MICRO_WALLY * (i / 2);
+
+    if (element & (1 << i))
+      BlitBitmap(bitmap, drawto, gx, gy, MICRO_WALLX, MICRO_WALLY, xpos, ypos);
+    else
+      ClearRectangle(drawto, xpos, ypos, MICRO_WALLX, MICRO_WALLY);
+  }
+}
+
+void DrawMicroElement_MM(int x, int y, int element)
+{
+  Bitmap *bitmap;
+  int graphic = el2gfx(element);
+  int gx, gy;
+
+  if (element == EL_EMPTY)
+    return;
+
+  if (IS_WALL(element))
+  {
+    DrawMicroWalls_MM(x, y, element);
+    return;
+  }
+
+  getMicroGraphicSource(graphic, &bitmap, &gx, &gy);
+
+  BlitBitmap(bitmap, drawto, gx, gy, MICRO_TILEX, MICRO_TILEY,
+            MICROLEV_XPOS + x * MICRO_TILEX, MICROLEV_YPOS + y * MICRO_TILEY);
+}
+
+void DrawMicroLevelExt_MM(int xpos, int ypos)
+{
+  int x,y;
+
+  ClearRectangle(drawto, xpos, ypos, MICROLEV_XSIZE, MICROLEV_YSIZE);
+
+  for (x=0; x<STD_LEV_FIELDX; x++)
+    for (y=0; y<STD_LEV_FIELDY; y++)
+      DrawMicroElement_MM(x, y, Ur[x][y]);
+
+  redraw_mask |= REDRAW_FIELD;
+}
+
+void DrawMiniLevel_MM(int size_x, int size_y, int scroll_x, int scroll_y)
+{
+  int x,y;
+
+  for(x=0; x<size_x; x++)
+    for(y=0; y<size_y; y++)
+      DrawMiniElementOrWall_MM(x, y, scroll_x, scroll_y);
+
+  redraw_mask |= REDRAW_FIELD;
+}
+
+int REQ_in_range(int x, int y)
+{
+  if (y > DY+249 && y < DY+278)
+  {
+    if (x > DX+1 && x < DX+48)
+      return 1;
+    else if (x > DX+51 && x < DX+98)
+      return 2;
+  }
+  return 0;
+}
+
+Pixel ReadPixel(DrawBuffer *bitmap, int x, int y)
+{
+#if defined(TARGET_SDL) || defined(TARGET_ALLEGRO)
+  return GetPixel(bitmap, x, y);
+#else
+  /* GetPixel() does also work for X11, but we use some optimization here */
+  unsigned int pixel_value;
+
+  if (bitmap == pix[PIX_BACK])
+  {
+    /* when reading pixel values from images, it is much faster to use
+       client side images (XImage) than server side images (Pixmap) */
+    static XImage *client_image = NULL;
+
+    if (client_image == NULL)  /* init image cache, if not existing */
+      client_image = XGetImage(display, bitmap->drawable,
+                              0,0, WIN_XSIZE,WIN_YSIZE, AllPlanes, ZPixmap);
+
+    pixel_value = XGetPixel(client_image, x, y);
+  }
+  else
+  {
+    XImage *pixel_image;
+
+    pixel_image = XGetImage(display, bitmap->drawable, x, y, 1, 1,
+                           AllPlanes, ZPixmap);
+    pixel_value = XGetPixel(pixel_image, 0, 0);
+
+    XDestroyImage(pixel_image);
+  }
+
+  return pixel_value;
+#endif
+}
+
+void SetRGB(unsigned int pixel,
+           unsigned short red, unsigned short green, unsigned short blue)
+{
+  return;
+
+#if 0
+  XColor color;
+
+  if (color_status==STATIC_COLORS)
+    return;
+
+  color.pixel = pixel;
+  color.red = red;
+  color.green = green;
+  color.blue = blue;
+  color.flags = DoRed | DoGreen | DoBlue;
+  XStoreColor(display, cmap, &color);
+  XFlush(display);
+#endif
+}
+
+int get_base_element(int element)
+{
+  if (IS_MIRROR(element))
+    return EL_MIRROR_START;
+  else if (IS_MIRROR_FIXED(element))
+    return EL_MIRROR_FIXED_START;
+  else if (IS_POLAR(element))
+    return EL_POLAR_START;
+  else if (IS_POLAR_CROSS(element))
+    return EL_POLAR_CROSS_START;
+  else if (IS_BEAMER(element))
+    return EL_BEAMER_RED_START + BEAMER_NR(element) * 16;
+  else if (IS_FIBRE_OPTIC(element))
+    return EL_FIBRE_OPTIC_START + FIBRE_OPTIC_NR(element) * 2;
+  else if (IS_MCDUFFIN(element))
+    return EL_MCDUFFIN_START;
+  else if (IS_LASER(element))
+    return EL_LASER_START;
+  else if (IS_RECEIVER(element))
+    return EL_RECEIVER_START;
+  else if (IS_DF_MIRROR(element))
+    return EL_DF_MIRROR_START;
+  else if (IS_DF_MIRROR_AUTO(element))
+    return EL_DF_MIRROR_AUTO_START;
+  else if (IS_PACMAN(element))
+    return EL_PACMAN_START;
+  else if (IS_GRID_STEEL(element))
+    return EL_GRID_STEEL_START;
+  else if (IS_GRID_WOOD(element))
+    return EL_GRID_WOOD_START;
+  else if (IS_GRID_STEEL_FIXED(element))
+    return EL_GRID_STEEL_FIXED_START;
+  else if (IS_GRID_WOOD_FIXED(element))
+    return EL_GRID_WOOD_FIXED_START;
+  else if (IS_GRID_STEEL_AUTO(element))
+    return EL_GRID_STEEL_AUTO_START;
+  else if (IS_GRID_WOOD_AUTO(element))
+    return EL_GRID_WOOD_AUTO_START;
+  else if (IS_WALL_STEEL(element))
+    return EL_WALL_STEEL_START;
+  else if (IS_WALL_WOOD(element))
+    return EL_WALL_WOOD_START;
+  else if (IS_WALL_ICE(element))
+    return EL_WALL_ICE_START;
+  else if (IS_WALL_AMOEBA(element))
+    return EL_WALL_AMOEBA_START;
+  else if (IS_DF_WALL_STEEL(element))
+    return EL_DF_WALL_STEEL_START;
+  else if (IS_DF_WALL_WOOD(element))
+    return EL_DF_WALL_WOOD_START;
+  else if (IS_CHAR(element))
+    return EL_CHAR_START;
+  else
+    return element;
+}
+
+int get_element_phase(int element)
+{
+  return element - get_base_element(element);
+}
+
+int get_num_elements(int element)
+{
+  if (IS_MIRROR(element) ||
+      IS_POLAR(element) ||
+      IS_BEAMER(element) ||
+      IS_DF_MIRROR(element) ||
+      IS_DF_MIRROR_AUTO(element))
+    return 16;
+  else if (IS_GRID_STEEL_FIXED(element) ||
+          IS_GRID_WOOD_FIXED(element) ||
+          IS_GRID_STEEL_AUTO(element) ||
+          IS_GRID_WOOD_AUTO(element))
+    return 8;
+  else if (IS_MIRROR_FIXED(element) ||
+          IS_POLAR_CROSS(element) ||
+          IS_MCDUFFIN(element) ||
+          IS_LASER(element) ||
+          IS_RECEIVER(element) ||
+          IS_PACMAN(element) ||
+          IS_GRID_STEEL(element) ||
+          IS_GRID_WOOD(element))
+    return 4;
+  else
+    return 1;
+}
+
+int get_rotated_element(int element, int step)
+{
+  int base_element = get_base_element(element);
+  int num_elements = get_num_elements(element);
+  int element_phase = element - base_element;
+
+  return base_element + (element_phase + step + num_elements) % num_elements;
+}
+
+int el2gfx(int element)
+{
+  switch(element)
+  {
+    case EL_EMPTY:             return -1;
+    case EL_GRID_STEEL_00:     return GFX_GRID_STEEL_00;
+    case EL_GRID_STEEL_01:     return GFX_GRID_STEEL_01;
+    case EL_GRID_STEEL_02:     return GFX_GRID_STEEL_02;
+    case EL_GRID_STEEL_03:     return GFX_GRID_STEEL_03;
+    case EL_MCDUFFIN_RIGHT:    return GFX_MCDUFFIN_RIGHT;
+    case EL_MCDUFFIN_UP:       return GFX_MCDUFFIN_UP;
+    case EL_MCDUFFIN_LEFT:     return GFX_MCDUFFIN_LEFT;
+    case EL_MCDUFFIN_DOWN:     return GFX_MCDUFFIN_DOWN;
+    case EL_EXIT_CLOSED:       return GFX_EXIT_CLOSED;
+    case EL_EXIT_OPENING_1:    return GFX_EXIT_OPENING_1;
+    case EL_EXIT_OPENING_2:    return GFX_EXIT_OPENING_2;
+    case EL_EXIT_OPEN:         return GFX_EXIT_OPEN;
+    case EL_KETTLE:            return GFX_KETTLE;
+    case EL_BOMB:              return GFX_BOMB;
+    case EL_PRISM:             return GFX_PRISM;
+    case EL_BLOCK_WOOD:                return GFX_BLOCK_WOOD;
+    case EL_BALL_GRAY:         return GFX_BALL_GRAY;
+    case EL_FUSE_ON:           return GFX_FUSE_ON;
+    case EL_PACMAN_RIGHT:      return GFX_PACMAN_RIGHT;
+    case EL_PACMAN_UP:         return GFX_PACMAN_UP;
+    case EL_PACMAN_LEFT:       return GFX_PACMAN_LEFT;
+    case EL_PACMAN_DOWN:       return GFX_PACMAN_DOWN;
+    case EL_POLAR_CROSS_00:    return GFX_POLAR_CROSS_00;
+    case EL_POLAR_CROSS_01:    return GFX_POLAR_CROSS_01;
+    case EL_POLAR_CROSS_02:    return GFX_POLAR_CROSS_02;
+    case EL_POLAR_CROSS_03:    return GFX_POLAR_CROSS_03;
+    case EL_MIRROR_FIXED_00:   return GFX_MIRROR_FIXED_00;
+    case EL_MIRROR_FIXED_01:   return GFX_MIRROR_FIXED_01;
+    case EL_MIRROR_FIXED_02:   return GFX_MIRROR_FIXED_02;
+    case EL_MIRROR_FIXED_03:   return GFX_MIRROR_FIXED_03;
+    case EL_GATE_STONE:                return GFX_GATE_STONE;
+    case EL_KEY:               return GFX_KEY;
+    case EL_LIGHTBULB_ON:      return GFX_LIGHTBULB_ON;
+    case EL_LIGHTBULB_OFF:     return GFX_LIGHTBULB_OFF;
+    case EL_LIGHTBALL:         return GFX_BALL_RED + RND(3);;
+    case EL_BLOCK_STONE:       return GFX_BLOCK_STONE;
+    case EL_GATE_WOOD:         return GFX_GATE_WOOD;
+    case EL_FUEL_FULL:         return GFX_FUEL_FULL;
+    case EL_GRID_WOOD_00:      return GFX_GRID_WOOD_00;
+    case EL_GRID_WOOD_01:      return GFX_GRID_WOOD_01;
+    case EL_GRID_WOOD_02:      return GFX_GRID_WOOD_02;
+    case EL_GRID_WOOD_03:      return GFX_GRID_WOOD_03;
+    case EL_FUEL_EMPTY:                return GFX_FUEL_EMPTY;
+    case EL_FUSE_OFF:          return GFX_FUSE_OFF;
+    case EL_PACMAN:            return GFX_PACMAN;
+    case EL_REFRACTOR:         return GFX_REFRACTOR;
+    case EL_CELL:              return GFX_CELL;
+    case EL_MINE:              return GFX_MINE;
+
+    /* pseudo-graphics; will be mapped to other graphics */
+    case EL_WALL_STEEL:                return GFX_WALL_STEEL;
+    case EL_WALL_WOOD:         return GFX_WALL_WOOD;
+    case EL_WALL_ICE:          return GFX_WALL_ICE;
+    case EL_WALL_AMOEBA:       return GFX_WALL_AMOEBA;
+    case EL_DF_WALL_STEEL:     return GFX_DF_WALL_STEEL;
+    case EL_DF_WALL_WOOD:      return GFX_DF_WALL_WOOD;
+
+    default:
+    {
+      boolean ed = (game_status == LEVELED);
+      int base_element = get_base_element(element);
+      int element_phase = element - base_element;
+      int base_graphic;
+
+      if (IS_BEAMER(element))
+       element_phase = element - EL_BEAMER_RED_START;
+      else if (IS_FIBRE_OPTIC(element))
+       element_phase = element - EL_FIBRE_OPTIC_START;
+
+      if (IS_MIRROR(element))
+       base_graphic = GFX_MIRROR_START;
+      else if (IS_BEAMER_OLD(element))
+       base_graphic = GFX_BEAMER_START;
+      else if (IS_POLAR(element))
+       base_graphic = GFX_POLAR_START;
+      else if (IS_CHAR(element))
+       base_graphic = GFX_CHAR_START;
+      else if (IS_GRID_WOOD_FIXED(element))
+       base_graphic = GFX_GRID_WOOD_FIXED_00;
+      else if (IS_GRID_STEEL_FIXED(element))
+       base_graphic = GFX_GRID_STEEL_FIXED_00;
+      else if (IS_DF_MIRROR(element))
+       base_graphic = GFX_DF_MIRROR_00;
+      else if (IS_LASER(element))
+       base_graphic = GFX_LASER_RIGHT;
+      else if (IS_RECEIVER(element))
+       base_graphic = GFX_RECEIVER_RIGHT;
+      else if (IS_DF_MIRROR(element))
+       base_graphic = GFX_DF_MIRROR_00;
+      else if (IS_FIBRE_OPTIC(element))
+       base_graphic = (ed ? GFX_FIBRE_OPTIC_ED_00 : GFX_FIBRE_OPTIC_00);
+      else if (IS_GRID_WOOD_AUTO(element))
+       base_graphic = (ed ? GFX_GRID_WOOD_AUTO_00 : GFX_GRID_WOOD_FIXED_00);
+      else if (IS_GRID_STEEL_AUTO(element))
+       base_graphic = (ed ? GFX_GRID_STEEL_AUTO_00 : GFX_GRID_STEEL_FIXED_00);
+      else if (IS_DF_MIRROR_AUTO(element))
+       base_graphic = (ed ? GFX_DF_MIRROR_AUTO_00 : GFX_DF_MIRROR_00);
+      else if (IS_BEAMER(element))
+       base_graphic = GFX_BEAMER_RED_START;
+      else
+       return GFX_EMPTY;
+
+      return base_graphic + element_phase;
+    }
+  }
+}
+
+void RedrawPlayfield_MM()
+{
+  DrawLevel_MM();
+}
+
+void BlitScreenToBitmap_MM(Bitmap *target_bitmap)
+{
+  BlitBitmap(drawto_field, target_bitmap, 0, 0, SXSIZE, SYSIZE, SX, SY);
+}
diff --git a/src/game_mm/mm_tools.h b/src/game_mm/mm_tools.h
new file mode 100644 (file)
index 0000000..39c58a8
--- /dev/null
@@ -0,0 +1,131 @@
+/***********************************************************
+* Mirror Magic -- McDuffin's Revenge                       *
+*----------------------------------------------------------*
+* (c) 1994-2001 Artsoft Entertainment                      *
+*               Holger Schemel                             *
+*               Detmolder Strasse 189                      *
+*               33604 Bielefeld                            *
+*               Germany                                    *
+*               e-mail: info@artsoft.org                   *
+*----------------------------------------------------------*
+* tools.h                                                  *
+***********************************************************/
+
+#ifndef MM_TOOLS_H
+#define MM_TOOLS_H
+
+#include "main_mm.h"
+
+/* for SetDrawtoField */
+#define DRAW_DIRECT            0
+#define DRAW_BUFFERED          1
+#define DRAW_BACKBUFFER                2
+
+/* for DrawElementShifted */
+#define NO_CUTTING             0
+#define CUT_ABOVE              (1 << 0)
+#define CUT_BELOW              (1 << 1)
+#define CUT_LEFT               (1 << 2)
+#define CUT_RIGHT              (1 << 3)
+
+/* for masking functions */
+#define NO_MASKING             0
+#define USE_MASKING            1
+
+/* for MoveDoor */
+#define DOOR_OPEN_1            (1 << 0)
+#define DOOR_OPEN_2            (1 << 1)
+#define DOOR_CLOSE_1           (1 << 2)
+#define DOOR_CLOSE_2           (1 << 3)
+#define DOOR_OPEN_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)
+#define DOOR_COPY_BACK         (1 << 4)
+#define DOOR_NO_DELAY          (1 << 5)
+#define DOOR_GET_STATE         (1 << 6)
+
+/* for Request */
+#define REQ_ASK                        (1 << 0)
+#define REQ_OPEN               (1 << 1)
+#define REQ_CLOSE              (1 << 2)
+#define REQ_CONFIRM            (1 << 3)
+#define REQ_STAY_CLOSED                (1 << 4)
+#define REQ_STAY_OPEN          (1 << 5)
+
+#define REQUEST_WAIT_FOR       (REQ_ASK | REQ_CONFIRM)
+
+/* font types */
+#define FS_SMALL               0
+#define FS_BIG                 1
+#define FS_MEDIUM              2
+
+
+void SetDrawtoField_MM(int);
+void BackToFront();
+void FadeToFront();
+void ClearWindow();
+
+void MarkTileDirty(int, int);
+
+void DrawAllPlayers_MM(void);
+void DrawPlayerField_MM(int, int);
+void DrawPlayer_MM(struct PlayerInfo *);
+void DrawGraphicAnimationExt_MM(int, int, int, int, int, int, int);
+void DrawGraphicAnimation_MM(int, int, int, int, int, int);
+void DrawGraphicAnimationThruMask_MM(int, int, int, int, int, int);
+
+void DrawGraphic_MM(int, int, int);
+void DrawGraphicExt_MM(DrawBuffer *, int, int, int);
+void DrawGraphicThruMask_MM(int, int, int);
+void DrawGraphicThruMaskExt_MM(DrawBuffer *, int, int, int);
+void DrawMiniGraphic_MM(int, int, int);
+void getMiniGraphicSource(int, Bitmap **, int *, int *);
+void DrawMiniGraphicExt_MM(DrawBuffer *, int, int, int);
+void DrawGraphicShifted_MM(int, int, int, int, int, int, int);
+void DrawGraphicShiftedThruMask_MM(int, int, int, int, int, int);
+void DrawScreenElementExt_MM(int, int, int, int, int, int, int);
+void DrawLevelElementExt_MM(int, int, int, int, int, int, int);
+void DrawScreenElementShifted_MM(int, int, int, int, int, int);
+void DrawLevelElementShifted_MM(int, int, int, int, int, int);
+void DrawScreenElementThruMask_MM(int, int, int);
+void DrawLevelElementThruMask_MM(int, int, int);
+void DrawLevelFieldThruMask_MM(int, int);
+void ErdreichAnbroeckeln(int, int);
+void DrawScreenElement_MM(int, int, int);
+void DrawLevelElement_MM(int, int, int);
+void DrawScreenField_MM(int, int);
+void DrawLevelField_MM(int, int);
+void DrawMiniElement_MM(int, int, int);
+void DrawMiniElementOrWall_MM(int, int, int, int);
+
+void DrawField_MM(int, int);
+void DrawLevel_MM(void);
+void DrawElement_MM(int, int, int);
+void DrawWallsExt_MM(int, int, int, int);
+void DrawWalls_MM(int, int, int);
+void DrawWallsAnimation_MM(int, int, int, int, int);
+void DrawMiniLevel_MM(int, int, int, int);
+void DrawMicroLevel_MM(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);
+void DrawSpecialEditorDoor_MM();
+void UndrawSpecialEditorDoor();
+Pixel ReadPixel(DrawBuffer *, int, int);
+void SetRGB(unsigned int, unsigned short, unsigned short, unsigned short);
+
+void CreateToolButtons();
+
+int get_base_element(int);
+int get_element_phase(int);
+int get_num_elements(int);
+int get_rotated_element(int, int);
+
+int el2gfx(int);
+
+#endif