From: Holger Schemel Date: Tue, 7 Feb 2017 20:29:46 +0000 (+0100) Subject: added and adjusted source files for Mirror Magic game engine X-Git-Tag: 4.1.0.0~233 X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=commitdiff_plain;h=8cef7dc31f5b96c492079d59acab2cb1587b3c8c added and adjusted source files for Mirror Magic game engine --- diff --git a/src/game_mm/Makefile b/src/game_mm/Makefile new file mode 100644 index 00000000..270ab7d1 --- /dev/null +++ b/src/game_mm/Makefile @@ -0,0 +1,62 @@ +# ============================================================================= +# Rocks'n'Diamonds - McDuffin Strikes Back! +# ----------------------------------------------------------------------------- +# (c) 1989-2017 by Artsoft Entertainment +# Holger Schemel +# info@artsoft.org +# http://www.artsoft.org/ +# ----------------------------------------------------------------------------- +# The native Mirror Magic game engine is based on: +# - Mirror Magic II by Holger Schemel (Linux/DOS/Windows version, 1995) +# - Mindbender by Holger Schemel (Amiga version, 1989) +# ----------------------------------------------------------------------------- +# src/game_mm/Makefile +# ============================================================================= + +# ----------------------------------------------------------------------------- +# configuration +# ----------------------------------------------------------------------------- + +SRCS = mm_init.c \ + mm_main.c \ + mm_game.c \ + mm_files.c \ + mm_tools.c + +OBJS = mm_init.o \ + mm_main.o \ + mm_game.o \ + mm_files.o \ + mm_tools.o + +GAME_MM = game_mm.a + + +# ----------------------------------------------------------------------------- +# build targets +# ----------------------------------------------------------------------------- + +all: $(GAME_MM) + +$(GAME_MM): $(OBJS) + $(AR) cru $(GAME_MM) $(OBJS) + $(RANLIB) $(GAME_MM) + +.c.o: + $(CC) $(PROFILING) $(CFLAGS) -c $*.c + +clean: + $(RM) $(OBJS) + $(RM) $(GAME_MM) + + +# ----------------------------------------------------------------------------- +# development only +# ----------------------------------------------------------------------------- + +depend: + for i in $(SRCS); do $(CPP) $(CFLAGS) -M $$i; done > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/src/game_mm/export.h b/src/game_mm/export.h new file mode 100644 index 00000000..2bbd5dc4 --- /dev/null +++ b/src/game_mm/export.h @@ -0,0 +1,193 @@ +#ifndef GAME_MM_EXPORT_H +#define GAME_MM_EXPORT_H + +/* ========================================================================= */ +/* functions and definitions exported from game_mm to main program */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* constant definitions */ +/* ------------------------------------------------------------------------- */ + +#define MM_MAX_PLAYFIELD_WIDTH MAX_PLAYFIELD_WIDTH +#define MM_MAX_PLAYFIELD_HEIGHT MAX_PLAYFIELD_HEIGHT + +#define MM_STD_PLAYFIELD_WIDTH 16 +#define MM_STD_PLAYFIELD_HEIGHT 12 + +#define MM_MAX_PLAYFIELD_SIZE (MM_MAX_PLAYFIELD_WIDTH * \ + MM_MAX_PLAYFIELD_HEIGHT) + +#define MAX_NUM_AMOEBA 100 +#define MAX_NUM_BEAMERS 8 + +#define MAX_LASER_LEN 256 +#define MAX_LASER_ENERGY 100 +#define MAX_LASER_OVERLOAD 100 + +#define MM_LEVEL_SCORE_ELEMENTS 16 + +#define MM_MAX_LEVEL_NAME_LEN 32 +#define MM_MAX_LEVEL_AUTHOR_LEN 32 + + +/* ------------------------------------------------------------------------- */ +/* data structure definitions */ +/* ------------------------------------------------------------------------- */ + +struct GlobalInfo_MM +{ +}; + +struct CycleList +{ + int x, y; + int steps; +}; + +struct MovingList +{ + int x, y; + int dir; +}; + +struct DamageList +{ + int x, y; + int edge, angle; + boolean is_mirror; +}; + +struct BeamerInfo +{ + int x, y; + int num; +}; + +struct PacMan +{ + int XP, YP; + int Dr; +}; + +struct LaserInfo +{ + struct XY start_edge; + int start_angle; + + int current_angle; + + struct DamageList damage[MAX_LASER_LEN + 10]; + int num_damages; + + struct XY edge[MAX_LASER_LEN + 10]; + int num_edges; + + struct BeamerInfo beamer[MAX_NUM_BEAMERS][2]; + int beamer_edge[MAX_NUM_BEAMERS]; + int beamer_nr[MAX_NUM_BEAMERS]; + int num_beamers; + + boolean overloaded; + int overload_value; + + boolean fuse_off; + int fuse_x, fuse_y; + + int dest_element; + boolean stops_inside_element; + + boolean redraw; + + int wall_mask; +}; + +struct GameInfo_MM +{ + boolean LevelSolved; + boolean GameOver; + + struct CycleList cycle[MM_MAX_PLAYFIELD_SIZE]; + int num_cycle; + + struct MovingList pacman[MM_MAX_PLAYFIELD_SIZE]; + int num_pacman; + + int score; + int energy_left; + int kettles_still_needed; + int lights_still_needed; + int num_keys; + + boolean level_solved; + boolean game_over; + int game_over_cause; + + boolean cheat_no_overload; + boolean cheat_no_explosion; +}; + +struct LevelInfo_MM +{ + int file_version; /* version of file the level was stored with */ + int game_version; /* version of game engine to play this level */ + boolean encoding_16bit_field; /* level contains 16-bit elements */ + + int fieldx; + int fieldy; + int time; + int kettles_needed; + boolean auto_count_kettles; + boolean laser_red, laser_green, laser_blue; + char name[MM_MAX_LEVEL_NAME_LEN + 1]; + char author[MM_MAX_LEVEL_AUTHOR_LEN + 1]; + int score[MM_LEVEL_SCORE_ELEMENTS]; + int amoeba_speed; + int time_fuse; + + short field[MAX_PLAYFIELD_WIDTH][MAX_PLAYFIELD_HEIGHT]; +}; + +struct GraphicInfo_MM +{ +}; + +struct EngineSnapshotInfo_MM +{ +}; + + +/* ------------------------------------------------------------------------- */ +/* exported functions */ +/* ------------------------------------------------------------------------- */ + +extern struct GlobalInfo_MM global_mm_info; +extern struct GameInfo_MM game_mm; +extern struct LevelInfo_MM native_mm_level; +extern struct EngineSnapshotInfo_MM engine_snapshot_mm; + +extern void mm_open_all(); +extern void mm_close_all(); + +extern void InitGfxBuffers_MM(); + +extern void InitGameEngine_MM(); +extern void GameActions_MM(byte *, boolean); + +extern unsigned int InitEngineRandom_MM(int); + +extern void setLevelInfoToDefaults_MM(); +extern void copyInternalEngineVars_MM(); +extern boolean LoadNativeLevel_MM(char *, boolean); +extern void SaveNativeLevel_MM(char *); + +extern int getFieldbufferOffsetX_MM(); +extern int getFieldbufferOffsetY_MM(); + +extern void BlitScreenToBitmap_MM(Bitmap *); +extern void RedrawPlayfield_MM(); + +extern void LoadEngineSnapshotValues_MM(); +extern void SaveEngineSnapshotValues_MM(ListNode **); + +#endif /* GAME_MM_EXPORT_H */ diff --git a/src/game_mm/game_mm.h b/src/game_mm/game_mm.h new file mode 100644 index 00000000..eab1a757 --- /dev/null +++ b/src/game_mm/game_mm.h @@ -0,0 +1,19 @@ +// ============================================================================ +// Mirror Magic -- McDuffin's Revenge +// ---------------------------------------------------------------------------- +// (c) 1994-2017 by Artsoft Entertainment +// Holger Schemel +// info@artsoft.org +// http://www.artsoft.org/ +// ---------------------------------------------------------------------------- +// game_mm.h +// ============================================================================ + +#ifndef GAME_MM_H +#define GAME_MM_H + +#define GAME_MM_VERSION_1_0_0 + +#include "export.h" + +#endif /* GAME_MM_H */ diff --git a/src/game_mm/main_mm.h b/src/game_mm/main_mm.h new file mode 100644 index 00000000..4eb98fc7 --- /dev/null +++ b/src/game_mm/main_mm.h @@ -0,0 +1,80 @@ +#ifndef MAIN_MM_H +#define MAIN_MM_H + +/* ========================================================================= */ +/* external functions and definitions imported from main program to game_mm */ +/* ========================================================================= */ + +#include "../engines.h" +#include "../conf_gfx.h" + + +/* ========================================================================= */ +/* functions and definitions that are exported from game_mm to main program */ +/* ========================================================================= */ + +#include "export.h" + + +/* ========================================================================= */ +/* internal functions and definitions that are not exported to main program */ +/* ========================================================================= */ + + +/* ------------------------------------------------------------------------- */ +/* constant definitions */ +/* ------------------------------------------------------------------------- */ + +/* screen sizes and positions for MM engine */ + +extern int TILESIZE_VAR; + +#define TILESIZE 32 +#define TILEX TILESIZE +#define TILEY TILESIZE +#define TILEX_VAR TILESIZE_VAR +#define TILEY_VAR TILESIZE_VAR + +extern int SCR_FIELDX, SCR_FIELDY; + +#define MAX_BUF_XSIZE SCR_FIELDX +#define MAX_BUF_YSIZE SCR_FIELDY + +/* often used screen positions */ + +extern int SX, SY; + +#define SXSIZE (SCR_FIELDX * TILEX_VAR) +#define SYSIZE (SCR_FIELDY * TILEY_VAR) +#define FXSIZE (MAX_BUF_XSIZE * TILEX_VAR) +#define FYSIZE (MAX_BUF_YSIZE * TILEY_VAR) + +extern int REAL_SX, REAL_SY; + +#define FULL_SXSIZE (2 + SXSIZE + 2) +#define FULL_SYSIZE (2 + SYSIZE + 2) + + +/* ------------------------------------------------------------------------- */ +/* data structure definitions */ +/* ------------------------------------------------------------------------- */ + +/* ------------------------------------------------------------------------- */ +/* exported variables */ +/* ------------------------------------------------------------------------- */ + +extern struct LevelInfo_MM native_mm_level; + +extern Bitmap *bitmap_db_field_mm; + +extern int GfxElementLast[MM_MAX_PLAYFIELD_WIDTH][MM_MAX_PLAYFIELD_HEIGHT]; +extern int GfxGraphicLast[MM_MAX_PLAYFIELD_WIDTH][MM_MAX_PLAYFIELD_HEIGHT]; +extern int GfxGraphic[MM_MAX_PLAYFIELD_WIDTH][MM_MAX_PLAYFIELD_HEIGHT]; +extern int GfxFrame[MM_MAX_PLAYFIELD_WIDTH][MM_MAX_PLAYFIELD_HEIGHT]; + + +/* ------------------------------------------------------------------------- */ +/* exported functions */ +/* ------------------------------------------------------------------------- */ + +#endif /* MAIN_MM_H */ diff --git a/src/game_mm/mm_files.c b/src/game_mm/mm_files.c new file mode 100644 index 00000000..277a2fb9 --- /dev/null +++ b/src/game_mm/mm_files.c @@ -0,0 +1,418 @@ +// ============================================================================ +// Mirror Magic -- McDuffin's Revenge +// ---------------------------------------------------------------------------- +// (c) 1994-2017 by Artsoft Entertainment +// Holger Schemel +// info@artsoft.org +// http://www.artsoft.org/ +// ---------------------------------------------------------------------------- +// mm_files.c +// ============================================================================ + +#include +#include +#include +#include + +#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= 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; iname[i] = getFile8Bit(file); + level->name[MAX_LEVEL_NAME_LEN] = 0; + + for(i=0; iscore[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; iauthor[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; yfieldy; y++) + for(x=0; xfieldx; 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; iname[i], file); + + for(i=0; iscore[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; iauthor[i], file); +} + +static void SaveLevel_MM_BODY(FILE *file, struct LevelInfo_MM *level) +{ + int x, y; + + for(y=0; yfieldy; y++) + for(x=0; xfieldx; 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 255) + native_mm_level.encoding_16bit_field = TRUE; + + body_chunk_size = + native_mm_level.fieldx * native_mm_level.fieldy * (native_mm_level.encoding_16bit_field ? 2 : 1); + + putFileChunk(file, "MMII", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN); + putFileChunk(file, "CAVE", CHUNK_SIZE_NONE, BYTE_ORDER_BIG_ENDIAN); + + putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN); + WriteChunk_MM_VERS(file, MM_FILE_VERSION_ACTUAL, MM_GAME_VERSION_ACTUAL); + + putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN); + SaveLevel_MM_HEAD(file, &native_mm_level); + + putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN); + SaveLevel_MM_AUTH(file, &native_mm_level); + + putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN); + SaveLevel_MM_BODY(file, &native_mm_level); + + fclose(file); + + SetFilePermissions(filename, PERMS_PRIVATE); +} diff --git a/src/game_mm/mm_game.c b/src/game_mm/mm_game.c new file mode 100644 index 00000000..5e42ceaa --- /dev/null +++ b/src/game_mm/mm_game.c @@ -0,0 +1,3895 @@ +// ============================================================================ +// Mirror Magic -- McDuffin's Revenge +// ---------------------------------------------------------------------------- +// (c) 1994-2017 by Artsoft Entertainment +// Holger Schemel +// info@artsoft.org +// http://www.artsoft.org/ +// ---------------------------------------------------------------------------- +// mm_game.c +// ============================================================================ + +#include "main_mm.h" + +#include "mm_main.h" +#include "mm_game.h" +#include "mm_tools.h" + +/* graphic position values for game controls */ +#define ENERGY_XSIZE 32 +#define ENERGY_YSIZE MAX_LASER_ENERGY +#define OVERLOAD_XSIZE ENERGY_XSIZE +#define OVERLOAD_YSIZE MAX_LASER_OVERLOAD + +/* values for Explode() */ +#define EX_PHASE_START 0 +#define EX_NORMAL 0 +#define EX_KETTLE 1 +#define EX_SHORT 2 + +/* special positions in the game control window (relative to control window) */ +#define XX_LEVEL 36 +#define YY_LEVEL 23 +#define XX_KETTLES 29 +#define YY_KETTLES 63 +#define XX_SCORE 22 +#define YY_SCORE 101 +#define XX_ENERGY 8 +#define YY_ENERGY 158 +#define XX_OVERLOAD 60 +#define YY_OVERLOAD YY_ENERGY + +/* special positions in the game control window (relative to main window) */ +#define DX_LEVEL (DX + XX_LEVEL) +#define DY_LEVEL (DY + YY_LEVEL) +#define DX_KETTLES (DX + XX_KETTLES) +#define DY_KETTLES (DY + YY_KETTLES) +#define DX_SCORE (DX + XX_SCORE) +#define DY_SCORE (DY + YY_SCORE) +#define DX_ENERGY (DX + XX_ENERGY) +#define DY_ENERGY (DY + YY_ENERGY) +#define DX_OVERLOAD (DX + XX_OVERLOAD) +#define DY_OVERLOAD (DY + YY_OVERLOAD) + +#define IS_LOOP_SOUND(s) ((s) == SND_FUEL) +#define IS_MUSIC_SOUND(s) ((s) == SND_TYGER || (s) == SND_VOYAGER) + +/* game button identifiers */ +#define GAME_CTRL_ID_LEFT 0 +#define GAME_CTRL_ID_MIDDLE 1 +#define GAME_CTRL_ID_RIGHT 2 + +#define NUM_GAME_BUTTONS 3 + +/* values for DrawLaser() */ +#define DL_LASER_DISABLED 0 +#define DL_LASER_ENABLED 1 + +/* values for 'click_delay_value' in ClickElement() */ +#define CLICK_DELAY_SHORT 125 +#define CLICK_DELAY_LONG 250 +#define AUTO_ROTATE_DELAY CLICK_DELAY_SHORT + +/* forward declaration for internal use */ + + +void GetPlayerConfig() +{ + if (!audio.sound_available) + setup.sound = FALSE; + + if (!audio.loops_available) + { + setup.sound_loops = FALSE; + setup.sound_music = FALSE; + } + + if (!video.fullscreen_available) + setup.fullscreen = FALSE; + + setup.sound_simple = setup.sound; + + SetAudioMode(setup.sound); +} + +static int get_element_angle(int element) +{ + int element_phase = get_element_phase(element); + + if (IS_MIRROR_FIXED(element) || + IS_MCDUFFIN(element) || + IS_LASER(element) || + IS_RECEIVER(element)) + return 4 * element_phase; + else + return element_phase; +} + +static int get_opposite_angle(int angle) +{ + int opposite_angle = angle + ANG_RAY_180; + + /* make sure "opposite_angle" is in valid interval [0, 15] */ + return (opposite_angle + 16) % 16; +} + +static int get_mirrored_angle(int laser_angle, int mirror_angle) +{ + int reflected_angle = 16 - laser_angle + mirror_angle; + + /* make sure "reflected_angle" is in valid interval [0, 15] */ + return (reflected_angle + 16) % 16; +} + +void InitMovDir(int x, int y) +{ + int element = Feld[x][y]; + static int direction[3][4] = + { + { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN }, + { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP }, + { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN } + }; + + switch(element) + { + case EL_PACMAN_RIGHT: + case EL_PACMAN_UP: + case EL_PACMAN_LEFT: + case EL_PACMAN_DOWN: + Feld[x][y] = EL_PACMAN; + MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT]; + break; + + default: + break; + } +} + +static void InitField(int x, int y, boolean init_game) +{ + int element = Feld[x][y]; + + switch (element) + { + case EL_DF_EMPTY: + Feld[x][y] = EL_EMPTY; + break; + + case EL_KETTLE: + case EL_CELL: + if (native_mm_level.auto_count_kettles) + game_mm.kettles_still_needed++; + break; + + case EL_LIGHTBULB_OFF: + game_mm.lights_still_needed++; + break; + + default: + if (IS_MIRROR(element) || + IS_BEAMER_OLD(element) || + IS_BEAMER(element) || + IS_POLAR(element) || + IS_POLAR_CROSS(element) || + IS_DF_MIRROR(element) || + IS_DF_MIRROR_AUTO(element) || + IS_GRID_STEEL_AUTO(element) || + IS_GRID_WOOD_AUTO(element) || + IS_FIBRE_OPTIC(element)) + { + if (IS_BEAMER_OLD(element)) + { + Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START); + element = Feld[x][y]; + } + + if (!IS_FIBRE_OPTIC(element)) + { + static int steps_grid_auto = 0; + + if (game_mm.num_cycle == 0) /* initialize cycle steps for grids */ + steps_grid_auto = RND(16) * (RND(2) ? -1 : +1); + + if (IS_GRID_STEEL_AUTO(element) || + IS_GRID_WOOD_AUTO(element)) + game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto; + else + game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1); + + game_mm.cycle[game_mm.num_cycle].x = x; + game_mm.cycle[game_mm.num_cycle].y = y; + game_mm.num_cycle++; + } + + if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) + { + int beamer_nr = BEAMER_NR(element); + int nr = laser.beamer[beamer_nr][0].num; + + laser.beamer[beamer_nr][nr].x = x; + laser.beamer[beamer_nr][nr].y = y; + laser.beamer[beamer_nr][nr].num = 1; + } + } + else if (IS_PACMAN(element)) + { +#if 0 + int phase = element - EL_PACMAN_RIGHT; + + game_mm.pacman[game_mm.num_pacman].x = x; + game_mm.pacman[game_mm.num_pacman].y = y; + game_mm.pacman[game_mm.num_pacman].dir = phase + ((phase + 1) % 2) * 2; + game_mm.num_pacman++; +#else + InitMovDir(x, y); +#endif + } + else if (IS_MCDUFFIN(element) || IS_LASER(element)) + { + laser.start_edge.x = x; + laser.start_edge.y = y; + laser.start_angle = get_element_angle(element); + } + + break; + } +} + +static void InitCycleElements() +{ + int i, j; + + if (game_mm.num_cycle == 0) /* no elements to cycle */ + return; + + for(i=0; i<16; i++) + { + for(j=0; jchecked = 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 %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=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> 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 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 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 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; lk; l--) + { + strcpy(highscore[l].Name, highscore[l - 1].Name); + highscore[l].Score = highscore[l - 1].Score; + } + } + +#ifdef ONE_PER_NAME + put_into_list: +#endif + strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN); + highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0'; + highscore[k].Score = game_mm.score; + position = k; + break; + } + +#ifdef ONE_PER_NAME + else if (!strncmp(setup.player_name, highscore[k].Name, + MAX_PLAYER_NAME_LEN)) + break; /* player already there with a higher score */ +#endif + + } + + // if (position >= 0) + // SaveScore(level_nr); + + return position; +} + +void InitMovingField(int x, int y, int direction) +{ + int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); + int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); + + MovDir[x][y] = direction; + MovDir[newx][newy] = direction; + if (Feld[newx][newy] == EL_EMPTY) + Feld[newx][newy] = EL_BLOCKED; +} + +void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y) +{ + int direction = MovDir[x][y]; + int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); + int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); + + *goes_to_x = newx; + *goes_to_y = newy; +} + +void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y) +{ + int oldx = x, oldy = y; + int direction = MovDir[x][y]; + + if (direction == MV_LEFT) + oldx++; + else if (direction == MV_RIGHT) + oldx--; + else if (direction == MV_UP) + oldy++; + else if (direction == MV_DOWN) + oldy--; + + *comes_from_x = oldx; + *comes_from_y = oldy; +} + +int MovingOrBlocked2Element(int x, int y) +{ + int element = Feld[x][y]; + + if (element == EL_BLOCKED) + { + int oldx, oldy; + + Blocked2Moving(x, y, &oldx, &oldy); + return Feld[oldx][oldy]; + } + else + return element; +} + +#if 0 +static void RemoveField(int x, int y) +{ + Feld[x][y] = EL_EMPTY; + MovPos[x][y] = 0; + MovDir[x][y] = 0; + MovDelay[x][y] = 0; +} +#endif + +void RemoveMovingField(int x, int y) +{ + int oldx = x, oldy = y, newx = x, newy = y; + + if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y)) + return; + + if (IS_MOVING(x, y)) + { + Moving2Blocked(x, y, &newx, &newy); + if (Feld[newx][newy] != EL_BLOCKED) + return; + } + else if (Feld[x][y] == EL_BLOCKED) + { + Blocked2Moving(x, y, &oldx, &oldy); + if (!IS_MOVING(oldx, oldy)) + return; + } + + Feld[oldx][oldy] = EL_EMPTY; + Feld[newx][newy] = EL_EMPTY; + MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0; + MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0; + + DrawLevelField_MM(oldx, oldy); + DrawLevelField_MM(newx, newy); +} + +void PlaySoundLevel(int x, int y, int sound_nr) +{ + int sx = SCREENX(x), sy = SCREENY(y); + int volume, stereo; + int silence_distance = 8; + + if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) || + (!setup.sound_loops && IS_LOOP_SOUND(sound_nr))) + return; + + if (!IN_LEV_FIELD(x, y) || + sx < -silence_distance || sx >= SCR_FIELDX+silence_distance || + sy < -silence_distance || sy >= SCR_FIELDY+silence_distance) + return; + + volume = SOUND_MAX_VOLUME; + +#ifndef MSDOS + stereo = (sx - SCR_FIELDX/2) * 12; +#else + stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5; + if (stereo > SOUND_MAX_RIGHT) + stereo = SOUND_MAX_RIGHT; + if (stereo < SOUND_MAX_LEFT) + stereo = SOUND_MAX_LEFT; +#endif + + if (!IN_SCR_FIELD(sx, sy)) + { + int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2; + int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2; + + volume -= volume * (dx > dy ? dx : dy) / silence_distance; + } + + PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND); +} + +void RaiseScore(int value) +{ + game_mm.score += value; +#if 0 + DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4), + FONT_TEXT_2); +#endif +} + +void RaiseScoreElement(int element) +{ + switch(element) + { + case EL_PACMAN: + RaiseScore(native_mm_level.score[SC_PACMAN]); + break; + case EL_KEY: + RaiseScore(native_mm_level.score[SC_KEY]); + break; + default: + break; + } +} diff --git a/src/game_mm/mm_game.h b/src/game_mm/mm_game.h new file mode 100644 index 00000000..2098506f --- /dev/null +++ b/src/game_mm/mm_game.h @@ -0,0 +1,119 @@ +// ============================================================================ +// Mirror Magic -- McDuffin's Revenge +// ---------------------------------------------------------------------------- +// (c) 1994-2017 by Artsoft Entertainment +// Holger Schemel +// info@artsoft.org +// http://www.artsoft.org/ +// ---------------------------------------------------------------------------- +// mm_game.h +// ============================================================================ + +#ifndef MM_GAME_H +#define MM_GAME_H + +#include "main_mm.h" + +/* score for elements (also used by editor.c) */ +#define SC_EDELSTEIN 0 +#define SC_DIAMANT 1 +#define SC_KAEFER 2 +#define SC_FLIEGER 3 +#define SC_MAMPFER 4 +#define SC_ROBOT 5 +#define SC_PACMAN 6 +#define SC_KOKOSNUSS 7 +#define SC_DYNAMIT 8 +#define SC_KEY 9 +#define SC_ZEITBONUS 10 + +void GetPlayerConfig(void); +void InitGame(void); +void InitMovDir(int, int); +void InitAmoebaNr(int, int); +void GameWon(void); +int NewHiScore(void); +void InitMovingField(int, int, int); +void Moving2Blocked(int, int, int *, int *); +void Blocked2Moving(int, int, int *, int *); +int MovingOrBlocked2Element(int, int); +void RemoveMovingField(int, int); + + + + + +void Blurb(int, int); +void Impact(int, int); +void TurnRound(int, int); +void StartMoving(int, int); +void ContinueMoving(int, int); +int AmoebeNachbarNr(int, int); +void AmoebeUmwandeln(int, int); +void AmoebeUmwandelnBD(int, int, int); +void AmoebeWaechst(int, int); +void AmoebeAbleger(int, int); +void Life(int, int); +void Ablenk(int, int); +void Blubber(int, int); +void NussKnacken(int, int); +void SiebAktivieren(int, int, int); +void AusgangstuerPruefen(int, int); +void AusgangstuerOeffnen(int, int); +void AusgangstuerBlinken(int, int); +void EdelsteinFunkeln(int, int); +void MauerWaechst(int, int); +void MauerAbleger(int, int); + + + +boolean MoveFigureOneStep(struct PlayerInfo *, int, int, int, int); +boolean MoveFigure(struct PlayerInfo *, int, int); +void ScrollFigure(struct PlayerInfo *, int); +void ScrollScreen(struct PlayerInfo *, int); + +void TestIfGoodThingHitsBadThing(int, int); +void TestIfBadThingHitsGoodThing(int, int); +void TestIfHeroHitsBadThing(int, int); +void TestIfBadThingHitsHero(int, int); +void TestIfFriendHitsBadThing(int, int); +void TestIfBadThingHitsFriend(int, int); +void TestIfBadThingHitsOtherBadThing(int, int); +void KillHero(struct PlayerInfo *); +void BuryHero(struct PlayerInfo *); +void RemoveHero(struct PlayerInfo *); +int DigField(struct PlayerInfo *, int, int, int, int, int); +boolean SnapField(struct PlayerInfo *, int, int); +boolean PlaceBomb(struct PlayerInfo *); +void PlaySoundLevel(int, int, int); +void RaiseScore(int); +void RaiseScoreElement(int); + +void CreateGameButtons(); +void UnmapGameButtons(); + + +void InitGame(void); +void AddLaserEdge(int, int); +void AddDamagedField(int, int); +void ScanLaser(void); +void DrawLaser(int, int); +boolean HitElement(int, int); +boolean HitOnlyAnEdge(int, int); +boolean HitPolarizer(int, int); +boolean HitBlock(int, int); +boolean HitLaserSource(int, int); +boolean HitLaserDestination(int, int); +boolean HitReflectingWalls(int, int); +boolean HitAbsorbingWalls(int, int); +void Bang(int, int); +void ClickElement(int, int, int); +void RotateMirror(int, int, int); +boolean ObjHit(int, int, int); +void DeletePacMan(int, int); + +void ColorCycling(void); +void GameActions(void); +void MovePacMen(void); + +#endif diff --git a/src/game_mm/mm_init.c b/src/game_mm/mm_init.c new file mode 100644 index 00000000..eec9ee1e --- /dev/null +++ b/src/game_mm/mm_init.c @@ -0,0 +1,17 @@ +// ============================================================================ +// Mirror Magic -- McDuffin's Revenge +// ---------------------------------------------------------------------------- +// (c) 1994-2017 by Artsoft Entertainment +// Holger Schemel +// info@artsoft.org +// http://www.artsoft.org/ +// ---------------------------------------------------------------------------- +// mm_init.c +// ============================================================================ + +#include "main_mm.h" + +unsigned int InitEngineRandom_MM(int seed) +{ + return InitEngineRandom(seed); +} diff --git a/src/game_mm/mm_main.c b/src/game_mm/mm_main.c new file mode 100644 index 00000000..44ca0ad0 --- /dev/null +++ b/src/game_mm/mm_main.c @@ -0,0 +1,520 @@ +// ============================================================================ +// Mirror Magic -- McDuffin's Revenge +// ---------------------------------------------------------------------------- +// (c) 1994-2017 by Artsoft Entertainment +// Holger Schemel +// info@artsoft.org +// http://www.artsoft.org/ +// ---------------------------------------------------------------------------- +// mm_main.c +// ============================================================================ + +#include "main_mm.h" + +#include "mm_main.h" + + +struct GameInfo_MM game_mm; +struct LevelInfo_MM native_mm_level; + +short Ur[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +short Hit[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +short Box[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +short Angle[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; + +unsigned int Elementeigenschaften[MAX_ELEMENTS]; + +short LX,LY, XS,YS, ELX,ELY; +short CT,Ct; + +Pixel pen_fg, pen_bg, pen_ray, pen_magicolor[2]; +int color_status; + +struct XY Step[16] = +{ + { 1, 0 }, + { 2, -1 }, + { 1, -1 }, + { 1, -2 }, + { 0, -1 }, + { -1, -2 }, + { -1, -1 }, + { -2, -1 }, + { -1, 0 }, + { -2, 1 }, + { -1, 1 }, + { -1, 2 }, + { 0, 1 }, + { 1, 2 }, + { 1, 1 }, + { 2, 1 } +}; + +/* "Sign" has the following structure: + + each 4-bit-value represents the values d*8 + c*4 + b*2 + a*1 + at the pixel positions + + a b 1 2 + c d 4 8 + + so the value "0xA" (=> (d=1)*8 + (c=0)*4 + (b=1)*2 + (a=0)*1) + would result in the pixel pattern + + 0 1 _ x + 0 1 or _ x + + x x x x x x x x x x + x _ x x _ _ x x _ x + 6 2 + x x 5 4 3 x x + x x 7 1 x x + + x _ _ x + x _ 8 0 _ x + + x x 9 15 x x + x x 11 12 13 x x + 10 14 + x _ x x _ _ x x _ x + x x x x x x x x x x + + */ + +short Sign[16] = +{ + 0xA,0xF,0xB,0xF, + 0x3,0xF,0x7,0xF, + 0x5,0xF,0xD,0xF, + 0xC,0xF,0xE,0xF +}; + +char *element_info_mm[] = +{ + "empty space", /* 0 */ + "mirror (0\xb0)", + "mirror (11.25\xb0)", + "mirror (22.5\xb0)", + "mirror (33.75\xb0)", + "mirror (45\xb0)", + "mirror (56.25\xb0)", + "mirror (67.5\xb0)", + "mirror (78.75\xb0)", + "mirror (90\xb0)", + "mirror (101.25\xb0)", /* 10 */ + "mirror (112.5\xb0)", + "mirror (123.75\xb0)", + "mirror (135\xb0)", + "mirror (146.25\xb0)", + "mirror (157.5\xb0)", + "mirror (168.75\xb0)", + "fixed steel polarisator (0\xb0)", + "fixed steel polarisator (90\xb0)", + "fixed steel polarisator (45\xb0)", + "fixed steel polarisator (135\xb0)", /* 20 */ + "Gregor McDuffin (looking right)", + "Gregor McDuffin (looking up)", + "Gregor McDuffin (looking left)", + "Gregor McDuffin (looking down)", + "closed exit", + "opening exit", + "opening exit", + "open exit", + "magic kettle", + "bomb", /* 30 */ + "prism", + "steel wall", + "steel wall", + "steel wall", + "steel wall", + "steel wall", + "steel wall", + "steel wall", + "steel wall", + "steel wall", /* 40 */ + "steel wall", + "steel wall", + "steel wall", + "steel wall", + "steel wall", + "steel wall", + "steel wall", + "wooden wall", + "wooden wall", + "wooden wall", /* 50 */ + "wooden wall", + "wooden wall", + "wooden wall", + "wooden wall", + "wooden wall", + "wooden wall", + "wooden wall", + "wooden wall", + "wooden wall", + "wooden wall", /* 60 */ + "wooden wall", + "wooden wall", + "wooden wall", + "ice wall", + "ice wall", + "ice wall", + "ice wall", + "ice wall", + "ice wall", + "ice wall", /* 70 */ + "ice wall", + "ice wall", + "ice wall", + "ice wall", + "ice wall", + "ice wall", + "ice wall", + "ice wall", + "ice wall", + "amoeba wall", /* 80 */ + "amoeba wall", + "amoeba wall", + "amoeba wall", + "amoeba wall", + "amoeba wall", + "amoeba wall", + "amoeba wall", + "amoeba wall", + "amoeba wall", + "amoeba wall", /* 90 */ + "amoeba wall", + "amoeba wall", + "amoeba wall", + "amoeba wall", + "amoeba wall", + "wooden block", + "gray ball", + "beamer (0\xb0)", + "beamer (22.5\xb0)", + "beamer (45\xb0)", /* 100 */ + "beamer (67.5\xb0)", + "beamer (90\xb0)", + "beamer (112.5\xb0)", + "beamer (135\xb0)", + "beamer (157.5\xb0)", + "beamer (180\xb0)", + "beamer (202.5\xb0)", + "beamer (225\xb0)", + "beamer (247.5\xb0)", + "beamer (270\xb0)", /* 110 */ + "beamer (292.5\xb0)", + "beamer (315\xb0)", + "beamer (337.5\xb0)", + "fuse", + "pac man (starts moving right)", + "pac man (starts moving up)", + "pac man (starts moving left)", + "pac man (starts moving down)", + "polarisator (0\xb0)", + "polarisator (11.25\xb0)", /* 120 */ + "polarisator (22.5\xb0)", + "polarisator (33.75\xb0)", + "polarisator (45\xb0)", + "polarisator (56.25\xb0)", + "polarisator (67.5\xb0)", + "polarisator (78.75\xb0)", + "polarisator (90\xb0)", + "polarisator (101.25\xb0)", + "polarisator (112.5\xb0)", + "polarisator (123.75\xb0)", /* 130 */ + "polarisator (135\xb0)", + "polarisator (146.25\xb0)", + "polarisator (157.5\xb0)", + "polarisator (168.75\xb0)", + "two-way polarisator (0\xb0)", + "two-way polarisator (22.5\xb0)", + "two-way polarisator (45\xb0)", + "two-way polarisator (67.5\xb0)", + "fixed mirror (0\xb0)", + "fixed mirror (45\xb0)", /* 140 */ + "fixed mirror (90\xb0)", + "fixed mirror (135\xb0)", + "reflecting stone lock", + "key", + "light bulb (dark)", + "ligh bulb (glowing)", + "bonus ball", + "reflecting stone block", + "wooden lock", + "extra energy ball (full)", /* 150 */ + "fixed wooden polarisator (0\xb0)", + "fixed wooden polarisator (90\xb0)", + "fixed wooden polarisator (45\xb0)", + "fixed wooden polarisator (135\xb0)", + "extra energy ball (empty)", + "unused", + "unused", + "unused", + "unused", + "letter ' '", /* 160 */ + "letter '!'", + "letter '\"'", + "letter '#'", + "letter '$'", + "letter '%'", + "letter '&'", + "letter '''", + "letter '('", + "letter ')'", + "letter '*'", /* 170 */ + "letter '+'", + "letter ','", + "letter '-'", + "letter '.'", + "letter '/'", + "letter '0'", + "letter '1'", + "letter '2'", + "letter '3'", + "letter '4'", /* 180 */ + "letter '5'", + "letter '6'", + "letter '7'", + "letter '8'", + "letter '9'", + "letter ':'", + "letter ';'", + "letter '<'", + "letter '='", + "letter '>'", /* 190 */ + "letter '?'", + "letter '@'", + "letter 'A'", + "letter 'B'", + "letter 'C'", + "letter 'D'", + "letter 'E'", + "letter 'F'", + "letter 'G'", + "letter 'H'", /* 200 */ + "letter 'I'", + "letter 'J'", + "letter 'K'", + "letter 'L'", + "letter 'M'", + "letter 'N'", + "letter 'O'", + "letter 'P'", + "letter 'Q'", + "letter 'R'", /* 210 */ + "letter 'S'", + "letter 'T'", + "letter 'U'", + "letter 'V'", + "letter 'W'", + "letter 'X'", + "letter 'Y'", + "letter 'Z'", + "letter '\xc4'", + "letter '\xd6'", /* 220 */ + "letter '\xdc'", + "letter '^'", + "letter ''", + "letter ''", + "letter ''", + "letter ''", + "letter ''", + "letter ''", + "letter ''", + "letter ''", /* 230 */ + "letter ''", + "letter ''", + "letter ''", + "letter ''", + "letter ''", + "letter ''", + "letter ''", + "letter ''", + "letter ''", + "mirror (0\xb0)", /* 240 */ + "mirror (11.25\xb0)", + "mirror (22.5\xb0)", + "mirror (33.75\xb0)", + "mirror (45\xb0)", + "mirror (56.25\xb0)", + "mirror (67.5\xb0)", + "mirror (78.75\xb0)", + "mirror (90\xb0)", + "mirror (101.25\xb0)", + "mirror (112.5\xb0)", /* 250 */ + "mirror (123.75\xb0)", + "mirror (135\xb0)", + "mirror (146.25\xb0)", + "mirror (157.5\xb0)", + "mirror (168.75\xb0)", + "fixed wooden polarisator (0\xb0)", + "fixed wooden polarisator (22.5\xb0)", + "fixed wooden polarisator (45\xb0)", + "fixed wooden polarisator (67.5\xb0)", + "fixed wooden polarisator (90\xb0)", /* 260 */ + "fixed wooden polarisator (112.5\xb0)", + "fixed wooden polarisator (135\xb0)", + "fixed wooden polarisator (157.5\xb0)", + "fixed steel polarisator (0\xb0)", + "fixed steel polarisator (22.5\xb0)", + "fixed steel polarisator (45\xb0)", + "fixed steel polarisator (67.5\xb0)", + "fixed steel polarisator (90\xb0)", + "fixed steel polarisator (112.5\xb0)", + "fixed steel polarisator (135\xb0)", /* 270 */ + "fixed steel polarisator (157.5\xb0)", + "deflektor style wooden wall", + "deflektor style wooden wall", + "deflektor style wooden wall", + "deflektor style wooden wall", + "deflektor style wooden wall", + "deflektor style wooden wall", + "deflektor style wooden wall", + "deflektor style wooden wall", + "deflektor style wooden wall", /* 280 */ + "deflektor style wooden wall", + "deflektor style wooden wall", + "deflektor style wooden wall", + "deflektor style wooden wall", + "deflektor style wooden wall", + "deflektor style wooden wall", + "deflektor style wooden wall", + "deflektor style steel wall", + "deflektor style steel wall", + "deflektor style steel wall", /* 290 */ + "deflektor style steel wall", + "deflektor style steel wall", + "deflektor style steel wall", + "deflektor style steel wall", + "deflektor style steel wall", + "deflektor style steel wall", + "deflektor style steel wall", + "deflektor style steel wall", + "deflektor style steel wall", + "deflektor style steel wall", /* 300 */ + "deflektor style steel wall", + "deflektor style steel wall", + "deflektor style steel wall", + "empty space", + "cell", + "mine", + "refractor", + "laser cannon (shooting right)", + "laser cannon (shooting up)", + "laser cannon (shooting left)", /* 310 */ + "laser cannon (shooting down)", + "laser receiver (directed right)", + "laser receiver (directed up)", + "laser receiver (directed left)", + "laser receiver (directed down)", + "fibre optic (1a)", + "fibre optic (1b)", + "fibre optic (2a)", + "fibre optic (2b)", + "fibre optic (3a)", /* 320 */ + "fibre optic (3b)", + "fibre optic (4a)", + "fibre optic (4b)", + "rotating mirror (0\xb0)", + "rotating mirror (11.25\xb0)", + "rotating mirror (22.5\xb0)", + "rotating mirror (33.75\xb0)", + "rotating mirror (45\xb0)", + "rotating mirror (56.25\xb0)", + "rotating mirror (67.5\xb0)", /* 330 */ + "rotating mirror (78.75\xb0)", + "rotating mirror (90\xb0)", + "rotating mirror (101.25\xb0)", + "rotating mirror (112.5\xb0)", + "rotating mirror (123.75\xb0)", + "rotating mirror (135\xb0)", + "rotating mirror (146.25\xb0)", + "rotating mirror (157.5\xb0)", + "rotating mirror (168.75\xb0)", + "rotating wooden polarisator (0\xb0)", /* 340 */ + "rotating wooden polarisator (22.5\xb0)", + "rotating wooden polarisator (45\xb0)", + "rotating wooden polarisator (67.5\xb0)", + "rotating wooden polarisator (90\xb0)", + "rotating wooden polarisator (112.5\xb0)", + "rotating wooden polarisator (135\xb0)", + "rotating wooden polarisator (157.5\xb0)", + "rotating steel polarisator (0\xb0)", + "rotating steel polarisator (22.5\xb0)", + "rotating steel polarisator (45\xb0)", /* 350 */ + "rotating steel polarisator (67.5\xb0)", + "rotating steel polarisator (90\xb0)", + "rotating steel polarisator (112.5\xb0)", + "rotating steel polarisator (135\xb0)", + "rotating steel polarisator (157.5\xb0)", + "red beamer (0\xb0)", + "red beamer (22.5\xb0)", + "red beamer (45\xb0)", + "red beamer (67.5\xb0)", + "red beamer (90\xb0)", /* 360 */ + "red beamer (112.5\xb0)", + "red beamer (135\xb0)", + "red beamer (157.5\xb0)", + "red beamer (180\xb0)", + "red beamer (202.5\xb0)", + "red beamer (225\xb0)", + "red beamer (247.5\xb0)", + "red beamer (270\xb0)", + "red beamer (292.5\xb0)", + "red beamer (315\xb0)", /* 370 */ + "red beamer (337.5\xb0)", + "yellow beamer (0\xb0)", + "yellow beamer (22.5\xb0)", + "yellow beamer (45\xb0)", + "yellow beamer (67.5\xb0)", + "yellow beamer (90\xb0)", + "yellow beamer (112.5\xb0)", + "yellow beamer (135\xb0)", + "yellow beamer (157.5\xb0)", + "yellow beamer (180\xb0)", /* 380 */ + "yellow beamer (202.5\xb0)", + "yellow beamer (225\xb0)", + "yellow beamer (247.5\xb0)", + "yellow beamer (270\xb0)", + "yellow beamer (292.5\xb0)", + "yellow beamer (315\xb0)", + "yellow beamer (337.5\xb0)", + "green beamer (0\xb0)", + "green beamer (22.5\xb0)", + "green beamer (45\xb0)", /* 390 */ + "green beamer (67.5\xb0)", + "green beamer (90\xb0)", + "green beamer (112.5\xb0)", + "green beamer (135\xb0)", + "green beamer (157.5\xb0)", + "green beamer (180\xb0)", + "green beamer (202.5\xb0)", + "green beamer (225\xb0)", + "green beamer (247.5\xb0)", + "green beamer (270\xb0)", /* 400 */ + "green beamer (292.5\xb0)", + "green beamer (315\xb0)", + "green beamer (337.5\xb0)", + "blue beamer (0\xb0)", + "blue beamer (22.5\xb0)", + "blue beamer (45\xb0)", + "blue beamer (67.5\xb0)", + "blue beamer (90\xb0)", + "blue beamer (112.5\xb0)", + "blue beamer (135\xb0)", /* 410 */ + "blue beamer (157.5\xb0)", + "blue beamer (180\xb0)", + "blue beamer (202.5\xb0)", + "blue beamer (225\xb0)", + "blue beamer (247.5\xb0)", + "blue beamer (270\xb0)", + "blue beamer (292.5\xb0)", + "blue beamer (315\xb0)", + "blue beamer (337.5\xb0)", + "unknown", /* 420 */ + + /* + "-------------------------------", + */ +}; +int num_element_info_mm = sizeof(element_info_mm)/sizeof(char *); diff --git a/src/game_mm/mm_main.h b/src/game_mm/mm_main.h new file mode 100644 index 00000000..98950edc --- /dev/null +++ b/src/game_mm/mm_main.h @@ -0,0 +1,1108 @@ +// ============================================================================ +// Mirror Magic -- McDuffin's Revenge +// ---------------------------------------------------------------------------- +// (c) 1994-2017 by Artsoft Entertainment +// Holger Schemel +// info@artsoft.org +// http://www.artsoft.org/ +// ---------------------------------------------------------------------------- +// mm_main.h +// ============================================================================ + +#ifndef MM_MAIN_H +#define MM_MAIN_H + +#define STD_LEV_FIELDX MM_STD_PLAYFIELD_WIDTH +#define STD_LEV_FIELDY MM_STD_PLAYFIELD_HEIGHT +#define MAX_LEV_FIELDX MM_MAX_PLAYFIELD_WIDTH +#define MAX_LEV_FIELDY MM_MAX_PLAYFIELD_HEIGHT + +#define SCREENX(a) (a) +#define SCREENY(a) (a) +#define LEVELX(a) (a) +#define LEVELY(a) (a) +#define IN_VIS_FIELD(x,y) ((x)>=0 && (x)=0 &&(y)=BX1 && (x)<=BX2 && (y)>=BY1 &&(y)<=BY2) +#define IN_LEV_FIELD(x,y) ((x)>=0 && (x)=0 &&(y)=0 && (x)=0 && (y)= EL_WALL_STEEL_START && \ + (e) <= EL_WALL_STEEL_END) +#define IS_WALL_WOOD(e) ((e) >= EL_WALL_WOOD_START && \ + (e) <= EL_WALL_WOOD_END) +#define IS_WALL_ICE(e) ((e) >= EL_WALL_ICE_START && \ + (e) <= EL_WALL_ICE_END) +#define IS_WALL_AMOEBA(e) ((e) >= EL_WALL_AMOEBA_START && \ + (e) <= EL_WALL_AMOEBA_END) +#define IS_DF_WALL_STEEL(e) ((e) >= EL_DF_WALL_STEEL_START && \ + (e) <= EL_DF_WALL_STEEL_END) +#define IS_DF_WALL_WOOD(e) ((e) >= EL_DF_WALL_WOOD_START && \ + (e) <= EL_DF_WALL_WOOD_END) +#define IS_DF_WALL(e) ((e) >= EL_DF_WALL_START && \ + (e) <= EL_DF_WALL_END) +#define IS_WALL(e) (IS_MM_WALL(e) || IS_DF_WALL(e)) +#define IS_WALL_CHANGING(e) ((e) >= EL_WALL_CHANGING_START && \ + (e) <= EL_WALL_CHANGING_END) +#define IS_GRID_STEEL(e) ((e) >= EL_GRID_STEEL_START && \ + (e) <= EL_GRID_STEEL_END) +#define IS_GRID_WOOD(e) ((e) >= EL_GRID_WOOD_START && \ + (e) <= EL_GRID_WOOD_END) +#define IS_GRID_WOOD_FIXED(e) ((e) >= EL_GRID_WOOD_FIXED_START && \ + (e) <= EL_GRID_WOOD_FIXED_END) +#define IS_GRID_STEEL_FIXED(e) ((e) >= EL_GRID_STEEL_FIXED_START && \ + (e) <= EL_GRID_STEEL_FIXED_END) +#define IS_GRID_WOOD_AUTO(e) ((e) >= EL_GRID_WOOD_AUTO_START && \ + (e) <= EL_GRID_WOOD_AUTO_END) +#define IS_GRID_STEEL_AUTO(e) ((e) >= EL_GRID_STEEL_AUTO_START && \ + (e) <= EL_GRID_STEEL_AUTO_END) +#define IS_DF_GRID(e) (IS_GRID_WOOD_FIXED(e) || \ + IS_GRID_STEEL_FIXED(e) || \ + IS_GRID_WOOD_AUTO(e) || \ + IS_GRID_STEEL_AUTO(e)) +#define IS_DF_MIRROR(e) ((e) >= EL_DF_MIRROR_START && \ + (e) <= EL_DF_MIRROR_END) +#define IS_DF_MIRROR_AUTO(e) ((e) >= EL_DF_MIRROR_AUTO_START && \ + (e) <= EL_DF_MIRROR_AUTO_END) +#define IS_LASER(e) ((e) >= EL_LASER_START && \ + (e) <= EL_LASER_END) +#define IS_RECEIVER(e) ((e) >= EL_RECEIVER_START && \ + (e) <= EL_RECEIVER_END) +#define IS_FIBRE_OPTIC(e) ((e) >= EL_FIBRE_OPTIC_START && \ + (e) <= EL_FIBRE_OPTIC_END) +#define FIBRE_OPTIC_NR(e) (((e) - EL_FIBRE_OPTIC_START) / 2) +#define IS_BEAMER(e) ((e) >= EL_BEAMER_RED_START && \ + (e) <= EL_BEAMER_BLUE_END) +#define BEAMER_CLASSIC_NR(e) (((e) - EL_BEAMER_RED_START) / 16) +#define BEAMER_NR(e) (IS_BEAMER(e) ? BEAMER_CLASSIC_NR(e) : \ + IS_FIBRE_OPTIC(e) ? (FIBRE_OPTIC_NR(e)+4) : 0) +#define IS_EXPLODING(e) ((e) == EL_EXPLODING_OPAQUE || \ + (e) == EL_EXPLODING_TRANSP) + +#define IS_EATABLE4PACMAN(e) ((e) == EL_EMPTY || \ + (e) == EL_KETTLE || \ + (e) == EL_CELL || \ + (e) == EL_BOMB || \ + IS_WALL_AMOEBA(e)) + +#define CAN_MOVE(e) ((e) == EL_PACMAN) +#define IS_FREE(x,y) (Feld[x][y] == EL_EMPTY) + +#define IS_MOVING(x,y) (MovPos[x][y] != 0) +#define IS_BLOCKED(x,y) (Feld[x][y] == EL_BLOCKED) +#define IS_DRAWABLE(e) ((e) < EL_BLOCKED) +#define IS_NOT_DRAWABLE(e) ((e) >= EL_BLOCKED) + +#define PLAYERINFO(x,y) (&stored_player[StorePlayer[x][y]-EL_SPIELER1]) + +#define WALL_BASE(e) ((e) & 0xfff0) +#define WALL_BITS(e) ((e) & 0x000f) + +/* Bitmaps with graphic file */ +#define PIX_BACK 0 +#define PIX_DOOR 1 +#define PIX_TOONS 2 +#define PIX_DF 3 +#define PIX_BIGFONT 4 +#define PIX_SMALLFONT 5 +#define PIX_MEDIUMFONT 6 +/* Bitmaps without graphic file */ +#define PIX_DB_DOOR 7 + +#define NUM_PICTURES 7 +#define NUM_BITMAPS 8 + +/* boundaries of arrays etc. */ +#define MAX_PLAYER_NAME_LEN 10 +#define MAX_LEVEL_NAME_LEN 32 +#define MAX_LEVEL_AUTHOR_LEN 32 +#define MAX_SCORE_ENTRIES 100 +#define MAX_ELEMENTS 700 /* 500 static + 200 runtime */ + +#define LEVEL_SCORE_ELEMENTS 16 /* level elements with score */ + +#define MICROLEVEL_SCROLL_DELAY 50 /* delay for scrolling micro level */ +#define MICROLEVEL_LABEL_DELAY 250 /* delay for micro level label */ + +struct HiScore +{ + char Name[MAX_PLAYER_NAME_LEN + 1]; + int Score; +}; + +struct EditorInfo +{ + boolean draw_walls_masked; +}; + +extern Bitmap *pix[]; +extern DrawBuffer *fieldbuffer; +extern DrawBuffer *drawto_field; + +extern int joystick_device; +extern char *joystick_device_name[]; + +extern int game_status; +extern boolean level_editor_test_game; +extern boolean network_playing; + +extern int key_joystick_mapping; +extern int global_joystick_status, joystick_status; +extern int sound_status; +extern boolean sound_loops_allowed; + +extern boolean redraw[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +extern int redraw_x1, redraw_y1; + +extern short Feld[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +extern short Ur[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +extern short Hit[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +extern short Box[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +extern short Angle[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; + +extern short MovPos[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +extern short MovDir[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +extern short MovDelay[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +extern short Store[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +extern short Store2[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +extern short StorePlayer[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +extern short Frame[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +extern boolean Stop[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +extern short JustStopped[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +extern short AmoebaNr[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; +extern short AmoebaCnt[MAX_NUM_AMOEBA], AmoebaCnt2[MAX_NUM_AMOEBA]; +extern unsigned int Elementeigenschaften[MAX_ELEMENTS]; + +extern int level_nr; +extern int lev_fieldx,lev_fieldy, scroll_x,scroll_y; + +extern int FX,FY, ScrollStepSize; +extern int ScreenMovDir, ScreenMovPos, ScreenGfxPos; +extern int GameFrameDelay; +extern int FfwdFrameDelay; +extern int BX1,BY1, BX2,BY2; +extern int SBX_Left, SBX_Right; +extern int SBY_Upper, SBY_Lower; +extern int ZX,ZY, ExitX,ExitY; +extern int AllPlayersGone; +extern int TimeFrames, TimePlayed, TimeLeft; +extern boolean SiebAktiv; +extern int SiebCount; + +extern struct LevelInfo_MM native_mm_level; +extern struct HiScore highscore[]; +extern struct TapeInfo tape; +extern struct SetupInfo setup; +extern struct GameInfo_MM game_mm; +extern struct LaserInfo laser; +extern struct EditorInfo editor; +extern struct GlobalInfo global; + +extern short LX,LY, XS,YS, ELX,ELY; +extern short CT,Ct; + +extern Pixel pen_fg, pen_bg, pen_ray, pen_magicolor[2]; +extern int color_status; + +extern struct XY Step[]; +extern short Sign[16]; + +extern char *sound_name[]; +extern int background_loop[]; +extern int num_bg_loops; +extern char *element_info[]; +extern int num_element_info; + +/* often used screen positions */ +#define SX 8 +#define SY 8 +#define REAL_SX (SX - 2) +#define REAL_SY (SY - 2) +#define DX 534 +#define DY 60 +#define EX DX +#define EY (DY + 178) +#define TILEX TILESIZE +#define TILEY TILESIZE +#define MINI_TILEX (TILEX / 2) +#define MINI_TILEY (TILEY / 2) +#define MICRO_TILEX (TILEX / 4) +#define MICRO_TILEY (TILEY / 4) +#define MICRO_WALLX (TILEX / 8) +#define MICRO_WALLY (TILEY / 8) +#define MIDPOSX (SCR_FIELDX / 2) +#define MIDPOSY (SCR_FIELDY / 2) +#define DXSIZE 100 +#define DYSIZE 280 +#define VXSIZE DXSIZE +#define VYSIZE 100 +#define EXSIZE DXSIZE +#define EYSIZE (VXSIZE + 44) +#define FULL_SXSIZE (2 + SXSIZE + 2) +#define FULL_SYSIZE (2 + SYSIZE + 2) +#define MICROLEV_XPOS (SX + 12 * TILEX) +#define MICROLEV_YPOS (SY + 6 * TILEY) +#define MICROLEV_XSIZE (STD_LEV_FIELDX * MICRO_TILEX) +#define MICROLEV_YSIZE (STD_LEV_FIELDY * MICRO_TILEY) +#define MICROLABEL_XPOS (SY) +#define MICROLABEL_YPOS (SY + 352) +#define MICROLABEL_XSIZE (SXSIZE) +#define MICROLABEL_YSIZE (FONT4_YSIZE) + +#define GFX_STARTX SX +#define GFX_STARTY SY +#define MINI_GFX_STARTX (SX + 8 * TILEX) +#define MINI_GFX_STARTY (SY + 6 * TILEY) +#define MICRO_GFX_STARTX (MINI_GFX_STARTX + 8 * MINI_TILEX) +#define MICRO_GFX_STARTY (MINI_GFX_STARTY + 6 * MINI_TILEY) +#define GFX_PER_LINE 16 +#define MINI_GFX_PER_LINE GFX_PER_LINE +#define MICRO_GFX_PER_LINE GFX_PER_LINE + +#define MINI_DF_STARTX (8 * TILEX) +#define MINI_DF_STARTY (8 * TILEY) +#define MICRO_DF_STARTX (MINI_DF_STARTX + 8 * MINI_TILEX) +#define MICRO_DF_STARTY (MINI_DF_STARTY + 8 * MINI_TILEY) +#define DF_PER_LINE 16 +#define MINI_DF_PER_LINE DF_PER_LINE +#define MICRO_DF_PER_LINE DF_PER_LINE + +#define MICRO_FONT_STARTX (MICRO_DF_STARTX + 8 * MICRO_TILEX) +#define MICRO_FONT_STARTY (MICRO_DF_STARTY + 8 * MICRO_TILEY) +#define MICRO_FONT_PER_LINE 8 + +/* wall positions (that can be OR'ed together) */ +#define WALL_TOPLEFT 1 +#define WALL_TOPRIGHT 2 +#define WALL_BOTTOMLEFT 4 +#define WALL_BOTTOMRIGHT 8 +#define WALL_LEFT (WALL_TOPLEFT | WALL_BOTTOMLEFT) +#define WALL_RIGHT (WALL_TOPRIGHT | WALL_BOTTOMRIGHT) +#define WALL_TOP (WALL_TOPLEFT | WALL_TOPRIGHT) +#define WALL_BOTTOM (WALL_BOTTOMLEFT | WALL_BOTTOMRIGHT) + +/* game elements: +** 0 - 499: real elements, stored in level file +** 500 - 699: flag elements, only used at runtime +*/ +/* "real" level elements */ +#define EL_EMPTY 0 +#define EL_MIRROR_START 1 +#define EL_MIRROR_00 (EL_MIRROR_START + 0) +#define EL_MIRROR_01 (EL_MIRROR_START + 1) +#define EL_MIRROR_02 (EL_MIRROR_START + 2) +#define EL_MIRROR_03 (EL_MIRROR_START + 3) +#define EL_MIRROR_04 (EL_MIRROR_START + 4) +#define EL_MIRROR_05 (EL_MIRROR_START + 5) +#define EL_MIRROR_06 (EL_MIRROR_START + 6) +#define EL_MIRROR_07 (EL_MIRROR_START + 7) +#define EL_MIRROR_08 (EL_MIRROR_START + 8) +#define EL_MIRROR_09 (EL_MIRROR_START + 9) +#define EL_MIRROR_10 (EL_MIRROR_START + 10) +#define EL_MIRROR_11 (EL_MIRROR_START + 11) +#define EL_MIRROR_12 (EL_MIRROR_START + 12) +#define EL_MIRROR_13 (EL_MIRROR_START + 13) +#define EL_MIRROR_14 (EL_MIRROR_START + 14) +#define EL_MIRROR_15 (EL_MIRROR_START + 15) +#define EL_MIRROR_END EL_MIRROR_15 +#define EL_GRID_STEEL_START 17 +#define EL_GRID_STEEL_00 (EL_GRID_STEEL_START + 0) +#define EL_GRID_STEEL_01 (EL_GRID_STEEL_START + 1) +#define EL_GRID_STEEL_02 (EL_GRID_STEEL_START + 2) +#define EL_GRID_STEEL_03 (EL_GRID_STEEL_START + 3) +#define EL_GRID_STEEL_END EL_GRID_STEEL_03 +#define EL_MCDUFFIN_START 21 +#define EL_MCDUFFIN_RIGHT (EL_MCDUFFIN_START + 0) +#define EL_MCDUFFIN_UP (EL_MCDUFFIN_START + 1) +#define EL_MCDUFFIN_LEFT (EL_MCDUFFIN_START + 2) +#define EL_MCDUFFIN_DOWN (EL_MCDUFFIN_START + 3) +#define EL_MCDUFFIN_END EL_MCDUFFIN_DOWN +#define EL_EXIT_CLOSED 25 +#define EL_EXIT_OPENING_1 26 +#define EL_EXIT_OPENING_2 27 +#define EL_EXIT_OPEN 28 +#define EL_KETTLE 29 +#define EL_BOMB 30 +#define EL_PRISM 31 +#define EL_WALL_START 32 +#define EL_WALL_EMPTY EL_WALL_START +#define EL_WALL_00 EL_WALL_START +#define EL_WALL_STEEL EL_WALL_00 +#define EL_WALL_STEEL_START EL_WALL_00 +#define EL_WALL_15 47 +#define EL_WALL_STEEL_END EL_WALL_15 +#define EL_WALL_16 48 +#define EL_WALL_WOOD EL_WALL_16 +#define EL_WALL_WOOD_START EL_WALL_16 +#define EL_WALL_31 63 +#define EL_WALL_WOOD_END EL_WALL_31 +#define EL_WALL_32 64 +#define EL_WALL_ICE EL_WALL_32 +#define EL_WALL_ICE_START EL_WALL_32 +#define EL_WALL_47 79 +#define EL_WALL_ICE_END EL_WALL_47 +#define EL_WALL_48 80 +#define EL_WALL_AMOEBA EL_WALL_48 +#define EL_WALL_AMOEBA_START EL_WALL_48 +#define EL_WALL_63 95 +#define EL_WALL_AMOEBA_END EL_WALL_63 +#define EL_WALL_END EL_WALL_63 +#define EL_BLOCK_WOOD 96 +#define EL_BALL_GRAY 97 +#define EL_BEAMER_START 98 +#define EL_BEAMER_00 (EL_BEAMER_START + 0) +#define EL_BEAMER_01 (EL_BEAMER_START + 1) +#define EL_BEAMER_02 (EL_BEAMER_START + 2) +#define EL_BEAMER_03 (EL_BEAMER_START + 3) +#define EL_BEAMER_04 (EL_BEAMER_START + 4) +#define EL_BEAMER_05 (EL_BEAMER_START + 5) +#define EL_BEAMER_06 (EL_BEAMER_START + 6) +#define EL_BEAMER_07 (EL_BEAMER_START + 7) +#define EL_BEAMER_08 (EL_BEAMER_START + 8) +#define EL_BEAMER_09 (EL_BEAMER_START + 9) +#define EL_BEAMER_10 (EL_BEAMER_START + 10) +#define EL_BEAMER_11 (EL_BEAMER_START + 11) +#define EL_BEAMER_12 (EL_BEAMER_START + 12) +#define EL_BEAMER_13 (EL_BEAMER_START + 13) +#define EL_BEAMER_14 (EL_BEAMER_START + 14) +#define EL_BEAMER_15 (EL_BEAMER_START + 15) +#define EL_BEAMER_END EL_BEAMER_15 +#define EL_FUSE_ON 114 +#define EL_PACMAN_START 115 +#define EL_PACMAN_RIGHT (EL_PACMAN_START + 0) +#define EL_PACMAN_UP (EL_PACMAN_START + 1) +#define EL_PACMAN_LEFT (EL_PACMAN_START + 2) +#define EL_PACMAN_DOWN (EL_PACMAN_START + 3) +#define EL_PACMAN_END EL_PACMAN_DOWN +#define EL_POLAR_START 119 +#define EL_POLAR_00 (EL_POLAR_START + 0) +#define EL_POLAR_01 (EL_POLAR_START + 1) +#define EL_POLAR_02 (EL_POLAR_START + 2) +#define EL_POLAR_03 (EL_POLAR_START + 3) +#define EL_POLAR_04 (EL_POLAR_START + 4) +#define EL_POLAR_05 (EL_POLAR_START + 5) +#define EL_POLAR_06 (EL_POLAR_START + 6) +#define EL_POLAR_07 (EL_POLAR_START + 7) +#define EL_POLAR_08 (EL_POLAR_START + 8) +#define EL_POLAR_09 (EL_POLAR_START + 9) +#define EL_POLAR_10 (EL_POLAR_START + 10) +#define EL_POLAR_11 (EL_POLAR_START + 11) +#define EL_POLAR_12 (EL_POLAR_START + 12) +#define EL_POLAR_13 (EL_POLAR_START + 13) +#define EL_POLAR_14 (EL_POLAR_START + 14) +#define EL_POLAR_15 (EL_POLAR_START + 15) +#define EL_POLAR_END EL_POLAR_15 +#define EL_POLAR_CROSS_START 135 +#define EL_POLAR_CROSS_00 (EL_POLAR_CROSS_START + 0) +#define EL_POLAR_CROSS_01 (EL_POLAR_CROSS_START + 1) +#define EL_POLAR_CROSS_02 (EL_POLAR_CROSS_START + 2) +#define EL_POLAR_CROSS_03 (EL_POLAR_CROSS_START + 3) +#define EL_POLAR_CROSS_END EL_POLAR_CROSS_03 +#define EL_MIRROR_FIXED_START 139 +#define EL_MIRROR_FIXED_00 (EL_MIRROR_FIXED_START + 0) +#define EL_MIRROR_FIXED_01 (EL_MIRROR_FIXED_START + 1) +#define EL_MIRROR_FIXED_02 (EL_MIRROR_FIXED_START + 2) +#define EL_MIRROR_FIXED_03 (EL_MIRROR_FIXED_START + 3) +#define EL_MIRROR_FIXED_END EL_MIRROR_FIXED_03 +#define EL_GATE_STONE 143 +#define EL_KEY 144 +#define EL_LIGHTBULB_OFF 145 +#define EL_LIGHTBULB_ON 146 +#define EL_LIGHTBALL 147 +#define EL_BLOCK_STONE 148 +#define EL_GATE_WOOD 149 +#define EL_FUEL_FULL 150 +#define EL_GRID_WOOD_START 151 +#define EL_GRID_WOOD_00 (EL_GRID_WOOD_START + 0) +#define EL_GRID_WOOD_01 (EL_GRID_WOOD_START + 1) +#define EL_GRID_WOOD_02 (EL_GRID_WOOD_START + 2) +#define EL_GRID_WOOD_03 (EL_GRID_WOOD_START + 3) +#define EL_GRID_WOOD_END EL_GRID_WOOD_03 +#define EL_FUEL_EMPTY 155 + +#define EL_CHAR_START 160 +#define EL_CHAR_ASCII0 (EL_CHAR_START-32) +#define EL_CHAR_AUSRUF (EL_CHAR_ASCII0+33) +#define EL_CHAR_ZOLL (EL_CHAR_ASCII0+34) +#define EL_CHAR_RAUTE (EL_CHAR_ASCII0+35) +#define EL_CHAR_DOLLAR (EL_CHAR_ASCII0+36) +#define EL_CHAR_PROZ (EL_CHAR_ASCII0+37) +#define EL_CHAR_AMPERSAND (EL_CHAR_ASCII0+38) +#define EL_CHAR_APOSTR (EL_CHAR_ASCII0+39) +#define EL_CHAR_KLAMM1 (EL_CHAR_ASCII0+40) +#define EL_CHAR_KLAMM2 (EL_CHAR_ASCII0+41) +#define EL_CHAR_MULT (EL_CHAR_ASCII0+42) +#define EL_CHAR_PLUS (EL_CHAR_ASCII0+43) +#define EL_CHAR_KOMMA (EL_CHAR_ASCII0+44) +#define EL_CHAR_MINUS (EL_CHAR_ASCII0+45) +#define EL_CHAR_PUNKT (EL_CHAR_ASCII0+46) +#define EL_CHAR_SLASH (EL_CHAR_ASCII0+47) +#define EL_CHAR_0 (EL_CHAR_ASCII0+48) +#define EL_CHAR_9 (EL_CHAR_ASCII0+57) +#define EL_CHAR_DOPPEL (EL_CHAR_ASCII0+58) +#define EL_CHAR_SEMIKL (EL_CHAR_ASCII0+59) +#define EL_CHAR_LT (EL_CHAR_ASCII0+60) +#define EL_CHAR_GLEICH (EL_CHAR_ASCII0+61) +#define EL_CHAR_GT (EL_CHAR_ASCII0+62) +#define EL_CHAR_FRAGE (EL_CHAR_ASCII0+63) +#define EL_CHAR_AT (EL_CHAR_ASCII0+64) +#define EL_CHAR_A (EL_CHAR_ASCII0+65) +#define EL_CHAR_Z (EL_CHAR_ASCII0+90) +#define EL_CHAR_AE (EL_CHAR_ASCII0+91) +#define EL_CHAR_OE (EL_CHAR_ASCII0+92) +#define EL_CHAR_UE (EL_CHAR_ASCII0+93) +#define EL_CHAR_COPY (EL_CHAR_ASCII0+94) +#define EL_CHAR_END (EL_CHAR_START+79) + +#define EL_CHAR(x) ((x) == CHAR_BYTE_UMLAUT_A ? EL_CHAR_AE : \ + (x) == CHAR_BYTE_UMLAUT_O ? EL_CHAR_OE : \ + (x) == CHAR_BYTE_UMLAUT_U ? EL_CHAR_UE : \ + EL_CHAR_A + (x) - 'A') + +/* elements for "Deflektor" style levels */ +#define EL_DF_START 240 + +#define EL_DF_MIRROR_START EL_DF_START +#define EL_DF_MIRROR_00 (EL_DF_MIRROR_START + 0) +#define EL_DF_MIRROR_01 (EL_DF_MIRROR_START + 1) +#define EL_DF_MIRROR_02 (EL_DF_MIRROR_START + 2) +#define EL_DF_MIRROR_03 (EL_DF_MIRROR_START + 3) +#define EL_DF_MIRROR_04 (EL_DF_MIRROR_START + 4) +#define EL_DF_MIRROR_05 (EL_DF_MIRROR_START + 5) +#define EL_DF_MIRROR_06 (EL_DF_MIRROR_START + 6) +#define EL_DF_MIRROR_07 (EL_DF_MIRROR_START + 7) +#define EL_DF_MIRROR_08 (EL_DF_MIRROR_START + 8) +#define EL_DF_MIRROR_09 (EL_DF_MIRROR_START + 9) +#define EL_DF_MIRROR_10 (EL_DF_MIRROR_START + 10) +#define EL_DF_MIRROR_11 (EL_DF_MIRROR_START + 11) +#define EL_DF_MIRROR_12 (EL_DF_MIRROR_START + 12) +#define EL_DF_MIRROR_13 (EL_DF_MIRROR_START + 13) +#define EL_DF_MIRROR_14 (EL_DF_MIRROR_START + 14) +#define EL_DF_MIRROR_15 (EL_DF_MIRROR_START + 15) +#define EL_DF_MIRROR_END EL_DF_MIRROR_15 + +#define EL_GRID_WOOD_FIXED_START 256 +#define EL_GRID_WOOD_FIXED_00 (EL_GRID_WOOD_FIXED_START + 0) /* 0.0° */ +#define EL_GRID_WOOD_FIXED_01 (EL_GRID_WOOD_FIXED_START + 1) /* 22.5° */ +#define EL_GRID_WOOD_FIXED_02 (EL_GRID_WOOD_FIXED_START + 2) /* 45.0° */ +#define EL_GRID_WOOD_FIXED_03 (EL_GRID_WOOD_FIXED_START + 3) /* 67.5° */ +#define EL_GRID_WOOD_FIXED_04 (EL_GRID_WOOD_FIXED_START + 4) /* 90.0° */ +#define EL_GRID_WOOD_FIXED_05 (EL_GRID_WOOD_FIXED_START + 5) /* 112.5° */ +#define EL_GRID_WOOD_FIXED_06 (EL_GRID_WOOD_FIXED_START + 6) /* 135.0° */ +#define EL_GRID_WOOD_FIXED_07 (EL_GRID_WOOD_FIXED_START + 7) /* 157.5° */ +#define EL_GRID_WOOD_FIXED_END EL_GRID_WOOD_FIXED_07 + +#define EL_GRID_STEEL_FIXED_START 264 +#define EL_GRID_STEEL_FIXED_00 (EL_GRID_STEEL_FIXED_START + 0) /* 0.0° */ +#define EL_GRID_STEEL_FIXED_01 (EL_GRID_STEEL_FIXED_START + 1) /* 22.5° */ +#define EL_GRID_STEEL_FIXED_02 (EL_GRID_STEEL_FIXED_START + 2) /* 45.0° */ +#define EL_GRID_STEEL_FIXED_03 (EL_GRID_STEEL_FIXED_START + 3) /* 67.5° */ +#define EL_GRID_STEEL_FIXED_04 (EL_GRID_STEEL_FIXED_START + 4) /* 90.0° */ +#define EL_GRID_STEEL_FIXED_05 (EL_GRID_STEEL_FIXED_START + 5) /* 112.5° */ +#define EL_GRID_STEEL_FIXED_06 (EL_GRID_STEEL_FIXED_START + 6) /* 135.0° */ +#define EL_GRID_STEEL_FIXED_07 (EL_GRID_STEEL_FIXED_START + 7) /* 157.5° */ +#define EL_GRID_STEEL_FIXED_END EL_GRID_STEEL_FIXED_07 + +#define EL_DF_WALL_WOOD 272 +#define EL_DF_WALL_START EL_DF_WALL_WOOD_START +#define EL_DF_WALL_WOOD_START (EL_DF_WALL_WOOD + 0) +#define EL_DF_WALL_WOOD_END (EL_DF_WALL_WOOD + 15) + +#define EL_DF_WALL_STEEL 288 +#define EL_DF_WALL_STEEL_START (EL_DF_WALL_STEEL + 0) +#define EL_DF_WALL_STEEL_END (EL_DF_WALL_STEEL + 15) +#define EL_DF_WALL_END EL_DF_WALL_STEEL_END + +#define EL_DF_EMPTY 304 +#define EL_CELL 305 +#define EL_MINE 306 +#define EL_REFRACTOR 307 + +#define EL_LASER_START 308 +#define EL_LASER_RIGHT (EL_LASER_START + 0) +#define EL_LASER_UP (EL_LASER_START + 1) +#define EL_LASER_LEFT (EL_LASER_START + 2) +#define EL_LASER_DOWN (EL_LASER_START + 3) +#define EL_LASER_END EL_LASER_DOWN + +#define EL_RECEIVER_START 312 +#define EL_RECEIVER_RIGHT (EL_RECEIVER_START + 0) +#define EL_RECEIVER_UP (EL_RECEIVER_START + 1) +#define EL_RECEIVER_LEFT (EL_RECEIVER_START + 2) +#define EL_RECEIVER_DOWN (EL_RECEIVER_START + 3) +#define EL_RECEIVER_END EL_RECEIVER_DOWN + +#define EL_FIBRE_OPTIC_START 316 +#define EL_FIBRE_OPTIC_00 (EL_FIBRE_OPTIC_START + 0) +#define EL_FIBRE_OPTIC_01 (EL_FIBRE_OPTIC_START + 1) +#define EL_FIBRE_OPTIC_02 (EL_FIBRE_OPTIC_START + 2) +#define EL_FIBRE_OPTIC_03 (EL_FIBRE_OPTIC_START + 3) +#define EL_FIBRE_OPTIC_04 (EL_FIBRE_OPTIC_START + 4) +#define EL_FIBRE_OPTIC_05 (EL_FIBRE_OPTIC_START + 5) +#define EL_FIBRE_OPTIC_06 (EL_FIBRE_OPTIC_START + 6) +#define EL_FIBRE_OPTIC_07 (EL_FIBRE_OPTIC_START + 7) +#define EL_FIBRE_OPTIC_END EL_FIBRE_OPTIC_07 + +#define EL_DF_MIRROR_AUTO_START 324 +#define EL_DF_MIRROR_AUTO_00 (EL_DF_MIRROR_AUTO_START + 0) +#define EL_DF_MIRROR_AUTO_01 (EL_DF_MIRROR_AUTO_START + 1) +#define EL_DF_MIRROR_AUTO_02 (EL_DF_MIRROR_AUTO_START + 2) +#define EL_DF_MIRROR_AUTO_03 (EL_DF_MIRROR_AUTO_START + 3) +#define EL_DF_MIRROR_AUTO_04 (EL_DF_MIRROR_AUTO_START + 4) +#define EL_DF_MIRROR_AUTO_05 (EL_DF_MIRROR_AUTO_START + 5) +#define EL_DF_MIRROR_AUTO_06 (EL_DF_MIRROR_AUTO_START + 6) +#define EL_DF_MIRROR_AUTO_07 (EL_DF_MIRROR_AUTO_START + 7) +#define EL_DF_MIRROR_AUTO_08 (EL_DF_MIRROR_AUTO_START + 8) +#define EL_DF_MIRROR_AUTO_09 (EL_DF_MIRROR_AUTO_START + 9) +#define EL_DF_MIRROR_AUTO_10 (EL_DF_MIRROR_AUTO_START + 10) +#define EL_DF_MIRROR_AUTO_11 (EL_DF_MIRROR_AUTO_START + 11) +#define EL_DF_MIRROR_AUTO_12 (EL_DF_MIRROR_AUTO_START + 12) +#define EL_DF_MIRROR_AUTO_13 (EL_DF_MIRROR_AUTO_START + 13) +#define EL_DF_MIRROR_AUTO_14 (EL_DF_MIRROR_AUTO_START + 14) +#define EL_DF_MIRROR_AUTO_15 (EL_DF_MIRROR_AUTO_START + 15) +#define EL_DF_MIRROR_AUTO_END EL_DF_MIRROR_AUTO_15 + +#define EL_GRID_WOOD_AUTO_START 340 +#define EL_GRID_WOOD_AUTO_00 (EL_GRID_WOOD_AUTO_START + 0) +#define EL_GRID_WOOD_AUTO_01 (EL_GRID_WOOD_AUTO_START + 1) +#define EL_GRID_WOOD_AUTO_02 (EL_GRID_WOOD_AUTO_START + 2) +#define EL_GRID_WOOD_AUTO_03 (EL_GRID_WOOD_AUTO_START + 3) +#define EL_GRID_WOOD_AUTO_04 (EL_GRID_WOOD_AUTO_START + 4) +#define EL_GRID_WOOD_AUTO_05 (EL_GRID_WOOD_AUTO_START + 5) +#define EL_GRID_WOOD_AUTO_06 (EL_GRID_WOOD_AUTO_START + 6) +#define EL_GRID_WOOD_AUTO_07 (EL_GRID_WOOD_AUTO_START + 7) +#define EL_GRID_WOOD_AUTO_END EL_GRID_WOOD_AUTO_07 + +#define EL_GRID_STEEL_AUTO_START 348 +#define EL_GRID_STEEL_AUTO_00 (EL_GRID_STEEL_AUTO_START + 0) +#define EL_GRID_STEEL_AUTO_01 (EL_GRID_STEEL_AUTO_START + 1) +#define EL_GRID_STEEL_AUTO_02 (EL_GRID_STEEL_AUTO_START + 2) +#define EL_GRID_STEEL_AUTO_03 (EL_GRID_STEEL_AUTO_START + 3) +#define EL_GRID_STEEL_AUTO_04 (EL_GRID_STEEL_AUTO_START + 4) +#define EL_GRID_STEEL_AUTO_05 (EL_GRID_STEEL_AUTO_START + 5) +#define EL_GRID_STEEL_AUTO_06 (EL_GRID_STEEL_AUTO_START + 6) +#define EL_GRID_STEEL_AUTO_07 (EL_GRID_STEEL_AUTO_START + 7) +#define EL_GRID_STEEL_AUTO_END EL_GRID_STEEL_AUTO_07 + +#define EL_DF_END 355 + +#define EL_BEAMER_RED_START 356 +#define EL_BEAMER_RED_END (EL_BEAMER_RED_START + 15) +#define EL_BEAMER_YELLOW_START 372 +#define EL_BEAMER_YELLOW_END (EL_BEAMER_YELLOW_START + 15) +#define EL_BEAMER_GREEN_START 388 +#define EL_BEAMER_GREEN_END (EL_BEAMER_GREEN_START + 15) +#define EL_BEAMER_BLUE_START 404 +#define EL_BEAMER_BLUE_END (EL_BEAMER_BLUE_START + 15) + +/* "real" (and therefore drawable) runtime elements */ +#define EL_FIRST_RUNTIME_EL 500 +#define EL_FUSE_OFF 501 +#define EL_PACMAN 502 +#define EL_EXIT_OPENING 503 +#define EL_GRAY_BALL_OPENING 504 + +#define EL_WALL_CHANGING 512 +#define EL_WALL_CHANGING_START (EL_WALL_CHANGING + 0) +#define EL_WALL_CHANGING_END (EL_WALL_CHANGING + 15) + +#if 0 +#define EL_MIRROR 5 +#define EL_GRID_STEEL 5 +#define EL_MCDUFFIN 5 +#define EL_BEAMER 5 +#define EL_POLAR 5 +#define EL_POLAR_CROSS 5 +#define EL_MIRROR_FIXED 5 +#define EL_GRID_WOOD 5 +#define EL_DF_MIRROR 5 +#define EL_GRID_WOOD_FIXED 5 +#define EL_GRID_STEEL_FIXED 5 +#define EL_LASER 5 +#define EL_RECEIVER 5 +#define EL_FIBRE_OPTIC 5 +#define EL_DF_MIRROR_AUTO 5 +#define EL_GRID_WOOD_AUTO 5 +#define EL_GRID_STEEL_AUTO 5 +#endif + +/* "unreal" (and therefore not drawable) runtime elements */ +#define EL_BLOCKED 600 +#define EL_EXPLODING_OPAQUE 601 +#define EL_EXPLODING_TRANSP 602 + + +/* game graphics: +** 0 - 191: graphics from "MirrorScreen" +** 192 - 255: pseudo graphics mapped to "MirrorScreen" +** 256 - 511: graphics from "MirrorFont" +** 512 - 767: graphics from "MirrorDF" +*/ + +#define GFX_START_MIRRORSCREEN 0 +#define GFX_END_MIRRORSCREEN 191 +#define GFX_START_PSEUDO 192 +#define GFX_END_PSEUDO 255 +#define GFX_START_MIRRORFONT 256 +#define GFX_END_MIRRORFONT 511 +#define GFX_START_MIRRORDF 512 +#define GFX_END_MIRRORDF 767 + +#define NUM_TILES 512 + +/* graphics from "MirrorScreen" */ +#define GFX_EMPTY (-1) +/* row 0 (0) */ +#define GFX_MIRROR_START 0 +#define GFX_MIRROR GFX_MIRROR_START +#define GFX_MIRROR_00 (GFX_MIRROR_START + 0) +#define GFX_MIRROR_01 (GFX_MIRROR_START + 1) +#define GFX_MIRROR_02 (GFX_MIRROR_START + 2) +#define GFX_MIRROR_03 (GFX_MIRROR_START + 3) +#define GFX_MIRROR_04 (GFX_MIRROR_START + 4) +#define GFX_MIRROR_05 (GFX_MIRROR_START + 5) +#define GFX_MIRROR_06 (GFX_MIRROR_START + 6) +#define GFX_MIRROR_07 (GFX_MIRROR_START + 7) +#define GFX_MIRROR_08 (GFX_MIRROR_START + 8) +#define GFX_MIRROR_09 (GFX_MIRROR_START + 9) +#define GFX_MIRROR_10 (GFX_MIRROR_START + 10) +#define GFX_MIRROR_11 (GFX_MIRROR_START + 11) +#define GFX_MIRROR_12 (GFX_MIRROR_START + 12) +#define GFX_MIRROR_13 (GFX_MIRROR_START + 13) +#define GFX_MIRROR_14 (GFX_MIRROR_START + 14) +#define GFX_MIRROR_15 (GFX_MIRROR_START + 15) +#define GFX_MIRROR_END GFX_MIRROR_15 +/* row 1 (16) */ +#define GFX_GRID_STEEL_START 16 +#define GFX_GRID_STEEL GFX_GRID_STEEL_START +#define GFX_GRID_STEEL_00 (GFX_GRID_STEEL_START + 0) +#define GFX_GRID_STEEL_01 (GFX_GRID_STEEL_START + 1) +#define GFX_GRID_STEEL_02 (GFX_GRID_STEEL_START + 2) +#define GFX_GRID_STEEL_03 (GFX_GRID_STEEL_START + 3) +#define GFX_MCDUFFIN_START 20 +#define GFX_MCDUFFIN GFX_MCDUFFIN_START +#define GFX_MCDUFFIN_RIGHT (GFX_MCDUFFIN_START + 0) +#define GFX_MCDUFFIN_UP (GFX_MCDUFFIN_START + 1) +#define GFX_MCDUFFIN_LEFT (GFX_MCDUFFIN_START + 2) +#define GFX_MCDUFFIN_DOWN (GFX_MCDUFFIN_START + 3) +#define GFX_EXIT_CLOSED 24 +#define GFX_EXIT_OPENING_1 25 +#define GFX_EXIT_OPENING_2 26 +#define GFX_EXIT_OPEN 27 +#define GFX_KETTLE 28 +#define GFX_EXPLOSION_KETTLE 29 +/* row 2 (32) */ +#define GFX_PRISM 32 +#define GFX_WALL_SEVERAL 33 +#define GFX_WALL_ANIMATION 34 +#define GFX_BLOCK_WOOD 36 +#define GFX_BOMB 37 +#define GFX_FUSE_ON 38 +#define GFX_FUSE_OFF 39 +#define GFX_GATE_STONE 40 +#define GFX_KEY 41 +#define GFX_LIGHTBULB_OFF 42 +#define GFX_LIGHTBULB_ON 43 +#define GFX_BALL_RED 44 +#define GFX_BALL_BLUE 45 +#define GFX_BALL_YELLOW 46 +#define GFX_BALL_GRAY 47 +/* row 3 (48) */ +#define GFX_BEAMER_START 48 +#define GFX_BEAMER_END 63 +/* row 4 (64) */ +#define GFX_PACMAN_START 64 +#define GFX_PACMAN GFX_PACMAN_START +#define GFX_PACMAN_RIGHT (GFX_PACMAN_START + 0) +#define GFX_PACMAN_UP (GFX_PACMAN_START + 1) +#define GFX_PACMAN_LEFT (GFX_PACMAN_START + 2) +#define GFX_PACMAN_DOWN (GFX_PACMAN_START + 3) +#define GFX_EXPLOSION_START 72 +#define GFX_EXPLOSION_SHORT 76 +#define GFX_EXPLOSION_LAST 78 +/* row 5 (80) */ +#define GFX_POLAR_START 80 +#define GFX_POLAR_END 95 +/* row 6 (96) */ +#define GFX_POLAR_CROSS_START 96 +#define GFX_POLAR_CROSS GFX_POLAR_CROSS_START +#define GFX_POLAR_CROSS_00 (GFX_POLAR_CROSS_START + 0) +#define GFX_POLAR_CROSS_01 (GFX_POLAR_CROSS_START + 1) +#define GFX_POLAR_CROSS_02 (GFX_POLAR_CROSS_START + 2) +#define GFX_POLAR_CROSS_03 (GFX_POLAR_CROSS_START + 3) +#define GFX_MIRROR_FIXED_START 100 +#define GFX_MIRROR_FIXED GFX_MIRROR_FIXED_START +#define GFX_MIRROR_FIXED_00 (GFX_MIRROR_FIXED_START + 0) +#define GFX_MIRROR_FIXED_01 (GFX_MIRROR_FIXED_START + 1) +#define GFX_MIRROR_FIXED_02 (GFX_MIRROR_FIXED_START + 2) +#define GFX_MIRROR_FIXED_03 (GFX_MIRROR_FIXED_START + 3) +/* row 7 (112) */ +#define GFX_BLOCK_STONE 112 +#define GFX_GATE_WOOD 113 +#define GFX_FUEL_FULL 114 +#define GFX_FUEL_EMPTY 115 +#define GFX_GRID_WOOD_00 116 +#define GFX_GRID_WOOD_01 117 +#define GFX_GRID_WOOD_02 118 +#define GFX_GRID_WOOD_03 119 +/* row 8 (128) */ +#define GFX_ARROW_BLUE_LEFT 128 +#define GFX_ARROW_BLUE_RIGHT 129 +#define GFX_ARROW_BLUE_UP 130 +#define GFX_ARROW_BLUE_DOWN 131 +#define GFX_ARROW_RED_LEFT 132 +#define GFX_ARROW_RED_RIGHT 133 +#define GFX_ARROW_RED_UP 134 +#define GFX_ARROW_RED_DOWN 135 +/* row 9 (144) */ +#define GFX_SCROLLBAR_BLUE 144 +#define GFX_SCROLLBAR_RED 145 +/* row 10 (160) */ +#define GFX_MASK_CIRCLE 160 +#define GFX_MASK_RECTANGLE 161 +#define GFX_MASK_RECTANGLE2 162 +#define GFX_MASK_RECTANGLE3 163 +#define GFX_MASK_GRID_00 164 +#define GFX_MASK_GRID_01 165 +#define GFX_MASK_GRID_02 166 +#define GFX_MASK_GRID_03 167 +/* row 11 (176) */ +#define GFX_MASK_MCDUFFIN_00 176 +#define GFX_MASK_MCDUFFIN_01 177 +#define GFX_MASK_MCDUFFIN_02 178 +#define GFX_MASK_MCDUFFIN_03 179 + +/* pseudo-graphics; will be mapped to other graphics */ +#define GFX_WALL_STEEL 192 +#define GFX_WALL_WOOD 193 +#define GFX_WALL_ICE 194 +#define GFX_WALL_AMOEBA 195 +#define GFX_DF_WALL_STEEL 196 +#define GFX_DF_WALL_WOOD 197 + +#define GFX_KUGEL_ROT GFX_BALL_RED +#define GFX_KUGEL_BLAU GFX_BALL_BLUE +#define GFX_KUGEL_GELB GFX_BALL_YELLOW +#define GFX_KUGEL_GRAU GFX_BALL_GRAY + +/* graphics from "MirrorFont" */ +#define GFX_CHAR_START (GFX_START_MIRRORFONT) +#define GFX_CHAR_ASCII0 (GFX_CHAR_START - 32) +#define GFX_CHAR_AUSRUF (GFX_CHAR_ASCII0 + 33) +#define GFX_CHAR_ZOLL (GFX_CHAR_ASCII0 + 34) +#define GFX_CHAR_DOLLAR (GFX_CHAR_ASCII0 + 36) +#define GFX_CHAR_PROZ (GFX_CHAR_ASCII0 + 37) +#define GFX_CHAR_APOSTR (GFX_CHAR_ASCII0 + 39) +#define GFX_CHAR_KLAMM1 (GFX_CHAR_ASCII0 + 40) +#define GFX_CHAR_KLAMM2 (GFX_CHAR_ASCII0 + 41) +#define GFX_CHAR_PLUS (GFX_CHAR_ASCII0 + 43) +#define GFX_CHAR_KOMMA (GFX_CHAR_ASCII0 + 44) +#define GFX_CHAR_MINUS (GFX_CHAR_ASCII0 + 45) +#define GFX_CHAR_PUNKT (GFX_CHAR_ASCII0 + 46) +#define GFX_CHAR_SLASH (GFX_CHAR_ASCII0 + 47) +#define GFX_CHAR_0 (GFX_CHAR_ASCII0 + 48) +#define GFX_CHAR_9 (GFX_CHAR_ASCII0 + 57) +#define GFX_CHAR_DOPPEL (GFX_CHAR_ASCII0 + 58) +#define GFX_CHAR_SEMIKL (GFX_CHAR_ASCII0 + 59) +#define GFX_CHAR_LT (GFX_CHAR_ASCII0 + 60) +#define GFX_CHAR_GLEICH (GFX_CHAR_ASCII0 + 61) +#define GFX_CHAR_GT (GFX_CHAR_ASCII0 + 62) +#define GFX_CHAR_FRAGE (GFX_CHAR_ASCII0 + 63) +#define GFX_CHAR_AT (GFX_CHAR_ASCII0 + 64) +#define GFX_CHAR_A (GFX_CHAR_ASCII0 + 65) +#define GFX_CHAR_Z (GFX_CHAR_ASCII0 + 90) +#define GFX_CHAR_AE (GFX_CHAR_ASCII0 + 91) +#define GFX_CHAR_OE (GFX_CHAR_ASCII0 + 92) +#define GFX_CHAR_UE (GFX_CHAR_ASCII0 + 93) +#define GFX_CHAR_COPY (GFX_CHAR_ASCII0 + 94) +#define GFX_CHAR_END (GFX_CHAR_START + 79) + +/* graphics from "MirrorDF" */ +#define GFX_DF_MIRROR_00 (GFX_START_MIRRORDF + 0 * DF_PER_LINE + 0) +#define GFX_DF_MIRROR_01 (GFX_START_MIRRORDF + 0 * DF_PER_LINE + 1) +#define GFX_DF_MIRROR_02 (GFX_START_MIRRORDF + 0 * DF_PER_LINE + 2) +#define GFX_DF_MIRROR_03 (GFX_START_MIRRORDF + 0 * DF_PER_LINE + 3) +#define GFX_DF_MIRROR_04 (GFX_START_MIRRORDF + 0 * DF_PER_LINE + 4) +#define GFX_DF_MIRROR_05 (GFX_START_MIRRORDF + 0 * DF_PER_LINE + 5) +#define GFX_DF_MIRROR_06 (GFX_START_MIRRORDF + 0 * DF_PER_LINE + 6) +#define GFX_DF_MIRROR_07 (GFX_START_MIRRORDF + 0 * DF_PER_LINE + 7) +#define GFX_DF_MIRROR_08 (GFX_START_MIRRORDF + 0 * DF_PER_LINE + 8) +#define GFX_DF_MIRROR_09 (GFX_START_MIRRORDF + 0 * DF_PER_LINE + 9) +#define GFX_DF_MIRROR_10 (GFX_START_MIRRORDF + 0 * DF_PER_LINE + 10) +#define GFX_DF_MIRROR_11 (GFX_START_MIRRORDF + 0 * DF_PER_LINE + 11) +#define GFX_DF_MIRROR_12 (GFX_START_MIRRORDF + 0 * DF_PER_LINE + 12) +#define GFX_DF_MIRROR_13 (GFX_START_MIRRORDF + 0 * DF_PER_LINE + 13) +#define GFX_DF_MIRROR_14 (GFX_START_MIRRORDF + 0 * DF_PER_LINE + 14) +#define GFX_DF_MIRROR_15 (GFX_START_MIRRORDF + 0 * DF_PER_LINE + 15) + +#define GFX_DF_MIRROR_AUTO_00 (GFX_START_MIRRORDF + 1 * DF_PER_LINE + 0) +#define GFX_DF_MIRROR_AUTO_01 (GFX_START_MIRRORDF + 1 * DF_PER_LINE + 1) +#define GFX_DF_MIRROR_AUTO_02 (GFX_START_MIRRORDF + 1 * DF_PER_LINE + 2) +#define GFX_DF_MIRROR_AUTO_03 (GFX_START_MIRRORDF + 1 * DF_PER_LINE + 3) +#define GFX_DF_MIRROR_AUTO_04 (GFX_START_MIRRORDF + 1 * DF_PER_LINE + 4) +#define GFX_DF_MIRROR_AUTO_05 (GFX_START_MIRRORDF + 1 * DF_PER_LINE + 5) +#define GFX_DF_MIRROR_AUTO_06 (GFX_START_MIRRORDF + 1 * DF_PER_LINE + 6) +#define GFX_DF_MIRROR_AUTO_07 (GFX_START_MIRRORDF + 1 * DF_PER_LINE + 7) +#define GFX_DF_MIRROR_AUTO_08 (GFX_START_MIRRORDF + 1 * DF_PER_LINE + 8) +#define GFX_DF_MIRROR_AUTO_09 (GFX_START_MIRRORDF + 1 * DF_PER_LINE + 9) +#define GFX_DF_MIRROR_AUTO_10 (GFX_START_MIRRORDF + 1 * DF_PER_LINE + 10) +#define GFX_DF_MIRROR_AUTO_11 (GFX_START_MIRRORDF + 1 * DF_PER_LINE + 11) +#define GFX_DF_MIRROR_AUTO_12 (GFX_START_MIRRORDF + 1 * DF_PER_LINE + 12) +#define GFX_DF_MIRROR_AUTO_13 (GFX_START_MIRRORDF + 1 * DF_PER_LINE + 13) +#define GFX_DF_MIRROR_AUTO_14 (GFX_START_MIRRORDF + 1 * DF_PER_LINE + 14) +#define GFX_DF_MIRROR_AUTO_15 (GFX_START_MIRRORDF + 1 * DF_PER_LINE + 15) + +#define GFX_GRID_STEEL_FIXED_00 (GFX_START_MIRRORDF + 2 * DF_PER_LINE + 0) +#define GFX_GRID_STEEL_FIXED_01 (GFX_START_MIRRORDF + 2 * DF_PER_LINE + 1) +#define GFX_GRID_STEEL_FIXED_02 (GFX_START_MIRRORDF + 2 * DF_PER_LINE + 2) +#define GFX_GRID_STEEL_FIXED_03 (GFX_START_MIRRORDF + 2 * DF_PER_LINE + 3) +#define GFX_GRID_STEEL_FIXED_04 (GFX_START_MIRRORDF + 2 * DF_PER_LINE + 4) +#define GFX_GRID_STEEL_FIXED_05 (GFX_START_MIRRORDF + 2 * DF_PER_LINE + 5) +#define GFX_GRID_STEEL_FIXED_06 (GFX_START_MIRRORDF + 2 * DF_PER_LINE + 6) +#define GFX_GRID_STEEL_FIXED_07 (GFX_START_MIRRORDF + 2 * DF_PER_LINE + 7) +#define GFX_GRID_STEEL_FIXED GFX_GRID_STEEL_FIXED_00 + +#define GFX_GRID_WOOD_FIXED_00 (GFX_START_MIRRORDF + 2 * DF_PER_LINE + 8) +#define GFX_GRID_WOOD_FIXED_01 (GFX_START_MIRRORDF + 2 * DF_PER_LINE + 9) +#define GFX_GRID_WOOD_FIXED_02 (GFX_START_MIRRORDF + 2 * DF_PER_LINE + 10) +#define GFX_GRID_WOOD_FIXED_03 (GFX_START_MIRRORDF + 2 * DF_PER_LINE + 11) +#define GFX_GRID_WOOD_FIXED_04 (GFX_START_MIRRORDF + 2 * DF_PER_LINE + 12) +#define GFX_GRID_WOOD_FIXED_05 (GFX_START_MIRRORDF + 2 * DF_PER_LINE + 13) +#define GFX_GRID_WOOD_FIXED_06 (GFX_START_MIRRORDF + 2 * DF_PER_LINE + 14) +#define GFX_GRID_WOOD_FIXED_07 (GFX_START_MIRRORDF + 2 * DF_PER_LINE + 15) +#define GFX_GRID_WOOD_FIXED GFX_GRID_WOOD_FIXED_00 + +#define GFX_GRID_STEEL_AUTO_00 (GFX_START_MIRRORDF + 3 * DF_PER_LINE + 0) +#define GFX_GRID_STEEL_AUTO_01 (GFX_START_MIRRORDF + 3 * DF_PER_LINE + 1) +#define GFX_GRID_STEEL_AUTO_02 (GFX_START_MIRRORDF + 3 * DF_PER_LINE + 2) +#define GFX_GRID_STEEL_AUTO_03 (GFX_START_MIRRORDF + 3 * DF_PER_LINE + 3) +#define GFX_GRID_STEEL_AUTO_04 (GFX_START_MIRRORDF + 3 * DF_PER_LINE + 4) +#define GFX_GRID_STEEL_AUTO_05 (GFX_START_MIRRORDF + 3 * DF_PER_LINE + 5) +#define GFX_GRID_STEEL_AUTO_06 (GFX_START_MIRRORDF + 3 * DF_PER_LINE + 6) +#define GFX_GRID_STEEL_AUTO_07 (GFX_START_MIRRORDF + 3 * DF_PER_LINE + 7) +#define GFX_GRID_STEEL_AUTO GFX_GRID_STEEL_AUTO_00 + +#define GFX_GRID_WOOD_AUTO_00 (GFX_START_MIRRORDF + 3 * DF_PER_LINE + 8) +#define GFX_GRID_WOOD_AUTO_01 (GFX_START_MIRRORDF + 3 * DF_PER_LINE + 9) +#define GFX_GRID_WOOD_AUTO_02 (GFX_START_MIRRORDF + 3 * DF_PER_LINE + 10) +#define GFX_GRID_WOOD_AUTO_03 (GFX_START_MIRRORDF + 3 * DF_PER_LINE + 11) +#define GFX_GRID_WOOD_AUTO_04 (GFX_START_MIRRORDF + 3 * DF_PER_LINE + 12) +#define GFX_GRID_WOOD_AUTO_05 (GFX_START_MIRRORDF + 3 * DF_PER_LINE + 13) +#define GFX_GRID_WOOD_AUTO_06 (GFX_START_MIRRORDF + 3 * DF_PER_LINE + 14) +#define GFX_GRID_WOOD_AUTO_07 (GFX_START_MIRRORDF + 3 * DF_PER_LINE + 15) +#define GFX_GRID_WOOD_AUTO GFX_GRID_WOOD_AUTO_00 + +#define GFX_BEAMER_RED_START (GFX_START_MIRRORDF + 4 * DF_PER_LINE + 0) +#define GFX_BEAMER_RED_END (GFX_START_MIRRORDF + 4 * DF_PER_LINE + 15) +#define GFX_BEAMER_YELLOW_START (GFX_START_MIRRORDF + 5 * DF_PER_LINE + 0) +#define GFX_BEAMER_YELLOW_END (GFX_START_MIRRORDF + 5 * DF_PER_LINE + 15) +#define GFX_BEAMER_GREEN_START (GFX_START_MIRRORDF + 6 * DF_PER_LINE + 0) +#define GFX_BEAMER_GREEN_END (GFX_START_MIRRORDF + 6 * DF_PER_LINE + 15) +#define GFX_BEAMER_BLUE_START (GFX_START_MIRRORDF + 7 * DF_PER_LINE + 0) +#define GFX_BEAMER_BLUE_END (GFX_START_MIRRORDF + 7 * DF_PER_LINE + 15) + +#define GFX_DF_WALL_SEVERAL (GFX_START_MIRRORDF + 8 * DF_PER_LINE + 0) +#define GFX_REFRACTOR (GFX_START_MIRRORDF + 8 * DF_PER_LINE + 1) +#define GFX_CELL (GFX_START_MIRRORDF + 8 * DF_PER_LINE + 2) +#define GFX_MINE (GFX_START_MIRRORDF + 8 * DF_PER_LINE + 4) + +#define GFX_LASER_RIGHT (GFX_START_MIRRORDF + 9 * DF_PER_LINE + 0) +#define GFX_LASER_UP (GFX_START_MIRRORDF + 9 * DF_PER_LINE + 1) +#define GFX_LASER_LEFT (GFX_START_MIRRORDF + 9 * DF_PER_LINE + 2) +#define GFX_LASER_DOWN (GFX_START_MIRRORDF + 9 * DF_PER_LINE + 3) +#define GFX_RECEIVER_RIGHT (GFX_START_MIRRORDF + 9 * DF_PER_LINE + 4) +#define GFX_RECEIVER_UP (GFX_START_MIRRORDF + 9 * DF_PER_LINE + 5) +#define GFX_RECEIVER_LEFT (GFX_START_MIRRORDF + 9 * DF_PER_LINE + 6) +#define GFX_RECEIVER_DOWN (GFX_START_MIRRORDF + 9 * DF_PER_LINE + 7) + +#define GFX_FIBRE_OPTIC_00 (GFX_START_MIRRORDF + 10 * DF_PER_LINE + 0) +#define GFX_FIBRE_OPTIC_01 (GFX_START_MIRRORDF + 10 * DF_PER_LINE + 1) +#define GFX_FIBRE_OPTIC_02 (GFX_START_MIRRORDF + 10 * DF_PER_LINE + 2) +#define GFX_FIBRE_OPTIC_03 (GFX_START_MIRRORDF + 10 * DF_PER_LINE + 3) +#define GFX_FIBRE_OPTIC_04 (GFX_START_MIRRORDF + 10 * DF_PER_LINE + 4) +#define GFX_FIBRE_OPTIC_05 (GFX_START_MIRRORDF + 10 * DF_PER_LINE + 5) +#define GFX_FIBRE_OPTIC_06 (GFX_START_MIRRORDF + 10 * DF_PER_LINE + 6) +#define GFX_FIBRE_OPTIC_07 (GFX_START_MIRRORDF + 10 * DF_PER_LINE + 7) + +#define GFX_FIBRE_OPTIC_ED_00 (GFX_START_MIRRORDF + 11 * DF_PER_LINE + 0) +#define GFX_FIBRE_OPTIC_ED_01 (GFX_START_MIRRORDF + 11 * DF_PER_LINE + 1) +#define GFX_FIBRE_OPTIC_ED_02 (GFX_START_MIRRORDF + 11 * DF_PER_LINE + 2) +#define GFX_FIBRE_OPTIC_ED_03 (GFX_START_MIRRORDF + 11 * DF_PER_LINE + 3) +#define GFX_FIBRE_OPTIC_ED_04 (GFX_START_MIRRORDF + 11 * DF_PER_LINE + 4) +#define GFX_FIBRE_OPTIC_ED_05 (GFX_START_MIRRORDF + 11 * DF_PER_LINE + 5) +#define GFX_FIBRE_OPTIC_ED_06 (GFX_START_MIRRORDF + 11 * DF_PER_LINE + 6) +#define GFX_FIBRE_OPTIC_ED_07 (GFX_START_MIRRORDF + 11 * DF_PER_LINE + 7) + +/* the names of the sounds */ +#define SND_AMOEBE 0 +#define SND_ANTIGRAV 1 +#define SND_AUTSCH 2 +#define SND_BONG 3 +#define SND_FUEL 4 +#define SND_HALLOFFAME 5 +#define SND_HOLZ 6 +#define SND_HUI 7 +#define SND_KABUMM 8 +#define SND_KINK 9 +#define SND_KLING 10 +#define SND_LASER 11 +#define SND_OEFFNEN 12 +#define SND_QUIEK 13 +#define SND_RHYTHMLOOP 14 +#define SND_ROAAAR 15 +#define SND_SIRR 16 +#define SND_SLURP 17 +#define SND_WARNTON 18 +#define SND_WHOOSH 19 + +#define NUM_SOUNDS 20 + +/* laser angles (directions) */ +#define ANG_RAY_RIGHT 0 +#define ANG_RAY_UP 4 +#define ANG_RAY_LEFT 8 +#define ANG_RAY_DOWN 12 + +/* laser angles (degree) */ +#define ANG_RAY_0 0 +#define ANG_RAY_90 4 +#define ANG_RAY_180 8 +#define ANG_RAY_270 12 +#define IS_22_5_ANGLE(angle) ((angle) % 2) +#define IS_90_ANGLE(angle) (!((angle) % 4)) +#define IS_HORIZ_ANGLE(angle) (!((angle) % 8)) +#define IS_VERT_ANGLE(angle) ((angle) % 8) + +/* mirror angles */ +#define ANG_MIRROR_0 0 +#define ANG_MIRROR_45 4 +#define ANG_MIRROR_90 8 +#define ANG_MIRROR_135 12 + +/* positions for checking where laser already hits element */ +#define HIT_POS_CENTER 1 +#define HIT_POS_EDGE 2 +#define HIT_POS_BETWEEN 4 + +/* masks for scanning elements */ +#define HIT_MASK_NO_HIT 0 +#define HIT_MASK_TOPLEFT 1 +#define HIT_MASK_TOPRIGHT 2 +#define HIT_MASK_BOTTOMLEFT 4 +#define HIT_MASK_BOTTOMRIGHT 8 +#define HIT_MASK_LEFT (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMLEFT) +#define HIT_MASK_RIGHT (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMRIGHT) +#define HIT_MASK_TOP (HIT_MASK_TOPLEFT | HIT_MASK_TOPRIGHT) +#define HIT_MASK_BOTTOM (HIT_MASK_BOTTOMLEFT | HIT_MASK_BOTTOMRIGHT) +#define HIT_MASK_ALL (HIT_MASK_LEFT | HIT_MASK_RIGHT) + +/* step values for rotating elements */ +#define ROTATE_NO_ROTATING 0 +#define ROTATE_LEFT (+1) +#define ROTATE_RIGHT (-1) +#define BUTTON_ROTATION(button) ((button) == MB_LEFTBUTTON ? ROTATE_LEFT : \ + (button) == MB_RIGHTBUTTON ? ROTATE_RIGHT : \ + ROTATE_NO_ROTATING) + +/* game over values */ +#define GAME_OVER_NO_ENERGY 1 +#define GAME_OVER_OVERLOADED 2 +#define GAME_OVER_BOMB 3 + +/* values for game_status */ +#define EXITGAME 0 +#define MAINMENU 1 +#define PLAYING 2 +#define LEVELED 3 +#define HELPSCREEN 4 +#define CHOOSELEVEL 5 +#define TYPENAME 6 +#define HALLOFFAME 7 +#define SETUP 8 + +/* return values for GameActions */ +#define ACT_GO_ON 0 +#define ACT_GAME_OVER 1 +#define ACT_NEW_GAME 2 + +/* values for color_status */ +#define STATIC_COLORS 0 +#define DYNAMIC_COLORS 1 + +#define PROGRAM_VERSION_MAJOR 2 +#define PROGRAM_VERSION_MINOR 0 +#define PROGRAM_VERSION_PATCH 2 +#define PROGRAM_VERSION_STRING "2.0.2" + +#define PROGRAM_TITLE_STRING "Mirror Magic II" +#define PROGRAM_AUTHOR_STRING "Holger Schemel" +#define PROGRAM_RIGHTS_STRING "Copyright ^1994-2001" +#define PROGRAM_DOS_PORT_STRING "DOS port based on code by Guido Schulz" +#define PROGRAM_IDENT_STRING PROGRAM_VERSION_STRING " " TARGET_STRING +#define WINDOW_TITLE_STRING PROGRAM_TITLE_STRING " " PROGRAM_IDENT_STRING +#define WINDOW_SUBTITLE_STRING PROGRAM_RIGHTS_STRING " " PROGRAM_AUTHOR_STRING +#define ICON_TITLE_STRING PROGRAM_TITLE_STRING +#define UNIX_USERDATA_DIRECTORY ".mirrormagic" + +#define X11_ICON_FILENAME "mirrormagic_icon.xbm" +#define X11_ICONMASK_FILENAME "mirrormagic_iconmask.xbm" +#define MSDOS_POINTER_FILENAME "mouse.pcx" + +/* functions for version handling */ +#define MM_VERSION_IDENT(x,y,z) ((x) * 10000 + (y) * 100 + (z)) +#define MM_VERSION_MAJOR(x) ((x) / 10000) +#define MM_VERSION_MINOR(x) (((x) % 10000) / 100) +#define MM_VERSION_PATCH(x) ((x) % 100) + +/* file version numbers for resource files (levels, score, setup, etc.) +** currently supported/known file version numbers: +** 1.4 (still in use) +** 2.0 (actual) +*/ +#define MM_FILE_VERSION_1_4 MM_VERSION_IDENT(1,4,0) +#define MM_FILE_VERSION_2_0 MM_VERSION_IDENT(2,0,0) + +/* file version does not change for every program version, but is changed + when new features are introduced that are incompatible with older file + versions, so that they can be treated accordingly */ +#define MM_FILE_VERSION_ACTUAL MM_FILE_VERSION_2_0 + +#define MM_GAME_VERSION_ACTUAL MM_VERSION_IDENT(PROGRAM_VERSION_MAJOR, \ + PROGRAM_VERSION_MINOR, \ + PROGRAM_VERSION_PATCH) + +/* sound control */ + +#define ST(x) (((x)-8)*16) + +#endif /* MM_MAIN_H */ diff --git a/src/game_mm/mm_tools.c b/src/game_mm/mm_tools.c new file mode 100644 index 00000000..bcc61e53 --- /dev/null +++ b/src/game_mm/mm_tools.c @@ -0,0 +1,904 @@ +/*********************************************************** +* Mirror Magic -- McDuffin's Revenge * +*----------------------------------------------------------* +* (c) 1994-2001 Artsoft Entertainment * +* Holger Schemel * +* Detmolder Strasse 189 * +* 33604 Bielefeld * +* Germany * +* e-mail: info@artsoft.org * +*----------------------------------------------------------* +* tools.c * +***********************************************************/ + +#include "main_mm.h" + +#include "mm_main.h" +#include "mm_tools.h" + + +/* forward declaration for internal use */ +static int getGraphicAnimationPhase_MM(int, int, int); + +void ClearWindow() +{ + ClearRectangle(backbuffer, REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE); + + SetDrawtoField(DRAW_BACKBUFFER); + + redraw_mask |= REDRAW_FIELD; +} + +static int getGraphicAnimationPhase_MM(int frames, int delay, int mode) +{ + int phase; + + if (mode == ANIM_PINGPONG) + { + int max_anim_frames = 2 * frames - 2; + phase = (FrameCounter % (delay * max_anim_frames)) / delay; + phase = (phase < frames ? phase : max_anim_frames - phase); + } + else + phase = (FrameCounter % (delay * frames)) / delay; + + if (mode == ANIM_REVERSE) + phase = -phase; + + return(phase); +} + +void DrawGraphicAnimationExt_MM(int x, int y, int graphic, + int frames, int delay, int mode, int mask_mode) +{ + int phase = getGraphicAnimationPhase_MM(frames, delay, mode); + + if (!(FrameCounter % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y))) + { + if (mask_mode == USE_MASKING) + DrawGraphicThruMask_MM(SCREENX(x), SCREENY(y), graphic + phase); + else + DrawGraphic_MM(SCREENX(x), SCREENY(y), graphic + phase); + } +} + +void DrawGraphicAnimation_MM(int x, int y, int graphic, + int frames, int delay, int mode) +{ + DrawGraphicAnimationExt_MM(x, y, graphic, frames, delay, mode, NO_MASKING); +} + +void DrawGraphicAnimationThruMask_MM(int x, int y, int graphic, + int frames, int delay, int mode) +{ + DrawGraphicAnimationExt_MM(x, y, graphic, frames, delay, mode, USE_MASKING); +} + +void DrawGraphic_MM(int x, int y, int graphic) +{ +#if DEBUG + if (!IN_SCR_FIELD(x,y)) + { + printf("DrawGraphic_MM(): x = %d, y = %d, graphic = %d\n",x,y,graphic); + printf("DrawGraphic_MM(): This should never happen!\n"); + +#if 1 + { + int i=0; + i=i/i; + } +#endif + + return; + } +#endif + + DrawGraphicExt_MM(drawto_field, FX + x*TILEX, FY + y*TILEY, graphic); + MarkTileDirty(x, y); +} + +void DrawGraphicExt_MM(DrawBuffer *d, int x, int y, int graphic) +{ + Bitmap *bitmap; + int src_x, src_y; + + getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y); + BlitBitmap(bitmap, d, src_x, src_y, TILEX, TILEY, x, y); +} + +void DrawGraphicThruMask_MM(int x, int y, int graphic) +{ +#if DEBUG + if (!IN_SCR_FIELD(x,y)) + { + printf("DrawGraphicThruMask_MM(): x = %d,y = %d, graphic = %d\n",x,y,graphic); + printf("DrawGraphicThruMask_MM(): This should never happen!\n"); + return; + } +#endif + + DrawGraphicThruMaskExt_MM(drawto_field, FX + x*TILEX, FY + y*TILEY, graphic); + MarkTileDirty(x,y); +} + +void DrawGraphicThruMaskExt_MM(DrawBuffer *d, int dest_x, int dest_y, int graphic) +{ + int src_x, src_y; + Bitmap *src_bitmap; + + if (graphic == GFX_EMPTY) + return; + + getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y); + + BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dest_x, dest_y); +} + +void DrawMiniGraphic_MM(int x, int y, int graphic) +{ + DrawMiniGraphicExt_MM(drawto, SX + x*MINI_TILEX, SY + y*MINI_TILEY, graphic); + MarkTileDirty(x/2, y/2); +} + +void getMicroGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y) +{ + getSizedGraphicSource(graphic, 0, TILESIZE / 4, bitmap, x, y); +} + +void DrawMiniGraphicExt_MM(DrawBuffer *d, int x, int y, int graphic) +{ + Bitmap *bitmap; + int src_x, src_y; + + getMiniGraphicSource(graphic, &bitmap, &src_x, &src_y); + BlitBitmap(bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y); +} + +void DrawGraphicShifted_MM(int x,int y, int dx,int dy, int graphic, + int cut_mode, int mask_mode) +{ + int width = TILEX, height = TILEY; + int cx = 0, cy = 0; + int src_x, src_y, dest_x, dest_y; + Bitmap *src_bitmap; + + if (graphic < 0) + { + DrawGraphic_MM(x, y, graphic); + return; + } + + if (dx || dy) /* Verschiebung der Grafik? */ + { + if (x < BX1) /* Element kommt von links ins Bild */ + { + x = BX1; + width = dx; + cx = TILEX - dx; + dx = 0; + } + else if (x > BX2) /* Element kommt von rechts ins Bild */ + { + x = BX2; + width = -dx; + dx = TILEX + dx; + } + else if (x==BX1 && dx < 0) /* Element verläßt links das Bild */ + { + width += dx; + cx = -dx; + dx = 0; + } + else if (x==BX2 && dx > 0) /* Element verläßt rechts das Bild */ + width -= dx; + else if (dx) /* allg. Bewegung in x-Richtung */ + MarkTileDirty(x + SIGN(dx), y); + + if (y < BY1) /* Element kommt von oben ins Bild */ + { + if (cut_mode==CUT_BELOW) /* Element oberhalb des Bildes */ + return; + + y = BY1; + height = dy; + cy = TILEY - dy; + dy = 0; + } + else if (y > BY2) /* Element kommt von unten ins Bild */ + { + y = BY2; + height = -dy; + dy = TILEY + dy; + } + else if (y==BY1 && dy < 0) /* Element verläßt oben das Bild */ + { + height += dy; + cy = -dy; + dy = 0; + } + else if (dy > 0 && cut_mode == CUT_ABOVE) + { + if (y == BY2) /* Element unterhalb des Bildes */ + return; + + height = dy; + cy = TILEY - dy; + dy = TILEY; + MarkTileDirty(x, y + 1); + } /* Element verläßt unten das Bild */ + else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW)) + height -= dy; + else if (dy) /* allg. Bewegung in y-Richtung */ + MarkTileDirty(x, y + SIGN(dy)); + } + + getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y); + + src_x += cx; + src_y += cy; + + dest_x = FX + x * TILEX + dx; + dest_y = FY + y * TILEY + dy; + +#if DEBUG + if (!IN_SCR_FIELD(x,y)) + { + printf("DrawGraphicShifted_MM(): x = %d, y = %d, graphic = %d\n",x,y,graphic); + printf("DrawGraphicShifted_MM(): This should never happen!\n"); + return; + } +#endif + + if (mask_mode == USE_MASKING) + { + BlitBitmapMasked(src_bitmap, drawto_field, + src_x, src_y, TILEX, TILEY, dest_x, dest_y); + } + else + BlitBitmap(src_bitmap, drawto_field, + src_x, src_y, width, height, dest_x, dest_y); + + MarkTileDirty(x,y); +} + +void DrawGraphicShiftedThruMask_MM(int x,int y, int dx,int dy, int graphic, + int cut_mode) +{ + DrawGraphicShifted_MM(x,y, dx,dy, graphic, cut_mode, USE_MASKING); +} + +void DrawScreenElementExt_MM(int x, int y, int dx, int dy, int element, + int cut_mode, int mask_mode) +{ + int ux = LEVELX(x), uy = LEVELY(y); + int graphic = el2gfx(element); + int phase8 = ABS(MovPos[ux][uy]) / (TILEX / 8); + int phase2 = phase8 / 4; + int dir = MovDir[ux][uy]; + + if (element == EL_PACMAN) + { + graphic += 4 * !phase2; + + if (dir == MV_UP) + graphic += 1; + else if (dir == MV_LEFT) + graphic += 2; + else if (dir == MV_DOWN) + graphic += 3; + } + + if (dx || dy) + DrawGraphicShifted_MM(x, y, dx, dy, graphic, cut_mode, mask_mode); + else if (mask_mode == USE_MASKING) + DrawGraphicThruMask_MM(x, y, graphic); + else + DrawGraphic_MM(x, y, graphic); +} + +void DrawLevelElementExt_MM(int x, int y, int dx, int dy, int element, + int cut_mode, int mask_mode) +{ + if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y))) + DrawScreenElementExt_MM(SCREENX(x), SCREENY(y), dx, dy, element, + cut_mode, mask_mode); +} + +void DrawScreenElementShifted_MM(int x, int y, int dx, int dy, int element, + int cut_mode) +{ + DrawScreenElementExt_MM(x, y, dx, dy, element, cut_mode, NO_MASKING); +} + +void DrawLevelElementShifted_MM(int x, int y, int dx, int dy, int element, + int cut_mode) +{ + DrawLevelElementExt_MM(x, y, dx, dy, element, cut_mode, NO_MASKING); +} + +void DrawScreenElementThruMask_MM(int x, int y, int element) +{ + DrawScreenElementExt_MM(x, y, 0, 0, element, NO_CUTTING, USE_MASKING); +} + +void DrawLevelElementThruMask_MM(int x, int y, int element) +{ + DrawLevelElementExt_MM(x, y, 0, 0, element, NO_CUTTING, USE_MASKING); +} + +void DrawLevelFieldThruMask_MM(int x, int y) +{ + DrawLevelElementExt_MM(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING); +} + +void DrawScreenElement_MM(int x, int y, int element) +{ + DrawScreenElementExt_MM(x, y, 0, 0, element, NO_CUTTING, NO_MASKING); +} + +void DrawLevelElement_MM(int x, int y, int element) +{ + if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y))) + DrawScreenElement_MM(SCREENX(x), SCREENY(y), element); +} + +void DrawScreenField_MM(int x, int y) +{ + int element = Feld[x][y]; + + if (!IN_LEV_FIELD(x, y)) + return; + + if (IS_MOVING(x, y)) + { + int horiz_move = (MovDir[x][y] == MV_LEFT || MovDir[x][y] == MV_RIGHT); + + DrawScreenElement_MM(x, y, EL_EMPTY); + + if (horiz_move) + DrawScreenElementShifted_MM(x, y, MovPos[x][y], 0, element, NO_CUTTING); + else + DrawScreenElementShifted_MM(x, y, 0, MovPos[x][y], element, NO_CUTTING); + } + else if (IS_BLOCKED(x, y)) + { + int oldx, oldy; + int sx, sy; + int horiz_move; + + Blocked2Moving(x, y, &oldx, &oldy); + sx = SCREENX(oldx); + sy = SCREENY(oldy); + horiz_move = (MovDir[oldx][oldy] == MV_LEFT || + MovDir[oldx][oldy] == MV_RIGHT); + + DrawScreenElement_MM(x, y, EL_EMPTY); + element = Feld[oldx][oldy]; + + if (horiz_move) + DrawScreenElementShifted_MM(sx,sy, MovPos[oldx][oldy],0,element,NO_CUTTING); + else + DrawScreenElementShifted_MM(sx,sy, 0,MovPos[oldx][oldy],element,NO_CUTTING); + } + else if (IS_DRAWABLE(element)) + DrawScreenElement_MM(x, y, element); + else + DrawScreenElement_MM(x, y, EL_EMPTY); +} + +void DrawLevelField_MM(int x, int y) +{ + DrawScreenField_MM(x, y); +} + +void DrawMiniElement_MM(int x, int y, int element) +{ + int graphic; + + if (!element) + { + DrawMiniGraphic_MM(x, y, GFX_EMPTY); + return; + } + + graphic = el2gfx(element); + DrawMiniGraphic_MM(x, y, graphic); +} + +void DrawMiniElementOrWall_MM(int sx, int sy, int scroll_x, int scroll_y) +{ + int x = sx + scroll_x, y = sy + scroll_y; + + if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy) + DrawMiniElement_MM(sx, sy, EL_EMPTY); + else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy) + DrawMiniElement_MM(sx, sy, Feld[x][y]); +} + +void DrawField_MM(int x, int y) +{ + int element = Feld[x][y]; + + DrawElement_MM(x, y, element); +} + +void DrawLevel_MM() +{ + int x,y; + + ClearWindow(); + + for (x=0; x DY+249 && y < DY+278) + { + if (x > DX+1 && x < DX+48) + return 1; + else if (x > DX+51 && x < DX+98) + return 2; + } + return 0; +} + +Pixel ReadPixel(DrawBuffer *bitmap, int x, int y) +{ +#if defined(TARGET_SDL) || defined(TARGET_ALLEGRO) + return GetPixel(bitmap, x, y); +#else + /* GetPixel() does also work for X11, but we use some optimization here */ + unsigned int pixel_value; + + if (bitmap == pix[PIX_BACK]) + { + /* when reading pixel values from images, it is much faster to use + client side images (XImage) than server side images (Pixmap) */ + static XImage *client_image = NULL; + + if (client_image == NULL) /* init image cache, if not existing */ + client_image = XGetImage(display, bitmap->drawable, + 0,0, WIN_XSIZE,WIN_YSIZE, AllPlanes, ZPixmap); + + pixel_value = XGetPixel(client_image, x, y); + } + else + { + XImage *pixel_image; + + pixel_image = XGetImage(display, bitmap->drawable, x, y, 1, 1, + AllPlanes, ZPixmap); + pixel_value = XGetPixel(pixel_image, 0, 0); + + XDestroyImage(pixel_image); + } + + return pixel_value; +#endif +} + +void SetRGB(unsigned int pixel, + unsigned short red, unsigned short green, unsigned short blue) +{ + return; + +#if 0 + XColor color; + + if (color_status==STATIC_COLORS) + return; + + color.pixel = pixel; + color.red = red; + color.green = green; + color.blue = blue; + color.flags = DoRed | DoGreen | DoBlue; + XStoreColor(display, cmap, &color); + XFlush(display); +#endif +} + +int get_base_element(int element) +{ + if (IS_MIRROR(element)) + return EL_MIRROR_START; + else if (IS_MIRROR_FIXED(element)) + return EL_MIRROR_FIXED_START; + else if (IS_POLAR(element)) + return EL_POLAR_START; + else if (IS_POLAR_CROSS(element)) + return EL_POLAR_CROSS_START; + else if (IS_BEAMER(element)) + return EL_BEAMER_RED_START + BEAMER_NR(element) * 16; + else if (IS_FIBRE_OPTIC(element)) + return EL_FIBRE_OPTIC_START + FIBRE_OPTIC_NR(element) * 2; + else if (IS_MCDUFFIN(element)) + return EL_MCDUFFIN_START; + else if (IS_LASER(element)) + return EL_LASER_START; + else if (IS_RECEIVER(element)) + return EL_RECEIVER_START; + else if (IS_DF_MIRROR(element)) + return EL_DF_MIRROR_START; + else if (IS_DF_MIRROR_AUTO(element)) + return EL_DF_MIRROR_AUTO_START; + else if (IS_PACMAN(element)) + return EL_PACMAN_START; + else if (IS_GRID_STEEL(element)) + return EL_GRID_STEEL_START; + else if (IS_GRID_WOOD(element)) + return EL_GRID_WOOD_START; + else if (IS_GRID_STEEL_FIXED(element)) + return EL_GRID_STEEL_FIXED_START; + else if (IS_GRID_WOOD_FIXED(element)) + return EL_GRID_WOOD_FIXED_START; + else if (IS_GRID_STEEL_AUTO(element)) + return EL_GRID_STEEL_AUTO_START; + else if (IS_GRID_WOOD_AUTO(element)) + return EL_GRID_WOOD_AUTO_START; + else if (IS_WALL_STEEL(element)) + return EL_WALL_STEEL_START; + else if (IS_WALL_WOOD(element)) + return EL_WALL_WOOD_START; + else if (IS_WALL_ICE(element)) + return EL_WALL_ICE_START; + else if (IS_WALL_AMOEBA(element)) + return EL_WALL_AMOEBA_START; + else if (IS_DF_WALL_STEEL(element)) + return EL_DF_WALL_STEEL_START; + else if (IS_DF_WALL_WOOD(element)) + return EL_DF_WALL_WOOD_START; + else if (IS_CHAR(element)) + return EL_CHAR_START; + else + return element; +} + +int get_element_phase(int element) +{ + return element - get_base_element(element); +} + +int get_num_elements(int element) +{ + if (IS_MIRROR(element) || + IS_POLAR(element) || + IS_BEAMER(element) || + IS_DF_MIRROR(element) || + IS_DF_MIRROR_AUTO(element)) + return 16; + else if (IS_GRID_STEEL_FIXED(element) || + IS_GRID_WOOD_FIXED(element) || + IS_GRID_STEEL_AUTO(element) || + IS_GRID_WOOD_AUTO(element)) + return 8; + else if (IS_MIRROR_FIXED(element) || + IS_POLAR_CROSS(element) || + IS_MCDUFFIN(element) || + IS_LASER(element) || + IS_RECEIVER(element) || + IS_PACMAN(element) || + IS_GRID_STEEL(element) || + IS_GRID_WOOD(element)) + return 4; + else + return 1; +} + +int get_rotated_element(int element, int step) +{ + int base_element = get_base_element(element); + int num_elements = get_num_elements(element); + int element_phase = element - base_element; + + return base_element + (element_phase + step + num_elements) % num_elements; +} + +int el2gfx(int element) +{ + switch(element) + { + case EL_EMPTY: return -1; + case EL_GRID_STEEL_00: return GFX_GRID_STEEL_00; + case EL_GRID_STEEL_01: return GFX_GRID_STEEL_01; + case EL_GRID_STEEL_02: return GFX_GRID_STEEL_02; + case EL_GRID_STEEL_03: return GFX_GRID_STEEL_03; + case EL_MCDUFFIN_RIGHT: return GFX_MCDUFFIN_RIGHT; + case EL_MCDUFFIN_UP: return GFX_MCDUFFIN_UP; + case EL_MCDUFFIN_LEFT: return GFX_MCDUFFIN_LEFT; + case EL_MCDUFFIN_DOWN: return GFX_MCDUFFIN_DOWN; + case EL_EXIT_CLOSED: return GFX_EXIT_CLOSED; + case EL_EXIT_OPENING_1: return GFX_EXIT_OPENING_1; + case EL_EXIT_OPENING_2: return GFX_EXIT_OPENING_2; + case EL_EXIT_OPEN: return GFX_EXIT_OPEN; + case EL_KETTLE: return GFX_KETTLE; + case EL_BOMB: return GFX_BOMB; + case EL_PRISM: return GFX_PRISM; + case EL_BLOCK_WOOD: return GFX_BLOCK_WOOD; + case EL_BALL_GRAY: return GFX_BALL_GRAY; + case EL_FUSE_ON: return GFX_FUSE_ON; + case EL_PACMAN_RIGHT: return GFX_PACMAN_RIGHT; + case EL_PACMAN_UP: return GFX_PACMAN_UP; + case EL_PACMAN_LEFT: return GFX_PACMAN_LEFT; + case EL_PACMAN_DOWN: return GFX_PACMAN_DOWN; + case EL_POLAR_CROSS_00: return GFX_POLAR_CROSS_00; + case EL_POLAR_CROSS_01: return GFX_POLAR_CROSS_01; + case EL_POLAR_CROSS_02: return GFX_POLAR_CROSS_02; + case EL_POLAR_CROSS_03: return GFX_POLAR_CROSS_03; + case EL_MIRROR_FIXED_00: return GFX_MIRROR_FIXED_00; + case EL_MIRROR_FIXED_01: return GFX_MIRROR_FIXED_01; + case EL_MIRROR_FIXED_02: return GFX_MIRROR_FIXED_02; + case EL_MIRROR_FIXED_03: return GFX_MIRROR_FIXED_03; + case EL_GATE_STONE: return GFX_GATE_STONE; + case EL_KEY: return GFX_KEY; + case EL_LIGHTBULB_ON: return GFX_LIGHTBULB_ON; + case EL_LIGHTBULB_OFF: return GFX_LIGHTBULB_OFF; + case EL_LIGHTBALL: return GFX_BALL_RED + RND(3);; + case EL_BLOCK_STONE: return GFX_BLOCK_STONE; + case EL_GATE_WOOD: return GFX_GATE_WOOD; + case EL_FUEL_FULL: return GFX_FUEL_FULL; + case EL_GRID_WOOD_00: return GFX_GRID_WOOD_00; + case EL_GRID_WOOD_01: return GFX_GRID_WOOD_01; + case EL_GRID_WOOD_02: return GFX_GRID_WOOD_02; + case EL_GRID_WOOD_03: return GFX_GRID_WOOD_03; + case EL_FUEL_EMPTY: return GFX_FUEL_EMPTY; + case EL_FUSE_OFF: return GFX_FUSE_OFF; + case EL_PACMAN: return GFX_PACMAN; + case EL_REFRACTOR: return GFX_REFRACTOR; + case EL_CELL: return GFX_CELL; + case EL_MINE: return GFX_MINE; + + /* pseudo-graphics; will be mapped to other graphics */ + case EL_WALL_STEEL: return GFX_WALL_STEEL; + case EL_WALL_WOOD: return GFX_WALL_WOOD; + case EL_WALL_ICE: return GFX_WALL_ICE; + case EL_WALL_AMOEBA: return GFX_WALL_AMOEBA; + case EL_DF_WALL_STEEL: return GFX_DF_WALL_STEEL; + case EL_DF_WALL_WOOD: return GFX_DF_WALL_WOOD; + + default: + { + boolean ed = (game_status == LEVELED); + int base_element = get_base_element(element); + int element_phase = element - base_element; + int base_graphic; + + if (IS_BEAMER(element)) + element_phase = element - EL_BEAMER_RED_START; + else if (IS_FIBRE_OPTIC(element)) + element_phase = element - EL_FIBRE_OPTIC_START; + + if (IS_MIRROR(element)) + base_graphic = GFX_MIRROR_START; + else if (IS_BEAMER_OLD(element)) + base_graphic = GFX_BEAMER_START; + else if (IS_POLAR(element)) + base_graphic = GFX_POLAR_START; + else if (IS_CHAR(element)) + base_graphic = GFX_CHAR_START; + else if (IS_GRID_WOOD_FIXED(element)) + base_graphic = GFX_GRID_WOOD_FIXED_00; + else if (IS_GRID_STEEL_FIXED(element)) + base_graphic = GFX_GRID_STEEL_FIXED_00; + else if (IS_DF_MIRROR(element)) + base_graphic = GFX_DF_MIRROR_00; + else if (IS_LASER(element)) + base_graphic = GFX_LASER_RIGHT; + else if (IS_RECEIVER(element)) + base_graphic = GFX_RECEIVER_RIGHT; + else if (IS_DF_MIRROR(element)) + base_graphic = GFX_DF_MIRROR_00; + else if (IS_FIBRE_OPTIC(element)) + base_graphic = (ed ? GFX_FIBRE_OPTIC_ED_00 : GFX_FIBRE_OPTIC_00); + else if (IS_GRID_WOOD_AUTO(element)) + base_graphic = (ed ? GFX_GRID_WOOD_AUTO_00 : GFX_GRID_WOOD_FIXED_00); + else if (IS_GRID_STEEL_AUTO(element)) + base_graphic = (ed ? GFX_GRID_STEEL_AUTO_00 : GFX_GRID_STEEL_FIXED_00); + else if (IS_DF_MIRROR_AUTO(element)) + base_graphic = (ed ? GFX_DF_MIRROR_AUTO_00 : GFX_DF_MIRROR_00); + else if (IS_BEAMER(element)) + base_graphic = GFX_BEAMER_RED_START; + else + return GFX_EMPTY; + + return base_graphic + element_phase; + } + } +} + +void RedrawPlayfield_MM() +{ + DrawLevel_MM(); +} + +void BlitScreenToBitmap_MM(Bitmap *target_bitmap) +{ + BlitBitmap(drawto_field, target_bitmap, 0, 0, SXSIZE, SYSIZE, SX, SY); +} diff --git a/src/game_mm/mm_tools.h b/src/game_mm/mm_tools.h new file mode 100644 index 00000000..39c58a8b --- /dev/null +++ b/src/game_mm/mm_tools.h @@ -0,0 +1,131 @@ +/*********************************************************** +* Mirror Magic -- McDuffin's Revenge * +*----------------------------------------------------------* +* (c) 1994-2001 Artsoft Entertainment * +* Holger Schemel * +* Detmolder Strasse 189 * +* 33604 Bielefeld * +* Germany * +* e-mail: info@artsoft.org * +*----------------------------------------------------------* +* tools.h * +***********************************************************/ + +#ifndef MM_TOOLS_H +#define MM_TOOLS_H + +#include "main_mm.h" + +/* for SetDrawtoField */ +#define DRAW_DIRECT 0 +#define DRAW_BUFFERED 1 +#define DRAW_BACKBUFFER 2 + +/* for DrawElementShifted */ +#define NO_CUTTING 0 +#define CUT_ABOVE (1 << 0) +#define CUT_BELOW (1 << 1) +#define CUT_LEFT (1 << 2) +#define CUT_RIGHT (1 << 3) + +/* for masking functions */ +#define NO_MASKING 0 +#define USE_MASKING 1 + +/* for MoveDoor */ +#define DOOR_OPEN_1 (1 << 0) +#define DOOR_OPEN_2 (1 << 1) +#define DOOR_CLOSE_1 (1 << 2) +#define DOOR_CLOSE_2 (1 << 3) +#define DOOR_OPEN_ALL (DOOR_OPEN_1 | DOOR_OPEN_2) +#define DOOR_CLOSE_ALL (DOOR_CLOSE_1 | DOOR_CLOSE_2) +#define DOOR_ACTION_1 (DOOR_OPEN_1 | DOOR_CLOSE_1) +#define DOOR_ACTION_2 (DOOR_OPEN_2 | DOOR_CLOSE_2) +#define DOOR_ACTION (DOOR_ACTION_1 | DOOR_ACTION_2) +#define DOOR_COPY_BACK (1 << 4) +#define DOOR_NO_DELAY (1 << 5) +#define DOOR_GET_STATE (1 << 6) + +/* for Request */ +#define REQ_ASK (1 << 0) +#define REQ_OPEN (1 << 1) +#define REQ_CLOSE (1 << 2) +#define REQ_CONFIRM (1 << 3) +#define REQ_STAY_CLOSED (1 << 4) +#define REQ_STAY_OPEN (1 << 5) + +#define REQUEST_WAIT_FOR (REQ_ASK | REQ_CONFIRM) + +/* font types */ +#define FS_SMALL 0 +#define FS_BIG 1 +#define FS_MEDIUM 2 + + +void SetDrawtoField_MM(int); +void BackToFront(); +void FadeToFront(); +void ClearWindow(); + +void MarkTileDirty(int, int); + +void DrawAllPlayers_MM(void); +void DrawPlayerField_MM(int, int); +void DrawPlayer_MM(struct PlayerInfo *); +void DrawGraphicAnimationExt_MM(int, int, int, int, int, int, int); +void DrawGraphicAnimation_MM(int, int, int, int, int, int); +void DrawGraphicAnimationThruMask_MM(int, int, int, int, int, int); + +void DrawGraphic_MM(int, int, int); +void DrawGraphicExt_MM(DrawBuffer *, int, int, int); +void DrawGraphicThruMask_MM(int, int, int); +void DrawGraphicThruMaskExt_MM(DrawBuffer *, int, int, int); +void DrawMiniGraphic_MM(int, int, int); +void getMiniGraphicSource(int, Bitmap **, int *, int *); +void DrawMiniGraphicExt_MM(DrawBuffer *, int, int, int); +void DrawGraphicShifted_MM(int, int, int, int, int, int, int); +void DrawGraphicShiftedThruMask_MM(int, int, int, int, int, int); +void DrawScreenElementExt_MM(int, int, int, int, int, int, int); +void DrawLevelElementExt_MM(int, int, int, int, int, int, int); +void DrawScreenElementShifted_MM(int, int, int, int, int, int); +void DrawLevelElementShifted_MM(int, int, int, int, int, int); +void DrawScreenElementThruMask_MM(int, int, int); +void DrawLevelElementThruMask_MM(int, int, int); +void DrawLevelFieldThruMask_MM(int, int); +void ErdreichAnbroeckeln(int, int); +void DrawScreenElement_MM(int, int, int); +void DrawLevelElement_MM(int, int, int); +void DrawScreenField_MM(int, int); +void DrawLevelField_MM(int, int); +void DrawMiniElement_MM(int, int, int); +void DrawMiniElementOrWall_MM(int, int, int, int); + +void DrawField_MM(int, int); +void DrawLevel_MM(void); +void DrawElement_MM(int, int, int); +void DrawWallsExt_MM(int, int, int, int); +void DrawWalls_MM(int, int, int); +void DrawWallsAnimation_MM(int, int, int, int, int); +void DrawMiniLevel_MM(int, int, int, int); +void DrawMicroLevel_MM(int, int, boolean); + +boolean Request(char *, unsigned int); +unsigned int OpenDoor(unsigned int); +unsigned int CloseDoor(unsigned int); +unsigned int GetDoorState(void); +unsigned int MoveDoor(unsigned int); +void DrawSpecialEditorDoor_MM(); +void UndrawSpecialEditorDoor(); +Pixel ReadPixel(DrawBuffer *, int, int); +void SetRGB(unsigned int, unsigned short, unsigned short, unsigned short); + +void CreateToolButtons(); + +int get_base_element(int); +int get_element_phase(int); +int get_num_elements(int); +int get_rotated_element(int, int); + +int el2gfx(int); + +#endif