rnd-20010715-1-src
[rocksndiamonds.git] / src / game.c
index 65fff98e2ae17ea72b48b3aa81b902dbcf7aacab..37608e5d5152f84946ad85f2d1c5e92a344d5606 100644 (file)
@@ -1278,7 +1278,7 @@ void CheckDynamite(int x, int y)
 void Explode(int ex, int ey, int phase, int mode)
 {
   int x, y;
-  int num_phase = 9, delay = 2;
+  int num_phase = 9, delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
   int last_phase = num_phase * delay;
   int half_phase = (num_phase / 2) * delay;
   int first_phase_after_start = EX_PHASE_START + 1;
@@ -1463,6 +1463,9 @@ void Explode(int ex, int ey, int phase, int mode)
     if (CAN_MOVE(element) || COULD_MOVE(element))
       InitMovDir(x, y);
     DrawLevelField(x, y);
+
+    if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
+      StorePlayer[x][y] = 0;
   }
   else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
   {
@@ -1476,7 +1479,15 @@ void Explode(int ex, int ey, int phase, int mode)
     if (phase == delay)
       ErdreichAnbroeckeln(SCREENX(x), SCREENY(y));
 
-    DrawGraphic(SCREENX(x), SCREENY(y), graphic + (phase / delay - 1));
+    graphic += (phase / delay - 1);
+
+    if (IS_PFORTE(Store[x][y]))
+    {
+      DrawLevelElement(x, y, Store[x][y]);
+      DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic);
+    }
+    else
+      DrawGraphic(SCREENX(x), SCREENY(y), graphic);
   }
 }
 
@@ -1886,6 +1897,13 @@ void Impact(int x, int y)
        return;
       }
     }
+    else if ((element == EL_SP_INFOTRON || element == EL_SP_ZONK) &&
+            (smashed == EL_SP_SNIKSNAK || smashed == EL_SP_ELECTRON ||
+             smashed == EL_SP_DISK_ORANGE))
+    {
+      Bang(x, y+1);
+      return;
+    }
     else if (element == EL_FELSBROCKEN ||
             element == EL_SP_ZONK ||
             element == EL_BD_ROCK)
