pen_ray =
GetPixelFromRGB(window,
- (native_mm_level.laser_red ? color_max : color_up),
- (native_mm_level.laser_green ? color_down : color_min),
- (native_mm_level.laser_blue ? color_down : color_min));
+ (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)
}
}
-static void InitField(int x, int y)
+static void InitField(int x, int y, boolean init_game)
{
int element = Tile[x][y];
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;
}
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;
+ }
}
break;
(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.level_solved = FALSE;
game_mm.game_over = FALSE;
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;
Store[x][y] = Store2[x][y] = 0;
Stop[x][y] = FALSE;
- InitField(x, y);
+ InitField(x, y, TRUE);
}
}
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)
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;
return hit_mask;
}
+static void DeactivateLaserTargetElement(void)
+{
+ 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_BALL_GRAY);
+
+ if (Tile[x][y] == EL_BALL_GRAY)
+ MovDelay[x][y] = 0;
+
+ laser.dest_element_last = EL_EMPTY;
+ laser.dest_element_last_x = -1;
+ laser.dest_element_last_y = -1;
+ }
+}
+
void ScanLaser(void)
{
- int element;
+ 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
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;
hit_mask, LX, LY, ELX, ELY);
#endif
+ last_element = element;
+
element = Tile[ELX][ELY];
laser.dest_element = element;
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(hit_mask))
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
#endif
}
+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;
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)
{
Warn("DrawLaser: laser.num_edges - start_edge < 0");
// this is more precise: check if laser would go through the center
if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
{
+ 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;
return TRUE;
}
- if (element == EL_BOMB || element == EL_MINE)
+ if (element == EL_BOMB || element == EL_MINE || element == EL_BALL_GRAY)
{
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);
+
+ 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);
int delay = 2;
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, bitmap_db_field, cSX + x * TILEX, cSY + y * TILEY,
+ TILEX, TILEY, x * TILEX, y * TILEY);
+
+ DrawElement_MM(x, y, EL_BALL_GRAY);
+ }
+
MovDelay[x][y] = 50 * delay;
+ }
if (MovDelay[x][y]) // wait some time before next frame
{
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, gx + dx, gy + dy, 6, 6,
cSX + x * TILEX + dx, cSY + y * TILEY + dy);
+ laser.redraw = TRUE;
+
MarkTileDirty(x, y);
}
if (!MovDelay[x][y])
{
Tile[x][y] = Store[x][y];
- Store[x][y] = 0;
+ 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_FromLastMirror();
+ }
+ }
+}
+
+static void OpenEnvelope(int x, int y)
+{
+ int num_frames = 8; // seven frames plus final empty space
+
+ if (!MovDelay[x][y]) // next animation frame
+ MovDelay[x][y] = num_frames;
+
+ if (MovDelay[x][y]) // wait some time before next frame
+ {
+ int nr = ENVELOPE_OPENING_NR(Tile[x][y]);
+
+ MovDelay[x][y]--;
+
+ if (MovDelay[x][y] > 0 && IN_SCR_FIELD(x, y))
+ {
+ int graphic = el_act2gfx(EL_ENVELOPE_1 + nr, MM_ACTION_COLLECTING);
+ int frame = num_frames - MovDelay[x][y] - 1;
+
+ DrawGraphicAnimation_MM(x, y, graphic, frame);
+
+ laser.redraw = TRUE;
+ }
+
+ if (MovDelay[x][y] == 0)
+ {
+ Tile[x][y] = EL_EMPTY;
+
DrawField_MM(x, y);
ScanLaser();
+
+ ShowEnvelope_MM(nr);
}
}
}
if (!MovDelay[x][y])
{
- int i;
-
Tile[x][y] = real_element & (wall_mask ^ 0xFF);
Store[x][y] = Store2[x][y] = 0;
if (Tile[x][y] == EL_WALL_ICE)
Tile[x][y] = EL_EMPTY;
- for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
- if (laser.damage[i].is_mirror)
- break;
-
- if (i > 0)
- DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
- else
- DrawLaser(0, DL_LASER_DISABLED);
-
- ScanLaser();
+ ScanLaser_FromLastMirror();
}
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 (IS_BLOCKED(x, y))
+ if (!getGraphicInfo_NewFrame(x, y, graphic))
return;
DrawField_MM(x, y);
- if (IS_MIRROR(element) ||
- IS_MIRROR_FIXED(element) ||
- element == EL_PRISM)
+ laser.redraw = TRUE;
+}
+
+static void DrawFieldTwinkle(int x, int y)
+{
+ if (MovDelay[x][y] != 0) // wait some time before next frame
{
- if (MovDelay[x][y] != 0) // wait some time before next frame
- {
- MovDelay[x][y]--;
+ MovDelay[x][y]--;
- if (MovDelay[x][y] != 0)
- {
- int graphic = IMG_TWINKLE_WHITE;
- int frame = getGraphicAnimationFrame(graphic, 10 - MovDelay[x][y]);
+ DrawField_MM(x, y);
- DrawGraphicThruMask_MM(SCREENX(x), SCREENY(y), graphic, frame);
- }
+ 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;
+ laser.redraw = TRUE;
+ }
}
static void Explode_MM(int x, int y, int phase, int mode)
Tile[x][y] = center_element;
}
- if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
- Store[x][y] = center_element;
- else
- Store[x][y] = EL_EMPTY;
-
+ Store[x][y] = center_element;
Store2[x][y] = mode;
Tile[x][y] = EL_EXPLODING_OPAQUE;
- GfxElement[x][y] = center_element;
+
+ GfxElement[x][y] = (center_element == EL_BOMB_ACTIVE ? EL_BOMB :
+ IS_MCDUFFIN(center_element) ? EL_MCDUFFIN :
+ center_element);
MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
if (phase == last_phase)
{
- if (Store[x][y] == EL_BOMB)
+ if (Store[x][y] == EL_BOMB_ACTIVE)
{
DrawLaser(0, DL_LASER_DISABLED);
InitLaser();
Bang_MM(laser.start_edge.x, laser.start_edge.y);
- Store[x][y] = EL_EMPTY;
GameOver_MM(GAME_OVER_DELAYED);
}
else if (IS_MCDUFFIN(Store[x][y]))
{
- Store[x][y] = EL_EMPTY;
-
GameOver_MM(GAME_OVER_BOMB);
}
- Tile[x][y] = Store[x][y];
+ Tile[x][y] = EL_EMPTY;
+
Store[x][y] = Store2[x][y] = 0;
MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
- InitField(x, y);
+ InitField(x, y, FALSE);
DrawField_MM(x, y);
}
else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(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);
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);
new_button = FALSE;
IS_POLAR(Tile[x][y]) ||
IS_POLAR_CROSS(Tile[x][y])) && x == ELX && y == ELY)
{
- check = 0;
-
if (IS_BEAMER(Tile[x][y]))
{
#if 0
LX, LY, laser.beamer_edge, laser.beamer[1].num);
#endif
-#if 0
- laser.num_edges--;
-#endif
+ if (check == 1)
+ laser.num_edges--;
}
ScanLaser();
+
+ check = 0;
}
if (check == 2)
OpenExit(x, y);
else if (element == EL_GRAY_BALL_OPENING)
OpenSurpriseBall(x, y);
+ else if (IS_ENVELOPE_OPENING(element))
+ OpenEnvelope(x, y);
else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
MeltIce(x, y);
else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
GrowAmoeba(x, y);
- else
+ 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 (!laser.overloaded && laser.overload_value == 0 &&
element != EL_BOMB &&
+ element != EL_BOMB_ACTIVE &&
element != EL_MINE &&
+ element != EL_MINE_ACTIVE &&
element != EL_BALL_GRAY &&
+ element != EL_GRAY_BALL_ACTIVE &&
element != EL_BLOCK_STONE &&
element != EL_BLOCK_WOOD &&
element != EL_FUSE_ON &&
if (element == EL_BALL_GRAY && 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));
+ int last_anim_random_frame = gfx.anim_random_frame;
+ int element_pos;
+
+ if (native_mm_level.ball_choice_mode == ANIM_RANDOM)
+ gfx.anim_random_frame = RND(native_mm_level.num_ball_contents);
+
+ element_pos = getAnimationFrame(native_mm_level.num_ball_contents, 1,
+ native_mm_level.ball_choice_mode, 0,
+ game_mm.ball_choice_pos);
+
+ if (native_mm_level.ball_choice_mode == ANIM_RANDOM)
+ gfx.anim_random_frame = last_anim_random_frame;
+
+ game_mm.ball_choice_pos++;
+
+ int new_element = native_mm_level.ball_content[element_pos];
+ int new_element_unmapped = unmap_element(new_element);
+
+ if (IS_WALL(new_element_unmapped))
+ {
+ // always use completely filled wall element
+ new_element = new_element_unmapped | 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));
+ }
+
+ Store[ELX][ELY] = new_element;
+ Store2[ELX][ELY] = TRUE;
+ }
+
Tile[ELX][ELY] = EL_GRAY_BALL_OPENING;
- // !!! CHECK AGAIN: Laser on Polarizer !!!
- ScanLaser();
+ laser.dest_element_last = Tile[ELX][ELY];
return;
LY = laser.edge[laser.num_edges].y - cSY2;
*/
- 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();
+ ScanLaser_FromLastMirror();
return;
}