rnd-20080125-2-src
[rocksndiamonds.git] / src / game.c
index 441793cef025f261b769d8f6dab212a4b23825dc..9097cb1fe26ae68fe8df79475d4e42def0a9e2f1 100644 (file)
 
 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF          * 1)
 #define USE_FIX_IMPACT_COLLISION       (USE_NEW_STUFF          * 1)
+#define USE_FIX_CE_ACTION_WITH_PLAYER  (USE_NEW_STUFF          * 1)
 
 #define USE_GFX_RESET_WHEN_NOT_MOVING  (USE_NEW_STUFF          * 1)
 
-#define USE_DELAYED_GFX_REDRAW         (USE_NEW_STUFF          * 1)
+#define USE_DELAYED_GFX_REDRAW         (USE_NEW_STUFF          * 0)
 
 #if USE_DELAYED_GFX_REDRAW
 #define TEST_DrawLevelField(x, y)                              \
@@ -1125,6 +1126,7 @@ void TestIfBadThingRunsIntoPlayer(int, int, int);
 void TestIfFriendTouchesBadThing(int, int);
 void TestIfBadThingTouchesFriend(int, int);
 void TestIfBadThingTouchesOtherBadThing(int, int);
+void TestIfGoodThingGetsHitByBadThing(int, int, int);
 
 void KillPlayer(struct PlayerInfo *);
 void BuryPlayer(struct PlayerInfo *);
@@ -1720,6 +1722,7 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
       }
       else
       {
+       stored_player[0].initial_element = element;
        stored_player[0].use_murphy = TRUE;
 
        if (!level.use_artwork_element[0])
@@ -3716,10 +3719,11 @@ void InitGame()
     player->Frame = 0;
     player->StepFrame = 0;
 
-    player->use_murphy = FALSE;
+    player->initial_element = player->element_nr;
     player->artwork_element =
       (level.use_artwork_element[i] ? level.artwork_element[i] :
        player->element_nr);
+    player->use_murphy = FALSE;
 
     player->block_last_field = FALSE;  /* initialized in InitPlayerField() */
     player->block_delay_adjustment = 0;        /* initialized in InitPlayerField() */
@@ -4015,6 +4019,7 @@ void InitGame()
          some_player->present = FALSE;
          some_player->active = FALSE;
 
+         player->initial_element = some_player->initial_element;
          player->artwork_element = some_player->artwork_element;
 
          player->block_last_field       = some_player->block_last_field;
@@ -5136,7 +5141,10 @@ static void RemoveField(int x, int y)
   GfxElement[x][y] = EL_UNDEFINED;
   GfxAction[x][y] = ACTION_DEFAULT;
   GfxDir[x][y] = MV_NONE;
+#if 0
+  /* !!! this would prevent the removed tile from being redrawn !!! */
   GfxRedraw[x][y] = GFX_REDRAW_NONE;
+#endif
 }
 
 void RemoveMovingField(int x, int y)
@@ -5951,8 +5959,12 @@ void Bang(int x, int y)
   {
     struct PlayerInfo *player = PLAYERINFO(x, y);
 
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+    element = Feld[x][y] = player->initial_element;
+#else
     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
                            player->element_nr);
+#endif
 
     if (level.use_explosion_element[player->index_nr])
     {
@@ -6424,6 +6436,8 @@ static void RedrawAllInvisibleElementsForMagnifier()
                    element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
                    IS_EMC_GATE_GRAY(element) ?
                    element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
+                   IS_DC_GATE_GRAY(element) ?
+                   EL_DC_GATE_WHITE_GRAY_ACTIVE :
                    element);
       TEST_DrawLevelField(x, y);
     }
@@ -6436,6 +6450,8 @@ static void RedrawAllInvisibleElementsForMagnifier()
                    element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
                    IS_EMC_GATE_GRAY_ACTIVE(element) ?
                    element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
+                   IS_DC_GATE_GRAY_ACTIVE(element) ?
+                   EL_DC_GATE_WHITE_GRAY :
                    element);
       TEST_DrawLevelField(x, y);
     }
