rnd-20040319-B-src
[rocksndiamonds.git] / src / game.c
index 2731d0ef9cd301832ead0a7081d430670ad2ce63..5e5fe3ea5af646bd18f97feac68c7080dbb91366 100644 (file)
 
 /* for Explode() */
 #define EX_PHASE_START         0
-#define EX_NO_EXPLOSION                0
-#define EX_NORMAL              1
-#define EX_CENTER              2
-#define EX_BORDER              3
+#define EX_TYPE_NONE           0
+#define EX_TYPE_NORMAL         (1 << 0)
+#define EX_TYPE_CENTER         (1 << 1)
+#define EX_TYPE_BORDER         (1 << 2)
+#define EX_TYPE_CROSS          (1 << 3)
+#define EX_TYPE_SINGLE_TILE    (EX_TYPE_CENTER | EX_TYPE_BORDER)
 
 /* special positions in the game control window (relative to control window) */
 #define XX_LEVEL               37
@@ -1201,6 +1203,10 @@ static void InitGameEngine()
     ei->change->post_change_function = ch_delay->post_change_function;
 
     ei->change_events |= CH_EVENT_BIT(CE_DELAY);
+
+#if 1
+    SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
+#endif
   }
 
 #if 1
@@ -1609,7 +1615,7 @@ void InitGame()
 
       ExplodePhase[x][y] = 0;
       ExplodeDelay[x][y] = 0;
-      ExplodeField[x][y] = EX_NO_EXPLOSION;
+      ExplodeField[x][y] = EX_TYPE_NONE;
 
       RunnerVisit[x][y] = 0;
       PlayerVisit[x][y] = 0;
