rnd-20040112-3-src
[rocksndiamonds.git] / src / game.c
index 7ce333bc84e20af17e4d4e5226960c82a694203e..f29584b4be06e36a1a0807689abb5dc35555826b 100644 (file)
@@ -787,6 +787,108 @@ void DrawGameDoorValues()
           int2str(TimeLeft, 3), FONT_TEXT_2);
 }
 
+#if 1
+
+static void resolve_group_element(int group_element, int recursion_depth)
+{
+  static struct ElementGroupInfo *group;
+  struct ElementGroupInfo *actual_group = element_info[group_element].group;
+  int i;
+
+  if (recursion_depth > NUM_GROUP_ELEMENTS)    /* recursion too deep */
+  {
+    Error(ERR_WARN, "recursion too deep when resolving group element %d",
+         group_element - EL_GROUP_START + 1);
+
+    /* replace element which caused too deep recursion by question mark */
+    group->element_resolved[group->num_elements_resolved++] = EL_CHAR_QUESTION;
+
+    return;
+  }
+
+  if (recursion_depth == 0)                    /* initialization */
+  {
+    group = element_info[group_element].group;
+    group->num_elements_resolved = 0;
+  }
+
+  for (i = 0; i < actual_group->num_elements; i++)
+  {
+    int element = actual_group->element[i];
+
+    if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
+      break;
+
+    if (IS_GROUP_ELEMENT(element))
+      resolve_group_element(element, recursion_depth + 1);
+    else
+      group->element_resolved[group->num_elements_resolved++] = element;
+  }
+
+#if 1
+  if (recursion_depth == 0 && group_element <= EL_GROUP_4)
+  {
+    printf("::: group %d: %d resolved elements\n",
+          group_element - EL_GROUP_START, group->num_elements_resolved);
+    for (i = 0; i < group->num_elements_resolved; i++)
+      printf("::: - %d ['%s']\n", group->element_resolved[i],
+            element_info[group->element_resolved[i]].token_name);
+  }
+#endif
+}
+
+#else
+
+static void resolve_group_element(int group_element, int recursion_depth)
+{
+  static short element_list_count[NUM_FILE_ELEMENTS];
+  struct ElementGroupInfo *group = element_info[group_element].group;
+  int i, j;
+
+  if (group == NULL)
+    return;
+
+  if (recursion_depth > NUM_GROUP_ELEMENTS)    /* recursion too deep */
+    return;
+
+  if (recursion_depth == 0)                    /* initialization */
+    for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+      element_list_count[i] = 0;
+
+  for (i = 0; i < group->num_elements; i++)
+  {
+    int element = group->element[i];
+
+    if (IS_GROUP_ELEMENT(element))
+      resolve_group_element(element, recursion_depth + 1);
+    else if (element < NUM_FILE_ELEMENTS)
+      element_list_count[group->element[i]]++;
+  }
+
+  if (recursion_depth == 0)                    /* finalization */
+  {
+    group->num_elements_resolved = 0;
+
+    for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+      for (j = 0; j < element_list_count[i]; j++)
+       if (group->num_elements_resolved < NUM_FILE_ELEMENTS)
+         group->element_resolved[group->num_elements_resolved++] = i;
+
+#if 1
+    if (group_element <= EL_GROUP_8)
+    {
+      printf("::: group %d: %d resolved elements\n",
+            group_element - EL_GROUP_START, group->num_elements_resolved);
+      for (i = 0; i < group->num_elements_resolved; i++)
+       printf("::: - %d ['%s']\n", group->element_resolved[i],
+              element_info[group->element_resolved[i]].token_name);
+    }
+#endif
+  }
+}
+
+#endif
+
 
 /*
   =============================================================================
@@ -815,6 +917,11 @@ static void InitGameEngine()
   printf("       => game.engine_version == %06d\n", game.engine_version);
 #endif
 
+  /* ---------- recursively resolve group elements ------------------------- */
+
+  for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+    resolve_group_element(EL_GROUP_START + i, 0);
+
   /* ---------- initialize player's initial move delay --------------------- */
 
   /* dynamically adjust player properties according to game engine version */
@@ -1076,6 +1183,7 @@ void InitGame()
     player->is_collecting = FALSE;
     player->is_pushing = FALSE;
     player->is_switching = FALSE;