@@ -8935,6 +8951,11 @@ void ContinueMoving(int x, int y)
   else if (element == EL_PENGUIN)
     TestIfFriendTouchesBadThing(newx, newy);
 
+  if (DONT_GET_HIT_BY(element))
+  {
+    TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
+  }
+
   /* give the player one last chance (one more frame) to move away */
   if (CAN_FALL(element) && direction == MV_DOWN &&
       (last_line || (!IS_FREE(x, newy + 1) &&
@@ -12553,6 +12574,9 @@ void GameActions_RND()
        GfxRedraw[x][y] != GFX_REDRAW_NONE)
 #endif
     {
+      /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
+        !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
+
       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
        DrawLevelField(x, y);
 
@@ -12654,7 +12678,8 @@ static boolean AllPlayersInVisibleScreen()
 
 void ScrollLevel(int dx, int dy)
 {
-#if 1
+#if 0
+  /* (directly solved in BlitBitmap() now) */
   static Bitmap *bitmap_db_field2 = NULL;
   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
   int x, y;
@@ -12669,7 +12694,8 @@ void ScrollLevel(int dx, int dy)
     return;
 #endif
 
-#if 1
+#if 0
+  /* (directly solved in BlitBitmap() now) */
   if (bitmap_db_field2 == NULL)
     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
 
@@ -13312,8 +13338,13 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
                                          CE_PLAYER_ENTERS_X,
                                          player->index_bit, enter_side);
 
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+      CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
+                                       CE_MOVE_OF_X, move_direction);
+#else
       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
                                        CE_MOVE_OF_X, move_direction);
+#endif
     }
 
     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
@@ -13440,7 +13471,7 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
     if (!IN_LEV_FIELD(xx, yy))
       continue;
 
-    if (IS_PLAYER(x, y))
+    if (IS_PLAYER(x, y))               /* player found at center element */
     {
       struct PlayerInfo *player = PLAYERINFO(x, y);
 
@@ -13458,8 +13489,21 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
                                          CE_PLAYER_TOUCHES_X,
                                          player->index_bit, border_side);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+      {
+       /* use player element that is initially defined in the level playfield,
+          not the player element that corresponds to the runtime player number
+          (example: a level that contains EL_PLAYER_3 as the only player would
+          incorrectly give EL_PLAYER_1 for "player->element_nr") */
+       int player_element = PLAYERINFO(x, y)->initial_element;
+
+       CheckElementChangeBySide(xx, yy, border_element, player_element,
+                                CE_TOUCHING_X, border_side);
+      }
+#endif
     }
-    else if (IS_PLAYER(xx, yy))
+    else if (IS_PLAYER(xx, yy))                /* player found at border element */
     {
       struct PlayerInfo *player = PLAYERINFO(xx, yy);
 
@@ -13474,6 +13518,20 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
       CheckTriggeredElementChangeByPlayer(x, y, center_element,
                                          CE_PLAYER_TOUCHES_X,
                                          player->index_bit, center_side);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+      {
+       /* use player element that is initially defined in the level playfield,
+          not the player element that corresponds to the runtime player number
+          (example: a level that contains EL_PLAYER_3 as the only player would
+          incorrectly give EL_PLAYER_1 for "player->element_nr") */
+       int player_element = PLAYERINFO(xx, yy)->initial_element;
+
+       CheckElementChangeBySide(x, y, center_element, player_element,
+                                CE_TOUCHING_X, center_side);
+      }
+#endif
+
       break;
     }
   }