@@ -1649,7 +1655,7 @@ void InitGame()
     if (!IS_CUSTOM_ELEMENT(i))
     {
       int num_phase = 8;
-      int delay = ((IS_SP_ELEMENT(i) &&
+      int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
                    game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
                   game.emulation == EMU_SUPAPLEX ? 3 : 2);
       int last_phase = (num_phase + 1) * delay;
@@ -2703,9 +2709,11 @@ void RelocatePlayer(int x, int y, int element_raw)
   else
   {
 #if 1
+#if 0
     int offset = (setup.scroll_delay ? 3 : 0);
     int jx = local_player->jx;
     int jy = local_player->jy;
+#endif
     int scroll_xx = -999, scroll_yy = -999;
 
     ScrollScreen(NULL, SCROLL_GO_ON);  /* scroll last frame to full tile */
@@ -2840,11 +2848,12 @@ void Explode(int ex, int ey, int phase, int mode)
     /* --- 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 >= VERSION_IDENT(2,2,0,7) &&
-       center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
+       center_element == EL_EMPTY &&
+       (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
       return;
 #endif
 
-    if (mode == EX_NORMAL || mode == EX_CENTER)
+    if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
       PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
 
     /* remove things displayed in background while burning dynamite */
@@ -2879,11 +2888,20 @@ void Explode(int ex, int ey, int phase, int mode)
       int element;
 
 #if 1
-      if (!IN_LEV_FIELD(x, y) || (mode != EX_NORMAL && (x != ex || y != ey)))
+#if 1
+      if (!IN_LEV_FIELD(x, y) ||
+         (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
+         (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
        continue;
 #else
       if (!IN_LEV_FIELD(x, y) ||
-         ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
+         (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
+       continue;
+#endif
+#else
+      if (!IN_LEV_FIELD(x, y) ||
+         ((mode != EX_TYPE_NORMAL ||
+           center_element == EL_AMOEBA_TO_DIAMOND) &&
           (x != ex || y != ey)))
        continue;
 #endif
@@ -3022,7 +3040,7 @@ void Explode(int ex, int ey, int phase, int mode)
        Store[x][y] = EL_EMPTY;
 
       if (x != ex || y != ey ||
-         center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
+         center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_TYPE_BORDER)
        Store2[x][y] = element;
 
 #if 0
@@ -3212,6 +3230,10 @@ void Explode(int ex, int ey, int phase, int mode)
   {
     int element;
 
+#if 0
+  printf("::: done: phase == %d\n", phase);
+#endif
+
 #if 0
     printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
 #endif
@@ -3345,7 +3367,7 @@ void DynaExplode(int ex, int ey)
     player->dynabombs_left++;
   }
 
-  Explode(ex, ey, EX_PHASE_START, EX_CENTER);
+  Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
 
   for (i = 0; i < NUM_DIRECTIONS; i++)
   {
@@ -3364,7 +3386,7 @@ void DynaExplode(int ex, int ey)
       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
        continue;
 
-      Explode(x, y, EX_PHASE_START, EX_BORDER);
+      Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
 
       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
       if (element != EL_EMPTY &&
@@ -3424,7 +3446,7 @@ void Bang(int x, int y)
     case EL_PACMAN:
     case EL_MOLE:
       RaiseScoreElement(element);
-      Explode(x, y, EX_PHASE_START, EX_NORMAL);
+      Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
       break;
     case EL_DYNABOMB_PLAYER_1_ACTIVE:
     case EL_DYNABOMB_PLAYER_2_ACTIVE:
@@ -3442,17 +3464,21 @@ void Bang(int x, int y)
     case EL_AMOEBA_TO_DIAMOND:
 #endif
       if (IS_PLAYER(x, y))
-       Explode(x, y, EX_PHASE_START, EX_NORMAL);
+       Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
       else
-       Explode(x, y, EX_PHASE_START, EX_CENTER);
+       Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
       break;
     default:
-      if (CAN_EXPLODE_DYNA(element))
+      if (CAN_EXPLODE_CROSS(element))
+#if 1
+       Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
+#else
        DynaExplode(x, y);
+#endif
       else if (CAN_EXPLODE_1X1(element))
-       Explode(x, y, EX_PHASE_START, EX_CENTER);
+       Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
       else
-       Explode(x, y, EX_PHASE_START, EX_NORMAL);
+       Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
       break;
   }
 
@@ -5810,11 +5836,14 @@ void ContinueMoving(int x, int y)
   MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
   MovDelay[newx][newy] = 0;
 
-  /* copy element change control values to new field */
-  ChangeDelay[newx][newy] = ChangeDelay[x][y];
-  ChangePage[newx][newy] = ChangePage[x][y];
-  Changed[newx][newy] = Changed[x][y];
-  ChangeEvent[newx][newy] = ChangeEvent[x][y];
+  if (CAN_CHANGE(element))
+  {
+    /* copy element change control values to new field */
+    ChangeDelay[newx][newy] = ChangeDelay[x][y];
+    ChangePage[newx][newy]  = ChangePage[x][y];
+    Changed[newx][newy]     = Changed[x][y];
+    ChangeEvent[newx][newy] = ChangeEvent[x][y];
+  }
 
   ChangeDelay[x][y] = 0;
   ChangePage[x][y] = -1;
@@ -7108,9 +7137,8 @@ static void ChangeElement(int x, int y, int page)
   struct ElementInfo *ei = &element_info[element];
   struct ElementChangeInfo *change = &ei->change_page[page];
 
-#if 0
 #ifdef DEBUG
-  if (!CAN_CHANGE(element))
+  if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
   {
     printf("\n\n");
     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
@@ -7119,8 +7147,18 @@ static void ChangeElement(int x, int y, int page)
     printf("\n\n");
   }
 #endif
+
+  /* this can happen with classic bombs on walkable, changing elements */
+  if (!CAN_CHANGE(element))
+  {
+#if 0
+    if (!CAN_CHANGE(Back[x][y]))       /* prevent permanent repetition */
+      ChangeDelay[x][y] = 0;
 #endif
 
+    return;
+  }
+
   if (ChangeDelay[x][y] == 0)          /* initialize element change */
   {
     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
@@ -7264,6 +7302,20 @@ static boolean CheckElementChangeExt(int x, int y,
     element = Feld[x][y];
   }
 
+#if 1
+  if (Feld[x][y] != element)   /* check if element has already changed */
+  {
+#if 0
+    printf("::: %d ('%s') != %d ('%s') [%d]\n",
+          Feld[x][y], element_info[Feld[x][y]].token_name,
+          element, element_info[element].token_name,
+          trigger_event);
+#endif
+
+    return FALSE;
+  }
+#endif
+
 #if 1
   if (trigger_page < 0)
   {
@@ -8065,7 +8117,7 @@ void GameActions()
       CheckDynamite(x, y);
 #if 0
     else if (element == EL_EXPLOSION && !game.explosions_delayed)
-      Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
+      Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
 #endif
     else if (element == EL_AMOEBA_GROWING)
       AmoebeWaechst(x, y);
@@ -8181,9 +8233,9 @@ void GameActions()
       if (ExplodeField[x][y])
        Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
       else if (element == EL_EXPLOSION)
-       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
+       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
 
-      ExplodeField[x][y] = EX_NO_EXPLOSION;
+      ExplodeField[x][y] = EX_TYPE_NONE;
     }
 
     game.explosions_delayed = TRUE;
@@ -8518,11 +8570,13 @@ static void CheckGravityMovement(struct PlayerInfo *player)
        !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
 
 #if 0
-    printf("::: checking gravity NOW [%d, %d, %d] [%d] ...\n",
+    printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
           player_can_fall_down,
           player_is_standing_on_valid_field,
           player_is_moving_to_valid_field,
-          (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1));
+          (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
+          player->effective_action,
+          player->can_fall_into_acid);
 #endif
 
     if (player_can_fall_down &&
@@ -8881,24 +8935,22 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
 
 #if 1
+      CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+                                       CE_OTHER_GETS_LEFT,
+                                       player->index_bit, leave_side);
+
       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
-      {
-       CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
-                                         CE_OTHER_GETS_LEFT,
-                                         player->index_bit, leave_side);
        CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
                                 CE_LEFT_BY_PLAYER,
                                 player->index_bit, leave_side);
-      }
+
+      CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
+                                       CE_OTHER_GETS_ENTERED,
+                                       player->index_bit, enter_side);
 
       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
-      {
-       CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
-                                         CE_OTHER_GETS_ENTERED,
-                                         player->index_bit, enter_side);
        CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
                                 player->index_bit, enter_side);
-      }
 #endif
 
     }
@@ -9034,24 +9086,22 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
       int old_jy = last_jy;
 
 #if 1
+      CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+                                       CE_OTHER_GETS_LEFT,
+                                       player->index_bit, leave_side);
+
       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
-      {
-       CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
-                                         CE_OTHER_GETS_LEFT,
-                                         player->index_bit, leave_side);
        CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
                                 CE_LEFT_BY_PLAYER,
                                 player->index_bit, leave_side);
-      }
+
+      CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
+                                       CE_OTHER_GETS_ENTERED,
+                                       player->index_bit, enter_side);
 
       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
