rnd-20030513-1-src
[rocksndiamonds.git] / src / game.c
index 82aa0240f6011653f4032bc378cc66c92b4cce40..e259a0251a9b80531b01f47657abbd4bba2ccf58 100644 (file)
@@ -154,38 +154,144 @@ struct ChangingElementInfo
 
 static struct ChangingElementInfo changing_element_list[] =
 {
-  { EL_NUT_BREAKING,           EL_EMERALD,              6, NULL, NULL, NULL },
-  { EL_PEARL_BREAKING,         EL_EMPTY,                8, NULL, NULL, NULL },
-  { EL_EXIT_OPENING,           EL_EXIT_OPEN,           29, NULL, NULL, NULL },
-
-  { EL_SWITCHGATE_OPENING,     EL_SWITCHGATE_OPEN,     29, NULL, NULL, NULL },
-  { EL_SWITCHGATE_CLOSING,     EL_SWITCHGATE_CLOSED,   29, NULL, NULL, NULL },
-
-  { EL_TIMEGATE_OPENING,       EL_TIMEGATE_OPEN,       29, NULL, NULL, NULL },
-  { EL_TIMEGATE_CLOSING,       EL_TIMEGATE_CLOSED,     29, NULL, NULL, NULL },
-
-  { EL_ACID_SPLASH_LEFT,       EL_EMPTY,                8, NULL, NULL, NULL },
-  { EL_ACID_SPLASH_RIGHT,      EL_EMPTY,                8, NULL, NULL, NULL },
-
-  { EL_SP_BUGGY_BASE,          EL_SP_BUGGY_BASE_ACTIVATING, 0,
-    InitBuggyBase, NULL, NULL },
-  { EL_SP_BUGGY_BASE_ACTIVATING,EL_SP_BUGGY_BASE_ACTIVE, 0,
-    InitBuggyBase, NULL, NULL },
-  { EL_SP_BUGGY_BASE_ACTIVE,   EL_SP_BUGGY_BASE,        0,
-    InitBuggyBase, WarnBuggyBase, NULL },
-
-  { EL_TRAP,                   EL_TRAP_ACTIVE,          0,
-    InitTrap, NULL, ActivateTrap },
-  { EL_TRAP_ACTIVE,            EL_TRAP,                31,
-    NULL, ChangeActiveTrap, NULL },
-
-  { EL_ROBOT_WHEEL_ACTIVE,     EL_ROBOT_WHEEL,          0,
-    InitRobotWheel, RunRobotWheel, StopRobotWheel },
+  {
+    EL_NUT_BREAKING,
+    EL_EMERALD,
+    6,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_PEARL_BREAKING,
+    EL_EMPTY,
+    8,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_EXIT_OPENING,
+    EL_EXIT_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_SWITCHGATE_OPENING,
+    EL_SWITCHGATE_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_SWITCHGATE_CLOSING,
+    EL_SWITCHGATE_CLOSED,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_TIMEGATE_OPENING,
+    EL_TIMEGATE_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_TIMEGATE_CLOSING,
+    EL_TIMEGATE_CLOSED,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
 
-  { EL_TIMEGATE_SWITCH_ACTIVE, EL_TIMEGATE_SWITCH,      0,
-    InitTimegateWheel, RunTimegateWheel, NULL },
+  {
+    EL_ACID_SPLASH_LEFT,
+    EL_EMPTY,
+    8,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_ACID_SPLASH_RIGHT,
+    EL_EMPTY,
+    8,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_SP_BUGGY_BASE,
+    EL_SP_BUGGY_BASE_ACTIVATING,
+    0,
+    InitBuggyBase,
+    NULL,
+    NULL
+  },
+  {
+    EL_SP_BUGGY_BASE_ACTIVATING,
+    EL_SP_BUGGY_BASE_ACTIVE,
+    0,
+    InitBuggyBase,
+    NULL,
+    NULL
+  },
+  {
+    EL_SP_BUGGY_BASE_ACTIVE,
+    EL_SP_BUGGY_BASE,
+    0,
+    InitBuggyBase,
+    WarnBuggyBase,
+    NULL
+  },
+  {
+    EL_TRAP,
+    EL_TRAP_ACTIVE,
+    0,
+    InitTrap,
+    NULL,
+    ActivateTrap
+  },
+  {
+    EL_TRAP_ACTIVE,
+    EL_TRAP,
+    31,
+    NULL,
+    ChangeActiveTrap,
+    NULL
+  },
+  {
+    EL_ROBOT_WHEEL_ACTIVE,
+    EL_ROBOT_WHEEL,
+    0,
+    InitRobotWheel,
+    RunRobotWheel,
+    StopRobotWheel
+  },
+  {
+    EL_TIMEGATE_SWITCH_ACTIVE,
+    EL_TIMEGATE_SWITCH,
+    0,
+    InitTimegateWheel,
+    RunTimegateWheel,
+    NULL
+  },
 
-  { EL_UNDEFINED,              EL_UNDEFINED,           -1, NULL        }
+  {
+    EL_UNDEFINED,
+    EL_UNDEFINED,
+    -1,
+    NULL,
+    NULL,
+    NULL
+  }
 };
 
 static struct ChangingElementInfo changing_element[MAX_NUM_ELEMENTS];
@@ -561,6 +667,7 @@ static void InitGameEngine()
     changing_element[i].post_change_function = NULL;
   }
 
