// (c) 1994-2017 by Artsoft Entertainment
// Holger Schemel
// info@artsoft.org
-// http://www.artsoft.org/
+// https://www.artsoft.org/
// ----------------------------------------------------------------------------
// mm_game.c
// ============================================================================
#include "mm_game.h"
#include "mm_tools.h"
-/* graphic position values for game controls */
+// 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_MM() */
+// values for Explode_MM()
#define EX_PHASE_START 0
-#define EX_NORMAL 0
-#define EX_KETTLE 1
-#define EX_SHORT 2
+#define EX_TYPE_NONE 0
+#define EX_TYPE_NORMAL (1 << 0)
-/* special positions in the game control window (relative to control window) */
+// special positions in the game control window (relative to control window)
#define XX_LEVEL 36
#define YY_LEVEL 23
#define XX_KETTLES 29
#define XX_OVERLOAD 60
#define YY_OVERLOAD YY_ENERGY
-/* special positions in the game control window (relative to main window) */
+// 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 IS_LOOP_SOUND(s) ((s) == SND_FUEL)
#define IS_MUSIC_SOUND(s) ((s) == SND_TYGER || (s) == SND_VOYAGER)
-/* game button identifiers */
+// 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() */
+// values for DrawLaser()
#define DL_LASER_DISABLED 0
#define DL_LASER_ENABLED 1
-/* values for 'click_delay_value' in ClickElement() */
-#define CLICK_DELAY_FIRST 12 /* delay (frames) after first click */
-#define CLICK_DELAY 6 /* delay (frames) for pressed butten */
+// values for 'click_delay_value' in ClickElement()
+#define CLICK_DELAY_FIRST 12 // delay (frames) after first click
+#define CLICK_DELAY 6 // delay (frames) for pressed butten
#define AUTO_ROTATE_DELAY CLICK_DELAY
#define INIT_GAME_ACTIONS_DELAY (ONE_SECOND_DELAY / GAME_FRAME_DELAY)
program.headless = last_headless; \
} \
-/* forward declaration for internal use */
+// forward declaration for internal use
static int MovingOrBlocked2Element_MM(int, int);
static void Bang_MM(int, int);
static void RaiseScore_MM(int);
static void RemoveMovingField_MM(int, int);
static void InitMovingField_MM(int, int, int);
static void ContinueMoving_MM(int, int);
-static void Moving2Blocked_MM(int, int, int *, int *);
-/* bitmap for laser beam detection */
+static void AddLaserEdge(int, int);
+static void ScanLaser(void);
+static void DrawLaser(int, int);
+static boolean HitElement(int, int);
+static boolean HitOnlyAnEdge(int);
+static boolean HitPolarizer(int, int);
+static boolean HitBlock(int, int);
+static boolean HitLaserSource(int, int);
+static boolean HitLaserDestination(int, int);
+static boolean HitReflectingWalls(int, int);
+static boolean HitAbsorbingWalls(int, int);
+static void RotateMirror(int, int, int);
+static boolean ObjHit(int, int, int);
+static void DeletePacMan(int, int);
+static void MovePacMen(void);
+
+// bitmap for laser beam detection
static Bitmap *laser_bitmap = NULL;
-/* variables for laser control */
+// variables for laser control
static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
static int hold_x = -1, hold_y = -1;
-/* variables for pacman control */
+// variables for pacman control
static int pacman_nr = -1;
-/* various game engine delay counters */
-static unsigned int rotate_delay = 0;
-static unsigned int pacman_delay = 0;
-static unsigned int energy_delay = 0;
-static unsigned int overload_delay = 0;
-
-/* element masks for scanning pixels of MM elements */
-static const char mm_masks[10][16][16 + 1] =
+// various game engine delay counters
+static DelayCounter rotate_delay = { AUTO_ROTATE_DELAY };
+static DelayCounter pacman_delay = { PACMAN_MOVE_DELAY };
+static DelayCounter energy_delay = { ENERGY_DELAY };
+static DelayCounter overload_delay = { 0 };
+
+// element mask positions for scanning pixels of MM elements
+#define MM_MASK_MCDUFFIN_RIGHT 0
+#define MM_MASK_MCDUFFIN_UP 1
+#define MM_MASK_MCDUFFIN_LEFT 2
+#define MM_MASK_MCDUFFIN_DOWN 3
+#define MM_MASK_GRID_1 4
+#define MM_MASK_GRID_2 5
+#define MM_MASK_GRID_3 6
+#define MM_MASK_GRID_4 7
+#define MM_MASK_SLOPE_1 8
+#define MM_MASK_SLOPE_2 9
+#define MM_MASK_SLOPE_3 10
+#define MM_MASK_SLOPE_4 11
+#define MM_MASK_RECTANGLE 12
+#define MM_MASK_CIRCLE 13
+
+#define NUM_MM_MASKS 14
+
+// element masks for scanning pixels of MM elements
+static const char mm_masks[NUM_MM_MASKS][16][16 + 1] =
{
{
" ",
" XXX XXXX ",
" XX XXXXX ",
},
+ {
+ " X",
+ " XX",
+ " XXX",
+ " XXXX",
+ " XXXXX",
+ " XXXXXX",
+ " XXXXXXX",
+ " XXXXXXXX",
+ " XXXXXXXXX",
+ " XXXXXXXXXX",
+ " XXXXXXXXXXX",
+ " XXXXXXXXXXXX",
+ " XXXXXXXXXXXXX",
+ " XXXXXXXXXXXXXX",
+ " XXXXXXXXXXXXXXX",
+ "XXXXXXXXXXXXXXXX",
+ },
+ {
+ "X ",
+ "XX ",
+ "XXX ",
+ "XXXX ",
+ "XXXXX ",
+ "XXXXXX ",
+ "XXXXXXX ",
+ "XXXXXXXX ",
+ "XXXXXXXXX ",
+ "XXXXXXXXXX ",
+ "XXXXXXXXXXX ",
+ "XXXXXXXXXXXX ",
+ "XXXXXXXXXXXXX ",
+ "XXXXXXXXXXXXXX ",
+ "XXXXXXXXXXXXXXX ",
+ "XXXXXXXXXXXXXXXX",
+ },
+ {
+ "XXXXXXXXXXXXXXXX",
+ "XXXXXXXXXXXXXXX ",
+ "XXXXXXXXXXXXXX ",
+ "XXXXXXXXXXXXX ",
+ "XXXXXXXXXXXX ",
+ "XXXXXXXXXXX ",
+ "XXXXXXXXXX ",
+ "XXXXXXXXX ",
+ "XXXXXXXX ",
+ "XXXXXXX ",
+ "XXXXXX ",
+ "XXXXX ",
+ "XXXX ",
+ "XXX ",
+ "XX ",
+ "X ",
+ },
+ {
+ "XXXXXXXXXXXXXXXX",
+ " XXXXXXXXXXXXXXX",
+ " XXXXXXXXXXXXXX",
+ " XXXXXXXXXXXXX",
+ " XXXXXXXXXXXX",
+ " XXXXXXXXXXX",
+ " XXXXXXXXXX",
+ " XXXXXXXXX",
+ " XXXXXXXX",
+ " XXXXXXX",
+ " XXXXXX",
+ " XXXXX",
+ " XXXX",
+ " XXX",
+ " XX",
+ " X",
+ },
{
"XXXXXXXXXXXXXXXX",
"XXXXXXXXXXXXXXXX",
IS_LASER(element) ||
IS_RECEIVER(element))
return 4 * element_phase;
+ else if (IS_DF_SLOPE(element))
+ return 4 + (element_phase % 2) * 8;
else
return element_phase;
}
{
int opposite_angle = angle + ANG_RAY_180;
- /* make sure "opposite_angle" is in valid interval [0, 15] */
+ // make sure "opposite_angle" is in valid interval [0, 15]
return (opposite_angle + 16) % 16;
}
{
int reflected_angle = 16 - laser_angle + mirror_angle;
- /* make sure "reflected_angle" is in valid interval [0, 15] */
+ // make sure "reflected_angle" is in valid interval [0, 15]
return (reflected_angle + 16) % 16;
}
Pixel pixel_drawto = (mode == DL_LASER_ENABLED ? pen_ray : pen_bg);
Pixel pixel_buffer = (mode == DL_LASER_ENABLED ? WHITE_PIXEL : BLACK_PIXEL);
- DrawLines(drawto, points, num_points, pixel_drawto);
+ DrawLines(drawto_mm, points, num_points, pixel_drawto);
BEGIN_NO_HEADLESS
{
return (pixel == WHITE_PIXEL);
}
-static void CheckExitMM()
+static void CheckExitMM(void)
{
int exit_element = EL_EMPTY;
int exit_x = 0;
{
for (x = 0; x < lev_fieldx; x++)
{
- if (Feld[x][y] == EL_EXIT_CLOSED)
+ if (Tile[x][y] == EL_EXIT_CLOSED)
{
- /* initiate opening animation of exit door */
- Feld[x][y] = EL_EXIT_OPENING;
+ // initiate opening animation of exit door
+ Tile[x][y] = EL_EXIT_OPENING;
exit_element = EL_EXIT_OPEN;
exit_x = x;
exit_y = y;
}
- else if (IS_RECEIVER(Feld[x][y]))
+ else if (IS_RECEIVER(Tile[x][y]))
{
- /* remove field that blocks receiver */
- int phase = Feld[x][y] - EL_RECEIVER_START;
+ // remove field that blocks receiver
+ int phase = Tile[x][y] - EL_RECEIVER_START;
int blocking_x, blocking_y;
blocking_x = x + xy[phase][0];
if (IN_LEV_FIELD(blocking_x, blocking_y))
{
- Feld[blocking_x][blocking_y] = EL_EMPTY;
+ Tile[blocking_x][blocking_y] = EL_EMPTY;
DrawField_MM(blocking_x, blocking_y);
}
PlayLevelSound_MM(exit_x, exit_y, exit_element, MM_ACTION_OPENING);
}
+static void SetLaserColor(int brightness)
+{
+ int color_min = 0x00;
+ int color_max = brightness; // (0x00 <= brightness <= 0xFF)
+ int color_up = color_max * laser.overload_value / MAX_LASER_OVERLOAD;
+ int color_down = color_max - color_up;
+
+ pen_ray =
+ GetPixelFromRGB(window,
+ (game_mm.laser_red ? color_max : color_up),
+ (game_mm.laser_green ? color_down : color_min),
+ (game_mm.laser_blue ? color_down : color_min));
+}
+
static void InitMovDir_MM(int x, int y)
{
- int element = Feld[x][y];
+ int element = Tile[x][y];
static int direction[3][4] =
{
{ MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
{ MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
};
- switch(element)
+ switch (element)
{
case EL_PACMAN_RIGHT:
case EL_PACMAN_UP:
case EL_PACMAN_LEFT:
case EL_PACMAN_DOWN:
- Feld[x][y] = EL_PACMAN;
+ Tile[x][y] = EL_PACMAN;
MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
break;
static void InitField(int x, int y, boolean init_game)
{
- int element = Feld[x][y];
+ int element = Tile[x][y];
switch (element)
{
case EL_DF_EMPTY:
- Feld[x][y] = EL_EMPTY;
+ Tile[x][y] = EL_EMPTY;
break;
case EL_KETTLE:
case EL_CELL:
- if (native_mm_level.auto_count_kettles)
+ if (init_game && native_mm_level.auto_count_kettles)
game_mm.kettles_still_needed++;
break;
{
if (IS_BEAMER_OLD(element))
{
- Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
- element = Feld[x][y];
+ Tile[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
+ element = Tile[x][y];
}
if (!IS_FIBRE_OPTIC(element))
{
static int steps_grid_auto = 0;
- if (game_mm.num_cycle == 0) /* initialize cycle steps for grids */
+ 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) ||
}
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);
+ if (init_game)
+ {
+ laser.start_edge.x = x;
+ laser.start_edge.y = y;
+ laser.start_angle = get_element_angle(element);
+ }
+
+ if (IS_MCDUFFIN(element))
+ {
+ game_mm.laser_red = native_mm_level.mm_laser_red;
+ game_mm.laser_green = native_mm_level.mm_laser_green;
+ game_mm.laser_blue = native_mm_level.mm_laser_blue;
+ }
+ else
+ {
+ game_mm.laser_red = native_mm_level.df_laser_red;
+ game_mm.laser_green = native_mm_level.df_laser_green;
+ game_mm.laser_blue = native_mm_level.df_laser_blue;
+ }
+
+ game_mm.has_mcduffin = (IS_MCDUFFIN(element));
}
break;
}
}
-static void InitCycleElements_RotateSingleStep()
+static void InitCycleElements_RotateSingleStep(void)
{
int i;
- if (game_mm.num_cycle == 0) /* no elements to cycle */
+ if (game_mm.num_cycle == 0) // no elements to cycle
return;
for (i = 0; i < game_mm.num_cycle; i++)
int x = game_mm.cycle[i].x;
int y = game_mm.cycle[i].y;
int step = SIGN(game_mm.cycle[i].steps);
- int last_element = Feld[x][y];
+ int last_element = Tile[x][y];
int next_element = get_rotated_element(last_element, step);
if (!game_mm.cycle[i].steps)
continue;
- Feld[x][y] = next_element;
+ Tile[x][y] = next_element;
- DrawField_MM(x, y);
game_mm.cycle[i].steps -= step;
}
}
-static void InitLaser()
+static void InitLaser(void)
{
- int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
+ int start_element = Tile[laser.start_edge.x][laser.start_edge.y];
int step = (IS_LASER(start_element) ? 4 : 0);
LX = laser.start_edge.x * TILEX;
laser.dest_element = EL_EMPTY;
laser.wall_mask = 0;
- AddLaserEdge(LX, LY); /* set laser starting edge */
+ AddLaserEdge(LX, LY); // set laser starting edge
- pen_ray = GetPixelFromRGB(window,
- native_mm_level.laser_red * 0xFF,
- native_mm_level.laser_green * 0xFF,
- native_mm_level.laser_blue * 0xFF);
+ SetLaserColor(0xFF);
}
-void InitGameEngine_MM()
+void InitGameEngine_MM(void)
{
int i, x, y;
BEGIN_NO_HEADLESS
{
- /* initialize laser bitmap to current playfield (screen) size */
- ReCreateBitmap(&laser_bitmap, drawto->width, drawto->height);
- ClearRectangle(laser_bitmap, 0, 0, drawto->width, drawto->height);
+ // initialize laser bitmap to current playfield (screen) size
+ ReCreateBitmap(&laser_bitmap, drawto_mm->width, drawto_mm->height);
+ ClearRectangle(laser_bitmap, 0, 0, drawto_mm->width, drawto_mm->height);
}
END_NO_HEADLESS
- /* set global game control values */
+ // set global game control values
game_mm.num_cycle = 0;
game_mm.num_pacman = 0;
(native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
game_mm.lights_still_needed = 0;
game_mm.num_keys = 0;
+ game_mm.ball_choice_pos = 0;
+
+ game_mm.laser_red = FALSE;
+ game_mm.laser_green = FALSE;
+ game_mm.laser_blue = TRUE;
+ game_mm.has_mcduffin = TRUE;
game_mm.level_solved = FALSE;
game_mm.game_over = FALSE;
game_mm.game_over_cause = 0;
+ game_mm.game_over_message = NULL;
game_mm.laser_overload_value = 0;
game_mm.laser_enabled = FALSE;
- /* set global laser control values (must be set before "InitLaser()") */
+ // set global laser control values (must be set before "InitLaser()")
laser.start_edge.x = 0;
laser.start_edge.y = 0;
laser.start_angle = 0;
laser.fuse_x = laser.fuse_y = -1;
laser.dest_element = EL_EMPTY;
+ laser.dest_element_last = EL_EMPTY;
+ laser.dest_element_last_x = -1;
+ laser.dest_element_last_y = -1;
laser.wall_mask = 0;
last_LX = 0;
CT = Ct = 0;
- rotate_delay = 0;
- pacman_delay = 0;
- energy_delay = 0;
- overload_delay = 0;
+ rotate_delay.count = 0;
+ pacman_delay.count = 0;
+ energy_delay.count = 0;
+ overload_delay.count = 0;
ClickElement(-1, -1, -1);
{
for (y = 0; y < lev_fieldy; y++)
{
- Feld[x][y] = Ur[x][y];
+ Tile[x][y] = Ur[x][y];
Hit[x][y] = Box[x][y] = 0;
Angle[x][y] = 0;
MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
Store[x][y] = Store2[x][y] = 0;
- Frame[x][y] = 0;
Stop[x][y] = FALSE;
InitField(x, y, TRUE);
}
}
-#if 0
- CloseDoor(DOOR_CLOSE_1);
-#endif
-
DrawLevel_MM();
}
-void InitGameActions_MM()
+void InitGameActions_MM(void)
{
int num_init_game_frames = INIT_GAME_ACTIONS_DELAY;
int cycle_steps_done = 0;
InitLaser();
-#if 0
- /* copy default game door content to main double buffer */
- BlitBitmap(pix[PIX_DOOR], drawto,
- DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
-#endif
-
-#if 0
- DrawText(DX_LEVEL, DY_LEVEL,
- int2str(level_nr, 2), FONT_TEXT_2);
- DrawText(DX_KETTLES, DY_KETTLES,
- int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
- DrawText(DX_SCORE, DY_SCORE,
- int2str(game_mm.score, 4), FONT_TEXT_2);
-#endif
-
-#if 0
- UnmapGameButtons();
- MapGameButtons();
-#endif
-
-#if 0
- /* 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);
-#endif
-
-#if 0
- OpenDoor(DOOR_OPEN_ALL);
-#endif
-
for (i = 0; i <= num_init_game_frames; i++)
{
if (i == num_init_game_frames)
cycle_steps_done++;
}
- BackToFront();
+ AdvanceFrameCounter();
+ AdvanceGfxFrame();
- ColorCycling();
+ if (PendingEscapeKeyEvent())
+ continue;
#ifdef DEBUG
if (setup.quick_doors)
continue;
#endif
+
+ DrawLevel_MM();
+
+ BackToFront_MM();
}
-#if 0
- if (setup.sound_music && num_bg_loops)
- PlayMusic(level_nr % num_bg_loops);
+#ifdef DEBUG
+ if (setup.quick_doors)
+ DrawLevel_MM();
#endif
ScanLaser();
SetTileCursorXY(laser.start_edge.x, laser.start_edge.y);
SetTileCursorActive(TRUE);
+
+ // restart all delay counters after initially cycling game elements
+ ResetFrameCounter(&rotate_delay);
+ ResetFrameCounter(&pacman_delay);
+ ResetFrameCounter(&energy_delay);
+ ResetFrameCounter(&overload_delay);
+}
+
+static void FadeOutLaser(void)
+{
+ int i;
+
+ for (i = 15; i >= 0; i--)
+ {
+ SetLaserColor(0x11 * i);
+
+ DrawLaser(0, DL_LASER_ENABLED);
+
+ BackToFront_MM();
+ Delay_WithScreenUpdates(50);
+ }
+
+ DrawLaser(0, DL_LASER_DISABLED);
+
+ StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
+}
+
+static void GameOver_MM(int game_over_cause)
+{
+ game_mm.game_over = TRUE;
+ game_mm.game_over_cause = game_over_cause;
+ game_mm.game_over_message = (game_mm.has_mcduffin ?
+ (game_over_cause == GAME_OVER_BOMB ?
+ "Bomb killed Mc Duffin!" :
+ game_over_cause == GAME_OVER_NO_ENERGY ?
+ "Out of magic energy!" :
+ game_over_cause == GAME_OVER_OVERLOADED ?
+ "Magic spell hit Mc Duffin!" :
+ NULL) :
+ (game_over_cause == GAME_OVER_BOMB ?
+ "Bomb destroyed laser cannon!" :
+ game_over_cause == GAME_OVER_NO_ENERGY ?
+ "Out of laser energy!" :
+ game_over_cause == GAME_OVER_OVERLOADED ?
+ "Laser beam hit laser cannon!" :
+ NULL));
+
+ SetTileCursorActive(FALSE);
}
-void AddLaserEdge(int lx, int ly)
+static void AddLaserEdge(int lx, int ly)
{
- if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
+ int full_sxsize = MAX(FULL_SXSIZE, lev_fieldx * TILEX);
+ int full_sysize = MAX(FULL_SYSIZE, lev_fieldy * TILEY);
+
+ // check if laser is still inside visible playfield area (or inside level)
+ if (cSX + lx < REAL_SX || cSX + lx >= REAL_SX + full_sxsize ||
+ cSY + ly < REAL_SY || cSY + ly >= REAL_SY + full_sysize)
{
- Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
+ 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.edge[laser.num_edges].x = cSX2 + lx;
+ laser.edge[laser.num_edges].y = cSY2 + ly;
laser.num_edges++;
laser.redraw = TRUE;
}
-void AddDamagedField(int ex, int ey)
+static void AddDamagedField(int ex, int ey)
{
+ // prevent adding the same field position again
+ if (laser.num_damages > 0 &&
+ laser.damage[laser.num_damages - 1].x == ex &&
+ laser.damage[laser.num_damages - 1].y == ey &&
+ laser.damage[laser.num_damages - 1].edge == laser.num_edges)
+ return;
+
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.num_damages++;
}
-boolean StepBehind()
+static boolean StepBehind(void)
{
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;
+ int last_x = laser.edge[laser.num_edges - 1].x - cSX2;
+ int last_y = laser.edge[laser.num_edges - 1].y - cSY2;
return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
}
static int getMaskFromElement(int element)
{
- if (IS_GRID(element))
- return IMG_MM_MASK_GRID_1 + get_element_phase(element);
- else if (IS_MCDUFFIN(element))
- return IMG_MM_MASK_MCDUFFIN_RIGHT + get_element_phase(element);
- else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
- return IMG_MM_MASK_RECTANGLE;
+ if (IS_MCDUFFIN(element))
+ return MM_MASK_MCDUFFIN_RIGHT + get_element_phase(element);
+ else if (IS_GRID(element))
+ return MM_MASK_GRID_1 + get_element_phase(element);
+ else if (IS_DF_GRID(element))
+ return MM_MASK_RECTANGLE;
+ else if (IS_DF_SLOPE(element))
+ return MM_MASK_SLOPE_1 + get_element_phase(element);
+ else if (IS_RECTANGLE(element))
+ return MM_MASK_RECTANGLE;
else
- return IMG_MM_MASK_CIRCLE;
+ return MM_MASK_CIRCLE;
+}
+
+static int getPixelFromMask(int pos, int dx, int dy)
+{
+ return (mm_masks[pos][dy / 2][dx / 2] == 'X' ? 1 : 0);
+}
+
+static int getLevelFromLaserX(int x)
+{
+ return x / TILEX - (x < 0 ? 1 : 0); // correct negative values
+}
+
+static int getLevelFromLaserY(int y)
+{
+ return y / TILEY - (y < 0 ? 1 : 0); // correct negative values
}
-int ScanPixel()
+static int ScanPixel(void)
{
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);
+ Debug("game:mm:ScanPixel", "start scanning at (%d, %d) [%d, %d] [%d, %d]",
+ LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
#endif
- /* follow laser beam until it hits something (at least the screen border) */
+ // follow laser beam until it hits something (at least the screen border)
while (hit_mask == HIT_MASK_NO_HIT)
{
int i;
#if 0
- /* for safety */
+ // for safety
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");
+ Debug("game:mm:ScanPixel", "touched screen border!");
return HIT_MASK_ALL;
}
#endif
+ // check if laser scan has crossed element boundaries (not just mini tiles)
+ boolean cross_x = (LX / TILEX != (LX + 2) / TILEX);
+ boolean cross_y = (LY / TILEY != (LY + 2) / TILEY);
+
+ if (cross_x && cross_y)
+ {
+ int elx1 = (LX - XS) / TILEX;
+ int ely1 = (LY + YS) / TILEY;
+ int elx2 = (LX + XS) / TILEX;
+ int ely2 = (LY - YS) / TILEY;
+
+ // add element corners left and right from the laser beam to damage list
+
+ if (IN_LEV_FIELD(elx1, ely1) && Tile[elx1][ely1] != EL_EMPTY)
+ AddDamagedField(elx1, ely1);
+
+ if (IN_LEV_FIELD(elx2, ely2) && Tile[elx2][ely2] != EL_EMPTY)
+ AddDamagedField(elx2, ely2);
+ }
+
for (i = 0; i < 4; i++)
{
int px = LX + (i % 2) * 2;
int py = LY + (i / 2) * 2;
int dx = px % TILEX;
int dy = py % TILEY;
- int lx = (px + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
- int ly = (py + TILEY) / TILEY - 1; /* negative values! */
+ int lx = getLevelFromLaserX(px);
+ int ly = getLevelFromLaserY(py);
Pixel pixel;
if (IN_LEV_FIELD(lx, ly))
{
- int element = Feld[lx][ly];
+ int element = Tile[lx][ly];
if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
{
}
else
{
- int pos = getMaskFromElement(element) - IMG_MM_MASK_MCDUFFIN_RIGHT;
+ int pos = getMaskFromElement(element);
- pixel = (mm_masks[pos][dy / 2][dx / 2] == 'X' ? 1 : 0);
+ pixel = getPixelFromMask(pos, dx, dy);
}
}
else
{
- pixel = (SX + px < REAL_SX || SX + px >= REAL_SX + FULL_SXSIZE ||
- SY + py < REAL_SY || SY + py >= REAL_SY + FULL_SYSIZE);
+ // check if laser is still inside visible playfield area
+ pixel = (cSX + px < REAL_SX || cSX + px >= REAL_SX + FULL_SXSIZE ||
+ cSY + py < REAL_SY || cSY + py >= REAL_SY + FULL_SYSIZE);
}
if ((Sign[laser.current_angle] & (1 << i)) && pixel)
if (hit_mask == HIT_MASK_NO_HIT)
{
- /* hit nothing -- go on with another step */
+ // hit nothing -- go on with another step
LX += XS;
LY += YS;
}
return hit_mask;
}
-void ScanLaser()
+static void DeactivateLaserTargetElement(void)
{
- int element;
+ if (laser.dest_element_last == EL_BOMB_ACTIVE ||
+ laser.dest_element_last == EL_MINE_ACTIVE ||
+ laser.dest_element_last == EL_GRAY_BALL_ACTIVE ||
+ laser.dest_element_last == EL_GRAY_BALL_OPENING)
+ {
+ int x = laser.dest_element_last_x;
+ int y = laser.dest_element_last_y;
+ int element = laser.dest_element_last;
+
+ if (Tile[x][y] == element)
+ Tile[x][y] = (element == EL_BOMB_ACTIVE ? EL_BOMB :
+ element == EL_MINE_ACTIVE ? EL_MINE : EL_GRAY_BALL);
+
+ if (Tile[x][y] == EL_GRAY_BALL)
+ MovDelay[x][y] = 0;
+
+ laser.dest_element_last = EL_EMPTY;
+ laser.dest_element_last_x = -1;
+ laser.dest_element_last_y = -1;
+ }
+}
+
+static void ScanLaser(void)
+{
+ int element = EL_EMPTY;
+ int last_element = EL_EMPTY;
int end = 0, rf = laser.num_edges;
- /* do not scan laser again after the game was lost for whatever reason */
+ // do not scan laser again after the game was lost for whatever reason
if (game_mm.game_over)
return;
+ // do not scan laser if fuse is off
+ if (laser.fuse_off)
+ return;
+
+ DeactivateLaserTargetElement();
+
laser.overloaded = FALSE;
laser.stops_inside_element = FALSE;
DrawLaser(0, DL_LASER_ENABLED);
#if 0
- printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
- LX, LY, XS, YS);
+ Debug("game:mm:ScanLaser",
+ "Start scanning with LX == %d, LY == %d, XS == %d, YS == %d",
+ LX, LY, XS, YS);
#endif
while (1)
hit_mask = ScanPixel();
#if 0
- printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
- LX, LY, XS, YS);
+ Debug("game:mm:ScanLaser",
+ "Hit something at LX == %d, LY == %d, XS == %d, YS == %d",
+ LX, LY, XS, YS);
#endif
- /* hit something -- check out what it was */
- ELX = (LX + XS) / TILEX;
- ELY = (LY + YS) / TILEY;
+ // check if laser scan has hit two diagonally adjacent element corners
+ boolean diag_1 = ((hit_mask & HIT_MASK_DIAGONAL_1) == HIT_MASK_DIAGONAL_1);
+ boolean diag_2 = ((hit_mask & HIT_MASK_DIAGONAL_2) == HIT_MASK_DIAGONAL_2);
+
+ // check if laser scan has crossed element boundaries (not just mini tiles)
+ boolean cross_x = (getLevelFromLaserX(LX) != getLevelFromLaserX(LX + 2));
+ boolean cross_y = (getLevelFromLaserY(LY) != getLevelFromLaserY(LY + 2));
+
+ if (cross_x || cross_y)
+ {
+ // hit something at next tile -- check out what it was
+ ELX = getLevelFromLaserX(LX + XS);
+ ELY = getLevelFromLaserY(LY + YS);
+ }
+ else
+ {
+ // hit something at same tile -- check out what it was
+ ELX = getLevelFromLaserX(LX);
+ ELY = getLevelFromLaserY(LY);
+ }
#if 0
- printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
- hit_mask, LX, LY, ELX, ELY);
+ Debug("game:mm:ScanLaser", "hit_mask (1) == '%x' (%d, %d) (%d, %d)",
+ hit_mask, LX, LY, ELX, ELY);
#endif
- if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
+ if (!IN_LEV_FIELD(ELX, ELY))
{
+ // laser next step position
+ int x = cSX + LX + XS;
+ int y = cSY + LY + YS;
+
+ // check if next step of laser is still inside visible playfield area
+ if (x >= REAL_SX && x < REAL_SX + FULL_SXSIZE &&
+ y >= REAL_SY && y < REAL_SY + FULL_SYSIZE)
+ {
+ // go on with another step
+ LX += XS;
+ LY += YS;
+
+ continue;
+ }
+
element = EL_EMPTY;
laser.dest_element = element;
break;
}
- if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
+ // handle special case of laser hitting two diagonally adjacent elements
+ // (with or without a third corner element behind these two elements)
+ if ((diag_1 || diag_2) && cross_x && cross_y)
{
- /* 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;
- }
+ // compare the two diagonally adjacent elements
+ int xoffset = 2;
+ int yoffset = 2 * (diag_1 ? -1 : +1);
+ int elx1 = (LX - xoffset) / TILEX;
+ int ely1 = (LY + yoffset) / TILEY;
+ int elx2 = (LX + xoffset) / TILEX;
+ int ely2 = (LY - yoffset) / TILEY;
+ int e1 = Tile[elx1][ely1];
+ int e2 = Tile[elx2][ely2];
+ boolean use_element_1 = FALSE;
+
+ if (IS_WALL_ICE(e1) || IS_WALL_ICE(e2))
+ {
+ if (IS_WALL_ICE(e1) && IS_WALL_ICE(e2))
+ use_element_1 = (RND(2) ? TRUE : FALSE);
+ else if (IS_WALL_ICE(e1))
+ use_element_1 = TRUE;
+ }
+ else if (IS_WALL_AMOEBA(e1) || IS_WALL_AMOEBA(e2))
+ {
+ // if both tiles match, we can just select the first one
+ if (IS_WALL_AMOEBA(e1))
+ use_element_1 = TRUE;
+ }
+ else if (IS_ABSORBING_BLOCK(e1) || IS_ABSORBING_BLOCK(e2))
+ {
+ // if both tiles match, we can just select the first one
+ if (IS_ABSORBING_BLOCK(e1))
+ use_element_1 = TRUE;
+ }
- 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;
+ ELX = (use_element_1 ? elx1 : elx2);
+ ELY = (use_element_1 ? ely1 : ely2);
}
#if 0
- printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
- hit_mask, LX, LY, ELX, ELY);
+ Debug("game:mm:ScanLaser", "hit_mask (2) == '%x' (%d, %d) (%d, %d)",
+ hit_mask, LX, LY, ELX, ELY);
#endif
- element = Feld[ELX][ELY];
+ last_element = element;
+
+ element = Tile[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);
+ Debug("game:mm:ScanLaser",
+ "Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]",
+ 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);
+ Debug("game:mm:ScanLaser", "WARNING! (1) %d, %d (%d)",
+ ELX, ELY, element);
#endif
+ // special case: leaving fixed MM steel grid (upwards) with non-90° angle
+ if (element == EL_EMPTY &&
+ IS_GRID_STEEL(last_element) &&
+ laser.current_angle % 4) // angle is not 90°
+ element = last_element;
+
if (element == EL_EMPTY)
{
- if (!HitOnlyAnEdge(element, hit_mask))
+ if (!HitOnlyAnEdge(hit_mask))
break;
}
else if (element == EL_FUSE_ON)
if (rf)
DrawLaser(rf - 1, DL_LASER_ENABLED);
rf = laser.num_edges;
+
+ if (!IS_DF_WALL_STEEL(element))
+ {
+ // only used for scanning DF steel walls; reset for all other elements
+ last_LX = 0;
+ last_LY = 0;
+ last_hit_mask = 0;
+ }
}
#if 0
- if (laser.dest_element != Feld[ELX][ELY])
+ if (laser.dest_element != Tile[ELX][ELY])
{
- printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
- laser.dest_element, Feld[ELX][ELY]);
+ Debug("game:mm:ScanLaser",
+ "ALARM: laser.dest_element == %d, Tile[ELX][ELY] == %d",
+ laser.dest_element, Tile[ELX][ELY]);
}
#endif
if (!end && !laser.stops_inside_element && !StepBehind())
{
#if 0
- printf("ScanLaser: Go one step back\n");
+ Debug("game:mm:ScanLaser", "Go one step back");
#endif
LX -= XS;
#if 0
if (!IN_LEV_FIELD(ELX, ELY))
- printf("WARNING! (2) %d, %d\n", ELX, ELY);
+ Debug("game:mm:ScanLaser", "WARNING! (2) %d, %d", ELX, ELY);
#endif
}
-void DrawLaserExt(int start_edge, int num_edges, int mode)
+static void ScanLaser_FromLastMirror(void)
+{
+ int start_pos = (laser.num_damages > 0 ? laser.num_damages - 1 : 0);
+ int i;
+
+ for (i = start_pos; i >= 0; i--)
+ if (laser.damage[i].is_mirror)
+ break;
+
+ int start_edge = (i > 0 ? laser.damage[i].edge - 1 : 0);
+
+ DrawLaser(start_edge, DL_LASER_DISABLED);
+
+ ScanLaser();
+}
+
+static 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);
+ Debug("game:mm:DrawLaserExt", "start_edge, num_edges, mode == %d, %d, %d",
+ start_edge, num_edges, mode);
#endif
if (start_edge < 0)
{
- Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
+ Warn("DrawLaserExt: start_edge < 0");
return;
}
if (num_edges < 0)
{
- Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
+ Warn("DrawLaserExt: num_edges < 0");
return;
}
#if 0
if (mode == DL_LASER_DISABLED)
{
- printf("DrawLaser: Delete laser from edge %d\n", start_edge);
+ Debug("game:mm:DrawLaserExt", "Delete laser from edge %d", start_edge);
}
#endif
- /* now draw the laser to the backbuffer and (if enabled) to the screen */
+ // now draw the laser to the backbuffer and (if enabled) to the screen
DrawLaserLines(&laser.edge[start_edge], num_edges, mode);
redraw_mask |= REDRAW_FIELD;
if (mode == DL_LASER_ENABLED)
return;
- /* after the laser was deleted, the "damaged" graphics must be restored */
+ // 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 */
+ // determine the starting edge, from which graphics need to be restored
if (start_edge > 0)
{
for (i = 0; i < laser.num_damages; i++)
}
}
- /* restore graphics from this starting edge to the end of damage list */
+ // restore graphics from this starting edge to the end of damage list
for (i = damage_start; i < laser.num_damages; i++)
{
int lx = laser.damage[i].x;
int ly = laser.damage[i].y;
- int element = Feld[lx][ly];
+ int element = Tile[lx][ly];
if (Hit[lx][ly] == laser.damage[i].edge)
if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
elx = laser.damage[damage_start].x;
ely = laser.damage[damage_start].y;
- element = Feld[elx][ely];
+ element = Tile[elx][ely];
#if 0
if (IS_BEAMER(element))
int i;
for (i = 0; i < laser.num_beamers; i++)
- printf("-> %d\n", laser.beamer_edge[i]);
- printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
- mode, elx, ely, Hit[elx][ely], start_edge);
- printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
- get_element_angle(element), laser.damage[damage_start].angle);
+ Debug("game:mm:DrawLaserExt", "-> %d", laser.beamer_edge[i]);
+
+ Debug("game:mm:DrawLaserExt", "IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]",
+ mode, elx, ely, Hit[elx][ely], start_edge);
+ Debug("game:mm:DrawLaserExt", "IS_BEAMER: %d / %d",
+ get_element_angle(element), laser.damage[damage_start].angle);
}
#endif
laser.num_beamers > 0 &&
start_edge == laser.beamer_edge[laser.num_beamers - 1])
{
- /* element is outgoing beamer */
+ // element is outgoing beamer
laser.num_damages = damage_start + 1;
if (IS_BEAMER(element))
}
else
{
- /* element is incoming beamer or other element */
+ // 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) */
+ // 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];
+ element = Tile[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);
+ LX = laser.edge[start_edge].x - cSX2;
+ LY = laser.edge[start_edge].y - cSY2;
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);
+ Debug("game:mm:DrawLaserExt", "Set (LX, LY) to (%d, %d) [%d]",
+ LX, LY, element);
#endif
if (start_edge > 0)
int step_size;
#if 0
- printf("element == %d\n", element);
+ Debug("game:mm:DrawLaserExt", "element == %d", element);
#endif
- if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
+ 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;
(laser.num_beamers == 0 ||
start_edge != laser.beamer_edge[laser.num_beamers - 1])))
{
- /* element is incoming beamer or other element */
+ // 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);
- }
+ Debug("game:mm:DrawLaserExt",
+ "start_edge == %d, laser.beamer_edge == %d",
+ start_edge, laser.beamer_edge);
#endif
LX += step_size * XS;
}
#if 0
- printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
- LX, LY, element);
+ Debug("game:mm:DrawLaserExt", "Finally: (LX, LY) to (%d, %d) [%d]",
+ LX, LY, element);
#endif
}
void DrawLaser(int start_edge, int mode)
{
+ // do not draw laser if fuse is off
+ if (laser.fuse_off && mode == DL_LASER_ENABLED)
+ return;
+
+ if (mode == DL_LASER_DISABLED)
+ DeactivateLaserTargetElement();
+
if (laser.num_edges - start_edge < 0)
{
- Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
+ Warn("DrawLaser: laser.num_edges - start_edge < 0");
return;
}
- /* check if laser is interrupted by beamer element */
+ // check if laser is interrupted by beamer element
if (laser.num_beamers > 0 &&
start_edge < laser.beamer_edge[laser.num_beamers - 1])
{
int i;
int tmp_start_edge = start_edge;
- /* draw laser segments forward from the start to the last beamer */
+ // draw laser segments forward from the start to the last beamer
for (i = 0; i < laser.num_beamers; i++)
{
int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
continue;
#if 0
- printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
- i, laser.beamer_edge[i], tmp_start_edge);
+ Debug("game:mm:DrawLaser", "DL_LASER_ENABLED: i==%d: %d, %d",
+ i, laser.beamer_edge[i], tmp_start_edge);
#endif
DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
tmp_start_edge = laser.beamer_edge[i];
}
- /* draw last segment from last beamer to the end */
+ // draw last segment from last beamer to the end
DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
DL_LASER_ENABLED);
}
int last_num_edges = laser.num_edges;
int num_beamers = laser.num_beamers;
- /* delete laser segments backward from the end to the first beamer */
+ // delete laser segments backward from the end to the first beamer
for (i = num_beamers - 1; i >= 0; i--)
{
int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
#if 0
if (last_num_edges - start_edge <= 0)
- printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
- last_num_edges, start_edge);
+ Debug("game:mm:DrawLaser", "DL_LASER_DISABLED: %d, %d",
+ last_num_edges, start_edge);
#endif
// special case when rotating first beamer: delete laser edge on beamer
if (last_num_edges - start_edge == 1 && start_edge > 0)
DrawLaserLines(&laser.edge[start_edge - 1], 2, DL_LASER_DISABLED);
- /* delete first segment from start to the first beamer */
+ // delete first segment from start to the first beamer
DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
}
}
game_mm.laser_enabled = mode;
}
-void DrawLaser_MM()
+void DrawLaser_MM(void)
{
DrawLaser(0, game_mm.laser_enabled);
}
-boolean HitElement(int element, int hit_mask)
+static boolean HitElement(int element, int hit_mask)
{
- if (HitOnlyAnEdge(element, hit_mask))
- return FALSE;
+ if (IS_DF_SLOPE(element))
+ {
+ // check if laser scan has crossed element boundaries (not just mini tiles)
+ boolean cross_x = (getLevelFromLaserX(LX) != getLevelFromLaserX(LX + 2));
+ boolean cross_y = (getLevelFromLaserY(LY) != getLevelFromLaserY(LY + 2));
+ int element_angle = get_element_angle(element);
+ int mirrored_angle = get_mirrored_angle(laser.current_angle, element_angle);
+ int opposite_angle = get_opposite_angle(laser.current_angle);
+
+ // check if wall (horizontal or vertical) side of slope was hit
+ if (hit_mask == HIT_MASK_LEFT ||
+ hit_mask == HIT_MASK_RIGHT ||
+ hit_mask == HIT_MASK_TOP ||
+ hit_mask == HIT_MASK_BOTTOM)
+ {
+ boolean hit_slope_corner_in_laser_direction =
+ ((hit_mask == HIT_MASK_LEFT && (element == EL_DF_SLOPE_01 ||
+ element == EL_DF_SLOPE_02)) ||
+ (hit_mask == HIT_MASK_RIGHT && (element == EL_DF_SLOPE_00 ||
+ element == EL_DF_SLOPE_03)) ||
+ (hit_mask == HIT_MASK_TOP && (element == EL_DF_SLOPE_02 ||
+ element == EL_DF_SLOPE_03)) ||
+ (hit_mask == HIT_MASK_BOTTOM && (element == EL_DF_SLOPE_00 ||
+ element == EL_DF_SLOPE_01)));
+
+ boolean hit_slope_corner_in_laser_direction_double_checked =
+ (cross_x && cross_y &&
+ laser.current_angle == mirrored_angle &&
+ hit_slope_corner_in_laser_direction);
+
+ // check special case of laser hitting the corner of a slope and another
+ // element (either wall or another slope), following the diagonal side
+ // of the slope which has the same angle as the direction of the laser
+ if (!hit_slope_corner_in_laser_direction_double_checked)
+ return HitReflectingWalls(element, hit_mask);
+ }
+
+ // check if an edge was hit while crossing element borders
+ if (cross_x && cross_y && get_number_of_bits(hit_mask) == 1)
+ {
+ // check both sides of potentially diagonal side of slope
+ int dx1 = (LX + XS) % TILEX;
+ int dy1 = (LY + YS) % TILEY;
+ int dx2 = (LX + XS + 2) % TILEX;
+ int dy2 = (LY + YS + 2) % TILEY;
+ int pos = getMaskFromElement(element);
+
+ // check if we are entering empty space area after hitting edge
+ if (!getPixelFromMask(pos, dx1, dy1) &&
+ !getPixelFromMask(pos, dx2, dy2))
+ {
+ // we already know that we hit an edge, but use this function to go on
+ if (HitOnlyAnEdge(hit_mask))
+ return FALSE;
+ }
+ }
+
+ // check if laser is reflected by slope by 180°
+ if (mirrored_angle == opposite_angle)
+ {
+ AddDamagedField(LX / TILEX, LY / TILEY);
+
+ laser.overloaded = TRUE;
+
+ return TRUE;
+ }
+ }
+ else
+ {
+ if (HitOnlyAnEdge(hit_mask))
+ return FALSE;
+ }
if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
element = MovingOrBlocked2Element_MM(ELX, ELY);
#if 0
- printf("HitElement (1): element == %d\n", element);
+ Debug("game:mm:HitElement", "(1): element == %d", 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);
+ Debug("game:mm:HitElement", "(%d): EXACT MATCH @ (%d, %d)",
+ element, ELX, ELY);
else
- printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
+ Debug("game:mm:HitElement", "(%d): FUZZY MATCH @ (%d, %d)",
+ element, ELX, ELY);
#endif
AddDamagedField(ELX, ELY);
- /* this is more precise: check if laser would go through the center */
- if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
+ boolean through_center = ((ELX * TILEX + 14 - LX) * YS ==
+ (ELY * TILEY + 14 - LY) * XS);
+
+ // this is more precise: check if laser would go through the center
+ if (!IS_DF_SLOPE(element) && !through_center)
{
- /* skip the whole element before continuing the scan */
+ int skip_count = 0;
+
+ // prevent cutting through laser emitter with laser beam
+ if (IS_LASER(element))
+ return TRUE;
+
+ // skip the whole element before continuing the scan
do
{
LX += XS;
LY += YS;
+
+ skip_count++;
}
while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
- if (LX/TILEX > ELX || LY/TILEY > ELY)
+ if ((LX/TILEX > ELX || LY/TILEY > ELY) && skip_count > 1)
{
/* 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) */
+ /* ... but only roll back scan position if more than one step done */
LX -= XS;
LY -= YS;
}
#if 0
- printf("HitElement (2): element == %d\n", element);
+ Debug("game:mm:HitElement", "(2): element == %d", element);
#endif
if (LX + 5 * XS < 0 ||
}
#if 0
- printf("HitElement (3): element == %d\n", element);
+ Debug("game:mm:HitElement", "(3): element == %d", element);
#endif
if (IS_POLAR(element) &&
return TRUE;
}
- if (!IS_BEAMER(element) &&
- !IS_FIBRE_OPTIC(element) &&
- !IS_GRID_WOOD(element) &&
- element != EL_FUEL_EMPTY)
+ if (IS_DF_SLOPE(element) && !through_center)
+ {
+ int correction = 2;
+
+ if (hit_mask == HIT_MASK_ALL)
+ {
+ // laser already inside slope -- go back half step
+ LX -= XS / 2;
+ LY -= YS / 2;
+
+ correction = 1;
+ }
+
+ AddLaserEdge(LX, LY);
+
+ LX -= (ABS(XS) < ABS(YS) ? correction * SIGN(XS) : 0);
+ LY -= (ABS(YS) < ABS(XS) ? correction * SIGN(YS) : 0);
+ }
+ else 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);
+ Debug("game:mm:HitElement", "EXACT MATCH @ (%d, %d)", ELX, ELY);
else
- printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
+ Debug("game:mm:HitElement", "FUZZY MATCH @ (%d, %d)", ELX, ELY);
#endif
LX = ELX * TILEX + 14;
IS_POLAR_CROSS(element) ||
IS_DF_MIRROR(element) ||
IS_DF_MIRROR_AUTO(element) ||
+ IS_DF_MIRROR_FIXED(element) ||
+ IS_DF_SLOPE(element) ||
element == EL_PRISM ||
element == EL_REFRACTOR)
{
if (IS_MIRROR(element) ||
IS_MIRROR_FIXED(element) ||
IS_DF_MIRROR(element) ||
- IS_DF_MIRROR_AUTO(element))
+ IS_DF_MIRROR_AUTO(element) ||
+ IS_DF_MIRROR_FIXED(element) ||
+ IS_DF_SLOPE(element))
laser.current_angle = get_mirrored_angle(laser.current_angle,
get_element_angle(element));
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;
+ if (through_center)
+ {
+ // start from center position for all game elements but slope
+ 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;
+ LX += step_size * XS;
+ LY += step_size * YS;
+ }
+ else
+ {
+ // advance laser position until reaching the next tile (slopes)
+ while (LX / TILEX == ELX && (LX + 2) / TILEX == ELX &&
+ LY / TILEY == ELY && (LY + 2) / TILEY == ELY)
+ {
+ LX += XS;
+ LY += YS;
+ }
+ }
-#if 0
- /* draw sparkles on mirror */
- if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
+ // draw sparkles on mirror
+ if ((IS_MIRROR(element) ||
+ IS_MIRROR_FIXED(element) ||
+ element == EL_PRISM) &&
current_angle != laser.current_angle)
{
- MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
+ MovDelay[ELX][ELY] = 11; // start animation
}
-#endif
if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
current_angle != laser.current_angle)
(get_opposite_angle(laser.current_angle) ==
laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
+ if (IS_DF_SLOPE(element))
+ {
+ // handle special cases for slope element
+
+ if (IS_45_ANGLE(laser.current_angle))
+ {
+ int elx, ely;
+
+ elx = getLevelFromLaserX(LX + XS);
+ ely = getLevelFromLaserY(LY + YS);
+
+ if (IN_LEV_FIELD(elx, ely))
+ {
+ int element_next = Tile[elx][ely];
+
+ // check if slope is followed by slope with opposite orientation
+ if (IS_DF_SLOPE(element_next) && ABS(element - element_next) == 2)
+ laser.overloaded = TRUE;
+ }
+
+ int nr = element - EL_DF_SLOPE_START;
+ int dx = (nr == 0 ? (XS > 0 ? TILEX - 1 : -1) :
+ nr == 1 ? (XS > 0 ? TILEX : 0) :
+ nr == 2 ? (XS > 0 ? TILEX : 0) :
+ nr == 3 ? (XS > 0 ? TILEX - 1 : -1) : 0);
+ int dy = (nr == 0 ? (YS > 0 ? TILEY - 1 : -1) :
+ nr == 1 ? (YS > 0 ? TILEY - 1 : -1) :
+ nr == 2 ? (YS > 0 ? TILEY : 0) :
+ nr == 3 ? (YS > 0 ? TILEY : 0) : 0);
+
+ int px = ELX * TILEX + dx;
+ int py = ELY * TILEY + dy;
+
+ dx = px % TILEX;
+ dy = py % TILEY;
+
+ elx = getLevelFromLaserX(px);
+ ely = getLevelFromLaserY(py);
+
+ if (IN_LEV_FIELD(elx, ely))
+ {
+ int element_side = Tile[elx][ely];
+
+ // check if end of slope is blocked by other element
+ if (IS_WALL(element_side) || IS_WALL_CHANGING(element_side))
+ {
+ int pos = dy / MINI_TILEY * 2 + dx / MINI_TILEX;
+
+ if (element & (1 << pos))
+ laser.overloaded = TRUE;
+ }
+ else
+ {
+ int pos = getMaskFromElement(element_side);
+
+ if (getPixelFromMask(pos, dx, dy))
+ laser.overloaded = TRUE;
+ }
+ }
+ }
+ }
+
return (laser.overloaded ? TRUE : FALSE);
}
return TRUE;
}
- if (element == EL_BOMB || element == EL_MINE)
+ if (element == EL_BOMB || element == EL_MINE || element == EL_GRAY_BALL)
{
PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
+ Tile[ELX][ELY] = (element == EL_BOMB ? EL_BOMB_ACTIVE :
+ element == EL_MINE ? EL_MINE_ACTIVE :
+ EL_GRAY_BALL_ACTIVE);
+
+ GfxFrame[ELX][ELY] = 0; // restart animation
+
+ laser.dest_element_last = Tile[ELX][ELY];
+ laser.dest_element_last_x = ELX;
+ laser.dest_element_last_y = ELY;
+
if (element == EL_MINE)
laser.overloaded = TRUE;
}
element == EL_KEY ||
element == EL_LIGHTBALL ||
element == EL_PACMAN ||
- IS_PACMAN(element))
+ IS_PACMAN(element) ||
+ IS_ENVELOPE(element))
{
- if (!IS_PACMAN(element))
+ if (!IS_PACMAN(element) &&
+ !IS_ENVELOPE(element))
Bang_MM(ELX, ELY);
if (element == EL_PACMAN)
{
DeletePacMan(ELX, ELY);
}
+ else if (IS_ENVELOPE(element))
+ {
+ Tile[ELX][ELY] = EL_ENVELOPE_1_OPENING + ENVELOPE_NR(Tile[ELX][ELY]);
+ }
RaiseScoreElement_MM(element);
DrawLaser(0, DL_LASER_ENABLED);
- if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
+ if (Tile[ELX][ELY] == EL_LIGHTBULB_OFF)
{
- Feld[ELX][ELY] = EL_LIGHTBULB_ON;
+ Tile[ELX][ELY] = EL_LIGHTBULB_ON;
game_mm.lights_still_needed--;
}
else
{
- Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
+ Tile[ELX][ELY] = EL_LIGHTBULB_OFF;
game_mm.lights_still_needed++;
}
}
#if 0
- printf("HitElement (4): element == %d\n", element);
+ Debug("game:mm:HitElement", "(4): element == %d", element);
#endif
if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
int step_size;
#if 0
- printf("HitElement (BEAMER): element == %d\n", element);
+ Debug("game:mm:HitElement", "(BEAMER): element == %d", element);
#endif
laser.num_damages--;
if (IS_BEAMER(element))
{
- laser.current_angle = get_element_angle(Feld[ELX][ELY]);
+ laser.current_angle = get_element_angle(Tile[ELX][ELY]);
XS = 2 * Step[laser.current_angle].x;
YS = 2 * Step[laser.current_angle].y;
}
return TRUE;
}
-boolean HitOnlyAnEdge(int element, int hit_mask)
+static boolean HitOnlyAnEdge(int hit_mask)
{
- /* check if the laser hit only the edge of an element and, if so, go on */
+ // 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);
+ Debug("game:mm:HitOnlyAnEdge", "LX, LY, hit_mask == %d, %d, %d",
+ 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° */
+ laser.current_angle % 4) // angle is not 90°
{
int dx, dy;
dx = -1;
dy = +1;
}
- else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
+ else // (hit_mask == HIT_MASK_BOTTOMRIGHT)
{
dx = +1;
dy = +1;
LY += YS;
#if 0
- printf("[HitOnlyAnEdge() == TRUE]\n");
+ Debug("game:mm:HitOnlyAnEdge", "[HitOnlyAnEdge() == TRUE]");
#endif
return TRUE;
}
#if 0
- printf("[HitOnlyAnEdge() == FALSE]\n");
+ Debug("game:mm:HitOnlyAnEdge", "[HitOnlyAnEdge() == FALSE]");
#endif
return FALSE;
}
-boolean HitPolarizer(int element, int hit_mask)
+static boolean HitPolarizer(int element, int hit_mask)
{
- if (HitOnlyAnEdge(element, hit_mask))
+ if (HitOnlyAnEdge(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);
+ Debug("game:mm:HitPolarizer", "angle: grid == %d, laser == %d",
+ grid_angle, laser.current_angle);
#endif
AddLaserEdge(LX, LY);
if (laser.current_angle == grid_angle ||
laser.current_angle == get_opposite_angle(grid_angle))
{
- /* skip the whole element before continuing the scan */
+ // skip the whole element before continuing the scan
do
{
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);
+ Debug("game:mm:HitPolarizer", "LX, LY == %d, %d [%d, %d] [%d, %d]",
+ LX, LY,
+ LX / TILEX, LY / TILEY,
+ LX % TILEX, LY % TILEY);
#endif
return FALSE;
}
else if (IS_GRID_STEEL(element))
{
+ // may be required if graphics for steel grid redefined
+ AddDamagedField(ELX, ELY);
+
return HitReflectingWalls(element, hit_mask);
}
- else /* IS_GRID_WOOD */
+ else // IS_GRID_WOOD
{
+ // may be required if graphics for wooden grid redefined
+ AddDamagedField(ELX, ELY);
+
return HitAbsorbingWalls(element, hit_mask);
}
return TRUE;
}
-boolean HitBlock(int element, int hit_mask)
+static boolean HitBlock(int element, int hit_mask)
{
boolean check = FALSE;
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)
+ if ((hit_mask & HIT_MASK_DIAGONAL_1) == HIT_MASK_DIAGONAL_1 ||
+ (hit_mask & HIT_MASK_DIAGONAL_2) == HIT_MASK_DIAGONAL_2)
{
laser.overloaded = (element == EL_GATE_STONE);
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)
+ if ((hit_mask & HIT_MASK_DIAGONAL_1) == HIT_MASK_DIAGONAL_1 ||
+ (hit_mask & HIT_MASK_DIAGONAL_2) == HIT_MASK_DIAGONAL_2)
{
laser.overloaded = (element == EL_BLOCK_STONE);
return TRUE;
}
-boolean HitLaserSource(int element, int hit_mask)
+static boolean HitLaserSource(int element, int hit_mask)
{
- if (HitOnlyAnEdge(element, hit_mask))
+ if (HitOnlyAnEdge(hit_mask))
return FALSE;
PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
return TRUE;
}
-boolean HitLaserDestination(int element, int hit_mask)
+static boolean HitLaserDestination(int element, int hit_mask)
{
- if (HitOnlyAnEdge(element, hit_mask))
+ if (HitOnlyAnEdge(hit_mask))
return FALSE;
if (element != EL_EXIT_OPEN &&
return TRUE;
}
-boolean HitReflectingWalls(int element, int hit_mask)
+static boolean HitReflectingWalls(int element, int hit_mask)
{
- /* check if laser hits side of a wall with an angle that is not 90° */
+ // 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 ||
if (!IS_DF_GRID(element))
AddLaserEdge(LX, LY);
- /* check if laser hits wall with an angle of 45° */
+ // 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)
laser.current_angle = get_mirrored_angle(laser.current_angle,
ANG_MIRROR_0);
}
- else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
+ else // hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT
{
LY += 2 * YS;
laser.current_angle = get_mirrored_angle(laser.current_angle,
return FALSE;
}
- else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
+ else // hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT
{
laser.current_angle = get_mirrored_angle(laser.current_angle,
ANG_MIRROR_90);
}
}
- /* reflection at the edge of reflecting DF style wall */
+ // 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) &&
}
}
- /* reflection inside an edge of reflecting DF style wall */
+ // 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) &&
}
}
- /* check if laser hits DF style wall with an angle of 90° */
+ // 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) &&
(IS_VERT_ANGLE(laser.current_angle) &&
(!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
{
- /* laser at last step touched nothing or the same side of the wall */
+ // 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);
}
}
- if (!HitOnlyAnEdge(element, hit_mask))
+ if (!HitOnlyAnEdge(hit_mask))
{
laser.overloaded = TRUE;
return FALSE;
}
-boolean HitAbsorbingWalls(int element, int hit_mask)
+static boolean HitAbsorbingWalls(int element, int hit_mask)
{
- if (HitOnlyAnEdge(element, hit_mask))
+ if (HitOnlyAnEdge(hit_mask))
return FALSE;
if (ABS(XS) == 4 &&
if (IS_WALL_ICE(element))
{
+ int lx = LX + XS;
+ int ly = LY + YS;
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 hit adjacent edges of two diagonal tiles
+ if (ELX != lx / TILEX)
+ lx = LX - XS;
+ if (ELY != ly / TILEY)
+ ly = LY - YS;
- /* check if laser hits wall with an angle of 90° */
+ mask = lx / MINI_TILEX - ELX * 2 + 1; // Quadrant (horizontal)
+ mask <<= ((ly / MINI_TILEY - ELY * 2) > 0 ? 2 : 0); // || (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);
{
int elx = (LX - 2 * XS) / TILEX;
int ely = (LY - 2 * YS) / TILEY;
- int element2 = Feld[elx][ely];
+ int element2 = Tile[elx][ely];
int mask;
if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
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.dest_element = element2 | EL_WALL_AMOEBA_BASE;
laser.wall_mask = mask;
}
return TRUE;
}
-void OpenExit(int x, int y)
+static void OpenExit(int x, int y)
{
int delay = 6;
- if (!MovDelay[x][y]) /* next animation frame */
+ if (!MovDelay[x][y]) // next animation frame
MovDelay[x][y] = 4 * delay;
- if (MovDelay[x][y]) /* wait some time before next frame */
+ if (MovDelay[x][y]) // wait some time before next frame
{
int phase;
if (!MovDelay[x][y])
{
- Feld[x][y] = EL_EXIT_OPEN;
+ Tile[x][y] = EL_EXIT_OPEN;
DrawField_MM(x, y);
}
}
}
-void OpenSurpriseBall(int x, int y)
+static void OpenGrayBall(int x, int y)
{
int delay = 2;
- if (!MovDelay[x][y]) /* next animation frame */
+ if (!MovDelay[x][y]) // next animation frame
+ {
+ if (IS_WALL(Store[x][y]))
+ {
+ DrawWalls_MM(x, y, Store[x][y]);
+
+ // copy wall tile to spare bitmap for "melting" animation
+ BlitBitmap(drawto_mm, bitmap_db_field, cSX + x * TILEX, cSY + y * TILEY,
+ TILEX, TILEY, x * TILEX, y * TILEY);
+
+ DrawElement_MM(x, y, EL_GRAY_BALL);
+ }
+
MovDelay[x][y] = 50 * delay;
+ }
- if (MovDelay[x][y]) /* wait some time before next frame */
+ if (MovDelay[x][y]) // wait some time before next frame
{
MovDelay[x][y]--;
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);
+ if (IS_WALL(Store[x][y]))
+ {
+ // copy wall tile from spare bitmap for "melting" animation
+ bitmap = bitmap_db_field;
+ gx = x * TILEX;
+ gy = y * TILEY;
+ }
+ else
+ {
+ int graphic = el2gfx(Store[x][y]);
+
+ getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
+ }
+
+ BlitBitmap(bitmap, drawto_mm, gx + dx, gy + dy, 6, 6,
+ cSX + x * TILEX + dx, cSY + y * TILEY + dy);
- BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
- SX + x * TILEX + dx, SY + y * TILEY + dy);
+ laser.redraw = TRUE;
MarkTileDirty(x, y);
}
if (!MovDelay[x][y])
{
- Feld[x][y] = Store[x][y];
- Store[x][y] = 0;
+ Tile[x][y] = Store[x][y];
+ Store[x][y] = Store2[x][y] = 0;
+ MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
+
+ InitField(x, y, FALSE);
DrawField_MM(x, y);
- ScanLaser();
+ ScanLaser_FromLastMirror();
}
}
}
-void MeltIce(int x, int y)
+static void OpenEnvelope(int x, int y)
{
- int frames = 5;
- int delay = 5;
+ int num_frames = 8; // seven frames plus final empty space
- if (!MovDelay[x][y]) /* next animation frame */
- MovDelay[x][y] = frames * delay;
+ if (!MovDelay[x][y]) // next animation frame
+ MovDelay[x][y] = num_frames;
- if (MovDelay[x][y]) /* wait some time before next frame */
+ 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;
+ int nr = ENVELOPE_OPENING_NR(Tile[x][y]);
MovDelay[x][y]--;
- phase = frames - MovDelay[x][y] / delay - 1;
- if (!MovDelay[x][y])
+ if (MovDelay[x][y] > 0 && IN_SCR_FIELD(x, y))
{
- int i;
-
- Feld[x][y] = real_element & (wall_mask ^ 0xFF);
- Store[x][y] = Store2[x][y] = 0;
+ int graphic = el_act2gfx(EL_ENVELOPE_1 + nr, MM_ACTION_COLLECTING);
+ int frame = num_frames - MovDelay[x][y] - 1;
- DrawWalls_MM(x, y, Feld[x][y]);
+ DrawGraphicAnimation_MM(x, y, graphic, frame);
- if (Feld[x][y] == EL_WALL_ICE)
- Feld[x][y] = EL_EMPTY;
+ laser.redraw = TRUE;
+ }
- for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
- if (laser.damage[i].is_mirror)
- break;
+ if (MovDelay[x][y] == 0)
+ {
+ Tile[x][y] = EL_EMPTY;
- if (i > 0)
- DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
- else
- DrawLaser(0, DL_LASER_DISABLED);
+ DrawField_MM(x, y);
ScanLaser();
+
+ ShowEnvelope(nr);
+ }
+ }
+}
+
+static 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 = Tile[x][y] - EL_WALL_CHANGING_BASE + EL_WALL_ICE_BASE;
+
+ MovDelay[x][y]--;
+ phase = frames - MovDelay[x][y] / delay - 1;
+
+ if (!MovDelay[x][y])
+ {
+ Tile[x][y] = real_element & (wall_mask ^ 0xFF);
+ Store[x][y] = Store2[x][y] = 0;
+
+ DrawWalls_MM(x, y, Tile[x][y]);
+
+ if (Tile[x][y] == EL_WALL_ICE_BASE)
+ Tile[x][y] = EL_EMPTY;
+
+ ScanLaser_FromLastMirror();
}
else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
{
}
}
-void GrowAmoeba(int x, int y)
+static void GrowAmoeba(int x, int y)
{
int frames = 5;
int delay = 1;
- if (!MovDelay[x][y]) /* next animation frame */
+ if (!MovDelay[x][y]) // next animation frame
MovDelay[x][y] = frames * delay;
- if (MovDelay[x][y]) /* wait some time before next frame */
+ 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;
+ int real_element = Tile[x][y] - EL_WALL_CHANGING_BASE + EL_WALL_AMOEBA_BASE;
MovDelay[x][y]--;
phase = MovDelay[x][y] / delay;
if (!MovDelay[x][y])
{
- Feld[x][y] = real_element;
+ Tile[x][y] = real_element;
Store[x][y] = Store2[x][y] = 0;
- DrawWalls_MM(x, y, Feld[x][y]);
+ DrawWalls_MM(x, y, Tile[x][y]);
DrawLaser(0, DL_LASER_ENABLED);
}
else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
}
}
+static void DrawFieldAnimated_MM(int x, int y)
+{
+ DrawField_MM(x, y);
+
+ laser.redraw = TRUE;
+}
+
+static void DrawFieldAnimatedIfNeeded_MM(int x, int y)
+{
+ int element = Tile[x][y];
+ int graphic = el2gfx(element);
+
+ if (!getGraphicInfo_NewFrame(x, y, graphic))
+ return;
+
+ DrawField_MM(x, y);
+
+ laser.redraw = TRUE;
+}
+
+static void DrawFieldTwinkle(int x, int y)
+{
+ if (MovDelay[x][y] != 0) // wait some time before next frame
+ {
+ MovDelay[x][y]--;
+
+ DrawField_MM(x, y);
+
+ if (MovDelay[x][y] != 0)
+ {
+ int graphic = IMG_TWINKLE_WHITE;
+ int frame = getGraphicAnimationFrame(graphic, 10 - MovDelay[x][y]);
+
+ DrawGraphicThruMask_MM(SCREENX(x), SCREENY(y), graphic, frame);
+ }
+
+ laser.redraw = TRUE;
+ }
+}
+
static void Explode_MM(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;
+ int center_element;
laser.redraw = TRUE;
- if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
+ if (phase == EX_PHASE_START) // initialize 'Store[][]' field
{
- int center_element = Feld[x][y];
+ center_element = Tile[x][y];
if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
{
- /* put moving element to center field (and let it explode there) */
+ // put moving element to center field (and let it explode there)
center_element = MovingOrBlocked2Element_MM(x, y);
RemoveMovingField_MM(x, y);
- Feld[x][y] = center_element;
+ Tile[x][y] = center_element;
}
- if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
- Store[x][y] = center_element;
- else
+ if (center_element != EL_GRAY_BALL_ACTIVE)
Store[x][y] = EL_EMPTY;
+ Store2[x][y] = center_element;
+
+ Tile[x][y] = EL_EXPLODING_OPAQUE;
+
+ GfxElement[x][y] = (center_element == EL_BOMB_ACTIVE ? EL_BOMB :
+ center_element == EL_GRAY_BALL_ACTIVE ? EL_GRAY_BALL :
+ IS_MCDUFFIN(center_element) ? EL_MCDUFFIN :
+ center_element);
- Store2[x][y] = mode;
- Feld[x][y] = EL_EXPLODING_OPAQUE;
MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
- Frame[x][y] = 1;
+
+ ExplodePhase[x][y] = 1;
return;
}
- Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
+ if (phase == 1)
+ GfxFrame[x][y] = 0; // restart explosion animation
- if (phase == half_phase)
+ ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
+
+ center_element = Store2[x][y];
+
+ if (phase == half_phase && Store[x][y] == EL_EMPTY)
{
- Feld[x][y] = EL_EXPLODING_TRANSP;
+ Tile[x][y] = EL_EXPLODING_TRANSP;
if (x == ELX && y == ELY)
ScanLaser();
if (phase == last_phase)
{
- if (Store[x][y] == EL_BOMB)
+ if (center_element == EL_BOMB_ACTIVE)
{
DrawLaser(0, DL_LASER_DISABLED);
InitLaser();
Bang_MM(laser.start_edge.x, laser.start_edge.y);
- Store[x][y] = EL_EMPTY;
-
- game_mm.game_over = TRUE;
- game_mm.game_over_cause = GAME_OVER_BOMB;
-
- SetTileCursorActive(FALSE);
laser.overloaded = FALSE;
}
- else if (IS_MCDUFFIN(Store[x][y]))
+ else if (IS_MCDUFFIN(center_element) || IS_LASER(center_element))
{
- Store[x][y] = EL_EMPTY;
+ GameOver_MM(GAME_OVER_BOMB);
}
- Feld[x][y] = Store[x][y];
+ Tile[x][y] = Store[x][y];
+
Store[x][y] = Store2[x][y] = 0;
MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
InitField(x, y, FALSE);
DrawField_MM(x, y);
+
+ if (center_element == EL_GRAY_BALL_ACTIVE)
+ ScanLaser_FromLastMirror();
}
else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
{
- int graphic = IMG_MM_DEFAULT_EXPLODING;
- int graphic_phase = (phase / delay - 1);
- Bitmap *bitmap;
- int src_x, src_y;
-
- if (Store2[x][y] == EX_KETTLE)
- {
- if (graphic_phase < 3)
- {
- graphic = IMG_MM_KETTLE_EXPLODING;
- }
- else if (graphic_phase < 5)
- {
- graphic_phase += 3;
- }
- else
- {
- graphic = IMG_EMPTY;
- graphic_phase = 0;
- }
- }
- else if (Store2[x][y] == EX_SHORT)
- {
- if (graphic_phase < 4)
- {
- graphic_phase += 4;
- }
- else
- {
- graphic = IMG_EMPTY;
- graphic_phase = 0;
- }
- }
-
- getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
+ int graphic = el_act2gfx(GfxElement[x][y], MM_ACTION_EXPLODING);
+ int frame = getGraphicAnimationFrameXY(graphic, x, y);
- BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
- FX + x * TILEX, FY + y * TILEY);
+ DrawGraphicAnimation_MM(x, y, graphic, frame);
MarkTileDirty(x, y);
}
static void Bang_MM(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;
- }
+ int element = Tile[x][y];
if (IS_PACMAN(element))
PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
- else if (element == EL_BOMB || IS_MCDUFFIN(element))
+ else if (element == EL_BOMB_ACTIVE || IS_MCDUFFIN(element))
PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
else if (element == EL_KEY)
PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
else
PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
- Explode_MM(x, y, EX_PHASE_START, mode);
+ Explode_MM(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
}
-void TurnRound(int x, int y)
+static 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 }
+ { 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 },
+ { 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 }
+ { 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 element = Tile[x][y];
int old_move_dir = MovDir[x][y];
int right_dir = turn[old_move_dir].right;
int back_dir = turn[old_move_dir].back;
boolean can_turn_right = FALSE;
if (IN_LEV_FIELD(right_x, right_y) &&
- IS_EATABLE4PACMAN(Feld[right_x][right_y]))
+ IS_EATABLE4PACMAN(Tile[right_x][right_y]))
can_turn_right = TRUE;
if (can_turn_right)
static void StartMoving_MM(int x, int y)
{
- int element = Feld[x][y];
+ int element = Tile[x][y];
if (Stop[x][y])
return;
{
int newx, newy;
- if (MovDelay[x][y]) /* wait some time before next movement */
+ if (MovDelay[x][y]) // wait some time before next movement
{
MovDelay[x][y]--;
return;
}
- /* now make next step */
+ // now make next step
- Moving2Blocked_MM(x, y, &newx, &newy); /* get next screen position */
+ Moving2Blocked(x, y, &newx, &newy); // get next screen position
if (element == EL_PACMAN &&
- IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
+ IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Tile[newx][newy]) &&
!ObjHit(newx, newy, HIT_POS_CENTER))
{
- Store[newx][newy] = Feld[newx][newy];
- Feld[newx][newy] = EL_EMPTY;
+ Store[newx][newy] = Tile[newx][newy];
+ Tile[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 */
+ // object was running against a wall
TurnRound(x, y);
static void ContinueMoving_MM(int x, int y)
{
- int element = Feld[x][y];
+ int element = Tile[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 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 */
+ if (ABS(MovPos[x][y]) >= TILEX) // object reached its destination
{
- Feld[x][y] = EL_EMPTY;
- Feld[newx][newy] = element;
+ Tile[x][y] = EL_EMPTY;
+ Tile[newx][newy] = element;
MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
MovDelay[newx][newy] = 0;
}
}
}
- else /* still moving on */
+ else // still moving on
{
DrawField_MM(x, y);
}
boolean ClickElement(int x, int y, int button)
{
- static unsigned int click_delay = 0;
- static int click_delay_value = CLICK_DELAY;
+ static DelayCounter click_delay = { CLICK_DELAY };
static boolean new_button = TRUE;
boolean element_clicked = FALSE;
int element;
if (button == -1)
{
- /* initialize static variables */
- click_delay = 0;
- click_delay_value = CLICK_DELAY;
+ // initialize static variables
+ click_delay.count = 0;
+ click_delay.value = CLICK_DELAY;
new_button = TRUE;
return FALSE;
}
- /* do not rotate objects hit by the laser after the game was solved */
+ // do not rotate objects hit by the laser after the game was solved
if (game_mm.level_solved && Hit[x][y])
return FALSE;
if (button == MB_RELEASED)
{
new_button = TRUE;
- click_delay_value = CLICK_DELAY;
+ click_delay.value = CLICK_DELAY;
- /* release eventually hold auto-rotating mirror */
+ // release eventually hold auto-rotating mirror
RotateMirror(x, y, MB_RELEASED);
return FALSE;
}
- if (!FrameReached(&click_delay, click_delay_value) && !new_button)
+ if (!FrameReached(&click_delay) && !new_button)
return FALSE;
- if (button == MB_MIDDLEBUTTON) /* middle button has no function */
+ if (button == MB_MIDDLEBUTTON) // middle button has no function
return FALSE;
if (!IN_LEV_FIELD(x, y))
return FALSE;
- if (Feld[x][y] == EL_EMPTY)
+ if (Tile[x][y] == EL_EMPTY)
return FALSE;
- element = Feld[x][y];
+ element = Tile[x][y];
if (IS_MIRROR(element) ||
IS_BEAMER(element) ||
}
else if (IS_MCDUFFIN(element))
{
- if (!laser.fuse_off)
- {
- DrawLaser(0, DL_LASER_DISABLED);
+ boolean has_laser = (x == laser.start_edge.x && y == laser.start_edge.y);
- /*
- BackToFront();
- */
- }
+ if (has_laser && !laser.fuse_off)
+ DrawLaser(0, DL_LASER_DISABLED);
element = get_rotated_element(element, BUTTON_ROTATION(button));
- laser.start_angle = get_element_angle(element);
- InitLaser();
-
- Feld[x][y] = element;
+ Tile[x][y] = element;
DrawField_MM(x, y);
- /*
- BackToFront();
- */
+ if (has_laser)
+ {
+ laser.start_angle = get_element_angle(element);
- if (!laser.fuse_off)
- ScanLaser();
+ InitLaser();
+
+ if (!laser.fuse_off)
+ ScanLaser();
+ }
element_clicked = TRUE;
}
element_clicked = TRUE;
}
+ else if (IS_ENVELOPE(element))
+ {
+ Tile[x][y] = EL_ENVELOPE_1_OPENING + ENVELOPE_NR(element);
+
+ element_clicked = TRUE;
+ }
- click_delay_value = (new_button ? CLICK_DELAY_FIRST : CLICK_DELAY);
+ click_delay.value = (new_button ? CLICK_DELAY_FIRST : CLICK_DELAY);
new_button = FALSE;
return element_clicked;
}
-void RotateMirror(int x, int y, int button)
+static void RotateMirror(int x, int y, int button)
{
if (button == MB_RELEASED)
{
- /* release eventually hold auto-rotating mirror */
+ // 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]))
+ if (IS_MIRROR(Tile[x][y]) ||
+ IS_POLAR_CROSS(Tile[x][y]) ||
+ IS_POLAR(Tile[x][y]) ||
+ IS_BEAMER(Tile[x][y]) ||
+ IS_DF_MIRROR(Tile[x][y]) ||
+ IS_GRID_STEEL_AUTO(Tile[x][y]) ||
+ IS_GRID_WOOD_AUTO(Tile[x][y]))
{
- Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
+ Tile[x][y] = get_rotated_element(Tile[x][y], BUTTON_ROTATION(button));
}
- else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
+ else if (IS_DF_MIRROR_AUTO(Tile[x][y]))
{
if (button == MB_LEFTBUTTON)
{
- /* left mouse button only for manual adjustment, no auto-rotating;
- freeze mirror for until mouse button released */
+ // 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);
+ Tile[x][y] = get_rotated_element(Tile[x][y], ROTATE_RIGHT);
}
}
- if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
+ if (IS_GRID_STEEL_AUTO(Tile[x][y]) || IS_GRID_WOOD_AUTO(Tile[x][y]))
{
int edge = Hit[x][y];
if (edge == 0)
{
- Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
+ Warn("RotateMirror: inconsistent field Hit[][]!\n");
+
edge = 1;
}
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)
+ if ((IS_BEAMER(Tile[x][y]) ||
+ IS_POLAR(Tile[x][y]) ||
+ IS_POLAR_CROSS(Tile[x][y])) && x == ELX && y == ELY)
{
- check = 0;
-
- if (IS_BEAMER(Feld[x][y]))
+ if (IS_BEAMER(Tile[x][y]))
{
#if 0
- printf("TEST (%d, %d) [%d] [%d]\n",
- LX, LY,
- laser.beamer_edge, laser.beamer[1].num);
+ Debug("game:mm:RotateMirror", "TEST (%d, %d) [%d] [%d]",
+ LX, LY, laser.beamer_edge, laser.beamer[1].num);
#endif
- laser.num_edges--;
+ if (check == 1)
+ laser.num_edges--;
}
ScanLaser();
+
+ check = 0;
}
if (check == 2)
}
}
-void AutoRotateMirrors()
+static void AutoRotateMirrors(void)
{
int x, y;
- if (!FrameReached(&rotate_delay, AUTO_ROTATE_DELAY))
+ if (!FrameReached(&rotate_delay))
return;
for (x = 0; x < lev_fieldx; x++)
{
for (y = 0; y < lev_fieldy; y++)
{
- int element = Feld[x][y];
+ int element = Tile[x][y];
- /* do not rotate objects hit by the laser after the game was solved */
+ // do not rotate objects hit by the laser after the game was solved
if (game_mm.level_solved && Hit[x][y])
continue;
IS_GRID_WOOD_AUTO(element) ||
IS_GRID_STEEL_AUTO(element) ||
element == EL_REFRACTOR)
+ {
RotateMirror(x, y, MB_RIGHTBUTTON);
+
+ laser.redraw = TRUE;
+ }
}
}
}
-boolean ObjHit(int obx, int oby, int bits)
+static boolean ObjHit(int obx, int oby, int bits)
{
int i;
if (bits & HIT_POS_CENTER)
{
- if (CheckLaserPixel(SX + obx + 15,
- SY + oby + 15))
+ if (CheckLaserPixel(cSX + obx + 15,
+ cSY + oby + 15))
return TRUE;
}
if (bits & HIT_POS_EDGE)
{
for (i = 0; i < 4; i++)
- if (CheckLaserPixel(SX + obx + 31 * (i % 2),
- SY + oby + 31 * (i / 2)))
+ if (CheckLaserPixel(cSX + obx + 31 * (i % 2),
+ cSY + oby + 31 * (i / 2)))
return TRUE;
}
if (bits & HIT_POS_BETWEEN)
{
for (i = 0; i < 4; i++)
- if (CheckLaserPixel(SX + 4 + obx + 22 * (i % 2),
- SY + 4 + oby + 22 * (i / 2)))
+ if (CheckLaserPixel(cSX + 4 + obx + 22 * (i % 2),
+ cSY + 4 + oby + 22 * (i / 2)))
return TRUE;
}
return FALSE;
}
-void DeletePacMan(int px, int py)
+static void DeletePacMan(int px, int py)
{
int i, j;
}
}
-void ColorCycling(void)
-{
- static int CC, Cc = 0;
-
- static int color, old = 0xF00, new = 0x010, mult = 1;
- static unsigned short red, green, blue;
-
- if (color_status == STATIC_COLORS)
- return;
-
- CC = FrameCounter;
-
- if (CC < Cc || CC > Cc + 2)
- {
- 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);
- }
-}
-
-static void GameActions_MM_Ext(struct MouseActionInfo action, boolean warp_mode)
+static void GameActions_MM_Ext(void)
{
int element;
int x, y, i;
for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
{
- element = Feld[x][y];
+ element = Tile[x][y];
if (!IS_MOVING(x, y) && CAN_MOVE(element))
StartMoving_MM(x, y);
else if (IS_MOVING(x, y))
ContinueMoving_MM(x, y);
else if (IS_EXPLODING(element))
- Explode_MM(x, y, Frame[x][y], EX_NORMAL);
+ Explode_MM(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
else if (element == EL_EXIT_OPENING)
OpenExit(x, y);
else if (element == EL_GRAY_BALL_OPENING)
- OpenSurpriseBall(x, y);
- else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
+ OpenGrayBall(x, y);
+ else if (IS_ENVELOPE_OPENING(element))
+ OpenEnvelope(x, y);
+ else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE_BASE)
MeltIce(x, y);
- else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
+ else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA_BASE)
GrowAmoeba(x, y);
+ else if (IS_MIRROR(element) ||
+ IS_MIRROR_FIXED(element) ||
+ element == EL_PRISM)
+ DrawFieldTwinkle(x, y);
+ else if (element == EL_GRAY_BALL_ACTIVE ||
+ element == EL_BOMB_ACTIVE ||
+ element == EL_MINE_ACTIVE)
+ DrawFieldAnimated_MM(x, y);
+ else if (!IS_BLOCKED(x, y))
+ DrawFieldAnimatedIfNeeded_MM(x, y);
}
AutoRotateMirrors();
#if 1
- /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
+ // !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!!
- /* redraw after Explode_MM() ... */
+ // redraw after Explode_MM() ...
if (laser.redraw)
DrawLaser(0, DL_LASER_ENABLED);
laser.redraw = FALSE;
CT = FrameCounter;
- if (game_mm.num_pacman && FrameReached(&pacman_delay, PACMAN_MOVE_DELAY))
+ if (game_mm.num_pacman && FrameReached(&pacman_delay))
{
MovePacMen();
}
}
- if (FrameReached(&energy_delay, ENERGY_DELAY))
- {
- if (game_mm.energy_left > 0)
- {
- game_mm.energy_left--;
-
-#if 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);
-#endif
- redraw_mask |= REDRAW_DOOR_1;
- }
- else if (setup.time_limit && !game_mm.game_over)
- {
- 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);
- }
+ // skip all following game actions if game is over
+ if (game_mm.game_over)
+ return;
- StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
-#if 0
- FadeMusic();
-#endif
+ if (game_mm.energy_left == 0 && !game.no_level_time_limit && game.time_limit)
+ {
+ FadeOutLaser();
- DrawLaser(0, DL_LASER_DISABLED);
- game_mm.game_over = TRUE;
- game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
+ GameOver_MM(GAME_OVER_NO_ENERGY);
- SetTileCursorActive(FALSE);
+ return;
+ }
-#if 0
- if (Request("Out of magic energy ! Play it again ?",
- REQ_ASK | REQ_STAY_CLOSED))
- {
- InitGame();
- }
- else
- {
- game_status = MAINMENU;
- DrawMainMenu();
- }
-#endif
+ if (FrameReached(&energy_delay))
+ {
+ if (game_mm.energy_left > 0)
+ game_mm.energy_left--;
- return;
- }
+ // when out of energy, wait another frame to play "out of time" sound
}
element = laser.dest_element;
#if 0
- if (element != Feld[ELX][ELY])
+ if (element != Tile[ELX][ELY])
{
- printf("element == %d, Feld[ELX][ELY] == %d\n",
- element, Feld[ELX][ELY]);
+ Debug("game:mm:GameActions_MM_Ext", "element == %d, Tile[ELX][ELY] == %d",
+ element, Tile[ELX][ELY]);
}
#endif
if (!laser.overloaded && laser.overload_value == 0 &&
element != EL_BOMB &&
+ element != EL_BOMB_ACTIVE &&
element != EL_MINE &&
- element != EL_BALL_GRAY &&
+ element != EL_MINE_ACTIVE &&
+ element != EL_GRAY_BALL &&
+ element != EL_GRAY_BALL_ACTIVE &&
element != EL_BLOCK_STONE &&
element != EL_BLOCK_WOOD &&
element != EL_FUSE_ON &&
!IS_WALL_AMOEBA(element))
return;
+ overload_delay.value = HEALTH_DELAY(laser.overloaded);
+
if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
(!laser.overloaded && laser.overload_value > 0)) &&
- FrameReached(&overload_delay, HEALTH_DELAY(laser.overloaded)))
+ FrameReached(&overload_delay))
{
if (laser.overloaded)
laser.overload_value++;
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));
+ SetLaserColor(0xFF);
DrawLaser(0, DL_LASER_ENABLED);
-#if 0
- BackToFront();
-#endif
}
if (!laser.overloaded)
else
PlaySound_MM(SND_MM_GAME_HEALTH_CHARGING);
- if (laser.overloaded)
- {
-#if 0
- 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);
-#endif
- redraw_mask |= REDRAW_DOOR_1;
- }
- else
- {
-#if 0
- 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);
-#endif
- redraw_mask |= REDRAW_DOOR_1;
- }
-
if (laser.overload_value == MAX_LASER_OVERLOAD)
{
- int i;
+ UpdateAndDisplayGameControlValues();
- for (i = 15; i >= 0; i--)
- {
-#if 0
- SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
-#endif
+ FadeOutLaser();
- 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;
-
- SetTileCursorActive(FALSE);
-
-#if 0
- if (Request("Magic spell hit Mc Duffin ! Play it again ?",
- REQ_ASK | REQ_STAY_CLOSED))
- {
- InitGame();
- }
- else
- {
- game_status = MAINMENU;
- DrawMainMenu();
- }
-#endif
+ GameOver_MM(GAME_OVER_OVERLOADED);
return;
}
if (game_mm.cheat_no_explosion)
return;
-#if 0
- laser.num_damages--;
- DrawLaser(0, DL_LASER_DISABLED);
- laser.num_edges = 0;
-#endif
-
Bang_MM(ELX, ELY);
laser.dest_element = EL_EXPLODING_OPAQUE;
-#if 0
- Bang_MM(ELX, ELY);
- laser.num_damages--;
- DrawLaser(0, DL_LASER_DISABLED);
-
- laser.num_edges = 0;
- Bang_MM(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;
}
DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
}
- if (element == EL_BALL_GRAY && CT > native_mm_level.time_ball)
+ if (element == EL_GRAY_BALL && CT > native_mm_level.time_ball)
{
- static int new_elements[] =
+ if (!Store2[ELX][ELY]) // check if content element not yet determined
{
- 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();
+ int last_anim_random_frame = gfx.anim_random_frame;
+ int element_pos;
- return;
+ if (native_mm_level.ball_choice_mode == ANIM_RANDOM)
+ gfx.anim_random_frame = RND(native_mm_level.num_ball_contents);
-#if 0
- int graphic;
+ element_pos = getAnimationFrame(native_mm_level.num_ball_contents, 1,
+ native_mm_level.ball_choice_mode, 0,
+ game_mm.ball_choice_pos);
- switch (RND(5))
- {
- case 0:
- element = EL_MIRROR_START + RND(16);
- break;
- case 1:
- {
- int rnd = RND(3);
+ if (native_mm_level.ball_choice_mode == ANIM_RANDOM)
+ gfx.anim_random_frame = last_anim_random_frame;
- element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
- }
- break;
- default:
- {
- int rnd = RND(3);
+ game_mm.ball_choice_pos++;
- 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);
+ int new_element = native_mm_level.ball_content[element_pos];
+ int new_element_base = map_wall_to_base_element(new_element);
-#if 0
- 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);
-#endif
- MarkTileDirty(ELX, ELY);
- BackToFront();
-
- DrawLaser(0, DL_LASER_ENABLED);
+ if (IS_WALL(new_element_base))
+ {
+ // always use completely filled wall element
+ new_element = new_element_base | 0x000f;
+ }
+ else if (native_mm_level.rotate_ball_content &&
+ get_num_elements(new_element) > 1)
+ {
+ // randomly rotate newly created game element
+ new_element = get_rotated_element(new_element, RND(16));
+ }
- Delay(50);
+ Store[ELX][ELY] = new_element;
+ Store2[ELX][ELY] = TRUE;
}
- 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);
+ if (native_mm_level.explode_ball)
+ Bang_MM(ELX, ELY);
else
- DrawLaser(0, DL_LASER_DISABLED);
-#else
- DrawLaser(0, DL_LASER_DISABLED);
-#endif
-
- ScanLaser();
+ Tile[ELX][ELY] = EL_GRAY_BALL_OPENING;
- /*
- printf("TEST ELEMENT: %d\n", Feld[0][0]);
- */
-#endif
+ laser.dest_element = laser.dest_element_last = Tile[ELX][ELY];
return;
}
{
PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_SHRINKING);
- {
- 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);
-*/
+ Tile[ELX][ELY] = Tile[ELX][ELY] - EL_WALL_ICE_BASE + EL_WALL_CHANGING_BASE;
+ Store[ELX][ELY] = EL_WALL_ICE_BASE;
+ Store2[ELX][ELY] = laser.wall_mask;
- 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();
+ laser.dest_element = Tile[ELX][ELY];
return;
}
if (IS_WALL_AMOEBA(element) && CT > 60)
{
- int k1, k2, k3, dx, dy, de, dm;
- int element2 = Feld[ELX][ELY];
+ int k1, k2, k3;
+ int element2 = Tile[ELX][ELY];
if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
return;
{
if (laser.wall_mask & (1 << i))
{
- if (CheckLaserPixel(SX + ELX * TILEX + 14 + (i % 2) * 2,
- SY + ELY * TILEY + 31 * (i / 2)))
+ if (CheckLaserPixel(cSX + ELX * TILEX + 14 + (i % 2) * 2,
+ cSY + ELY * TILEY + 31 * (i / 2)))
break;
- if (CheckLaserPixel(SX + ELX * TILEX + 31 * (i % 2),
- SY + ELY * TILEY + 14 + (i / 2) * 2))
+ if (CheckLaserPixel(cSX + ELX * TILEX + 31 * (i % 2),
+ cSY + ELY * TILEY + 14 + (i / 2) * 2))
break;
}
}
{
if (laser.wall_mask & (1 << i))
{
- if (CheckLaserPixel(SX + ELX * TILEX + 31 * (i % 2),
- SY + ELY * TILEY + 31 * (i / 2)))
+ if (CheckLaserPixel(cSX + ELX * TILEX + 31 * (i % 2),
+ cSY + ELY * TILEY + 31 * (i / 2)))
break;
}
}
if (laser.num_beamers > 0 ||
k1 < 1 || k2 < 4 || k3 < 4 ||
- CheckLaserPixel(SX + ELX * TILEX + 14,
- SY + ELY * TILEY + 14))
+ CheckLaserPixel(cSX + ELX * TILEX + 14,
+ cSY + ELY * TILEY + 14))
{
laser.num_edges = r;
laser.num_damages = d;
DrawLaser(0, DL_LASER_DISABLED);
}
- Feld[ELX][ELY] = element | laser.wall_mask;
+ Tile[ELX][ELY] = element | laser.wall_mask;
- dx = ELX;
- dy = ELY;
- de = Feld[ELX][ELY];
- dm = laser.wall_mask;
+ int x = ELX, y = ELY;
+ int wall_mask = laser.wall_mask;
-#if 1
- {
- int x = ELX, y = ELY;
- int wall_mask = laser.wall_mask;
-
- ScanLaser();
- DrawLaser(0, DL_LASER_ENABLED);
-
- PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
-
- 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);
- PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
+ PlayLevelSound_MM(x, y, element, MM_ACTION_GROWING);
- for (i = 4; i >= 0; i--)
- {
- DrawWallsAnimation_MM(dx, dy, de, i, dm);
-
- BackToFront();
- Delay(20);
- }
-
- DrawLaser(0, DL_LASER_ENABLED);
+ Tile[x][y] = Tile[x][y] - EL_WALL_AMOEBA_BASE + EL_WALL_CHANGING_BASE;
+ Store[x][y] = EL_WALL_AMOEBA_BASE;
+ Store2[x][y] = wall_mask;
return;
}
x = ELX + Step[k * 4].x;
y = ELY + Step[k * 4].y;
- if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
+ if (!IN_LEV_FIELD(x, y) || Tile[x][y] != EL_EMPTY)
continue;
if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_PUSHING);
- Feld[ELX][ELY] = 0;
- Feld[x][y] = element;
+ Tile[ELX][ELY] = 0;
+ Tile[x][y] = element;
DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
DrawField_MM(x, y);
if (element == EL_FUEL_FULL && CT > 10)
{
- for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
+ int num_init_game_frames = INIT_GAME_ACTIONS_DELAY;
+ int start = num_init_game_frames * game_mm.energy_left / native_mm_level.time;
+
+ for (i = start; i <= num_init_game_frames; i++)
{
-#if 0
- 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);
-#endif
+ if (i == num_init_game_frames)
+ StopSound_MM(SND_MM_GAME_LEVELTIME_CHARGING);
+ else if (setup.sound_loops)
+ PlaySoundLoop_MM(SND_MM_GAME_LEVELTIME_CHARGING);
+ else
+ PlaySound_MM(SND_MM_GAME_LEVELTIME_CHARGING);
- redraw_mask |= REDRAW_DOOR_1;
- BackToFront();
+ game_mm.energy_left = native_mm_level.time * i / num_init_game_frames;
- Delay(20);
+ UpdateAndDisplayGameControlValues();
+
+ BackToFront_MM();
}
- game_mm.energy_left = MAX_LASER_ENERGY;
- Feld[ELX][ELY] = EL_FUEL_EMPTY;
+ Tile[ELX][ELY] = laser.dest_element = EL_FUEL_EMPTY;
+
DrawField_MM(ELX, ELY);
DrawLaser(0, DL_LASER_ENABLED);
return;
}
-
- return;
}
-void GameActions_MM(struct MouseActionInfo action, boolean warp_mode)
+void GameActions_MM(struct MouseActionInfo action)
{
boolean element_clicked = ClickElement(action.lx, action.ly, action.button);
boolean button_released = (action.button == MB_RELEASED);
- GameActions_MM_Ext(action, warp_mode);
+ GameActions_MM_Ext();
CheckSingleStepMode_MM(element_clicked, button_released);
}
-void MovePacMen()
+static void MovePacMen(void)
{
int mx, my, ox, oy, nx, ny;
int element;
oy = game_mm.pacman[pacman_nr].y;
nx = ox + mx;
ny = oy + my;
- element = Feld[nx][ny];
+ element = Tile[nx][ny];
if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
continue;
if (ObjHit(nx, ny, HIT_POS_CENTER))
continue;
- Feld[ox][oy] = EL_EMPTY;
- Feld[nx][ny] =
+ Tile[ox][oy] = EL_EMPTY;
+ Tile[nx][ny] =
EL_PACMAN_RIGHT - 1 +
(game_mm.pacman[pacman_nr].dir - 1 +
(game_mm.pacman[pacman_nr].dir % 2) * 2);
if (element != EL_EMPTY)
{
- int graphic = el2gfx(Feld[nx][ny]);
+ int graphic = el2gfx(Tile[nx][ny]);
Bitmap *bitmap;
int src_x, src_y;
int i;
getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
CT = FrameCounter;
- ox = SX + ox * TILEX;
- oy = SY + oy * TILEY;
+ ox = cSX + ox * TILEX;
+ oy = cSY + oy * TILEY;
for (i = 1; i < 33; i += 2)
BlitBitmap(bitmap, window,
}
DrawField_MM(nx, ny);
- BackToFront();
+ BackToFront_MM();
if (!laser.fuse_off)
{
}
}
-void GameWon_MM()
-{
- 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_MM(native_mm_level.score[SC_ZEITBONUS]);
- */
-
- RaiseScore_MM(5);
-
- game_mm.energy_left--;
- if (game_mm.energy_left >= 0)
- {
-#if 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);
-#endif
- 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_MM(native_mm_level.score[SC_TIME_BONUS]);
- 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_MM()) >= 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_MM()
-{
- int k, l;
- int position = -1;
-
- // LoadScore(level_nr);
-
- if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
- game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
- return -1;
-
- for (k = 0; k < MAX_SCORE_ENTRIES; k++)
- {
- if (game_mm.score > highscore[k].Score)
- {
- /* player has made it to the hall of fame */
-
- if (k < MAX_SCORE_ENTRIES - 1)
- {
- int m = MAX_SCORE_ENTRIES - 1;
-
-#ifdef ONE_PER_NAME
- for (l = k; l < MAX_SCORE_ENTRIES; l++)
- if (!strcmp(setup.player_name, highscore[l].Name))
- m = l;
- if (m == k) /* player's new highscore overwrites his old one */
- goto put_into_list;
-#endif
-
- for (l = m; l>k; l--)
- {
- strcpy(highscore[l].Name, highscore[l - 1].Name);
- highscore[l].Score = highscore[l - 1].Score;
- }
- }
-
-#ifdef ONE_PER_NAME
- put_into_list:
-#endif
- strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
- highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
- highscore[k].Score = game_mm.score;
- position = k;
-
- break;
- }
-
-#ifdef ONE_PER_NAME
- else if (!strncmp(setup.player_name, highscore[k].Name,
- MAX_PLAYER_NAME_LEN))
- break; /* player already there with a higher score */
-#endif
-
- }
-
- // if (position >= 0)
- // SaveScore(level_nr);
-
- return position;
-}
-
static void InitMovingField_MM(int x, int y, int direction)
{
int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
MovDir[x][y] = direction;
MovDir[newx][newy] = direction;
- if (Feld[newx][newy] == EL_EMPTY)
- Feld[newx][newy] = EL_BLOCKED;
-}
-
-static void Moving2Blocked_MM(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;
-}
-
-static void Blocked2Moving_MM(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;
+ if (Tile[newx][newy] == EL_EMPTY)
+ Tile[newx][newy] = EL_BLOCKED;
}
static int MovingOrBlocked2Element_MM(int x, int y)
{
- int element = Feld[x][y];
+ int element = Tile[x][y];
if (element == EL_BLOCKED)
{
int oldx, oldy;
- Blocked2Moving_MM(x, y, &oldx, &oldy);
+ Blocked2Moving(x, y, &oldx, &oldy);
- return Feld[oldx][oldy];
+ return Tile[oldx][oldy];
}
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
-
static void RemoveMovingField_MM(int x, int y)
{
int oldx = x, oldy = y, newx = x, newy = y;
- if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
+ if (Tile[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
return;
if (IS_MOVING(x, y))
{
- Moving2Blocked_MM(x, y, &newx, &newy);
- if (Feld[newx][newy] != EL_BLOCKED)
+ Moving2Blocked(x, y, &newx, &newy);
+ if (Tile[newx][newy] != EL_BLOCKED)
return;
}
- else if (Feld[x][y] == EL_BLOCKED)
+ else if (Tile[x][y] == EL_BLOCKED)
{
- Blocked2Moving_MM(x, y, &oldx, &oldy);
+ Blocked2Moving(x, y, &oldx, &oldy);
if (!IS_MOVING(oldx, oldy))
return;
}
- Feld[oldx][oldy] = EL_EMPTY;
- Feld[newx][newy] = EL_EMPTY;
+ Tile[oldx][oldy] = EL_EMPTY;
+ Tile[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(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);
-}
-
static void RaiseScore_MM(int value)
{
game_mm.score += value;
-
-#if 0
- DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
- FONT_TEXT_2);
-#endif
}
void RaiseScoreElement_MM(int element)
{
- switch(element)
+ switch (element)
{
case EL_PACMAN:
case EL_PACMAN_RIGHT:
}
-/* ------------------------------------------------------------------------- */
-/* Mirror Magic game engine snapshot handling functions */
-/* ------------------------------------------------------------------------- */
+// ----------------------------------------------------------------------------
+// Mirror Magic game engine snapshot handling functions
+// ----------------------------------------------------------------------------
-void SaveEngineSnapshotValues_MM(ListNode **buffers)
+void SaveEngineSnapshotValues_MM(void)
{
int x, y;
engine_snapshot_mm.Hit[x][y] = Hit[x][y];
engine_snapshot_mm.Box[x][y] = Box[x][y];
engine_snapshot_mm.Angle[x][y] = Angle[x][y];
- engine_snapshot_mm.Frame[x][y] = Frame[x][y];
}
}
engine_snapshot_mm.overload_delay = overload_delay;
}
-void LoadEngineSnapshotValues_MM()
+void LoadEngineSnapshotValues_MM(void)
{
int x, y;
- /* stored engine snapshot buffers already restored at this point */
+ // stored engine snapshot buffers already restored at this point
game_mm = engine_snapshot_mm.game_mm;
laser = engine_snapshot_mm.laser;
Hit[x][y] = engine_snapshot_mm.Hit[x][y];
Box[x][y] = engine_snapshot_mm.Box[x][y];
Angle[x][y] = engine_snapshot_mm.Angle[x][y];
- Frame[x][y] = engine_snapshot_mm.Frame[x][y];
}
}
energy_delay = engine_snapshot_mm.energy_delay;
overload_delay = engine_snapshot_mm.overload_delay;
- RedrawPlayfield_MM(TRUE);
+ RedrawPlayfield_MM();
}
static int getAngleFromTouchDelta(int dx, int dy, int base)
int getButtonFromTouchPosition(int x, int y, int dst_mx, int dst_my)
{
// calculate start (source) position to be at the middle of the tile
- int src_mx = SX + x * TILESIZE_VAR + TILESIZE_VAR / 2;
- int src_my = SY + y * TILESIZE_VAR + TILESIZE_VAR / 2;
+ int src_mx = cSX + x * TILESIZE_VAR + TILESIZE_VAR / 2;
+ int src_my = cSY + y * TILESIZE_VAR + TILESIZE_VAR / 2;
int dx = dst_mx - src_mx;
int dy = dst_my - src_my;
int element;
if (!IN_LEV_FIELD(x, y))
return 0;
- element = Feld[x][y];
+ element = Tile[x][y];
if (!IS_MCDUFFIN(element) &&
!IS_MIRROR(element) &&