-      {
-       CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
-                                         CE_OTHER_GETS_ENTERED,
-                                         player->index_bit, enter_side);
        CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
                                 player->index_bit, enter_side);
-      }
 #endif
 
     }
@@ -10195,7 +10245,7 @@ int DigField(struct PlayerInfo *player,
        PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
 
        CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
-                                         player->index_bit, CH_SIDE_ANY);
+                                         player->index_bit, dig_side);
 
 #if 1
        if (mode == DF_SNAP)
@@ -10295,7 +10345,7 @@ int DigField(struct PlayerInfo *player,
 
        CheckTriggeredElementChangePlayer(x, y, element,
                                          CE_OTHER_GETS_COLLECTED,
-                                         player->index_bit, CH_SIDE_ANY);
+                                         player->index_bit, dig_side);
 
 #if 1
        if (mode == DF_SNAP)
@@ -10660,7 +10710,16 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
 
 boolean DropElement(struct PlayerInfo *player)
 {
+  static int trigger_sides[4] =
+  {
+    CH_SIDE_LEFT,      /* dropping left  */
+    CH_SIDE_RIGHT,     /* dropping right */
+    CH_SIDE_TOP,       /* dropping up    */
+    CH_SIDE_BOTTOM,    /* dropping down  */
+  };
   int jx = player->jx, jy = player->jy;
+  int drop_direction = player->MovDir;
+  int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
   int old_element = Feld[jx][jy];
   int new_element = (player->inventory_size > 0 ?
                     player->inventory_element[player->inventory_size - 1] :
@@ -10739,9 +10798,9 @@ boolean DropElement(struct PlayerInfo *player)
 
     CheckTriggeredElementChangePlayer(jx, jy, new_element,
                                      CE_OTHER_GETS_DROPPED,
-                                     player->index_bit, CH_SIDE_ANY);
+                                     player->index_bit, drop_side);
     CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
-                            player->index_bit, CH_SIDE_ANY);
+                            player->index_bit, drop_side);
 
     TestIfElementTouchesCustomElement(jx, jy);
   }