rnd-20030716-1-src
[rocksndiamonds.git] / src / game.c
index c52d16250d7ce2e7f3b412dd44e500cf0c94a3cf..2a63d3cea336b8d82d9a92d23525ad97cf5055a1 100644 (file)
@@ -1054,6 +1054,7 @@ void InitGame()
       JustStopped[x][y] = 0;
       Stop[x][y] = FALSE;
       Pushed[x][y] = FALSE;
+      Changing[x][y] = FALSE;
       ExplodePhase[x][y] = 0;
       ExplodeField[x][y] = EX_NO_EXPLOSION;
 
@@ -1880,6 +1881,17 @@ void Explode(int ex, int ey, int phase, int mode)
   {
     int center_element = Feld[ex][ey];
 
+#if 0
+    /* --- This is only really needed (and now handled) in "Impact()". --- */
+    /* do not explode moving elements that left the explode field in time */
+    if (game.engine_version >= RELEASE_IDENT(2,2,0,7) &&
+       center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
+      return;
+#endif
+
+    if (mode == EX_NORMAL || mode == EX_CENTER)
+      PlaySoundLevelAction(ex, ey, ACTION_EXPLODING);
+
     /* remove things displayed in background while burning dynamite */
     if (!IS_INDESTRUCTIBLE(Back[ex][ey]))
       Back[ex][ey] = 0;
@@ -1906,7 +1918,9 @@ void Explode(int ex, int ey, int phase, int mode)
       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
       {
        element = MovingOrBlocked2Element(x, y);
-       RemoveMovingField(x, y);
+
+       if (!IS_EXPLOSION_PROOF(element))
+         RemoveMovingField(x, y);
       }
 
 #if 1
@@ -2206,6 +2220,7 @@ void Bang(int x, int y)
                            player->element_nr);
   }
 
+#if 0
 #if 1
   PlaySoundLevelAction(x, y, ACTION_EXPLODING);
 #else
@@ -2214,6 +2229,7 @@ void Bang(int x, int y)
   else
     PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
 #endif
+#endif
 
 #if 0
   if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
@@ -2587,6 +2603,29 @@ static void ActivateTimegateSwitch(int x, int y)
   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
 }
 
+inline static int getElementMoveStepsize(int x, int y)
+{
+  int element = Feld[x][y];
+  int direction = MovDir[x][y];
+  int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
+  int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
+  int horiz_move = (dx != 0);
+  int sign = (horiz_move ? dx : dy);
+  int step = sign * element_info[element].move_stepsize;
+
+  /* special values for move stepsize for spring and things on conveyor belt */
+  if (horiz_move)
+  {
+    if (CAN_FALL(element) &&
+       y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
+      step = sign * MOVE_STEPSIZE_NORMAL / 2;
+    else if (element == EL_SPRING)
+      step = sign * MOVE_STEPSIZE_NORMAL * 2;
+  }
+
+  return step;
+}
+
 void Impact(int x, int y)
 {
   boolean lastline = (y == lev_fieldy-1);
@@ -2603,6 +2642,12 @@ void Impact(int x, int y)
     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
                                         MovDir[x][y + 1] != MV_DOWN ||
                                         MovPos[x][y + 1] <= TILEY / 2));
+
+    /* do not smash moving elements that left the smashed field in time */
+    if (game.engine_version >= RELEASE_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
+       ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
+      object_hit = FALSE;
+
     if (object_hit)
       smashed = MovingOrBlocked2Element(x, y + 1);
 
@@ -2725,6 +2770,13 @@ void Impact(int x, int y)
       Bang(x, y + 1);
       return;
     }
+#if 0
+    else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
+    {
+      Bang(x, y + 1);
+      return;
+    }
+#endif
     else if (CAN_SMASH_EVERYTHING(element))
     {
       if (IS_CLASSIC_ENEMY(smashed) ||
@@ -4021,22 +4073,9 @@ void ContinueMoving(int x, int y)
   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
   int newx = x + dx, newy = y + dy;
   int nextx = newx + dx, nexty = newy + dy;
-  int horiz_move = (dx != 0);
-  int sign = (horiz_move ? dx : dy);
-  int step = sign * element_info[element].move_stepsize;
   boolean pushed = Pushed[x][y];
 
-  /* special values for move stepsize for spring and things on conveyor belt */
-  if (horiz_move)
-  {
-    if (CAN_FALL(element) &&
-       y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
-      step = sign * MOVE_STEPSIZE_NORMAL / 2;
-    else if (element == EL_SPRING)
-      step = sign * MOVE_STEPSIZE_NORMAL * 2;
-  }
-
-  MovPos[x][y] += step;
+  MovPos[x][y] += getElementMoveStepsize(x, y);
 
   if (pushed)          /* special case: moving object pushed by player */
     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
@@ -5089,14 +5128,23 @@ static void ChangeElementNowExt(int x, int y, int target_element)
 
   if (CAN_BE_CRUMBLED(Feld[x][y]))
     DrawLevelFieldCrumbledSandNeighbours(x, y);
+
+  TestIfBadThingTouchesHero(x, y);
+  TestIfPlayerTouchesCustomElement(x, y);
+  TestIfElementTouchesCustomElement(x, y);
 }
 
 static void ChangeElementNow(int x, int y, int element)
 {
   struct ElementChangeInfo *change = &element_info[element].change;
 
+  /* prevent CheckTriggeredElementChange() from looping */
+  Changing[x][y] = TRUE;
+
   CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
 
+  Changing[x][y] = FALSE;
+
   if (change->explode)
   {
     Bang(x, y);
@@ -5138,6 +5186,9 @@ static void ChangeElementNow(int x, int y, int element)
 
       e = Feld[ex][ey];
 
+      if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
+       e = MovingOrBlocked2Element(ex, ey);
+
       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
 
       if ((change->power <= CP_NON_DESTRUCTIVE  && !IS_FREE(ex, ey)) ||
@@ -5163,6 +5214,9 @@ static void ChangeElementNow(int x, int y, int element)
        if (can_change[xx][yy] && (!change->use_random_change ||
                                   RND(change->random) == 0))
        {
+         if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
+           RemoveMovingField(ex, ey);
+
          ChangeElementNowExt(ex, ey, change->content[xx][yy]);
 
          /* for symmetry reasons, stop newly created border elements */
@@ -5282,6 +5336,9 @@ static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
       if (x == lx && y == ly)  /* do not change trigger element itself */
        continue;
 
+      if (Changing[x][y])      /* do not change just changing elements */
+       continue;
+
       if (Feld[x][y] == i)
       {
        ChangeDelay[x][y] = 1;