+    player->is_dropping = FALSE;
 
     player->is_bored = FALSE;
     player->is_sleeping = FALSE;
@@ -1132,6 +1240,8 @@ void InitGame()
     player->move_delay       = game.initial_move_delay;
     player->move_delay_value = game.initial_move_delay_value;
 
+    player->move_delay_reset_counter = 0;
+
     player->push_delay = 0;
     player->push_delay_value = game.initial_push_delay_value;
 
@@ -3072,6 +3182,10 @@ void Impact(int x, int y)
                                         MovDir[x][y + 1] != MV_DOWN ||
                                         MovPos[x][y + 1] <= TILEY / 2));
 
+#if 0
+    object_hit = !IS_FREE(x, y + 1);
+#endif
+
     /* do not smash moving elements that left the smashed field in time */
     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
        ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
@@ -3845,7 +3959,8 @@ inline static void TurnRoundExt(int x, int y)
       MovDir[x][y] = old_move_dir;
     }
   }
-  else if (move_pattern == MV_WHEN_PUSHED)
+  else if (move_pattern == MV_WHEN_PUSHED ||
+          move_pattern == MV_WHEN_DROPPED)
   {
     if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
       MovDir[x][y] = MV_NO_MOVING;
@@ -4164,6 +4279,11 @@ void StartMoving(int x, int y)
         element; also, the case of the player being the element to smash was
         simply not covered here... :-/ ) */
 
+#if 0
+      WasJustMoving[x][y] = 0;
+      WasJustFalling[x][y] = 0;
+#endif
+
       Impact(x, y);
     }
     else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
@@ -4294,10 +4414,25 @@ void StartMoving(int x, int y)
             HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
 #endif
 
+#if 1
+      WasJustMoving[x][y] = 0;
+#endif
+
       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
 
+#if 0
+      if (Feld[x][y] != element)       /* element has changed */
+      {
+       element = Feld[x][y];
+       move_pattern = element_info[element].move_pattern;
+
+       if (!CAN_MOVE(element))
+         return;
+      }
+#else
       if (Feld[x][y] != element)       /* element has changed */
        return;
+#endif
     }
 #endif
 
@@ -6142,7 +6277,11 @@ static void ChangeElement(int x, int y, int page)
       ChangePage[x][y] = -1;
     }
 
+#if 0
+    if (IS_MOVING(x, y) && !change->explode)
+#else
     if (IS_MOVING(x, y))               /* never change a running system ;-) */
+#endif
     {
       ChangeDelay[x][y] = 1;           /* try change after next move step */
       ChangePage[x][y] = page;         /* remember page to use for change */
@@ -6452,6 +6591,11 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action)
   if (!player->active || tape.pausing)
     return 0;
 
+#if 0
+  printf("::: [%d %d %d %d] [%d %d]\n",
+        left, right, up, down, button1, button2);
+#endif
+
   if (player_action)
   {
 #if 0
@@ -6503,6 +6647,8 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action)
     if (player->MovPos == 0)   /* needed for tape.playing */
       player->is_moving = FALSE;
 
+    player->is_dropping = FALSE;
+
     return 0;
   }
 
@@ -7409,8 +7555,27 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
   int old_jx = jx, old_jy = jy;
   int moved = MF_NO_ACTION;
 
+#if 1
+  if (!player->active)
+    return FALSE;
+
+  if (!dx && !dy)
+  {
+    if (player->MovPos == 0)
+    {
+      player->is_moving = FALSE;
+      player->is_digging = FALSE;
+      player->is_collecting = FALSE;
+      player->is_snapping = FALSE;
+      player->is_pushing = FALSE;
+    }
+
+    return FALSE;
+  }
+#else
   if (!player->active || (!dx && !dy))
     return FALSE;
+#endif
 
 #if 0
   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
@@ -7565,6 +7730,8 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     player->is_switching = FALSE;
 #endif
 