+  /* add changing elements from pre-defined list */
   i = 0;
   while (changing_element_list[i].base_element != EL_UNDEFINED)
   {
@@ -576,6 +683,30 @@ static void InitGameEngine()
 
     i++;
   }
+
+  /* add changing elements from custom element configuration */
+  for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    struct CustomElementChangeInfo *change = &level.custom_element[i].change;
+    int element = EL_CUSTOM_START + i;
+
+    /* only add custom elements that change after fixed/random frame delay */
+    if (!IS_CHANGEABLE(element) ||
+       (!HAS_CHANGE_EVENT(element, CE_DELAY_FIXED) &&
+        !HAS_CHANGE_EVENT(element, CE_DELAY_RANDOM)))
+      continue;
+
+    changing_element[element].base_element = element;
+    changing_element[element].next_element = change->successor;
+    changing_element[element].change_delay = 0;
+
+    if (HAS_CHANGE_EVENT(element, CE_DELAY_FIXED))
+      changing_element[element].change_delay +=
+       change->delay_fixed * change->delay_frames;
+
+    if (HAS_CHANGE_EVENT(element, CE_DELAY_RANDOM));
+    /* random frame delay added at runtime for each element individually */
+  }
 }
 
 
@@ -705,7 +836,7 @@ void InitGame()
 
   AllPlayersGone = FALSE;
 
-  game.yam_content_nr = 0;
+  game.yamyam_content_nr = 0;
   game.magic_wall_active = FALSE;
   game.magic_wall_time_left = 0;
   game.light_time_left = 0;
@@ -1523,7 +1654,7 @@ void Explode(int ex, int ey, int phase, int mode)
       Feld[ex][ey] = center_element;
     }
 
-    for (y=ey-1; y<=ey+1; y++) for(x=ex-1; x<=ex+1; x++)
+    for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++)
     {
       int element;
 
@@ -1603,10 +1734,14 @@ void Explode(int ex, int ey, int phase, int mode)
        Store[x][y] = EL_BD_DIAMOND;
       else if (center_element == EL_SP_ELECTRON)
        Store[x][y] = EL_SP_INFOTRON;
-      else if (center_element == EL_YAMYAM)
-       Store[x][y] = level.yam_content[game.yam_content_nr][x-ex+1][y-ey+1];
       else if (center_element == EL_AMOEBA_TO_DIAMOND)
        Store[x][y] = level.amoeba_content;
+      else if (center_element == EL_YAMYAM)
+       Store[x][y] =
+         level.yamyam_content[game.yamyam_content_nr][x - ex + 1][y - ey + 1];
+      else if (IS_CUSTOM_ELEMENT(center_element))
+       Store[x][y] =
+         CUSTOM_ELEMENT_INFO(center_element).content[x - ex + 1][y - ey + 1];
       else if (element == EL_WALL_EMERALD)
        Store[x][y] = EL_EMERALD;
       else if (element == EL_WALL_DIAMOND)
@@ -1648,7 +1783,8 @@ void Explode(int ex, int ey, int phase, int mode)
     }
 
     if (center_element == EL_YAMYAM)
-      game.yam_content_nr = (game.yam_content_nr + 1) % level.num_yam_contents;
+      game.yamyam_content_nr =
+       (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
 
     return;
   }
@@ -1678,7 +1814,7 @@ void Explode(int ex, int ey, int phase, int mode)
 
     if (IS_PLAYER(x, y))
       KillHeroUnlessProtected(x, y);