@@ -2049,7 +2067,7 @@ void TurnRound(int x, int y)
 
   if (element == EL_KAEFER || element == EL_BUTTERFLY)
   {
-    TestIfBadThingHitsOtherBadThing(x, y);
+    TestIfBadThingTouchesOtherBadThing(x, y);
 
     if (IN_LEV_FIELD(right_x, right_y) &&
        IS_FREE(right_x, right_y))
@@ -2066,7 +2084,7 @@ void TurnRound(int x, int y)
   else if (element == EL_FLIEGER || element == EL_FIREFLY ||
           element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
   {
-    TestIfBadThingHitsOtherBadThing(x, y);
+    TestIfBadThingTouchesOtherBadThing(x, y);
 
     if (IN_LEV_FIELD(left_x, left_y) &&
        IS_FREE(left_x, left_y))
@@ -2730,7 +2748,7 @@ void StartMoving(int x, int y)
     {
 
 #if 1
-      TestIfBadThingHitsHero(x, y);
+      TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
       return;
 #else
       /* enemy got the player */
@@ -2920,7 +2938,7 @@ void StartMoving(int x, int y)
        DrawGraphicAnimation(x, y, GFX2_SP_ELECTRON, 8, 2, ANIM_NORMAL);
 
       if (DONT_TOUCH(element))
-       TestIfBadThingHitsHero(x, y);
+       TestIfBadThingTouchesHero(x, y);
 
       return;
     }
@@ -3062,12 +3080,12 @@ void ContinueMoving(int x, int y)
 
     if (DONT_TOUCH(element))   /* object may be nasty to player or others */
     {
-      TestIfBadThingHitsHero(newx, newy);
-      TestIfBadThingHitsFriend(newx, newy);
-      TestIfBadThingHitsOtherBadThing(newx, newy);
+      TestIfBadThingTouchesHero(newx, newy);
+      TestIfBadThingTouchesFriend(newx, newy);
+      TestIfBadThingTouchesOtherBadThing(newx, newy);
     }
     else if (element == EL_PINGUIN)
-      TestIfFriendHitsBadThing(newx, newy);
+      TestIfFriendTouchesBadThing(newx, newy);
 
     if (CAN_SMASH(element) && direction == MV_DOWN &&
        (newy == lev_fieldy-1 || !IS_FREE(x, newy+1)))
@@ -3437,14 +3455,14 @@ void AmoebeAbleger(int ax, int ay)
   if (element != EL_AMOEBE_NASS || neway < ay || !IS_FREE(newax, neway) ||
       (neway == lev_fieldy - 1 && newax != ax))
   {
-    Feld[newax][neway] = EL_AMOEBING;
+    Feld[newax][neway] = EL_AMOEBING;  /* simple growth of new amoeba tile */
     Store[newax][neway] = element;
   }
   else if (neway == ay)
-    Feld[newax][neway] = EL_TROPFEN;
+    Feld[newax][neway] = EL_TROPFEN;   /* drop left or right from amoeba */
   else
   {
-    InitMovingField(ax, ay, MV_DOWN);
+    InitMovingField(ax, ay, MV_DOWN);  /* drop dripping out of amoeba */
     Feld[ax][ay] = EL_AMOEBA_DRIPPING;
     Store[ax][ay] = EL_TROPFEN;
     ContinueMoving(ax, ay);
@@ -4232,6 +4250,9 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
     SnapField(player, 0, 0);
     CheckGravityMovement(player);
 
+    if (player->MovPos == 0)   /* needed for tape.playing */
+      player->last_move_dir = MV_NO_MOVING;
+
     if (++player->frame_reset_delay > player->move_delay_value)
       player->Frame = 0;
   }
@@ -4271,6 +4292,51 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
   }
 }
 
+static void sleep_milliseconds_x(unsigned long milliseconds_delay)
+{
+  boolean do_busy_waiting = (milliseconds_delay < 5 ? TRUE : FALSE);
+
+#if defined(PLATFORM_MSDOS)
+  /* don't use select() to perform waiting operations under DOS/Windows
+     environment; always use a busy loop for waiting instead */
+  do_busy_waiting = TRUE;
+#endif
+
+
+
+  do_busy_waiting = TRUE;
+
+
+
+  if (do_busy_waiting)
+  {
+    /* we want to wait only a few ms -- if we assume that we have a
+       kernel timer resolution of 10 ms, we would wait far to long;
+       therefore it's better to do a short interval of busy waiting
+       to get our sleeping time more accurate */
+
+    unsigned long base_counter = Counter(), actual_counter = Counter();
+
+    while (actual_counter < base_counter + milliseconds_delay &&
+          actual_counter >= base_counter)
+      actual_counter = Counter();
+  }
+  else
+  {
+#if defined(TARGET_SDL)
+    SDL_Delay(milliseconds_delay);
+#else
+    struct timeval delay;
+
+    delay.tv_sec  = milliseconds_delay / 1000;
+    delay.tv_usec = 1000 * (milliseconds_delay % 1000);
+
+    if (select(0, NULL, NULL, NULL, &delay) != 0)
+      Error(ERR_WARN, "sleep_milliseconds(): select() failed");
+#endif
+  }
+}
+
 void GameActions()
 {
   static unsigned long action_delay = 0;
@@ -4288,7 +4354,45 @@ void GameActions()
 
   /* ---------- main game synchronization point ---------- */
 
-  WaitUntilDelayReached(&action_delay, action_delay_value);
+
+
+#if 1
+    WaitUntilDelayReached(&action_delay, action_delay_value);
+#else
+  {
+    unsigned long count1 = SDL_GetTicks(), count2, current_ms, test;
+
+    /*
+    WaitUntilDelayReached(&action_delay, action_delay_value);
+    */
+
+    /*
+    SDL_Delay(20);
+    */
+
+    /*
+    sleep_milliseconds_x(20);
+    */
+
+    current_ms = SDL_GetTicks();
+    test = -1;
+    while (current_ms < count1 + 20)
+    {
+      current_ms = SDL_GetTicks();             /* busy wait! */
+
+      if (test != current_ms)
+      {
+       Error(ERR_RETURN, "current_ms == %ld", current_ms);
+       test = current_ms;
+      }
+    }
+
+    count2 = SDL_GetTicks();
+    Error(ERR_RETURN, "delay == %ld", count2 - count1);
+  }
+#endif
+
+
 
   if (network_playing && !network_player_action_received)
   {
@@ -4436,8 +4540,10 @@ void GameActions()
       AmoebeWaechst(x, y);
     else if (element == EL_DEAMOEBING)
       AmoebeSchrumpft(x, y);
+#if 0
     else if (IS_AMOEBALIVE(element))
       AmoebeAbleger(x, y);
+#endif
     else if (element == EL_LIFE || element == EL_LIFE_ASYNC)
       Life(x, y);
     else if (element == EL_ABLENK_EIN)
@@ -4521,6 +4627,44 @@ void GameActions()
     }
   }
 
+#if 1
+  /* new experimental amoeba growth stuff*/
+#if 1
+  if (!(FrameCounter % 8))
+#endif
+  {
+    static unsigned long random = 1684108901;
+
+    for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
+    {
+#if 0
+      x = (random >> 10) % lev_fieldx;
+      y = (random >> 20) % lev_fieldy;
+#else
+      x = RND(lev_fieldx);
+      y = RND(lev_fieldy);
+#endif
+      element = Feld[x][y];
+
+      if (!IS_PLAYER(x,y) &&
+         (element == EL_LEERRAUM ||
+          element == EL_ERDREICH ||
+          element == EL_MORAST_LEER ||
+          element == EL_BLURB_LEFT ||
+          element == EL_BLURB_RIGHT))
+      {
+       if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBE_NASS) ||
+           (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBE_NASS) ||
+           (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBE_NASS) ||
+           (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBE_NASS))
+         Feld[x][y] = EL_TROPFEN;
+      }
+
+      random = random * 129 + 1;
+    }
+  }
+#endif
+
 #if 0
   if (game.explosions_delayed)
 #endif