+    player->is_dropping = FALSE;
+
 
 #if 1
     {
@@ -7666,6 +7833,21 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
 
   if (player->MovPos == 0)     /* player reached destination field */
   {
+#if 1
+    if (player->move_delay_reset_counter > 0)
+    {
+      player->move_delay_reset_counter--;
+
+      if (player->move_delay_reset_counter == 0)
+      {
+       /* continue with normal speed after quickly moving through gate */
+       HALVE_PLAYER_SPEED(player);
+
+       /* be able to make the next move without delay */
+       player->move_delay = 0;
+      }
+    }
+#else
     if (IS_PASSABLE(Feld[last_jx][last_jy]))
     {
       /* continue with normal speed after quickly moving through gate */
@@ -7674,6 +7856,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
       /* be able to make the next move without delay */
       player->move_delay = 0;
     }
+#endif
 
     player->last_jx = jx;
     player->last_jy = jy;
@@ -7915,10 +8098,22 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
   int hitx = x + dx, hity = y + dy;
   int hitting_element = Feld[x][y];
+#if 0
+  boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
+                       !IS_FREE(hitx, hity) &&
+                       (!IS_MOVING(hitx, hity) ||
+                        MovDir[hitx][hity] != direction ||
+                        ABS(MovPos[hitx][hity]) <= TILEY / 2));
+#endif
 
   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
     return;
 
+#if 0
+  if (IN_LEV_FIELD(hitx, hity) && !object_hit)
+    return;
+#endif
+
   CheckElementSideChange(x, y, hitting_element,
                         direction, CE_HITTING_SOMETHING, -1);
 
@@ -7936,11 +8131,13 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
     int hitting_side = direction;
     int touched_side = opposite_direction;
     int touched_element = MovingOrBlocked2Element(hitx, hity);
+#if 1
     boolean object_hit = (!IS_MOVING(hitx, hity) ||
                          MovDir[hitx][hity] != direction ||
                          ABS(MovPos[hitx][hity]) <= TILEY / 2);
 
     object_hit = TRUE;
+#endif
 
     if (object_hit)
     {
@@ -8441,7 +8638,18 @@ int DigField(struct PlayerInfo *player,
 
       /* automatically move to the next field with double speed */
       player->programmed_action = move_direction;
+#if 1
+      if (player->move_delay_reset_counter == 0)
+      {
+       player->move_delay_reset_counter = 2;   /* two double speed steps */
+
+       DOUBLE_PLAYER_SPEED(player);
+      }
+#else
+      player->move_delay_reset_counter = 2;
+
       DOUBLE_PLAYER_SPEED(player);
+#endif
 
       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
       break;
@@ -8547,7 +8755,18 @@ int DigField(struct PlayerInfo *player,
 
        /* automatically move to the next field with double speed */
        player->programmed_action = move_direction;
+#if 1
+       if (player->move_delay_reset_counter == 0)
+       {
+         player->move_delay_reset_counter = 2; /* two double speed steps */
+
+         DOUBLE_PLAYER_SPEED(player);
+       }
+#else
+       player->move_delay_reset_counter = 2;
+
        DOUBLE_PLAYER_SPEED(player);
+#endif
 
        PlayLevelSoundAction(x, y, ACTION_PASSING);
 
@@ -8993,18 +9212,30 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
 
   player->MovDir = snap_direction;
 
-  player->is_moving = FALSE;
-  player->is_digging = FALSE;
-  player->is_collecting = FALSE;
+#if 1
+  if (player->MovPos == 0)
+#endif
+  {
+    player->is_moving = FALSE;
+    player->is_digging = FALSE;
+    player->is_collecting = FALSE;
+  }
+
+  player->is_dropping = FALSE;
 
   if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
     return FALSE;
 
   player->is_snapping = TRUE;
 
-  player->is_moving = FALSE;
-  player->is_digging = FALSE;
-  player->is_collecting = FALSE;
+#if 1
+  if (player->MovPos == 0)
+#endif
+  {
+    player->is_moving = FALSE;
+    player->is_digging = FALSE;
+    player->is_collecting = FALSE;
+  }
 
   DrawLevelField(x, y);
   BackToFront();
@@ -9094,7 +9325,7 @@ boolean DropElement(struct PlayerInfo *player)
   new_element = Feld[jx][jy];
 
   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
-      element_info[new_element].move_pattern == MV_PROJECTILE)
+      element_info[new_element].move_pattern == MV_WHEN_DROPPED)
   {
     int move_stepsize = element_info[new_element].move_stepsize;
     int direction, dx, dy, nextx, nexty;
@@ -9138,6 +9369,7 @@ boolean DropElement(struct PlayerInfo *player)
 
 #endif
 
+  player->is_dropping = TRUE;
 
 
   return TRUE;