--- /dev/null
+# =============================================================================
+# 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
--- /dev/null
+#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 */
--- /dev/null
+// ============================================================================
+// 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 */
--- /dev/null
+#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 */
--- /dev/null
+// ============================================================================
+// 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);
+}
--- /dev/null
+// ============================================================================
+// 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;
+ }
+}
--- /dev/null
+// ============================================================================
+// 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
--- /dev/null
+// ============================================================================
+// 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);
+}
--- /dev/null
+// ============================================================================
+// 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 *);
--- /dev/null
+// ============================================================================
+// 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 */
--- /dev/null
+/***********************************************************
+* 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);
+}
--- /dev/null
+/***********************************************************
+* 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