@@ -4798,7 +4942,7 @@ boolean MoveFigureOneStep(struct PlayerInfo *player,
       BuryHero(player);
     }
     else
-      TestIfBadThingHitsHero(new_jx, new_jy);
+      TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
 
     return MF_MOVING;
   }
@@ -4970,7 +5114,7 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
     player->last_move_dir = MV_NO_MOVING;
   }
 
-  TestIfHeroHitsBadThing(jx, jy);
+  TestIfHeroTouchesBadThing(jx, jy);
 
   if (!player->active)
     RemoveHero(player);
@@ -5065,7 +5209,7 @@ void ScrollScreen(struct PlayerInfo *player, int mode)
     ScreenMovDir = MV_NO_MOVING;
 }
 
-void TestIfGoodThingHitsBadThing(int goodx, int goody)
+void TestIfGoodThingHitsBadThing(int goodx, int goody, int move_dir)
 {
   int i, killx = goodx, killy = goody;
   static int xy[4][2] =
@@ -5075,7 +5219,7 @@ void TestIfGoodThingHitsBadThing(int goodx, int goody)
     { +1, 0 },
     { 0, +1 }
   };
-  static int harmless[4] =
+  static int xy_dir[4] =
   {
     MV_UP,
     MV_LEFT,
@@ -5098,11 +5242,12 @@ void TestIfGoodThingHitsBadThing(int goodx, int goody)
     element = MovingOrBlocked2ElementIfNotLeaving(x, y);
 #endif
 
-    if (DONT_TOUCH(element))
+    /* 1st case: good thing is moving towards DONT_GO_TO style bad thing;
+       2nd case: DONT_TOUCH style bad thing does not move away from good thing
+    */
+    if ((DONT_GO_TO(element) && move_dir == xy_dir[i]) ||
+       (DONT_TOUCH(element) && MovDir[x][y] != xy_dir[i]))
     {
-      if (MovDir[x][y] == harmless[i])
-       continue;
-
       killx = x;
       killy = y;
       break;
@@ -5125,9 +5270,10 @@ void TestIfGoodThingHitsBadThing(int goodx, int goody)
   }
 }
 