-    else if (IS_CAN_EXPLODE(element))
+    else if (CAN_EXPLODE(element))
     {
       Feld[x][y] = Store2[x][y];
       Store2[x][y] = 0;
@@ -1719,7 +1855,7 @@ void Explode(int ex, int ey, int phase, int mode)
     if (phase == delay)
       DrawLevelFieldCrumbledSand(x, y);
 
-    if (IS_WALKABLE_OVER(Back[x][y]))
+    if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
     {
       DrawLevelElement(x, y, Back[x][y]);
       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
@@ -3116,7 +3252,7 @@ void StartMoving(int x, int y)
          {
            int flamed = MovingOrBlocked2Element(xx, yy);
 
-           if (IS_ENEMY(flamed) || IS_CAN_EXPLODE(flamed))
+           if (IS_ENEMY(flamed) || CAN_EXPLODE(flamed))
              Bang(xx, yy);
            else
              RemoveMovingField(xx, yy);
@@ -4422,10 +4558,23 @@ static void ChangeElement(int x, int y)
 {
   int element = Feld[x][y];
 
+  if (IS_MOVING(x, y))                 /* never change a running system :-) */
+    return;
+
   if (MovDelay[x][y] == 0)             /* initialize element change */
   {
     MovDelay[x][y] = changing_element[element].change_delay + 1;
 
+    if (IS_CUSTOM_ELEMENT(element) &&
+       HAS_CHANGE_EVENT(element, CE_DELAY_RANDOM))
+    {
+      int i = element - EL_CUSTOM_START;
+      int max_random_delay = level.custom_element[i].change.delay_random;
+      int delay_frames = level.custom_element[i].change.delay_frames;
+
+      MovDelay[x][y] += RND(max_random_delay * delay_frames);
+    }
+
     ResetGfxAnimation(x, y);
     ResetRandomAnimationValue(x, y);
 
@@ -4450,6 +4599,11 @@ static void ChangeElement(int x, int y)
     ResetGfxAnimation(x, y);
     ResetRandomAnimationValue(x, y);
 
+#if 1
+    InitField(x, y, FALSE);
+    if (CAN_MOVE(element))
+      InitMovDir(x, y);
+#endif
     DrawLevelField(x, y);
 
     if (changing_element[element].post_change_function)
@@ -4653,6 +4807,15 @@ void GameActions()
     element = Feld[x][y];
     graphic = el2img(element);
 
+#if 0
+    if (element == -1)
+    {
+      printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
+
+      element = graphic = 0;
+    }
+#endif
+
     if (graphic_info[graphic].anim_global_sync)
       GfxFrame[x][y] = FrameCounter;
 
@@ -4729,13 +4892,21 @@ void GameActions()
       MauerAbleger(x, y);
     else if (element == EL_FLAMES)
       CheckForDragon(x, y);
+#if 0
     else if (IS_AUTO_CHANGING(element))
       ChangeElement(x, y);
+#endif
     else if (element == EL_EXPLOSION)
       ;        /* drawing of correct explosion animation is handled separately */
-    else if (IS_ANIMATED(graphic))
+    else if (IS_ANIMATED(graphic) && !IS_AUTO_CHANGING(element))
       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
+#if 1
+    /* this may take place after moving, therefore element may have changed */
+    if (IS_AUTO_CHANGING(Feld[x][y]))
+      ChangeElement(x, y);
+#endif
+
     if (IS_BELT_ACTIVE(element))
       PlaySoundLevelAction(x, y, ACTION_ACTIVE);
 
@@ -5152,24 +5323,6 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
   if (!player->active || (!dx && !dy))
     return FALSE;
 
-#if 0
-  {
-    static boolean done = FALSE;
-    static old_count = -1;
-
-    if (FrameCounter < old_count)
-      done = FALSE;
-
-    if (FrameCounter < 100)
-    {
-      printf("::: wanna move [%d] [%d]\n",
-            FrameCounter, player->push_delay_value);
-      done = TRUE;
-      old_count = FrameCounter;
-    }
-  }
-#endif
-
 #if 0
   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
       !tape.playing)
@@ -5756,24 +5909,6 @@ int DigField(struct PlayerInfo *player,
                        dy == +1 ? MV_DOWN : MV_NO_MOVING);
   int element;
 
-#if 0
-  {
-    static boolean done = FALSE;
-
-    if (FrameCounter < 10)
-      done = FALSE;
-
-    if (!done &&
-       real_dx == -1 &&
-       FrameCounter > 10)
-    {
-      printf("::: wanna move left [%d] [%d]\n",
-            FrameCounter, player->push_delay_value);
-      done = TRUE;
-    }
-  }
-#endif
-
   if (player->MovPos == 0)
   {
     player->is_digging = FALSE;
@@ -5787,6 +5922,7 @@ int DigField(struct PlayerInfo *player,
   {
     player->Switching = FALSE;
     player->push_delay = 0;
+
     return MF_NO_ACTION;
   }
 
@@ -5831,6 +5967,12 @@ int DigField(struct PlayerInfo *player,
 
   element = Feld[x][y];
 
+#if 1
+  if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
+      game.engine_version >= VERSION_IDENT(2,2,0))
+    return MF_NO_ACTION;
+#endif
+
   switch (element)
   {
     case EL_EMPTY:
@@ -6446,6 +6588,7 @@ int DigField(struct PlayerInfo *player,
       break;
 
     default:
+
       if (IS_WALKABLE(element))
       {
        break;
@@ -6456,7 +6599,8 @@ int DigField(struct PlayerInfo *player,
 #if 1
        if (mode != DF_SNAP)
        {
-         GfxElement[x][y] = element;
+         GfxElement[x][y] =
+           (CAN_BE_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
          player->is_digging = TRUE;
        }
 #endif
@@ -6528,6 +6672,9 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
   int jx = player->jx, jy = player->jy;
   int x = jx + dx, y = jy + dy;
 
+  if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0))
+    return FALSE;
+
   if (!player->active || !IN_LEV_FIELD(x, y))
     return FALSE;
 
@@ -6558,7 +6705,7 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
                    dy < 0 ? MV_UP :
                    dy > 0 ? MV_DOWN :  MV_NO_MOVING);
 
-  if (!DigField(player, x, y, 0, 0, DF_SNAP))
+  if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
     return FALSE;
 
   player->snapped = TRUE;