@@ -13550,6 +13608,8 @@ void TestIfElementTouchesCustomElement(int x, int y)
 
   for (i = 0; i < NUM_DIRECTIONS; i++)
   {
+    int xx = x + xy[i][0];
+    int yy = y + xy[i][1];
     int border_side = trigger_sides[i][1];
     int border_element = border_element_old[i];
 
@@ -13561,6 +13621,20 @@ void TestIfElementTouchesCustomElement(int x, int y)
       change_center_element =
        CheckElementChangeBySide(x, y, center_element, border_element,
                                 CE_TOUCHING_X, border_side);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+    if (IS_PLAYER(xx, yy))
+    {
+      /* use player element that is initially defined in the level playfield,
+        not the player element that corresponds to the runtime player number
+        (example: a level that contains EL_PLAYER_3 as the only player would
+        incorrectly give EL_PLAYER_1 for "player->element_nr") */
+      int player_element = PLAYERINFO(xx, yy)->initial_element;
+
+      CheckElementChangeBySide(x, y, center_element, player_element,
+                              CE_TOUCHING_X, border_side);
+    }
+#endif
   }
 }
 
@@ -13658,11 +13732,25 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
       CheckElementChangeBySide(x, y, hitting_element, touched_element,
                               CE_HITTING_X, touched_side);
 
-      CheckElementChangeBySide(hitx, hity, touched_element,
-                              hitting_element, CE_HIT_BY_X, hitting_side);
+      CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
+                              CE_HIT_BY_X, hitting_side);
 
       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
                               CE_HIT_BY_SOMETHING, opposite_direction);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+      if (IS_PLAYER(hitx, hity))
+      {
+       /* use player element that is initially defined in the level playfield,
+          not the player element that corresponds to the runtime player number
+          (example: a level that contains EL_PLAYER_3 as the only player would
+          incorrectly give EL_PLAYER_1 for "player->element_nr") */
+       int player_element = PLAYERINFO(hitx, hity)->initial_element;
+
+       CheckElementChangeBySide(x, y, hitting_element, player_element,
+                                CE_HITTING_X, touched_side);
+      }
+#endif
     }
   }
 
@@ -13835,6 +13923,7 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
 
     test_x = bad_x + test_xy[i][0];
     test_y = bad_y + test_xy[i][1];
+
     if (!IN_LEV_FIELD(test_x, test_y))
       continue;
 
@@ -13865,12 +13954,14 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
 
        kill_x = test_x;
        kill_y = test_y;
+
        break;
       }
       else if (test_element == EL_PENGUIN)
       {
        kill_x = test_x;
        kill_y = test_y;
+
        break;
       }
     }
@@ -13893,6 +13984,63 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
   }
 }
 
+void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
+{
+  int bad_element = Feld[bad_x][bad_y];
+  int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
+  int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
+  int test_x = bad_x + dx, test_y = bad_y + dy;
+  int test_move_dir, test_element;
+  int kill_x = -1, kill_y = -1;
+
+  if (!IN_LEV_FIELD(test_x, test_y))
+    return;
+
+  test_move_dir =
+    (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
+
+  test_element = Feld[test_x][test_y];
+
+  if (test_move_dir != bad_move_dir)
+  {
+    /* good thing can be player or penguin that does not move away */
+    if (IS_PLAYER(test_x, test_y))
+    {
+      struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
+
+      /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
+        player as being hit when he is moving towards the bad thing, because
+        the "get hit by" condition would be lost after the player stops) */
+      if (player->MovPos != 0 && player->MovDir == bad_move_dir)
+       return;         /* player moves away from bad thing */
+
+      kill_x = test_x;
+      kill_y = test_y;
+    }
+    else if (test_element == EL_PENGUIN)
+    {
+      kill_x = test_x;
+      kill_y = test_y;
+    }
+  }
+
+  if (kill_x != -1 || kill_y != -1)
+  {
+    if (IS_PLAYER(kill_x, kill_y))
+    {
+      struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
+
+      if (player->shield_deadly_time_left > 0 &&
+         !IS_INDESTRUCTIBLE(bad_element))
+       Bang(bad_x, bad_y);
+      else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
+       KillPlayer(player);
+    }
+    else
+      Bang(kill_x, kill_y);
+  }
+}
+
 void TestIfPlayerTouchesBadThing(int x, int y)
 {
   TestIfGoodThingHitsBadThing(x, y, MV_NONE);