-void TestIfBadThingHitsGoodThing(int badx, int bady)
+void TestIfBadThingHitsGoodThing(int badx, int bady, int move_dir)
 {
   int i, killx = badx, killy = bady;
+  int bad_element = Feld[badx][bady];
   static int xy[4][2] =
   {
     { 0, -1 },
@@ -5135,7 +5281,7 @@ void TestIfBadThingHitsGoodThing(int badx, int bady)
     { +1, 0 },
     { 0, +1 }
   };
-  static int harmless[4] =
+  static int xy_dir[4] =
   {
     MV_UP,
     MV_LEFT,
@@ -5143,7 +5289,7 @@ void TestIfBadThingHitsGoodThing(int badx, int bady)
     MV_DOWN
   };
 
-  if (Feld[badx][bady] == EL_EXPLODING)        /* skip just exploding bad things */
+  if (bad_element == EL_EXPLODING)     /* skip just exploding bad things */
     return;
 
   for (i=0; i<4; i++)
@@ -5157,20 +5303,31 @@ void TestIfBadThingHitsGoodThing(int badx, int bady)
 
     element = Feld[x][y];
 
-    if (IS_PLAYER(x, y))
-    {
-      killx = x;
-      killy = y;
-      break;
-    }
-    else if (element == EL_PINGUIN)
+    /* 1st case: good thing is moving towards DONT_GO_TO style bad thing;
+       2nd case: DONT_TOUCH style bad thing does not move away from good thing
+    */
+    if ((DONT_GO_TO(bad_element) && move_dir == xy_dir[i]) ||
+       (DONT_TOUCH(bad_element) && MovDir[x][y] != xy_dir[i]))
     {
-      if (MovDir[x][y] == harmless[i] && IS_MOVING(x, y))
-       continue;
+      /* good thing is player or penguin that does not move away */
+      if (IS_PLAYER(x, y))
+      {
+       struct PlayerInfo *player = PLAYERINFO(x, y);
 
-      killx = x;
-      killy = y;
-      break;
+       if (bad_element == EL_ROBOT && player->last_move_dir)
+         continue;     /* robot does not kill player if he moves */
+
+       killx = x;
+       killy = y;
+       break;
+      }
+      else if (element == EL_PINGUIN &&
+              (MovDir[x][y] != xy_dir[i] || !IS_MOVING(x, y)))
+      {
+       killx = x;
+       killy = y;
+       break;
+      }
     }
   }
 
@@ -5180,6 +5337,24 @@ void TestIfBadThingHitsGoodThing(int badx, int bady)
     {
       struct PlayerInfo *player = PLAYERINFO(killx, killy);
 
+#if 0
+      int dir = player->MovDir;
+      int newx = player->jx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
+      int newy = player->jy + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
+
+      if (Feld[badx][bady] == EL_ROBOT && player->last_move_dir &&
+         newx != badx && newy != bady)
+       ;       /* robot does not kill player if he moves */
+      else
+       printf("-> %d\n", player->MovDir);
+
+      if (Feld[badx][bady] == EL_ROBOT && player->last_move_dir &&
+         newx != badx && newy != bady)
+       ;       /* robot does not kill player if he moves */
+      else
+       ;
+#endif
+
       if (player->shield_active_time_left > 0)
        Bang(badx, bady);
       else if (!PLAYER_PROTECTED(killx, killy))
@@ -5190,27 +5365,37 @@ void TestIfBadThingHitsGoodThing(int badx, int bady)
   }
 }
 
-void TestIfHeroHitsBadThing(int x, int y)
+void TestIfHeroTouchesBadThing(int x, int y)
 {
-  TestIfGoodThingHitsBadThing(x, y);
+  TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
 }
 
-void TestIfBadThingHitsHero(int x, int y)
+void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
 {
-  TestIfBadThingHitsGoodThing(x, y);
+  TestIfGoodThingHitsBadThing(x, y, move_dir);
 }
 
-void TestIfFriendHitsBadThing(int x, int y)
+void TestIfBadThingTouchesHero(int x, int y)
 {
-  TestIfGoodThingHitsBadThing(x, y);
+  TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
 }
 
-void TestIfBadThingHitsFriend(int x, int y)
+void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
 {
-  TestIfBadThingHitsGoodThing(x, y);
+  TestIfBadThingHitsGoodThing(x, y, move_dir);
 }
 
-void TestIfBadThingHitsOtherBadThing(int badx, int bady)
+void TestIfFriendTouchesBadThing(int x, int y)
+{
+  TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
+}
+
+void TestIfBadThingTouchesFriend(int x, int y)
+{
+  TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
+}
+
+void TestIfBadThingTouchesOtherBadThing(int badx, int bady)
 {
   int i, killx = badx, killy = bady;
   static int xy[4][2] =
@@ -5290,7 +5475,8 @@ void RemoveHero(struct PlayerInfo *player)
   player->present = FALSE;
   player->active = FALSE;
 
-  StorePlayer[jx][jy] = 0;
+  if (!ExplodeField[jx][jy])
+    StorePlayer[jx][jy] = 0;
 
   for (i=0; i<MAX_PLAYERS; i++)
     if (stored_player[i].active)